Build an agent
Call flow
diagram field of the agent, which you can draw in the dashboard's Call Builder or write through the API.Nodes
| Type | What it does | Its data |
|---|---|---|
startCall | Opens the call. Holds the main prompt and the first sentence, and the outbound settings. | brief, welcomeMessage, outbound |
knowHow | A block of knowledge or of behaviour, with a title. Its prompt is HTML, and it is where tools are attached. | title, prompt |
forwardCall | Transfers the caller to a phone number, cold or warm: a fixed number, or one your service looks up during the call. | title, prompt, forwardingMessage, destinationType, forwardingPhoneNumber or lookupUrl, transferMode, options |
forwardAgent | Hands the conversation to another agent. | agentId, message, description |
endCall | Hangs up after a closing sentence. | endCallMessage |
Each node has an id, a type, a position on the canvas (x, y) and its data. Node ids are kept when an agent is duplicated, so a script can change the same node in every copy.
Edges and conditions
Edges link two nodes (source, target). An edge can carry a condition, which decides the path the call takes:
| Condition | The path is taken when |
|---|---|
isExistingContact / notExistingContact | The caller is, or is not, a contact of the CRM |
isNewCaller / notNewCaller | It is, or is not, the caller's first call |
inboundOnly / outboundOnly | The call is inbound, or outbound |
A minimal call flow
{
"nodes": [
{
"id": "start", "type": "startCall", "position": { "x": 0, "y": 0 },
"data": {
"brief": "<p>You are Alice, the receptionist of Club Lyon Part-Dieu. Answer briefly and warmly.</p>",
"welcomeMessage": "Hello, this is Alice, the AI assistant of Club Lyon Part-Dieu. How can I help?"
}
},
{
"id": "hours", "type": "knowHow", "position": { "x": 0, "y": 160 },
"data": { "title": "Opening hours", "prompt": "<p>The club is open every day from 6am to 11pm.</p>" }
},
{
"id": "end", "type": "endCall", "position": { "x": 0, "y": 320 },
"data": { "endCallMessage": "Thank you for calling. Have a good day!" }
}
],
"edges": [
{ "id": "e1", "source": "start", "target": "hours" },
{ "id": "e2", "source": "hours", "target": "end" }
]
}The prompt the model reads is the brief followed by the nodes in diagram order. Prompts are HTML: keep the tags when you write one back.
Outbound settings
The outbound object of startCall applies to the calls a campaign places.
| Field | Values |
|---|---|
welcomeMessage | The first sentence of an outbound call |
firstMessageMode | assistant-speaks-first, assistant-waits-for-user, assistant-speaks-first-with-model-generated-message |
pauseBeforeSpeakingSeconds | 0 to 5 |
voiceMailEnabled, voiceMailMessage | Leave a message when a voicemail answers |
voiceMailExpectedDuration | 5 to 60 seconds |
voiceMailProvider | google, openai: the voicemail detection |
Transfers
A forwardCall node runs when its prompt applies: the agent says the forwardingMessage and transfers the caller to the node's destination.
destinationType | The number | Its fields |
|---|---|---|
fixed (default) | Always the same line, written in the node | forwardingPhoneNumber, in international format (+33612345678) |
lookup | Picked by your service during the call, for this caller: see below | lookupUrl, lookupMessage, lookupParameters |
The transfer settings are the same for both:
| Field | Values |
|---|---|
transferMode | cold connects the caller straight away. warm first calls the person and briefs them, then connects the caller. |
greetingMessage | Warm mode, required: the first sentence the person hears when they pick up |
whisperMessage, threeWayIntroduction | Warm mode: what to tell the person about the caller, and how to introduce them. Instructions for the agent, not sentences it reads out. |
holdMusicUrl | The URL of the music the caller hears while waiting |
transferTimeout | 30 to 300 seconds (default 60) |
fallbackMessage | What the agent says when the transfer does not go through |
Look up the number during the call
When the right number depends on the caller (the subcontractor of their area, the agency of their postcode, the technician on call this week), set destinationType to lookup: your service picks the number while the caller waits. The number never passes through the model, so it cannot be misheard or made up, and hundreds of numbers stay out of the prompt.
{
"id": "technician", "type": "forwardCall", "position": { "x": 320, "y": 160 },
"data": {
"title": "Transfer to technician",
"prompt": "<p>Once the caller has given their address, transfer them.</p>",
"forwardingMessage": "I'm putting you through to the technician of your area.",
"destinationType": "lookup",
"lookupUrl": "https://example.com/api/transfer-number?token=YOUR_SECRET",
"lookupMessage": "One moment, I'm finding the technician of your area.",
"lookupParameters": {
"type": "object",
"properties": {
"address": { "type": "string", "description": "The caller's full address" }
},
"required": ["address"]
},
"transferMode": "cold",
"fallbackMessage": "The technician cannot be reached. I'll take a message."
}
}- The node becomes a tool of the agent, named after the camelCase form of its
title(Transfer to technician becomestransferToTechnician). Itsprompttells the agent when to call it, andlookupParameters, a JSON Schema in the format of a custom tool'sparameters, lists what the agent collects first. Without it, the agent sends no arguments. - The agent says the
lookupMessage(by default One moment please. in the agent's language), and CallShift posts tolookupUrlthe same request as to a custom tool: the tool's name inname, the arguments inargs, the call so far incalland, when the caller is a known contact, thecontact. - Your service answers with the number, or with the reason there is none.
- With a number, the agent says the
forwardingMessageand transfers the caller, cold or warm, with the node's settings.
Your service's answer
A match gives the number:
{
"number": "+33612345678",
"message": "I'm putting you through to Dupont Plumbing, the technician of your area.",
"context": "Subcontractor for Lyon 3rd, contract 4521"
}No match gives the reason, with any HTTP status:
{
"error": "Address outside our service area",
"context": "We cover Lyon and its suburbs only"
}| Field | What it does |
|---|---|
number | On a match: the number to transfer to, in international format, in the country of the line the call is on. |
message | On a match, optional: said word for word instead of the node's forwardingMessage. |
error | On no match: why there is no number. The agent is told the transfer is not possible, with this reason, and carries on the call. |
context | Optional: on a warm transfer, added to the whisperMessage to brief the person. When no transfer happens, given to the agent. |
Answer within 5 seconds. Past that, and for a body with neither number nor error, an error status without an error, a number that is not valid or is in another country, or a call from a browser, which is on no line, there is no transfer either: the agent is told it is not possible, and the reason is in the dashboard's tool logs. A number abroad is refused so that a service that is wrong, or compromised, cannot send your calls to another country.
The request carries no signature: put a secret token in the URL and check it.
A looked-up node's tool name must be free: a call flow where it is also the name of another looked-up node, of one of the account's tools or of a built-in tool (sendSms, transferCall) is refused with a 422. Give the node another title; renaming the title renames the tool.
A looked-up number works on agents on the current voice engine. Other agents leave the node out of their call flow, so they do not transfer: try a transfer on a test call.
Transferring, handing over to another agent and hanging up are nodes of the call flow. Everything else the agent does with your systems is a tool.
