Developer production · Server APIs

Conversations API · Integration guide

Build a complete practice conversation.

Prepare a room, generate its opening, continue with learner turns and save the closeout.

Add Conversation practice to your workspace.

Sign in, review the current terms and rate, then add this API. Fund live usage and create a scoped server key in the console.

1. Select a room and its participants

Read GET /v1/conversation/catalog with your server key, assignment and end-user headers. Use the returned room, persona and coaching-lens IDs. Room-specific participant limits, offered modes and actual user consent apply.

Send this body to POST /v1/conversation/sessions. Empty setup is free. Save the returned session ID and keep the same operation ID for an exact retry.

{
  "setup": {
    "kind": "room",
    "room_id": "S01",
    "mode": "guided",
    "participants": [
      {
        "persona_id": "jon_guarded_report"
      }
    ]
  },
  "controls": {
    "cast_language": "en-US",
    "cast_speech": "natural"
  }
}

For open practice, use kind: "open", mode: "open_practice", a supported participant list and cast_shape: "one_on_one" or "room". An optional topic is limited to 240 characters.

// Run on your server. Keep the key out of browser code and logs.
const base = process.env.FOREVERLEARNING_API_BASE; // assigned HTTPS /v1 host
const headers = {
  Authorization: 'Bearer ' + process.env.FOREVERLEARNING_API_KEY,
  'X-Assignment-Id': process.env.FOREVERLEARNING_ASSIGNMENT_ID,
  'X-User-Id': 'pseudonymous-learner-42',
  'Content-Type': 'application/json',
  'Idempotency-Key': savedSetupOperationId,
};
const response = await fetch(base + '/conversation/sessions', {
  method: 'POST', headers, body: JSON.stringify(setup), redirect: 'error',
});
if (!response.ok) throw new Error('Setup failed: ' + response.status);
const session = await response.json(); // persist session.session_id
// For POST /chat, use a NEW saved operation ID and read text/event-stream.
// On an uncertain outcome, retry the ORIGINAL body with its ORIGINAL ID.

A room’s handling comes from its authored tier, listed in the catalog as tiers and default_tier. For a gentler start, choose a gentle room or open practice with high_care: true; room setups refuse high_care. During a session, the learner’s soften consent choice switches any room to gentle handling. Consent is one decision per session: repeating the same decision returns the saved result, and a different second decision returns 409. An empty consent object grants nothing.

2. Open the room and continue

Send the opening to POST /v1/chat with a new saved idempotency key. An opening is a billable generated turn; it has no learner message.

{
  "session_id": "SAVED_SESSION_ID",
  "conversation": {
    "operation": "opening"
  }
}

Continue on the same session with the learner’s words. Five controls can change: cast language, speech style, challenge, emotional heat and coaching lens. Omitted controls keep their last saved value. Coach’s ordinary language is English. Cast language codes are en-US, es-US, fr-FR, fr-CA, de-DE, it-IT, pt-BR, ja-JP, ko-KR and cmn-CN.

{
  "session_id": "SAVED_SESSION_ID",
  "prompt": "What would help us agree on the next step?",
  "conversation": {
    "operation": "turn",
    "controls": {
      "challenge": "supportive"
    }
  }
}

Parse server-sent events. Content is provisional until the saved conversation_turn event; done supplies its text/audio receipt. Display safety status separately. A disconnect or error does not prove that a charge was refunded.

Emotional heat is calm, tense or heated when you prepare a room. raw, with no filter, is chosen inside the conversation, on the opening or any turn. Characters say exactly what they think, harshly and in character, and may walk out; the safety limits still apply. Offer raw only as your user’s own choice.

Retry the exact request with its original idempotency key, or use GET /v1/requests/REQUEST_ID to check it. Do not submit a fresh purchase while the earlier outcome is uncertain. Reusing a key with a different body returns 422. A turn that failed and was not charged is final for its key: the same key returns 409 REQUEST_FAILED, so send it again with a new key. If an opening fails, its conversation closes without charge; create a new session. If a generated reply fails validation before anything is shown, the service regenerates it once; that turn takes longer and is still charged once.

Close with POST /v1/sessions/SESSION_ID/end and a saved UUID operation_id. The generated closeout bills when committed. A room may also end naturally.

Show how each character responds (optional)

