Skip to main content
Planned design guide. Fictional voice checks can be tested, but this full workflow requires file uploads and live sessions, which are not served by the current sandbox. No provider voice is provisioned by these examples. Start with the runnable quickstart and sandbox support matrix.
These examples use Maya Okafor’s fictional speaking-voice license for personal, non-commercial use. They describe two target workflows: text-to-speech lines under one check, and a live session with renewals, samples and termination. The sandbox can exercise fixture checks, but the complete upload and live-session workflows are not available.

Speech lines

1

Export your test key

2

Check the first line with reuse

A text-to-speech product speaks many short lines for one user. Ask for reuse so one decision covers up to 50 lines over 15 minutes, and pass on_escalate: "deny" so your user never waits on a person.
The authorization covers 50 generations for this end user until it expires:
For speech, apply generation_instructions to the words: do not send a line that breaks one.
3

Speak and submit each line

Send each line to your voice provider with authorization.token, then submit the audio. Short lines can be sent inline as base64 data. The sandbox limit is 7 MiB decoded bytes in a 10 MiB JSON body; the protocol’s 10 MB inline limit is a future target. Under reuse, leave out candidate_index: each line is its own generation and counts once against max_generations. Send the line’s words as transcript; later lines are checked on their own audio and words.
Speech up to 60 seconds returns its decision in the response:
Embed c2pa in the audio file’s C2PA manifest, write the disclosure text into its metadata, and play it. When generations_used reaches 50 or the authorization expires, run a new check: a generation past either returns 409 authorization_consumed or 409 check_expired.
4

See what an interactive deny looks like

Run the check again with rfl_test_escalate anywhere in the script. Without on_escalate this would escalate to a person. With on_escalate: "deny" it returns at once:
Tell your user the line cannot be spoken, using reasons[].sentence. retry_with_hold means the same request with on_escalate: "hold" would go to a reviewer instead.

Live session

A session runs one check at the start, then keeps a short-lived token alive. The token expires 5 minutes after issue; you renew it every 60 seconds, submit samples of what the voice says, and stop within one second if Reflections terminates the session.
1

Create a session

If status is denied, no token is issued and reasons say why. Otherwise show the disclosure on screen, add the instructions to your voice model’s system prompt, and give the provider the session token.
2

Connect to the stream

The stream pushes session.terminated the moment it happens, so connect before any audio plays. From a client, authenticate with the session token as a WebSocket subprotocol; from a server, you can send your secret key in Authorization instead. Send a ping every 25 seconds when nothing else is flowing, inside the 30-second limit.
The first message is session.ready. session.warning arrives before something ends the session: renewal_due, sample_overdue, license_ending or max_duration, each with an act_by time.
3

Renew every 60 seconds

Each renewal returns a fresh token. Pass it to your voice provider. A renewal fails with 409 session_not_active once the license is revoked, suspended or ends, or the session was terminated: stop output at once. On a network error, 429 or 5xx, keep retrying, and stop when the current token expires if no renewal succeeded.
4

Submit samples

sampling sets the minimum: an audio window of at most 15 seconds for every 15 seconds of speech, and the words of every turn. Audio goes as base64 WAV or Ogg, up to 2 MB per sample; 16 kHz mono WAV keeps a 15-second window well under that.
5

Handle termination

Submit a transcript sample whose text contains rfl_test_terminate. The sample is denied:
and the stream pushes:
Stop output within one second of whichever arrives first. Do not renew again. The session.terminated webhook reports the same termination; read data.object.termination. To test a license ending instead, suspend the license with the test helper and action: "suspend": the stream carries license_suspended. Resume it with action: "resume" so the fixture keeps working.
Between samples, up to 15 seconds of speech is unchecked, and a revoked license can keep a session’s current token valid for the rest of its 5 minutes. Stopping on the stream message is what keeps both bounds short.
6

End the session

End every session when the call is over, including one Reflections terminated.
This session was terminated, so it keeps that status and its termination. A session your user ends returns status: "ended".
7

Report usage

The usage_report obligations ask for seconds within a day. Report the speech lines and the session together: each record names the generation or session it settles.
For the session, quantity is the seconds of speech it generated in her voice. Each record is accepted or rejected on its own:
A rejected record carries rejection.code, such as exceeds_observed when you report more than Reflections recorded. Send a correction as a new record with correction_of.

Next

Voice platforms

Provider-held voices, scripts and sessions in production.

Real-time sessions

Renewal, sampling, termination and the one-second stop.

Session stream

Every message on the WebSocket.

Reporting usage

Batching, units, reconciliation and corrections.