Connected agents
Agents you run on another platform, answering calls on your Vocalis numbers.
A connected agent is an AI agent you already run on another platform. The agent keeps running there; Vocalis carries the call, so every call it answers gets a transcript, a recording, analysis against the campaign's objectives, call intelligence, events and webhooks, like a call answered by a Vocalis agent. Calls and their events report engine: "external".
Calls reach the agent over SIP at sip:<sipUser>@<sipDomain>, the address the platform gave you, over transport. Each platform decides what those may be:
- bland (Bland): sipUser is an E.164 number, sipDomain is one of us2.sip.bland.ai, ca2.sip.bland.ai, eu2.sip.bland.ai, asia2.sip.bland.ai; transports tls-srtp; up to 10 routing headers.
- cognigy (Cognigy): sipUser is an E.164 number, sipDomain is the host the platform gave you; transports udp, tcp, tls, tls-srtp; up to 10 routing headers.
- elevenlabs (ElevenLabs): sipUser is an identifier (Imported number), sipDomain is one of sip.rtc.elevenlabs.io, sip-static.rtc.elevenlabs.io, sip-static.rtc.eu.residency.elevenlabs.io, sip-static.rtc.in.residency.elevenlabs.io, sip-static.rtc.sg.residency.elevenlabs.io; transports tls-srtp, tls, tcp; takes sipCredentials; up to 10 routing headers.
- koreai (Kore.ai): sipUser is an E.164 number, sipDomain is the host the platform gave you; transports udp, tcp, tls; up to 10 routing headers.
- polyai (PolyAI): sipUser is an identifier (Extension), sipDomain is the host the platform gave you; transports udp, tls-srtp; takes sipCredentials; up to 10 routing headers.
- retell (Retell AI): sipUser is an E.164 number, sipDomain is one of sip.retellai.com; transports tcp, udp, tls, tls-srtp; up to 10 routing headers.
- synthflow (Synthflow): sipUser is an E.164 number, sipDomain is one of sip.synthflow.ai, sip.us.synthflow.ai, sip.eu.synthflow.ai; transports udp, tcp, tls, tls-srtp; up to 10 routing headers.
- vapi (Vapi): sipUser is an identifier (SIP username), sipDomain is one of sip.vapi.ai, sip.eu.vapi.ai; transports udp, tcp, tls; takes sipCredentials (20 to 40 characters each); up to 10 routing headers.
- sierra (Sierra): sipUser is an E.164 number, sipDomain is the host the platform gave you; transports udp; up to 6 routing headers.
transport defaults to the platform's first; tls-srtp encrypts the audio as well as the signaling. Platforms that challenge calls take sipCredentials ({ username, password }); the password is write-only. routingHeaders carry headers the platform wants on every call, such as the ones that pick one of several agents. Their values are write-only too: stored encrypted, sent with every call, and returned by name only. Header values and the password belong to the address they were set for, so an update that changes sipDomain or sipUser has to send them again. Header names start with X-, values are visible ASCII up to 256 characters, and all of them together stay under 1,024 characters once encoded, counting the X-Vocalis- headers we add (up to 186 of those characters). The X-Vocalis- prefix is ours.
Every call also carries X-Vocalis-Call-Id, X-Vocalis-Caller, X-Vocalis-Called, X-Vocalis-Campaign-Id, for the agent to read; the caller's and the dialed number only when they are E.164.
To have the agent answer calls, create or update an inbound campaign with connectedAgentId set to the connected agent's id (and no agentId). Connected agents answer on Twilio numbers.
handBackRoutes decide where the agent may send a caller. To transfer, the agent sends a SIP REFER to sip:<route value>@vocalis.io, and may add short X-Transfer-Reason and X-Transfer-Summary headers or a User-to-User value to that address, all within 256 characters. Sierra names the route in X-RouteValue instead, with X-RouteReason and X-CustomMetaData, and ends the call with X-RouteType: hangup. The route value picks a route: an inbound campaign answered by a Vocalis agent (destination.type: "campaign"), whose agent picks up with the reason and summary the platform sent, or a phone number, which must be one outbound calls may reach. Values not listed are refused and the caller is told. At most 20 routes; values are up to 64 letters, digits or + . _ -. Routes name your campaigns and the numbers callers are sent to, so handBackRoutes is only returned to a key whose user can change the agent (its owner or an admin); for anyone else it is null, and handBackRouteCount says how many there are.
Anyone the agent is shared with can read it and use it for their own inbound campaigns. Changing or deleting it takes its owner or an admin of its organization, since every campaign using it follows; only the owner may change its address, its transport or who it's shared with.
List connected agents#
Connected agents in the key's organization that its user created or that are shared with them. Not paginated: the whole list is returned.
/api/v1/connected-agentsResponses
The connected agents.
curl -X GET "https://voice.evryhealth.com/api/v1/connected-agents" \-H "Authorization: Bearer YOUR_API_KEY"
{"data": {"connectedAgents": [{"id": "8d3f2c1a-…","platform": "sierra","name": "Member support","description": "Answers general member questions","sip": {"address": "sip:+15551234567@acme.sip.example.com","domain": "acme.sip.example.com","user": "+15551234567","transport": "udp","credentials": null,"routingHeaders": [{"name": "X-Agent","configured": true}]},"language": "en","handBackRoutes": [{"value": "billing","label": "Billing","destination": {"type": "campaign","campaignId": "b7e1…"}},{"value": "front-desk","label": "+15557654321","destination": {"type": "number","phoneNumber": "+15557654321"}}],"handBackRouteCount": 2,"campaignCount": 1,"isOrgShared": true,"createdAt": "2026-10-06T16:20:00.000Z","updatedAt": "2026-10-06T16:20:00.000Z"}]},"meta": {"requestId": "req_…"}}
Connect an agent#
`sipUser` and `sipDomain` are the address the platform gave you, without `sip:` or `@`; see the overview for what each platform accepts. `language` is what we transcribe the call in: a BCP47 code, or `auto`. Send an Idempotency-Key to retry safely without connecting the agent twice.
/api/v1/connected-agentsBody
application/jsonplatform- "bland" | "cognigy" | "elevenlabs" | "koreai" | "polyai" | "retell" | "synthflow" | "vapi" | "sierra" (default)
name- string
- required
description- string
sipDomain- string
- required
sipUser- string
- required
transport- "udp" | "tcp" | "tls" | "tls-srtp"
sipCredentials- { username, password } (password write-only)
routingHeaders- { name, value }[] (values write-only)
language- string (BCP47 or "auto", default "en")
handBackRoutes- { value, label, destination }[]
isOrgShared- boolean
Responses
The connected agent.
curl -X POST "https://voice.evryhealth.com/api/v1/connected-agents" \-H "Authorization: Bearer YOUR_API_KEY" \-H "Content-Type: application/json" \-d '{"platform": "sierra","name": "Member support","sipDomain": "acme.sip.example.com","sipUser": "+15551234567","routingHeaders": [{"name": "X-Agent","value": "member-support"}],"language": "en","handBackRoutes": [{"value": "billing","label": "Billing","destination": {"type": "campaign","campaignId": "b7e1…"}}]}'
{"data": {"id": "8d3f2c1a-…","platform": "sierra","name": "Member support","description": "Answers general member questions","sip": {"address": "sip:+15551234567@acme.sip.example.com","domain": "acme.sip.example.com","user": "+15551234567","transport": "udp","credentials": null,"routingHeaders": [{"name": "X-Agent","configured": true}]},"language": "en","handBackRoutes": [{"value": "billing","label": "Billing","destination": {"type": "campaign","campaignId": "b7e1…"}},{"value": "front-desk","label": "+15557654321","destination": {"type": "number","phoneNumber": "+15557654321"}}],"handBackRouteCount": 2,"campaignCount": 1,"isOrgShared": true,"createdAt": "2026-10-06T16:20:00.000Z","updatedAt": "2026-10-06T16:20:00.000Z"},"meta": {"requestId": "req_…"}}
Get a connected agent#
One connected agent.
/api/v1/connected-agents/{agentId}Path parameters
agentIdstringrequired- Connected agent id.
Responses
The connected agent.
curl -X GET "https://voice.evryhealth.com/api/v1/connected-agents/{agentId}" \-H "Authorization: Bearer YOUR_API_KEY"
{"data": {"id": "8d3f2c1a-…","platform": "sierra","name": "Member support","description": "Answers general member questions","sip": {"address": "sip:+15551234567@acme.sip.example.com","domain": "acme.sip.example.com","user": "+15551234567","transport": "udp","credentials": null,"routingHeaders": [{"name": "X-Agent","configured": true}]},"language": "en","handBackRoutes": [{"value": "billing","label": "Billing","destination": {"type": "campaign","campaignId": "b7e1…"}},{"value": "front-desk","label": "+15557654321","destination": {"type": "number","phoneNumber": "+15557654321"}}],"handBackRouteCount": 2,"campaignCount": 1,"isOrgShared": true,"createdAt": "2026-10-06T16:20:00.000Z","updatedAt": "2026-10-06T16:20:00.000Z"},"meta": {"requestId": "req_…"}}
Update a connected agent#
Send only what changes. `routingHeaders` and `handBackRoutes` replace the whole list; in `routingHeaders`, a `value` of null keeps the value stored under that name. Takes the agent's owner or an admin of its organization; only the owner may change `sipDomain`, `sipUser`, `transport` or `isOrgShared`. In `sipCredentials`, a `password` of null keeps the stored one, and `sipCredentials: null` removes them.
/api/v1/connected-agents/{agentId}Path parameters
agentIdstringrequired- Connected agent id.
Body
application/jsonname- string
description- string | null
sipDomain- string
sipUser- string
transport- "udp" | "tcp" | "tls" | "tls-srtp"
sipCredentials- { username, password: string | null } | null
routingHeaders- { name, value: string | null }[]
language- string
handBackRoutes- { value, label, destination }[]
isOrgShared- boolean
Responses
The updated connected agent.
curl -X PATCH "https://voice.evryhealth.com/api/v1/connected-agents/{agentId}" \-H "Authorization: Bearer YOUR_API_KEY" \-H "Content-Type: application/json" \-d '{"routingHeaders": [{"name": "X-Agent","value": null}],"language": "es"}'
{"data": {"id": "8d3f2c1a-…","platform": "sierra","name": "Member support","description": "Answers general member questions","sip": {"address": "sip:+15551234567@acme.sip.example.com","domain": "acme.sip.example.com","user": "+15551234567","transport": "udp","credentials": null,"routingHeaders": [{"name": "X-Agent","configured": true}]},"language": "en","handBackRoutes": [{"value": "billing","label": "Billing","destination": {"type": "campaign","campaignId": "b7e1…"}},{"value": "front-desk","label": "+15557654321","destination": {"type": "number","phoneNumber": "+15557654321"}}],"handBackRouteCount": 2,"campaignCount": 1,"isOrgShared": true,"createdAt": "2026-10-06T16:20:00.000Z","updatedAt": "2026-10-06T16:20:00.000Z"},"meta": {"requestId": "req_…"}}
Delete a connected agent#
Takes the agent's owner or an admin of its organization. Refused with a 409 while inbound campaigns are set to use it, paused ones included; `details.campaignCount` says how many. Pass `detachCampaigns=true` to delete it anyway: those campaigns are left without an agent. Past calls and recordings are kept.
/api/v1/connected-agents/{agentId}Path parameters
agentIdstringrequired- Connected agent id.
Query parameters
detachCampaignsboolean- Delete it even though campaigns use it, leaving them without an agent.
Responses
Deleted.
curl -X DELETE "https://voice.evryhealth.com/api/v1/connected-agents/{agentId}" \-H "Authorization: Bearer YOUR_API_KEY"
{"data": {"deleted": true},"meta": {"requestId": "req_…"}}