{"openapi":"3.1.0","info":{"title":"Nuance API","version":"1","description":"The routes your server and your page or app call to run a conversation."},"servers":[{"url":"https://api.nuancelabs.ai"}],"tags":[{"name":"Server","description":"Called from your server with an API key (`nk_…`)."},{"name":"Browser","description":"Called by your page or app with a session token (`st_…`)."}],"paths":{"/session-tokens":{"post":{"description":"Mints a session token that your page or app can start one Nuance Live conversation with. Call it from your server with an API key, never from a browser. What you send here (the interface and its setup, the personality and your id for the person) is fixed for the conversation the token creates.","operationId":"create-a-session-token","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSessionTokenRequest"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionTokenResponse"}}},"description":"Successful Response"}},"summary":"Create a session token","tags":["Server"],"parameters":[],"security":[{"apiKey":[]}]}},"/conversation":{"post":{"operationId":"create-a-conversation","parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateConversationRequest"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConversationResponse"}}},"description":"Successful Response"}},"summary":"Create a conversation","tags":["Browser"],"description":"Creates a conversation with an unused session token, which spends it. The conversation runs with what the token was minted with.","security":[{"sessionToken":[]}]}},"/conversation/{conversation_id}":{"get":{"operationId":"get-a-conversation","parameters":[{"in":"path","name":"conversation_id","required":true,"schema":{"format":"uuid","type":"string"},"description":"The conversation's id."}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConversationResponse"}}},"description":"Successful Response"}},"summary":"Get a conversation","tags":["Browser"],"description":"Reads a conversation's current state.","security":[{"sessionToken":[]}]}},"/conversation/{conversation_id}/events":{"get":{"description":"The conversation's events after the id `after`, oldest first: that its AI persona is getting ready, the room to join, its time limit once the persona starts, and its end. Held open for up to 20 seconds while nothing happens; an empty list means ask again with the same `after`. An open request keeps the conversation alive, so keep one open from creation to the end.","operationId":"follow-a-conversation","parameters":[{"in":"path","name":"conversation_id","required":true,"schema":{"format":"uuid","type":"string"},"description":"The conversation's id."}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionEvents"}}},"description":"Successful Response"}},"summary":"Follow a conversation","tags":["Browser"],"security":[{"sessionToken":[]}]}},"/conversation/{conversation_id}/abandon":{"post":{"description":"Gives up on a conversation that has not started, freeing its place under your limit on conversations at once. A conversation that started or ended is left as it is.","operationId":"abandon-a-conversation","parameters":[{"in":"path","name":"conversation_id","required":true,"schema":{"format":"uuid","type":"string"},"description":"The conversation's id."}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueueState"}}},"description":"Successful Response"}},"summary":"Abandon a conversation","tags":["Browser"],"security":[{"sessionToken":[]}]}},"/conversation/{conversation_id}/transcript":{"get":{"description":"What the person and the AI persona said in a conversation, turn by turn, with each word's time. It is ready shortly after the conversation ends; until then the response is `null`. Reaches only your organization's conversations: another's answers 404.","operationId":"get-a-transcript","parameters":[{"in":"path","name":"conversation_id","required":true,"schema":{"format":"uuid","type":"string"},"description":"The conversation's id."}],"responses":{"200":{"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/TranscriptResponse"},{"type":"null"}]}}},"description":"Successful Response"}},"summary":"Get a transcript","tags":["Server"],"security":[{"apiKey":[]}]}},"/conversations/active":{"get":{"description":"Your organization's active conversations, ones still starting included, oldest first: the ones its limit on conversations at once counts. Each has its id, its status, when it was created and when the AI persona started it, so you can find one to end. Reaches only your organization's conversations.","operationId":"list-active-conversations","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActiveConversations"}}},"description":"Successful Response"}},"summary":"List active conversations","tags":["Server"],"parameters":[],"security":[{"apiKey":[]}]}},"/conversation/{conversation_id}/end":{"post":{"description":"Ends one of your organization's conversations the way it ends when the person hangs up, though for now the page's `session.closed` may name the reason `error` rather than `close_requested`. One that hasn't started ends at once; a running one ends within a few seconds, once the AI persona leaves, and its place counts until then. Answers with the conversation as it stands, so a running one can still say `connected`. One already over is left as it is. It has no body; send `Content-Length: 0`. Reaches only your organization's conversations: another's answers 404.","operationId":"end-a-conversation","parameters":[{"in":"path","name":"conversation_id","required":true,"schema":{"format":"uuid","type":"string"},"description":"The conversation's id."}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConversationState"}}},"description":"Successful Response"}},"summary":"End a conversation","tags":["Server"],"security":[{"apiKey":[]}]}},"/usage":{"get":{"description":"Your organization's limits, today's credits, your credit balance and what is available in all, and how many conversations its API keys hold active now, ones still starting included. Check it before starting a conversation to avoid a 409 or 429.","operationId":"get-your-usage","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Usage"}}},"description":"Successful Response"}},"summary":"Get your usage","tags":["Server"],"parameters":[],"security":[{"apiKey":[]}]}}},"components":{"schemas":{"Usage":{"type":"object","description":"What your organization's API keys may use, and have used today.","properties":{"limits":{"$ref":"#/components/schemas/ApiLimits","description":"Your organization's limits."},"credits":{"$ref":"#/components/schemas/Credits","description":"Today's credits (UTC)."},"credit_balance":{"default":0,"description":"Credits added to your account beyond each day's, which last until spent and are used once today's are. 0 when there are none.","type":"integer"},"available_credits":{"default":0,"description":"What your API keys can spend now: today's credits left plus your credit balance. A conversation starts while this is above zero.","type":"integer"},"active_sessions":{"type":"integer","description":"Conversations your API keys hold active now, ones still starting included. A new one answers 409 once this reaches `limits.concurrent_sessions`."}},"required":["active_sessions","available_credits","credit_balance","credits","limits"]},"Credits":{"type":"object","description":"Your organization's credits today (UTC).","properties":{"used":{"type":"integer","description":"Spent today, conversations still running included."},"remaining":{"type":"integer","description":"Today's allowance less what is spent; below zero when a conversation ran past it. Once it reaches zero, conversations draw on `credit_balance`; a new one answers 429 only when both are spent."},"renews_at":{"format":"date-time","type":"string","description":"When the next day's allowance replaces what is left (UTC)."}},"required":["remaining","renews_at","used"]},"ApiLimits":{"type":"object","description":"What your organization's API keys may use.","properties":{"concurrent_sessions":{"minimum":0,"type":"integer","description":"Conversations your API keys may hold active at once, ones still starting included."},"daily_credits":{"minimum":0,"type":"integer","description":"Credits your API keys may spend in a UTC day. Unspent credits don't carry over."},"api_keys":{"minimum":0,"type":"integer","description":"Live API keys your organization may hold at once."},"hourly_conversations":{"minimum":0,"type":"integer","description":"Conversations your API keys may start in any hour, ended ones included, and session tokens they may mint in any hour. Past it, a create or a mint answers 429 `start_limit`."}},"required":["api_keys","concurrent_sessions","daily_credits","hourly_conversations"]},"ConversationState":{"type":"object","description":"Where one of your conversations stands.","properties":{"id":{"format":"uuid","type":"string","description":"The conversation's id."},"status":{"$ref":"#/components/schemas/ConversationStatus","description":"Where the conversation is in its life."},"created_at":{"format":"date-time","type":"string","description":"When it was created (UTC)."},"started_at":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}],"description":"When the persona started it (UTC); `null` while it waits or joins, and for one that ended without running."}},"required":["created_at","id","started_at","status"]},"ConversationStatus":{"enum":["created","ready","queued","admitted","connecting","connected","ended","failed","expired"],"title":"ConversationStatus","type":"string","description":"`created`, `ready` and `queued` before a runtime is held for it, `admitted` once one is, `connecting` and `connected` as the browser or app joins, then `ended`. `failed` and `expired` mean it ended without running."},"ActiveConversations":{"type":"object","description":"Your organization's active conversations.","properties":{"items":{"items":{"$ref":"#/components/schemas/ConversationState"},"type":"array","description":"Oldest first, ones still starting included."}},"required":["items"]},"TranscriptResponse":{"type":"object","description":"A conversation's transcript.","properties":{"turns":{"items":{"$ref":"#/components/schemas/TranscriptTurn"},"type":"array","description":"What was said, oldest first: one turn per stretch of one speaker's speech."}},"required":["turns"]},"TranscriptTurn":{"type":"object","description":"One speaker's stretch of speech.","properties":{"role":{"type":"string","description":"Who spoke: `user` for the person, `assistant` for the persona."},"content":{"type":"string","description":"What they said."},"start":{"type":"number","description":"When the turn starts, in seconds from the start of the conversation's recording."},"end":{"type":"number","description":"When the turn ends, in seconds from the start of the conversation's recording."},"words":{"items":{"$ref":"#/components/schemas/TimedWord"},"type":"array","description":"The turn's words in order, each with its time."}},"required":["content","end","role","start","words"]},"TimedWord":{"type":"object","description":"One word of a turn.","properties":{"text":{"type":"string","description":"The word, as it appears in the turn's `content`."},"offset":{"type":"integer","description":"Where the word starts in the turn's `content`, as a character index."},"start":{"type":"number","description":"When the word starts, in seconds from the start of the conversation's recording."},"end":{"type":"number","description":"When the word ends, in seconds from the start of the conversation's recording."}},"required":["end","offset","start","text"]},"QueueState":{"type":"object","description":"Where a conversation stands.","properties":{"status":{"$ref":"#/components/schemas/ConversationStatus","description":"Where the conversation is in its life."},"seconds_remaining":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Time left in a connected conversation, when it has a time limit."}},"required":["seconds_remaining","status"]},"SessionEvents":{"type":"object","description":"A conversation's events after the request's `after`.","properties":{"events":{"items":{"discriminator":{"mapping":{"admitted":"#/components/schemas/AdmittedEvent","ended":"#/components/schemas/EndedEvent","preparing":"#/components/schemas/PreparingEvent","queued":"#/components/schemas/QueuedEvent","started":"#/components/schemas/StartedEvent"},"propertyName":"type"},"oneOf":[{"$ref":"#/components/schemas/QueuedEvent"},{"$ref":"#/components/schemas/PreparingEvent"},{"$ref":"#/components/schemas/AdmittedEvent"},{"$ref":"#/components/schemas/StartedEvent"},{"$ref":"#/components/schemas/EndedEvent"}]},"type":"array","description":"Oldest first, each with an increasing `id`; empty when nothing happened while the request was held."}},"required":["events"]},"EndedEvent":{"type":"object","description":"The conversation is over. Nothing follows.","properties":{"type":{"const":"ended","default":"ended","type":"string","description":"`ended`."},"id":{"type":"integer","description":"The event's id."},"reason":{"type":"string","description":"Why: `time_limit`, `policy_violation`, `age_check`, `client_ended`, `client_silent` or another end."}},"required":["id","reason","type"]},"StartedEvent":{"type":"object","description":"The persona has started the conversation. Sent again if its time limit changes.","properties":{"type":{"const":"started","default":"started","type":"string","description":"`started`."},"id":{"type":"integer","description":"The event's id: pass the last one you saw as `after`."},"seconds_remaining":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"How long the conversation may run, from when this answer arrived; null when it has no time limit."}},"required":["id","seconds_remaining","type"]},"AdmittedEvent":{"type":"object","description":"The conversation's room is ready: join it.","properties":{"type":{"const":"admitted","default":"admitted","type":"string","description":"`admitted`."},"id":{"type":"integer","description":"The event's id: pass the last one you saw as `after`."},"livekit_url":{"type":"string","description":"The LiveKit server to join."},"token":{"type":"string","description":"The participant token to join it with."}},"required":["id","livekit_url","token","type"]},"PreparingEvent":{"type":"object","description":"A runtime is held for the conversation and is getting ready for it, which can take up to about half a minute; `admitted` follows. Sent once.","properties":{"type":{"const":"preparing","default":"preparing","type":"string","description":"`preparing`."},"id":{"type":"integer","description":"The event's id: pass the last one you saw as `after`."}},"required":["id","type"]},"QueuedEvent":{"type":"object","description":"Waiting for a slot. A conversation started with an API key or a session token never waits in a queue, so it never gets this event: it may get `preparing`, then gets `admitted`.","properties":{"type":{"const":"queued","default":"queued","type":"string","description":"`queued`."},"id":{"type":"integer","description":"The event's id: pass the last one you saw as `after`."},"position":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Its place in the queue, where 0 is next."},"estimated_wait_seconds":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Roughly how long until it is admitted, when known."}},"required":["estimated_wait_seconds","id","position","type"]},"ConversationResponse":{"type":"object","description":"A conversation.","properties":{"id":{"format":"uuid","type":"string","description":"The conversation's id."},"status":{"$ref":"#/components/schemas/ConversationStatus","description":"Where the conversation is in its life."},"created_at":{"format":"date-time","type":"string","description":"When it was created (UTC)."},"updated_at":{"format":"date-time","type":"string","description":"When its status last changed (UTC)."}},"required":["created_at","id","status","updated_at"]},"CreateConversationRequest":{"type":"object","description":"The conversation to create.","properties":{"timezone":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The person's IANA time zone, such as `Europe/London`, so the persona knows their local time."},"max_session_seconds":{"anyOf":[{"exclusiveMinimum":0,"type":"integer"},{"type":"null"}],"description":"The longest the conversation may run, in seconds. Held to the service's maximum, which is also what it runs for when you leave this out. The `started` event's `seconds_remaining` says the limit it runs with."},"green_screen":{"default":false,"type":"boolean","description":"The persona over solid green (`#00FF00`), so your page can key it out and draw it over its own background. Only where your deployment offers it; some personas can't be drawn over green, and a conversation that asks for it on one of them is refused."}}},"SessionTokenResponse":{"type":"object","description":"A session token to hand to your page.","properties":{"token":{"type":"string","description":"The session token, `st_…`, for your page or app to create the conversation with."},"expires_at":{"format":"date-time","type":"string","description":"When the token stops being able to start a conversation (UTC). Once it has started one, it lasts as long as that conversation."},"warnings":{"items":{"type":"string"},"type":"array","description":"What to change in how you mint tokens; empty when nothing. Without `end_user_id`, it says that policy pauses count against all your organization's conversations without one."}},"required":["expires_at","token","warnings"]},"CreateSessionTokenRequest":{"type":"object","description":"What the conversation the token creates will be, and who it is for. Send `end_user_id` with every token; leave the other fields out for the defaults.","properties":{"personality":{"anyOf":[{"$ref":"#/components/schemas/PersonalityChoice"},{"type":"null"}],"description":"Who the persona is in the conversation, fixed for its whole life. Leave it out for the default personality."},"end_user_id":{"anyOf":[{"pattern":"^[A-Za-z0-9_.:\\-]{1,128}$","type":"string"},{"type":"null"}],"description":"Your own id for the person the conversation is for, such as a hash of your user id. Repeated policy violations then pause new conversations for this person only. Letters, digits and `_ . : -`, up to 128 characters; never an email address or a name."},"interface":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"`nuance_live`, the default, so you can leave it out."},"interface_setup":{"anyOf":[{"$ref":"#/components/schemas/InterfaceSetup"},{"type":"null"}],"description":"A Nuance Live session's own settings. See [Set up a session](/setup)."},"expressiveness":{"anyOf":[{"maximum":2,"minimum":0,"type":"number"},{"type":"null"}],"description":"How much the persona's face and head move, from 0 to 2: lower is calmer, higher more expressive. Leave it out for 1, the persona's own tuned level. A value outside 0 to 2 is refused with `422`."}}},"InterfaceSetup":{"type":"object","description":"What you settle about a Nuance Live session. Any other key is refused with `422` `interface_setup`.","properties":{"delegation":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"description":"Who does the work the persona hands off: `{\"type\": \"client\"}`, the default, `{\"type\": \"responses\", \"responses\": {…}}` for a backend that calls your tools, or `{\"type\": \"none\"}` for nobody. See [Delegation and tools](/delegation)."},"video":{"anyOf":[{"$ref":"#/components/schemas/InterfaceVideo"},{"type":"null"}],"description":"`{\"input\": {\"camera\": false}}` keeps the persona from using the user's camera."},"runtime_tools":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"description":"The persona's own tools, each `true` or `false`: `stay_quiet` (on by default), `end_call` and `call_time` (off by default). See [the persona's own tools](/setup#the-personas-own-tools)."}}},"InterfaceVideo":{"type":"object","description":"The user's camera in a Nuance Live session.","properties":{"input":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"description":"`{\"camera\": false}` keeps the persona from using the camera your page publishes. Left out, the persona uses it."}}},"PersonalityChoice":{"type":"object","description":"Who the AI persona is in a conversation.","properties":{"system_prompt":{"anyOf":[{"maxLength":64000,"minLength":1,"type":"string"},{"type":"null"}],"description":"Your own prompt, in plain text: who the persona is, where it works, what it knows about the person and the situation."},"persona_id":{"anyOf":[{"pattern":"^[a-z0-9][a-z0-9_-]{0,63}$","type":"string"},{"type":"null"}],"description":"Who the persona looks and sounds like: `sam`, `sonia` or `mira` ([Personas](/personas) shows each). Left out, `sam`. Any other id is refused with a `400`."}}}},"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from your organization's API keys page on the platform, `nk_…`. Server only."},"sessionToken":{"type":"http","scheme":"bearer","description":"A session token your server minted, `st_…`."}}}}