Nah Mean · API Reference
Podcast · v4.0 draft 3
Data Model & Endpoints

Episodes

Sessions

The Podcast API: four entities, four endpoint groups, seven webhook events. Everything an episode says carries a claim marker, and every marker names who made it.

A live session is a record in waiting. The API treats them as one pipeline: a session ends, converts into an episode, and its markers carry over unchanged.

This is a design draft. The API is not live yet.

Conventions

AssumptionPodcast follows the platform's existing API conventions

Base path /v1/podcast, bearer-token auth, cursor pagination, JSON bodies, ISO 8601 UTC timestamps. This holds if the core API uses the same conventions. Check it against the UI & API documentation before implementation and correct this page where they differ.

HeuristicFour scopes, from least to most power

podcast:read lists and reads published content. podcast:write lets a listener raise a hand, tag a claim or set a reminder. podcast:host creates, edits and publishes episodes and sessions. podcast:moderate runs the queue and the floor during a live session.

HeuristicTags are a closed set

tag is one of fact, heuristic, assumption, caution. Anything else returns invalid_tag.

Data Model

episode
FieldTypeRule
idstringPrefix ep_. Immutable.
titlestringUp to 140 characters.
formatenumlong_take_audio, rebuttal, replay.
source_long_take_idstring, nullRequired for long_take_audio. For rebuttal it points at the piece being answered.
session_idstring, nullSet only when format is replay.
audio_urlstringSigned URL, expires after one hour.
duration_sintegerRead-only after upload processing.
transcript_statusenumpending, ready, failed.
audit_statusenumnot_requested, pending, complete, failed.
statusenumdraft, published, withdrawn.
published_attimestamp, nullNull until published.
session
FieldTypeRule
idstringPrefix se_.
titlestringUp to 140 characters.
kindenummoderated_debate, open_table, live_audit.
starts_attimestampMust be in the future when created.
planned_mininteger15 to 120.
host_idstringAccount id of the host.
seatsintegerListener capacity. Speakers are counted separately.
turn_secondsintegerDefaults to 90.
objection_minintegerMinutes reserved for the strongest objection. Defaults to 10.
recording_consentobjectMap of speaker id to pending, granted, declined.
statusenumSee the lifecycle below.
episode_idstring, nullSet when the session converts.
Session lifecycle
scheduled→live→ended→replay_ready

A session cannot move to live until every speaker's consent is granted. A session that ends without full consent stays ended and never converts.

claim_marker
FieldTypeRule
idstringPrefix mk_.
episode_idstringMarkers made live attach to the session first and move to the episode on conversion.
span_idstringPrefix sp_. Server-assigned. Markers that cover the same stretch of audio (50% overlap or more) share one span.
t_start_sintegerSeconds from the start of the audio.
t_end_sinteger, nullOptional. Must be greater than t_start_s.
tagenumClosed set of four.
textstringThe claim as spoken, up to 280 characters.
sourceenumauthor, auditor, listener. Set by the server, never by the client.
agreementenumsingle, agreed, disputed, resolved. Computed per span from author and auditor markers only. Listener markers never change it.
resolutionobject, nullSet by the host on a disputed span: decision (accepted_auditor, kept_author, noted), note, resolved_at. Both original markers stay visible.
queue_entry
FieldTypeRule
idstringPrefix qe_.
session_idstringForeign key to session.
user_idstringOne open entry per user per session.
claim_textstringWhat the person wants to say, up to 280 characters.
kindenumturn or objection. Objections feed the final slot.
statusenumwaiting, speaking, done, withdrawn.

Agreement Rule

HeuristicAgreement counts two voices: the author and the Auditor

On one span, the same tag from both means agreed. Different tags mean disputed. One source means single. Listener markers never move this field.

HeuristicListeners get a distribution, not a verdict

Each span reports how many distinct accounts chose each tag. One account has one vote per span and can change it. Distributions stay hidden until enough accounts have voted, so a handful of people never looks like a crowd.

AssumptionDivergence triggers a review, never a change

