Skip to main content
One agent can answer incoming calls and place outgoing calls. There is no direction setting and no second agent to keep in sync. The system prompt and first message stay the same, and each conversation fills them with its own values.

Choose the right option

One agent for inbound and outbound calls

{{system.direction}} reads inbound when someone called you and outbound when the agent placed the call. Write both cases into the system prompt:
A batch fills {{reason}} from a column in its recipient list. An inbound call can leave it empty, because that line doesn’t apply to it. Workflow conditions read system.direction too, so a workflow can send each direction down its own path. The first message is one field for both directions. Choose whichever fits:
  • Write an opening that suits both, such as Hi, this is Maya from Centillion Labs.
  • Leave First message blank. The agent waits for the person to speak. On a call the agent placed, it opens the call itself if the person answers and stays silent for three seconds.
  • Add a first_message column to a batch to replace the opening on those calls. Inbound calls keep the agent’s First message.
  • Have the initiation webhook return first_message to replace the opening on one inbound call.
The opening is selected in this order:
  1. A recipient’s first_message in a batch.
  2. A literal opening message from the active workflow.
  3. A permitted initiation first-message override for this conversation.
  4. The First message set for the call’s language under Language presets.
  5. The agent’s First message.
  6. Nothing. The agent waits for the person to speak.
An initiation override of first_message: "" keeps that conversation silent when there is no workflow opening, even if the agent has a First message. Batch first-message overrides must contain text. Whatever opens the call, you are responsible for the notices it has to give. See Disclosure requirements.

Give inbound callers their own start

An agent built for outbound calls usually opens with a step that explains why it called. A caller who rang in needs a different greeting, then the same conversation. Keep one agent and let the initiation webhook choose the step an inbound call starts on:
  1. Point a number at the agent. In Channels → Phone, assign a number to this agent. A number answers for one agent, so reassign it or import another.
  2. Add the inbound step. In Workflow, add a step for callers, for example “Inbound greeting”, with the same exits as the outbound greeting and wording such as “thanks for calling back” instead of “I’m calling about your form”. Copy its Step ID from the bottom of the step panel, then publish: live calls read the published workflow.
  3. Allow the overrides. On the Main branch, open Settings → Security and switch on First message and Workflow start node under Overrides. Then switch on Fetch initiation client data from a webhook.
  4. Set the webhook. Add it in Workspace settings → Webhooks, or add an override for this agent on the same Security tab. Add a secret header so your endpoint can tell the request comes from Centillion Labs.
  5. Build the webhook. Check direction first. For inbound, the caller’s number is in caller_id: look the caller up, and if there is no match, create a record right away so IDs your tools use are real. Return the variables, a greeting and the step:
    For an unknown caller, ask for their name in the greeting instead, and give the agent a tool that saves it. Never answer with an error: a failed lookup stops the call from starting. If something inside your endpoint fails, return the unknown-caller response. For outbound, return the variables without starting_workflow_node_id or a first message, so the call starts and opens as it always has. On those calls caller_id is your own number and called_number is the person called; don’t create a record for it.
  6. Make the prompt fit both directions. Replace a line such as “Outbound phone call to a lead” with one that reads {{system.direction}}.
A batch call supplies its own recipient data and skips the webhook, so it starts at the workflow’s entry as before. Other outbound calls that run the agent’s workflow do call the webhook, with direction set to outbound (see When it runs), which is why the webhook checks it. Test with a real call: Preview does not call the webhook. Open the conversation and its Workflow tab. It shows the move from Start to the inbound step. If the conversation started on the outbound greeting instead, the step ID was not in the published workflow: Centillion Labs starts at the entry rather than drop the call. A start step the agent has not allowed fails the call instead.

Test both directions in Preview

Preview runs as an inbound conversation and nothing is dialed. To try the outbound path, open Vars in Preview, set system.direction to outbound and start a new conversation. The row is there once the prompt uses the variable. Fill reason and any other variable the same way.

Dynamic variables

