Calls
Start outbound voice calls with use-case service routing, startup instructions, retry correlation, and best-effort patient lineage.
The outbound call endpoint lets you place a voice call from a workspace phone number to a patient. The request validates a patient entity and includes a reason; after the voice service accepts the call, the Platform API makes a best-effort attempt to emit lineage metadata on that entity.
The request requires a workspace API key, phone_to, use_case_id, reason, exactly one patient identifier, and a valid outbound voice setup. The service is resolved from the use case's workspace binding. No trigger or campaign resource is required for an on-demand call.
Quick Start
curl -X POST https://platform.api-us.concurrence.com/v1/{workspace_id}/calls/outbound \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"phone_to": "+18005551234",
"use_case_id": "cccccccc-0000-0000-0000-000000000001",
"patient_entity_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"reason": "lab_results"
}'The response includes a call_sid for tracking. Treat it as an opaque platform call handle: the current voice path returns a UUID-shaped reference, while older records can carry carrier-style identifiers. Store and return the exact string; do not require a CA prefix, require UUID syntax for historical records, or use it directly with a carrier API. It is distinct from the Runs API's run_id. When the best-effort lineage emit succeeds, event_id points to that world event; otherwise it is null:
{
"call_sid": "2c725f98-a9c6-4be2-8a48-6eb42d1f81cb",
"status": "queued",
"phone_from": "+12125559876",
"setup_id": null,
"event_id": "f9e8d7c6-b5a4-3210-fedc-ba0987654321"
}Endpoint
Initiate an outbound voice call from a workspace phone number. channel-manager selects the optimal number for the given use_case_id. Supports idempotency via the idempotency_key field.
API key issued via POST /v1/{workspace_id}/api-keys. Pass the returned api_key value as a Bearer token.
Request body for creating an outbound call.
use_case_id is required: channel-manager selects the optimal outbound
phone number bound to that use case (the sole caller-ID resolution path).
Destination phone number in E.164 format.
Channel-manager use case ID. channel-manager selects the optimal outbound phone number bound to this use case.
Patient entity UUID in the world model. Must exist in workspace as a person entity. Provide either patient_entity_id or patient_canonical_id.
Patient world model canonical_id of the form 'source:resource_type:id' (e.g. 'charmhealth:Patient:67890'). The structural regex on CanonicalIdString rejects spaces, names, DOBs, and similar regulated content so PHI cannot leak into audit events or pipeline projections. The raw value is deliberately not recorded in the outbound.initiated event — correlation back to the source system is via the resolved entity_id joined to world.entities_synced.canonical_id. Resolved against the SDP-projected world.entities_synced table; an entity created moments ago may not yet be visible if the projection is lagging. Provide either patient_entity_id or patient_canonical_id, not both.
^[A-Za-z0-9._-]+:[A-Za-z0-9._-]+:[A-Za-z0-9._-]+$Why the call is being made (e.g. appointment_reminder, follow_up, lab_results).
Optional system prompt override for this call.
What the call should accomplish. Injected into agent context.
Classification tags for analytics.
Prior call_sid if this is a callback from a previous call.
Client-provided idempotency key. Auto-generated if omitted.
World model outbound_task entity ID for completion feedback.
Successful Response
Response from creating an outbound call.
Twilio call SID for the outbound call
Initial call status (typically 'queued')
Resolved caller ID when use_case_id was used.
Channel-manager setup ID when use_case_id was used.
World event ID for the outbound.initiated event.
Invalid phone number format
No phone number available for use case
A call with this idempotency_key is still being placed (retry shortly), or the key was used for a different call
Validation Error
Rate limit exceeded
Upstream Twilio or voice agent error
The call could not be placed; retry with the same idempotency_key
Channel manager phone selection timed out
POST /v1/{workspace_id}/calls/outbound HTTP/1.1
Host: platform.api-us.concurrence.com
Authorization: Bearer YOUR_SECRET_TOKEN
Content-Type: application/json
Accept: */*
Content-Length: 442
{
"phone_to": "text",
"use_case_id": "123e4567-e89b-12d3-a456-426614174000",
"patient_entity_id": "123e4567-e89b-12d3-a456-426614174000",
"patient_canonical_id": "text",
"reason": "text",
"system_prompt": "text",
"goal": "text",
"tags": [
"text"
],
"metadata": {
"ANY_ADDITIONAL_PROPERTY": "text"
},
"context": {
"ANY_ADDITIONAL_PROPERTY": "text"
},
"derived_from_call_sid": "text",
"idempotency_key": "text",
"outbound_task_entity_id": "123e4567-e89b-12d3-a456-426614174000"
}{
"call_sid": "text",
"status": "text",
"phone_from": "text",
"setup_id": "123e4567-e89b-12d3-a456-426614174000",
"event_id": "123e4567-e89b-12d3-a456-426614174000"
}service_id is not a field on this request. Configure and inspect the use case's service binding; the Platform API validates start-time context against that bound service, and the voice runtime resolves its service through the same binding.
How It Works
When you create an outbound call, the platform coordinates the initial request and voice runtime while exposing later states separately:
Validation - The Platform API verifies that the patient resolves to a projected
personin the workspace.Voice preparation and dial - The voice service validates the service and use case, selects an assigned caller ID, prepares the first agent audio, and then asks the carrier to create the call. Preparation or routing failure can reject the request before a usable call is returned.
Connection - If the destination answers and the agent joins successfully, the runtime begins the configured outbound experience. Startup latency still depends on the selected runtime and providers; there is no one-second speech guarantee.
Conversation - The service's runtime determines available context, state-machine behavior, and tools. Outbound and inbound calls share infrastructure, but they are not contractually identical and do not universally expose every tool or clinical field.
Completion - Status and post-call artifacts become available as their operational stores and asynchronous analyses update.
The initial status is typically queued; later carrier/runtime states can include ringing, in-progress, completed, failed, no-answer, or busy. Use the canonical Runs inventory (kind=conversation&channel=voice) to browse call runs. The legacy GET /calls and GET /calls/{call_id} compatibility routes return Deprecation: true and Sunset: 2026-07-01; POST /calls/outbound remains current.
Startup Instructions and Patient Identity
When you provide a goal, the Platform API turns it into a startup instruction. If system_prompt is also present, the goal is appended to that instruction before it is sent to the voice service. In the standard voice runtime, this instruction takes precedence over task-derived greeting context; it does not replace the agent definition or Context Graph.
The patient identifier validates that a projected person exists in the workspace and anchors the best-effort outbound.initiated lineage event. The direct call request does not pass that entity ID to the voice service as a dedicated patient-context field. Any caller lookup or ambient context therefore depends on the configured service and the runtime's own identity-resolution path.
patient_entity_id or patient_canonical_id
Patient validation and best-effort lineage anchor; not a dedicated runtime context payload
goal
Startup instruction formatted as a call objective
system_prompt
Explicit startup instruction for the call
system_prompt + goal
Goal appended to the explicit startup instruction
Neither system_prompt nor goal
The service's normal outbound greeting and context behavior
Start-Time Tool Context
Use the request's context map for string and number values declared in the bound service's accepted_context. State-to-tool context_bindings select which parameters receive those values. Follow Pass Context to Tool Parameters for schema setup, received-input verification, and current runtime limits. On the live call, a tool bound to a declared key the request did not supply refuses to run until the key is set. To add a verified value after the call starts, see Set Context on a Live Voice Call.
The map is separate from startup instructions (goal and system_prompt), correlation metadata, and patient-identity resolution. Supplying a patient identifier in the map does not establish that person's identity or preload their world-model data. Automatic channel-id also depends on the runtime path described in the guide.
Phone Number Selection
Send phone_to as a valid international phone number in E.164 form. Validation checks the number, not just a leading + and digit count, so a placeholder with an invalid country or area code can be rejected. Accepted formatting does not prove reachability, ownership, or permission to call. Replace example numbers with an authorized test destination before dialing.
The caller ID is always resolved from the use case. The current selector chooses one phone number at random from the numbers assigned to that outbound-voice use case. The resolved number is returned as phone_from; the compatibility setup_id field is currently null.
Response:
Callback Chains
When a call is a follow-up to a previous conversation, pass derived_from_call_sid with the original call's identifier. When lineage emission succeeds, that identifier is stored in the new outbound.initiated event so your own queries can correlate the two calls. It does not create a world-model graph edge or prove that either call completed.
When the relevant initiation events are emitted and projected successfully, the patient's timeline can expose the call identifiers for correlation. Treat that timeline as asynchronous evidence of initiation, not as a guaranteed relationship view or confirmation that either call completed.
External System Correlation
Use tags and metadata to connect calls to records in your other systems. When lineage emission succeeds, these values are stored in the outbound.initiated event. They remain queryable event data but are not guaranteed to appear in every built-in analytics view.
Tags and metadata can support custom event filtering and joins back to source systems when the initiation event is available.
Idempotency
Include an idempotency_key on requests that may need an ambiguous retry:
A key stands for one call in your workspace for one hour. The key is claimed before anything is dialed:
A retry after the call was placed returns the original response without dialing again.
A retry while the first request is still placing the call returns
409. Wait briefly and retry with the same key.The same key sent with a different
use_case_id,phone_to, task entity, orcontextreturns409. Use a new key for a different call.If placing the call fails, the attempt is cleaned up and the key is released, so a retry with the same key places the call again.
If the platform cannot check the key, it returns
503and dials nothing. Retry with the same key.
A retry that replays the original response does not emit a second outbound.initiated event. If an outcome is still ambiguous, reconcile by call_sid or the Runs inventory before retrying. If you omit the key, the service generates one internally, so separate client retries do not share a deduplication key.
World Model Lineage
After the voice service accepts the outbound call, the Platform API attempts to emit an outbound.initiated event on the patient entity. Emission is best-effort: failure is logged, does not cancel the accepted call, and leaves event_id as null. A successfully emitted event captures:
Who requested the call - the API key identifier (not the secret)
Why - the
reasonandgoalyou providedWhat external systems are involved - your
tagsandmetadataWhere it fits in a sequence -
derived_from_call_sidif this is a callback
The event_id in the response points to this event. You can use it to:
Correlate initiation with available downstream call records by
call_sidQuery the patient's timeline for available initiation events
Build custom event analysis for API-initiated calls
Once the asynchronous world projection ingests the event, it can appear in the patient's timeline; read-after-write visibility is not immediate.
Error Responses
404
Patient entity does not exist in this workspace (or is not a person entity), the use case has no service binding, or no phone number is available for the use_case_id
Verify the patient_entity_id exists via GET /v1/{workspace_id}/world/entities/{entity_id}. For use case 404s, check the service binding and phone number provisioning.
409
A call with this idempotency_key is still being placed, or the key was already used for a different call
Retry shortly with the same key if the first request is still in flight. Use a new key for a different call.
422
Invalid E.164 phone number, invalid UUID, missing required field, invalid patient-identifier combination, context rejected by the bound service, or the call was refused by the channel provider
Supply phone_to, use_case_id, reason, exactly one patient identifier, and context matching the bound service schema. Do not retry a refused call unchanged.
429
Rate limit exceeded
Back off and retry. The limit applies per API key and per route (scoped to /calls/outbound).
502
The voice infrastructure returned an error
Retry with the same idempotency_key. A failed attempt is ended and its key released before the error returns, so the retry places the call.
503
The call could not be placed, or outbound calling is not configured for this workspace
Retry with the same idempotency_key. If it persists, contact support to confirm outbound calling is enabled.
504
The voice service did not respond in time; the call may still be placed
Retry with the same idempotency_key. A 409 means the first request is still placing the call; a later retry returns its original response.
Rate Limits
The outbound call endpoint allows 1000 requests per minute, enforced per API key and per route. This limit is scoped to /calls/outbound only; other Platform API routes apply their own limits.
If you exceed the limit, you receive a 429 response. Retry with the same idempotency_key; the key is claimed before dialing, so a retry that overlaps the first request returns 409 instead of placing a second call.
Carrier-level constraints on individual phone numbers are enforced separately by the downstream voice carrier and are independent of this API rate limit.
Full Example
A CRM webhook fires when a patient's lab results are ready. Your backend calls the outbound API to notify them:
The goal is appended to the prompt sent to the configured service. Patient-context availability depends on that service's runtime and tools, so validate the intended context path before relying on clinical fields during the call.
Troubleshooting
Call connects but patient hears silence/hold music
The prepared agent audio or conference join did not become available as expected.
Verify the use case's service binding, the service release, and outbound voice setup; inspect the resulting run and call status.
503 Service Unavailable
The call could not be placed, or call state could not be checked.
Retry with the same idempotency_key. If it persists, contact support to confirm outbound voice setup.
404 Not Found on patient_entity_id
The entity doesn't exist, isn't in this workspace, or isn't a person entity type.
Check the entity via GET /v1/{workspace_id}/world/entities/{entity_id}.
Call rings but goes to voicemail
The patient did not answer.
Check the Runs inventory. A later call attempt should use a deliberate retry policy and a new idempotency key; derived_from_call_sid can record correlation metadata.
Agent speaks but has no patient context
The direct endpoint validates the patient for lineage but does not pass the entity ID as a dedicated voice-runtime context field. Caller lookup, projection freshness, or service configuration may also be incomplete.
Verify the service's identity-resolution and world-tool path instead of assuming the request's patient identifier preloads context.
Last updated
Was this helpful?