Every saved turn includes characters, one entry per cast member, so your app can change a portrait or show an empty seat. Using them is optional; nothing else depends on them.

  • presence is present, stepped_away or left. Characters can step out and return, and someone pushed too far can leave for the rest of the session.
  • expression_group is the simpler set: steady_and_engaged, more_open, more_careful, more_strained or participating_less. Five portraits per character are enough.
  • expression is the detailed state, one of 16, for finer portraits. Each belongs to one group, listed in the OpenAPI.

Both are null when no expression was reported, including during a safety moment: show the neutral portrait. These are simulated cast responses, not a reading of a real person. Show them as faces or cues rather than labels, so your users practice reading people.

3. Return to saved practice

Read the session, paged playback, debrief and cross-session insights under /v1/conversation. These reads make no new generation purchase. Use the returned cursor to continue playback. Debrief becomes available when the practice closes.

Every saved turn includes stable speaker identities, its original controls and explicit structure availability. When attribution is unavailable, show the saved text without inventing a speaker. Debriefs describe the recorded practice, its conditions and the moments that mattered. They are not diagnoses.

4. Add optional whole-turn audio

The audio contract accepts a saved session and turn, never arbitrary text. POST /v1/conversation/voice with operation: "generate" creates one generation covering participating cast and Coach. A retry retrieves the same saved or pending generation.

A 202 response means the generation is pending. Save its audio_id. To continue, send POST /v1/conversation/audio/AUDIO_ID/continue with the same user and assignment, using a key with chat and sessions:read scopes. Respect Retry-After. Each request advances at most one segment of that existing purchase. Stop on a terminal status; a failed generation has no audio charge.

Find existing generations with GET /v1/conversation/sessions/SESSION_ID/audio, following its pagination cursor. GET /v1/conversation/audio/AUDIO_ID reads current status. Neither read creates or continues speech generation.

Fetch saved segment URLs through your authenticated server. Audio expires 24 hours after creation; playback does not extend that time or synthesize again. A segment fetched after expiry returns 410 AUDIO_EXPIRED. Use explicit regenerate, a new operation key and replaces_audio_id for a new purchase.

5. Provide export and deletion

Export paged saved turns, setup, consent, preferences, practice summaries and audio-generation metadata with POST /v1/conversation/data/export. Export requires its dedicated key scope. Download available recordings through their authenticated audio routes before expiry. Session deletion also removes session-derived memory and saved audio. End-user erasure covers the user’s practice data; content-free financial records remain.

Deletion immediately revokes content access. A 202 receipt with status: "pending" means private audio cleanup is not yet confirmed. Retry the same deletion with its original operation_id until the receipt says completed.

Text retention is 180 days since activity in live mode and 30 days in test mode. Reads do not renew retention. Audio always uses its separate 24-hour creation clock.

Contract downloads

OpenAPI · Request, event and resource schemas

Runnable server examples: TypeScript · Python · cURL · Complete workflow and recovery guide.

Each example sends one explicit request from a saved intent. They have offline tests; hosted access and any paid test still require an enabled assignment and an approved budget.

The v1 request envelope supports 1–5 participants, 10,000 learner characters per turn and at most 100 rows per read. Your workspace shows its current rate and test allowance. Request and concurrency limits apply across keys; a new key does not reset the workspace allowance.

Keep the assigned release and returned request ID when contacting developer support. Saved sessions retain their original release and rate.

One price for the whole turn.

Conversations API pricing in USD
UsagePriceIncludes
Text$0.03 per billable turnParticipating cast and Coach
Optional audio+$0.05 per spoken turnAll participating voices and spoken coaching
Text with audio$0.08 totalOne text turn and one saved audio generation

Ten billable text turns cost $0.30. With newly generated audio for each turn, the total is $0.80. The number of participants does not add a per-speaker fee.

Creating an empty room and reading saved conversations, debriefs and insights do not add a generation charge. Text opening and ordinary turns bill at dispatch, with the existing refund for an observed failure before content. Generated closeout bills when saved.

Audio bills once the complete turn audio is successfully saved. Replay is free for 24 hours from creation, with earlier removal when the session or user data is deleted. Regeneration is an explicit new purchase. Audio requires available balance.

Review the rate and workload allowance in your workspace before adding the API. These prices do not change any existing workspace assignment.