Type {{ in System prompt or First message to add a variable. The list shows the variables this agent already uses, then the built-in values, with a description and an example for the highlighted one. Keep typing to filter, and press Enter or Tab to insert. A name that isn’t on the list reads New and adds a variable.
System prompt editor with the variable list open at the cursor, showing user variables, system variables and a description of the highlighted one

Type {{ to pick a variable the agent already uses or a built-in value.

Supply values as strings, finite numbers or booleans. 0, false and an empty string are values; they do not fall back to test values. Use at most 100 custom variables, with strings up to 12,000 characters. Names use letters, numbers and underscores, start with a letter or underscore, and contain at most 64 characters. Do not use reserved names such as system, call, contact, campaign, names beginning with __, or the system__ namespace. Custom initiation variables keep multiline text in prompts. Imported contact, batch-recipient, tool-result and workflow values are limited to a single line of 200 characters, including a tool result that replaces an initiation variable. Substitution happens once: a value containing {{another_variable}} is treated as text, not another lookup.

Supplying live values

  • Inbound phone calls: return dynamic_variables from the initiation webhook.
  • Outbound batches and number test calls: supply the recipient’s variables. Each extra column in a batch’s recipient list becomes a variable. They use their own batch override permissions. A test call lists each variable the published version uses, starting from Vars, and sends what you see. After you upload a batch file, a notice names any variable it has no column for.
  • Developer API chat: include conversation_initiation_client_data in the first request. See the example below.
Website chat and messaging channels have no way to supply values, so a variable in text those channels use stays as written. Keep such text free of variables, or use the same words for every conversation. For an HTTP tool, use a variable in a URL or header, or choose a parameter’s Dynamic variable value source. A parameter can read any variable the conversation holds: a batch column, a system value such as system.conversation_id, or a value an earlier tool saved. A URL variable cannot change the tool’s server. Tool-result assignments can update ordinary variables during the conversation, including ones the conversation started with, and a workflow condition reads the new value straight away. They cannot replace system values or initiation secrets. A live call can’t start on a first message with a missing value. If First message uses a custom variable and the call has no value for it, the call ends before anyone hears anything, and a website call shows an error. Batch calls follow the same rule, and a recipient’s column counts as a value. So use variables in First message only when every call that speaks it supplies them, for example from the initiation webhook. A workflow that goes straight from Start to a Say step replaces the first message, so the first message’s variables are not required. A prompt is different. A variable with no value stays in the prompt as written and the call goes on, so a prompt can hold variables that only some calls fill. Test values are never live defaults.

Built-in values

These are provided by Centillion Labs and cannot be replaced by the webhook: A built-in value that isn’t known on a call becomes empty text. An inbound call’s caller_id and an outbound call’s called_number identify the other party. A withheld or unavailable number can be null.

Test values

Vars in the top bar lists the custom variables this branch uses. Enter a value and choose Text, Number or Boolean. Values save to the branch’s draft as you type and need no publish. Preview starts each conversation with them, and a variable with no value stays as written, even in the first message. Vars in Preview, or Overview in its Debugger, changes the values for one conversation and leaves the saved ones alone. Preview does not call the initiation webhook. Use synthetic test data. Secret variables are excluded from test values.
Dynamic variables panel open from the top bar, with a name and type for each variable and its test value

Test values keep their text, number or boolean type and never become live defaults.

Configure an initiation webhook

Workspace default

Open Workspace settings → Webhooks. Under Conversation initiation client data webhook, click Add webhook, enter a public HTTPS URL and add any request headers, then click Add webhook in the panel. This supplies a default endpoint; it does not turn initiation on for any agent. Edit opens the same panel. Remove asks first, because agents that use the workspace webhook cannot start phone calls until you add another or give them an override.
Workspace conversation initiation client data webhook card with a POST endpoint and an Edit button

The workspace default stores a public HTTPS endpoint and encrypted request headers.

Agent settings

On the agent’s Main branch, open Settings → Security. Overrides lists every part of the configuration a client can replace when a conversation starts. All start off. Switch on only the ones your endpoint or client should provide; hover a name to see the field it allows, or see Allowed overrides. Dynamic variables need no permission. Conversation initiation client data webhook has one switch, Fetch initiation client data from a webhook: To set the override, click Add webhook under Initiation data webhook override, enter the URL and headers, and click Add override. Remove override goes back to the workspace webhook. Turning the switch off keeps an override for the next time you turn it on. Each switch saves as you flip it. These security settings are shared across the agent’s branches and take effect for new conversations without publishing. Owners and members can manage them. Header values are encrypted and hidden after saving. Leave a saved value blank to keep it. Changing the endpoint to a different origin requires entering its headers again; saved credentials are not forwarded to a different server.
Agent Security settings with the First message and Workflow start node overrides allowed and the initiation webhook switched on with an agent override

Each agent allows individual overrides, turns the webhook on and can override its endpoint.

When it runs

Request

Centillion Labs sends an HTTPS POST with Content-Type: application/json and your configured headers:
call_sid is an empty string when unavailable. call_id is omitted when unavailable. Use conversation_id as an idempotency key in your backend. No transcript is sent in this request.

Response

Return a successful HTTP status and this JSON shape:
dynamic_variables defaults to an empty object. user_id, conversation_config_override and starting_workflow_node_id are optional. user_id supplies context; it does not authenticate the caller or change conversation ownership. starting_workflow_node_id starts the conversation on that workflow step instead of the entry, as if it had just moved there: the step’s own work runs on the first turn and its exits apply from then on. The conversation’s Workflow tab shows the move from Start. A step the agent’s published workflow does not have is ignored, and so is the Start step itself: the conversation starts at the entry.

Allowed overrides

All override permissions start off. Providing a field that is not allowed fails initialization; it is not silently ignored. Voice tuning is unavailable in the duplex pipeline and on expressive models that do not support tuning. Centillion staged voices retain speaking-speed control when expressive mode is on. An incompatible tuning or voice override prevents startup. Overrides cannot change the pipeline, model provider or recording policy. Unsupported fields are rejected. The overrides apply only to this conversation. They do not edit the agent or its published version. A prompt override replaces the agent’s base instructions; workflow steps and specialist instructions still control their own tasks.

Failures and retries

The endpoint must respond within four seconds after connection resolution. DNS resolution has a separate three-second limit. Responses must fit within 262,144 bytes. Private network addresses, redirects, non-success status codes, invalid JSON, unsupported fields and disallowed overrides fail startup. A successful initialization is reused on retries of the same conversation. A failed lookup is also remembered, so retrying that conversation does not repeatedly call your endpoint. Fix the response or settings, then start a new call. If your lookup finds no customer, return a valid response with safe values for every required variable to use a generic opening.

Secret variables

Prefix a credential with secret__, for example secret__account_token. It is encrypted for that agent and conversation, and can fill an HTTP or MCP tool’s header:
Secret values are not substituted into prompts, first messages or URLs. The Client data view shows [REDACTED]. Exact copies of the secret in public initiation text, tool results and tool errors are redacted as well. Do not use these variables for ordinary customer context or put credentials into a public variable, tool body, first message or test value. The initiation endpoint belongs to your workspace. For regulated data, use an endpoint covered by your organization’s agreements and send only the context the lookup needs.

Developer API chat

Send initialization data only when starting a new conversation:
Use this body with POST /api/v2/agents/{agentId}/chat. Later requests use the returned conversationId and omit initiation data. Retrying the same initial requestId must use the same data. Chat accepts agent overrides; voice and call-duration overrides require a voice conversation. See the developer API for authentication and responses.

Initiation and event webhooks

An initiation webhook supplies data before a conversation starts, and the conversation waits for its response. The event webhooks on the same workspace page report events such as completed calls and analysis. They use a delivery queue, signing secrets and delivery logs. Configuring one kind does not configure the other.
Last modified on November 2, 2026