Calls
A call is one phone conversation between one of your agents and one phone number. Jelliu creates a call record for every outbound call it places (from POST /api/calls, a campaign or the MCP server) and for every inbound call that reaches one of your numbers. The record follows the call from the moment it is queued until it ends, and then collects what happened on it: status, outcome, duration, summary, transcript, structured analysis and cost telemetry.
This page covers placing a call, the checks every outbound call must pass, the status lifecycle, outcomes, durations and time limits, recordings, live transfers, hanging up, and how inbound calls are routed.
How it works
Every outbound call, whether you place it directly or a campaign dials it, goes through the same sequence of checks before the phone rings. The first check that fails stops the call and returns an error; no call record is created for checks that run before the record is written.
After the provider accepts the call, the rest of the lifecycle is asynchronous. When the conversation ends, the voice provider reports the transcript and analysis to Jelliu, which closes the record, computes the outcome and sends your webhooks. A background reconciler runs every minute and closes any call whose end was not reported, so a record never stays open forever.
The response to POST /api/calls means the provider accepted the call, not that anyone answered. Track the result with GET /api/calls/{callId} or, better, subscribe to call.completed and call.failed.
The call object
GET /api/calls and GET /api/calls/{callId} return the same shape. The list omits the heavier fields marked detail only. POST /api/calls returns the raw record right after dialing, without the computed fields analysis, has_recording, agent_name, campaign_name and contact_name.
Internal provider identifiers (the carrier call SID and the voice provider’s conversation id) are never returned.
Identity and routing
Lifecycle
Analysis
Telemetry
All telemetry fields are null when the provider did not report them.
Transfer
Status lifecycle
Terminal statuses never move back to a live status: a late or duplicated event cannot resurrect a closed call. The one upgrade that does happen is to completed: if a call was closed as failed, no-answer, busy or canceled and the provider later delivers a non-empty transcript for it, the status becomes completed because a conversation evidently took place.
Do not wait for in-progress to decide that an outbound call was answered. On the default outbound path it is never set: the call goes from ringing straight to a terminal status. Use the terminal status, duration_seconds and outcome instead.
Status values use hyphens (in-progress, no-answer); outcome values use underscores (no_answer). They are different fields with different vocabularies.
Outcomes
outcome says what the call accomplished, independently of how it ended technically. It is null until analysis is available, which can be seconds or minutes after the call ends.
A call counts as successful when its outcome is a success outcome for its category:
Two outcomes are written outside the analysis:
no_answerwhen a call ended without a transcript and never connected (statusno-answer,busy,canceledorfailed, or zero duration). A call that did connect but whose transcript is late keepsoutcome: nulluntil the analysis arrives.escalated_to_humanwhen you complete a warm transfer, unless the call already had an outcome.
Durations and time limits
duration_secondscomes from the voice provider when the conversation ends, rounded to whole seconds. If you hang up a call whose carrier leg had already ended, the carrier’s duration is recorded right away; if you hang up a live call, the duration arrives with the post-call report a few seconds later.started_atis the conversation start reported by the provider (for inbound calls, the moment the record is created).ended_atis when Jelliu closed the record.
Every voice call has a hard ceiling after which the platform ends it with a short goodbye. The ceiling comes from the agent’s category, and the agent’s own maximum call duration setting (30 to 3600 seconds, see Agents) can only shorten it:
During a free trial, each call is additionally capped at the trial minutes you have left (never less than 60 seconds).
If a call ends sooner than expected, read termination_reason: it distinguishes a call that hit the maximum duration from one where the agent ended the call itself.
Transcripts
The detail endpoint returns transcript as an array of turns in order:
Transcripts and summaries are kept for your plan’s retention window (7 days on Starter, 30 on Growth, 90 on Business, unlimited on Enterprise), or less if you set a shorter window. Messages from other channels live in Conversations.
Common tasks
Place an outbound call
Pick an agent and a destination
You need the id of a provisioned agent (see Agents) and either a phone number in E.164 format or the id of an existing contact.
Wait for the result
Subscribe to call.completed and call.failed webhooks, or poll GET /api/calls/{callId} until status is terminal.
Request body
The agent that places the call. It must belong to your workspace, must not be paused, and must have finished provisioning.
Destination in E.164: +, a country code that does not start with 0, and up to 15 digits in total, for example +573001234567. Required unless you send contactId. When both are sent, phoneNumber is the number dialed.
An existing contact to call. Its phone number is used when phoneNumber is omitted.
Files the call under a campaign: it counts toward the campaign’s concurrency limit, feeds its reporting, and gives the agent the campaign’s context (product_context, target_audience, campaign name). See Campaigns.
A/B testing: the prompt variant to attribute this call to.
Replaces the agent’s prompt for this call only. Up to 5000 characters. Text that looks like a prompt injection (for example “ignore previous instructions” or “actúa como”) is rejected.
Replaces the agent’s opening line for this call only. Up to 500 characters, same injection screening.
What Jelliu does with the destination
- Contact filing. The call is always filed under the contact that owns the dialed number. If you omit
contactId, Jelliu reuses the oldest existing contact with that number, or creates a contact without a campaign. If you send acontactIdwhose number differs fromphoneNumber, the call is filed under the contact atphoneNumberinstead. - Campaign consistency. With a
campaignIdand aphoneNumberthat differs from the named contact’s, the number must itself be a contact of that campaign; otherwise the call is refused with400 VALIDATION_FAILED:El número de destino no pertenece a esta campaña. Agrégalo como contacto de la campaña o haz la llamada sin campaña. - Attempt counting. The contact’s
call_attemptsincreases only after the provider accepts the call, so failures before dialing do not use up compliance attempts. - Context. The agent receives the contact’s name and what earlier interactions with this person captured, on any channel, so it does not greet a returning customer as a stranger.
- Disclosures. When the destination requires it, a recording notice is added to the agent’s greeting. A greeting that already announces recording is left as it is.
- Caller ID. Calls are placed from your workspace’s own number when one is provisioned. During a trial without one, a shared platform number is used. On an active paid plan with no provisioned number the call fails with
PHONE_NUMBER_REQUIRED; see Phone numbers.
Compliance checks
Every outbound call, including campaign calls, is checked against the compliance rules for the destination’s country. The country is read from the number’s prefix (+57 Colombia, +52 Mexico, +55 Brazil, +1 United States, +54 Argentina, +56 Chile, +51 Peru; anything else uses the default profile).
All of them return 403 COMPLIANCE_BLOCKED. You can change the per-country values in Settings → Compliance or through /api/compliance/configs.
The calling-hours window is evaluated in your workspace’s time zone when one is set in your settings, not in the time zone of the country being called. A workspace in Bogotá calling Mexico City is checked against 08:00 to 21:00 Bogotá time. Only when no workspace time zone is set does the country’s own zone apply (Bogotá for numbers outside the modeled countries).
Destination countries. Independently of the person being called, the destination itself must be allowed:
- Premium-rate and high-risk ranges (for example
+1900,+53, satellite+881to+883, and many Caribbean+1area codes) are always refused:Calls to premium-rate or high-risk numbers are blocked (toll fraud protection). - If you configured allowed calling destinations in Settings → Compliance, only those are reachable.
- Otherwise the allowed set is Colombia, Mexico, Brazil, United States and Canada (
+1), Argentina, Chile, Peru and Spain, plus the country you chose at onboarding and the countries of your own voice numbers. Anything else is refused withThis destination is outside the countries this workspace calls. Add it to the allowed calling destinations in Settings → Compliance.
Each refusal is written to your audit log with the masked number.
Abuse protection. A workspace can call the same number at most 5 times per hour (429 CALL_SPAM_DETECTED, Phone number called 6 times in the last hour (max 5)), and place at most 500 calls per hour in total. Trial workspaces calling a number already called by other trial workspaces can be refused with 403 TRIAL_ABUSE_DETECTED.
An opt-out is total. When a person says they do not want to be called again, Jelliu adds their number to the suppression list, marks the contact do-not-call and never dials them again, from any campaign or contact record. See Contacts.
List calls
GET /api/calls uses timestamp-cursor pagination. Filter with agentId and campaignId.
List responses are cached for up to 60 seconds and call details for up to 5 minutes. Status changes Jelliu applies itself (hang-ups, post-call reports, the reconciler) clear the cache, so a freshly ended call normally shows up right away.
Get one call
The response is { "data": { ... } } with every field in the call object, including transcript.
Download the recording
GET /api/calls/{callId}/audio streams the recording as binary audio (Content-Type from the provider, audio/mpeg by default). Check has_recording first: it is true once the call is over and has audio to play.
A missing recording is always a 404 with code CALL_NOT_FOUND. The X-Recording-Status response header tells the two common cases apart:
Audio is retained by the voice provider for your plan’s window: 7 days on Starter, 30 on Growth, 90 on Business, and until you delete it on Enterprise. Download recordings you need to keep. Responses carry Cache-Control: private, max-age=300.
Hang up a call
POST /api/calls/{callId}/hangup ends a live call. It is idempotent: calling it on a call that has already ended does nothing and still returns 200.
Jelliu first asks the carrier for the call’s real state, then records the right terminal status:
Hanging up ends the telephone line. The concurrency slot is released immediately; the post-call report still arrives and completes the record.
Transfer a live call to a person
A warm transfer brings a person into a live call. The caller is moved into a conference, Jelliu dials the person you name from your workspace’s number, and both end up on the line. You then either complete the transfer (the person keeps the call) or cancel it (the person’s leg is hung up and the AI agent picks the conversation back up).
These three endpoints are admin operations and require a key with the full scope.
Start the transfer
transfer_to must be an E.164 number and passes the same destination rules as outbound calls. The call must be live on the carrier: Jelliu checks the carrier directly, not the stored status.
What each step does to the call:
- Complete does not hang up anyone. It sets
outcometoescalated_to_humanif the call had no outcome yet, and the record closes normally when the caller hangs up. - Cancel hangs up the person’s leg, starts a new agent conversation on the caller’s line and clears
transferred_to,transferred_atandtransfer_call_sid. It fails if the call’s agent is paused or no longer provisioned.
Agents can also hand calls to a person on their own, through a transfer tool configured on the agent. That transfer is executed by the voice provider during the conversation; the destination number is checked against the same destination rules when you save it on the agent. See Agents.
Inbound calls
When someone calls one of your numbers, Jelliu admits the call only if your workspace is not suspended, your plan allows calls and a concurrency slot is free. Then it picks the agent:
- The agent assigned to the number. If that agent is paused, the call is not passed to another agent: pausing the assigned agent stops that number from answering.
- Routing among your agents, choosing the least busy provisioned agent that is not paused.
- Your oldest provisioned, unpaused agent, as a last resort.
Numbers that were removed (soft-deleted) never route calls. An IVR menu, when enabled on the number, is played before routing. Inbound call records are created with call_direction: "inbound", status in-progress and no campaign. Assign agents and configure IVR on the number; see Phone numbers.
Retries
Standalone calls placed with POST /api/calls are never retried. Calls that belong to a campaign and end no-answer, busy, or without connecting are re-dialed according to the campaign’s maxRetryAttempts and retryIntervalMinutes, and each retry passes all the checks on this page again. See Campaigns.
Errors
All errors use the standard error envelope. Codes you can receive from the calls endpoints:
Do not retry CALL_INITIATION_FAILED or a timeout on POST /api/calls blindly. If the provider accepted the call but a later bookkeeping step failed, Jelliu still returns 201 for the live call; a network timeout on your side may hide a call that is already ringing. List recent calls for the number before dialing again.
Limits
Webhooks
call.started, call.recording_ready and call.transcript_ready are accepted in subscriptions but are not sent yet. See Webhooks for payloads, signatures and retries.
In call.completed and call.failed, sentimentScore is 0 when sentiment was not measured, while the API returns sentiment_score: null for the same call. Read the call from the API when you need to tell “neutral” from “not measured”.
Related
Create and configure the agents that place and answer calls.
Dial many contacts on a schedule, with retries and concurrency limits.
Provision numbers, assign agents and configure inbound routing.
The people you call, their opt-outs and suppression.
WhatsApp, email and web chat conversations with the same agents.
Get notified when calls end.
Every calls endpoint, parameter and response.

