RTFC API

Build your own fact-checking app

The full flow, end to end: provision a user, take a video or some text, stream results back, and account for the cost.

Two things your backend needs: a provision-scoped token, and somewhere to store each user's token encrypted.

bash
export RTFC=https://api.faktolumo.eu/v1
export PROV=rtfc_your_provisioner_token

1. A user signs up

bash
USER=$(curl -sX POST $RTFC/provision/user \
  -H "X-Auth-Token: $PROV" -H 'Content-Type: application/json' -d '{}')
USER_TOKEN=$(echo "$USER" | jq -r .token)
USER_ID=$(echo "$USER" | jq -r .user_id)

Store both. Everything below uses $USER_TOKEN - the provisioner token is not touched again until you delete the account.

2. They share a video

Check it before spending anything. The probe is anonymous and creates nothing:

bash
PROBE=$(curl -sX POST $RTFC/media/probe \
  -H "X-Auth-Token: $USER_TOKEN" -H 'Content-Type: application/json' \
  -d '{"url": "https://www.youtube.com/watch?v=…"}'
json
{"platform": "youtube", "external_id": "…", "title": "…", "duration_s": 812,
 "is_public": true, "reject_reason": "", "existing_debate_id": null}

Two fields decide what happens next:

bash
curl -sX POST $RTFC/media/youtube/$EXTERNAL_ID/claim-access -H "X-Auth-Token: $USER_TOKEN"

3. Otherwise, ingest it

bash
DEBATE=$(curl -sX POST $RTFC/debates \
  -H "X-Auth-Token: $USER_TOKEN" -H 'Content-Type: application/json' \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"title": "…", "research_modes": ["two_step","decompose"],
       "language": "auto", "country": "us", "elector_enabled": true}' | jq -r .id)

curl -sX POST $RTFC/debates/$DEBATE/upload-media \
  -H "X-Auth-Token: $USER_TOKEN" -H 'Content-Type: application/json' \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"url": "https://www.youtube.com/watch?v=…", "detector_mode": "batch"}'

elector_enabled: true lets an advisory judge select the agreement panel. Disagreements are adjudicated into one claim verdict. language: "auto" handles code-switching.

Set detection_filter_tier on POST /v1/debates (or PATCH /v1/debates/{id}): 1 requires a model accuracy score ≥ 75%, 2 ≥ 80%, and 3 ≥ 85%. The default is 3, including debates created automatically for followed accounts and alert groups. The dashboard exposes these three tiers under New debate → Advanced settings → Detection filter strength. Both realtime and batch claim detection apply the threshold. The gateway uses the measured detector benchmark pass rate when available, otherwise its catalog quality score; models without a known score are excluded. Response time does not filter eligibility, and detection disables latency scoring and group speed ceilings. Research worker selection is unchanged. If no eligible model is available, detection retains the threshold instead of falling back to a lower score.

Watch the response for matched_existing: true: it means nothing was ingested and the returned debate_id is a pre-existing analysis you were granted access to.

bash
curl -s "$RTFC/debates/$DEBATE/transcript?limit=1" -H "X-Auth-Token: $USER_TOKEN" | jq .ingest_status
json
{"stage": "transcribing", "since": "2026-09-25T08:44:17Z",
 "provider": "elevenlabs/scribe_v2", "audio_seconds": 2512}

A video is transcribed in one call once it is downloaded, so no segment appears for several minutes. Poll ingest_status on the transcript endpoint to show progress meanwhile: stage goes downloading → queued → decoding → transcribing → done (or failed, with reason download_failed or stt_failed). since is when the current stage started; audio_seconds is the length being transcribed; fallback: true means the first speech-to-text provider failed and another one took over. The field is null for live sessions.

If you transcribe on-device instead

Skip the media call. Start a detector-only session and push text - no audio leaves the device and there is no transcription cost:

bash
curl -sX POST $RTFC/session/client-start -H "X-Auth-Token: $USER_TOKEN" \
  -H 'Content-Type: application/json' -d "{\"debate_id\":\"$DEBATE\",\"language\":\"en\"}"

curl -sX POST $RTFC/debates/$DEBATE/transcript-segment -H "X-Auth-Token: $USER_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"segments":[{"text":"…","language":"en","segment_id":"your-uuid"}]}'

Passing your own segment_id is worth doing: it comes back in each claim's source_segment_ids, so you can highlight exactly which line produced a claim.

4. Stream results to the user

Mint a short-lived stream token server-side and hand it to the client:

bash
STREAM=$(curl -sX POST $RTFC/stream/token -H "X-Auth-Token: $USER_TOKEN" | jq -r .stream_token)
js
const es = new EventSource(`${RTFC}/stream?token=${STREAM}&debate_id=${debateId}`);
es.onmessage = (m) => {
  const ev = JSON.parse(m.data);          // note: data line, no event: field
  if (ev.type === "ping") return;
  if (ev.type === "new_claim") addPendingCard(ev);
  if (ev.type === "result")   showVerdict(ev);
};

Events arrive as JSON on the data: line - RTFC never sets the SSE event: field. A ping every 20s keeps intermediaries from closing an idle connection. Tokens last 5 minutes; mint a new one and reconnect.

Prefer not to hold a connection? Use webhooks instead.

5. Show the answer

bash
curl -s "$RTFC/claims/$CLAIM/results?debate_id=$DEBATE&lang=en" -H "X-Auth-Token: $USER_TOKEN"

Pass ?debate_id= whenever you have it. For a claim shared with another debate it scopes source_segment_ids and media_offset_ms to yours. ?lang= translates on demand and caches, so re-reads are free.

media_offset_ms is where the claim occurs in the video - enough to sync verdicts to playback.

6. Account for the cost

bash
curl -s $RTFC/claims/$CLAIM/cost -H "X-Auth-Token: $USER_TOKEN"
curl -s $RTFC/debates/$DEBATE/transcription-cost -H "X-Auth-Token: $USER_TOKEN"
curl -s $RTFC/usage/me/summary -H "X-Auth-Token: $USER_TOKEN"

Costs are already marked up per your plan - debit your user 1:1 against total_cost_usd and you cannot drift from what you are charged.

A reused claim costs $0. Do not treat that as an error.

7. Pause and resume

If your user runs out of credit mid-video, stop the spend without losing progress:

bash
curl -sX POST $RTFC/debates/$DEBATE/pause  -H "X-Auth-Token: $USER_TOKEN"
curl -sX POST $RTFC/debates/$DEBATE/resume -H "X-Auth-Token: $USER_TOKEN"

Pause records an exact resume offset and holds already-detected claims rather than dropping them. Resume replays ingest from that offset and re-queues the held claims, oldest first. Resume is always explicit - nothing restarts on its own.

8. They ask to be forgotten

bash
curl -sX DELETE $RTFC/provision/user/$USER_ID -H "X-Auth-Token: $PROV"