Guides
Assistants
The full assistant lifecycle — create, configure voice and behavior, attach knowledge and tasks, wire up webhooks, and enable SMS.
An assistant is a configured AI voice agent that answers calls (and optionally texts) on behalf of your business. This guide walks the whole lifecycle: pick the building blocks, create the assistant, tune its behavior, attach knowledge and task flows, point webhooks at your server, and toggle the SMS channel.
For endpoint-by-endpoint request/response schemas, see the Assistants API reference.
Pick a voice, model, and transcriber#
Every assistant references three lookup resources by UUID:
| Resource | Endpoint | What it is |
|---|---|---|
| Voice | GET /voices | The voice the assistant speaks with |
| Model | GET /models | The LLM that powers reasoning and responses |
| Transcriber | GET /transcribers | The speech-to-text engine |
All three lists support limit and offset query params, and each has a GET /{resource}/{id} detail endpoint.
curl "https://core-api.heysadie.ai/voices?limit=50" \
-H "Authorization: ApiKey YOUR_API_KEY"
curl "https://core-api.heysadie.ai/models?limit=50" \
-H "Authorization: ApiKey YOUR_API_KEY"
curl "https://core-api.heysadie.ai/transcribers?limit=50" \
-H "Authorization: ApiKey YOUR_API_KEY"Keep the id of the voice, model, and transcriber you want — you'll pass them as voiceId, modelId, and transcriberId when creating the assistant.
To help you choose, each voice row carries optional language (the language codes that voice can speak with its underlying TTS model) and tags (guidance badges such as gender, accent, and personality) — see Voices, Models & Transcribers. Voice display names are not unique (the same voice can appear once per underlying model), so always reference voices by id.
Create the assistant#
POST /assistants
curl -X POST https://core-api.heysadie.ai/assistants \
-H "Authorization: ApiKey YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Front Desk",
"voiceId": "0195f1e2-...",
"modelId": "0195f1e2-...",
"transcriberId": "0195f1e2-...",
"developerPrompt": "We are open {{opening_hours}}. Always ask for a callback number before ending the call.",
"parameters": { "opening_hours": "10am to 10pm" },
"firstMessage": "Hi! You have reached Joe'\''s Pizza. How can I help?",
"endOfCallMessage": "Thanks for calling — have a great day!",
"recordingEnabled": true,
"failoverNumber": "+15551234567",
"failoverMode": false,
"toolIds": [],
"timezone": "Australia/Sydney",
"address": "1 Example Street, Sydney",
"phoneNumberPassThrough": true
}'Placeholders must match
Make sure your parameters keys match the {{placeholders}} used in developerPrompt — if the prompt says {{opening_hours}}, then parameters must include "opening_hours".
Core field guide#
| Field | Type | Description |
|---|---|---|
name | string | Display name of the assistant (e.g. "OnCore", "Alex"). |
voiceId / modelId / transcriberId | UUID | The voice, LLM, and speech-to-text engine (see lookups above). |
developerPrompt | string | Your own section injected into the default system prompt. Example: "Always ask for membership ID before confirming a reservation." |
parameters | object | Dynamic values like {{opening_hours}} or {{branch_name}}, inserted into the prompt. |
firstMessage | string | What the assistant says at the start of the call. |
endOfCallMessage | string | The last thing the assistant says before hanging up. On updates you can clear it (empty string or null) for "no scripted goodbye". |
recordingEnabled | boolean | If true, calls are recorded. |
failoverNumber | string | E.164 number the call is forwarded to if the assistant fails or needs to escalate. On updates, an extension may follow a comma (e.g. +15551234567,101). |
failoverMode | boolean | If true, calls are forwarded to the failover number. |
phoneNumberId | UUID | Optional — links the assistant to a phone number you own. |
toolIds | UUID[] | Tools the assistant may use during a call. |
timezone | string | IANA timezone of the business (e.g. "Australia/Sydney"). |
address | string | The business address. |
phoneNumberPassThrough | boolean | If true, the assistant can reuse the caller's number ("Would you like to use the number ending in 595?"); if false, it always asks. |
summaryPrompt | string | Guides how the assistant summarizes the call at the end. |
structuredDataPrompt | string | Works with categories to classify calls. |
categories | string[] | Call classification labels, e.g. ["booking", "edit", "cancel"]. |
promptId | UUID | The prompt the assistant uses; omit for the default. |
chatPromptId | UUID | null | Optional chat/messaging prompt (nullable so it can be unset). |
whisperSummaryPromptId | UUID | null | Optional whisper-summary prompt (nullable so it can be unset). |
backgroundSound | boolean | Whether the assistant plays a background sound. |
serverUrl | URL | null | Webhook URL override for this assistant (see below). On updates, null (or an empty string) clears it. |
notifications | URL[] | Up to 10 notification webhook URLs. New URLs are appended to existing ones, not replaced. |
csatFeedback | boolean | Enables CSAT (customer satisfaction) feedback. Defaults to false. |
transferMode | string | Transfer mode for escalations. Defaults to cold-transfer. |
warmWhisperDelaySeconds | integer | Warm-transfer whisper delay, 0–30 seconds. Defaults to 0. |
transferDialTimeout | integer | Transfer dial timeout, 1–60 seconds. |
warmReturnMessage | string | What the assistant says when a warm-return transfer goes unanswered and the caller comes back (max 2000 characters). Stored under settings.warmReturnMessage; send "" to restore the built-in wording. Pairs with supervisor_answered: false on the end-of-call report. |
callTimeLimitSeconds | integer | Maximum call length, 10–43200 seconds. |
idleMessages / idleTimeoutSeconds / idleMessageMaxSpokenCount | — | Idle-caller behavior: messages spoken after idleTimeoutSeconds (max 60) of silence, at most idleMessageMaxSpokenCount (max 10) times. |
taskDrivenMode | boolean | Enables task-group-driven conversation flow. |
settings.conversationConfig | object | Conversation timing and speech delivery tuning — see below. |
Conversation timing (settings.conversationConfig)#
All sub-plans and fields are optional; omitted values use agent defaults, and null clears a stored value.
| Plan | Fields |
|---|---|
endpointingPlan | mode ("fixed" | "dynamic" — dynamic adapts to the caller's pause pacing), minDelay / maxDelay (seconds of silence before the agent responds). |
turnDetectionPlan | timeout (seconds), unlikelyThreshold (0–1 probability). |
vadPlan | minSpeechDuration, minSilenceDuration, prefixPaddingDuration (seconds). |
interruptionPlan | minDuration (0–5 s of sustained caller speech before a barge-in stops the agent), minWords (0–10 transcribed words required to register an interruption). |
Speech delivery (settings.conversationConfig.deliveryMode)#
How expressive the assistant's voice sounds. One mode per assistant; the mode is read at call start, so a change applies from the next call.
| Value | Behavior |
|---|---|
"none" | Plain delivery. The default. |
"emotion_tagging" | A classifier picks at most one fitting emotion per response and injects the matching emotion tag into the speech. Supported by Cartesia Sonic 3 / 3.5 and ElevenLabs v3 voices; English calls only. Other voices keep their normal delivery. |
"expressive" | The model itself steers delivery — pacing, natural disfluencies, and nonverbal sounds such as laughter. Supported by Cartesia, Fish Audio, xAI, and Inworld voices. Other voices keep their normal delivery. |
With deliveryMode: "expressive", the optional expressiveOptions object (also under settings.conversationConfig) tunes the built-in defaults. Only the keys you set override a default; send expressiveOptions: null to clear all stored overrides.
| Field | Type | Description |
|---|---|---|
disfluencies | boolean | Natural fillers ("um", "uh"). On by default; set false to opt out. |
nonverbalSounds | boolean | object | true / false switches all nonverbal sounds at once, or pass an object of per-category booleans: laughing, breathing, sighing, crying, vocalizing, mouthSounds, reflexSounds. |
pace | string | "slow", "normal", or "fast". Omit to let the model pace itself. |
instructionsAppend | string | Extra delivery guidance appended to the built-in instructions (max 2000 characters). |
Legacy flag
settings.conversationConfig.emotionTaggingEnabled (boolean) is the older opt-in for emotion tagging. It is superseded by deliveryMode but still honored when deliveryMode is absent.
Update the assistant#
PATCH /assistants/{id} — partial update; send only the fields you want to change. The body accepts the same fields as create.
curl -X PATCH https://core-api.heysadie.ai/assistants/ASSISTANT_ID \
-H "Authorization: ApiKey YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "firstMessage": "Thanks for calling Joe'\''s — what can I do for you?" }'Attach knowledge and task flows#
Once the assistant exists, wire in its supporting resources:
- Documents — attach knowledge-base documents with
POST /assistants/{id}/documents. See Knowledge base. - Task groups — link structured task flows with
POST /assistants/{id}/taskgroupsand set per-assistant task overrides. See Task groups. - Tools — set
toolIdson the assistant (create or PATCH). See Tools. - Custom instructions — add up to six short behavior tweaks. See Custom instructions.
- Capture forms — collect structured caller details. See Capture forms.
Point webhooks at your server (serverUrl)#
serverUrl is the per-assistant webhook URL override: set it (on create or PATCH) to receive call results on your own endpoint, e.g. the end-of-call report. notifications holds up to 10 additional webhook URLs — remember that PATCHing notifications appends new URLs rather than replacing the list.
curl -X PATCH https://core-api.heysadie.ai/assistants/ASSISTANT_ID \
-H "Authorization: ApiKey YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "serverUrl": "https://api.example.com/sadie/webhooks" }'URLs are validated for safety at write time — unresolvable or disallowed hosts are rejected with a validation error.
To remove the override, PATCH serverUrl to null (an empty string works too); deliveries then fall back to your tenant-level webhook URL.
Enable or disable SMS#
PATCH /assistants/{id}/sms-enabled
curl -X PATCH https://core-api.heysadie.ai/assistants/ASSISTANT_ID/sms-enabled \
-H "Authorization: ApiKey YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "smsEnabled": true }'smsEnabled: trueturns the SMS channel on;falseturns it off.- Enabling requires a linked Twilio phone number. The number is attached to the SMS sender pool automatically (idempotent). If no Twilio number is linked, the request fails with
400(NO_PHONE_NUMBER_LINKED/NON_TWILIO_PROVIDER) and the flag is left unchanged. - Disabling only clears the flag — the number intentionally stays registered for SMS so re-enabling doesn't require re-registration.
- The response returns the assistant
idand itsfeatureFlagsmap; other channel flags are preserved.
SMS traffic then shows up as conversations.
Delete the assistant#
DELETE /assistants/{id} — deletes the assistant. Attached sub-resources (documents, task groups, overrides) are managed through their own endpoints.
curl -X DELETE https://core-api.heysadie.ai/assistants/ASSISTANT_ID \
-H "Authorization: ApiKey YOUR_API_KEY"Next steps#
- Prompts — reusable system prompts and global templates.
- Phone numbers — buy a number and link it via
phoneNumberId. - Calls — read call activity for your assistants.