When at least 10 accounts have voted and 60% or more chose a tag different from the agreed one, the span gets listener_signal: "divergent" and the host is notified. Both numbers are starting values. They hold for a mid-sized audience and need calibrating against real episodes, since a small niche audience could clear 10 easily.

CautionBursts are quarantined

If more than 30% of a span's votes arrive within five minutes from accounts under seven days old, the span gets listener_signal: "suspect_burst". Its votes drop out of the distribution until a moderator clears it. These thresholds are also starting values, and they depend on the platform exposing account age.

HeuristicA dispute needs a human answer

The host resolves it by accepting the Auditor's tag, keeping their own, or adding a note. The resolution is recorded next to the two markers and never replaces them.

Endpoints

Webhooks

AssumptionPodcast events use the platform's existing webhook envelope

Same signing, retry and delivery rules as the core webhooks. Only the event types below are new.

EventFires whenPayload
episode.publishedA draft becomes public.episode
episode.audit_completedThe Argument Auditor finishes a run.episode, counts per tag, disputed count
marker.disagreement_flaggedTwo sources tag the same span differently.both markers
marker.listener_divergenceA span first meets the divergence rule, or is flagged suspect_burst.span, distribution, signal
session.startedA host starts a session.session
session.endedA session ends, with or without consent to publish.session, convertible flag
session.replay_readyConversion finishes and the draft episode exists.session, episode

Errors

AssumptionErrors share one body shape

Every error response is JSON with a machine-readable code and a human-readable message, for example { "code": "consent_required", "message": "Speaker u_34 has not granted consent." }. Clients should branch on code, never on message.

CodeHTTPMeaning
invalid_tag400Tag is outside the closed set.
consent_required403A speaker has not granted recording consent.
not_host403Caller lacks podcast:host on this resource.
session_not_live409Queue and floor actions only work while live.
not_disputed409Only a span marked disputed can be resolved.
queue_entry_exists409The user already has an open entry.
transcript_not_ready422Audit and publishing need a ready transcript.
rate_limited429Retry after the interval in Retry-After.

Node.js Quickstart

AssumptionA thin fetch client stands in for the SDK

These examples use Node 18 or later with ES modules ("type": "module" in package.json, or .mjs files) and nothing else to install. An official package would wrap the same calls. Its name is not decided, so none is used here.

HeuristicSet two environment variables first

NAHMEAN_API_URL is the API origin without a trailing slash. NAHMEAN_TOKEN is a bearer token with the scopes the call needs: podcast:write to join, raise a hand and tag, podcast:host to resolve.


  

The Essence

ONE PIPELINE from live room to published record. The API never lets a claim lose its source on the way.

CautionCounter-Argument Thread

Keeping listeners out of agreement protects the field from brigading, but it assumes the author and the Auditor are the reliable pair. They can agree and both be wrong, and an author who copies the Auditor's classifications would produce agreement that means nothing.

The thresholds (10 voters, 60% divergence, a 30% burst share, seven-day account age) are guesses until real episodes exist. If they are set too low, the divergence signal fires constantly and hosts learn to ignore it. If they are set too high, the signal never fires.

The stronger claim is that the rule should be tested the way Nah Mean tests arguments: run it on the first ten episodes, compare listener divergence against the disputes hosts resolve, and publish what it missed.

Changelog

VersionDateChange
v4.0 draft 3Oct 8, 2026Added the Node.js quickstart (client, join and queue, tag, resolve, webhooks), the shared error body shape, and span_id in marker examples and a response example for the queue endpoint.
v4.0 draft 2Oct 8, 2026Settled the agreement rule: author and Auditor only, listener distribution with a threshold, divergence and burst signals, host resolution. Added span_id, resolution, the resolve endpoint, one webhook event and the not_disputed error. Corrected the group count in the page intro.
v4.0 draftOct 8, 2026Added the Podcast section: episodes, sessions, claim markers, queue, six webhook events.
Fact verifiable claim
Heuristic reliable rule of thumb
Assumption holds under stated conditions
Caution contested or risk-bearing