Aquant | Conversational AI
API Reference

Call API

Place outbound calls from an Aquant agent and read back what happened on them — duration, transcript, and an AI-generated summary — over plain HTTPS and JSON. This is the REST surface that the SIP and PSTN telephony integrations point at once a call has ended.

Protocol: HTTPS · JSON Auth: Bearer (session token) Base: /acai Endpoints: 5 Scope: one agent per token
API reference · updated 28 August 2026. A standalone reference for the ACAI Call API. Your Aquant contact issues the API key, secret, and agent id for each agent and environment — substitute the values you were given. See what changed in this revision.
Overview What you need Authentication Placing a call Call details Call summary Finding a call id Rate limits Errors Full example Changelog

Overview

The Call API is an ordinary HTTPS/JSON interface. You exchange your API credentials for a short-lived bearer token, then use it to make an agent call someone, or to read back what happened on a call that has already ended. Everything is scoped to the one agent your token was issued for.

What it does

Place a call Have the agent dial a phone number, or every active member of a caller group, and converse with whoever answers. See Placing a call.
Read call details How long the call lasted and the speaker-attributed transcript of it. See Call details.
Read the summary The AI-generated summary of the conversation, produced by post-call analysis. See Call summary.

Endpoints

MethodPathPurpose
POST/acai/authExchange credentials for a bearer token.
POST/acai/auth/revokeInvalidate a token immediately.
POST/acai/call/placePlace an outbound call.
GET/acai/call/{call_record_id}Duration and transcript.
GET/acai/call/{call_record_id}/summaryPost-call summary.
Base URL and the /acp alias. All paths are relative to https://acai-api.aquant.ai. The API is served under /acai; /acp is retained as a permanent alias so existing integrations keep working. New work should use /acai. The two are the same endpoints, and a token issued at one prefix works at the other.
This is the REST half of a telephony integration. If you are connecting a phone system to an agent rather than reading call data back, start with the SIP or PSTN reference. Both of them end by pointing here.

What you need before you start

API key & secretA credential pair for your organization, issued from the Aquant console. Distinct from the SIP username/password used for SIP digest authentication.
Agent idThe id of the agent this integration acts as. A token is bound to exactly one agent, and every endpoint here is scoped to it.
A sender idA short label naming your system — free text, up to 256 characters. It is recorded against the session so activity can be attributed back to the integration that caused it.
An agent phone numberOnly for placing calls. The agent needs a number provisioned to call out from; without one, call placement is rejected.
Where do these come from? Your Aquant administrator creates the API key and secret and tells you the agent id. Ask them to (re)issue one if you don't have it yet. Treat the secret as a password — it is only ever sent to /acai/auth, never on the call endpoints.

Authentication

Two steps, always. Exchange the long-lived key and secret for a short-lived token, then send that token as a bearer credential on every other call. The key and secret never appear on the call endpoints themselves.

POSThttps://acai-api.aquant.ai/acai/auth

Request

FieldType
api_keyrequired stringYour organization's API key.
api_secretrequired stringThe matching secret.
agent_idrequired stringThe agent to bind this session to.
sender_idrequired stringA label identifying your system. Max 256 characters.
POST https://acai-api.aquant.ai/acai/auth
Content-Type: application/json

{ "api_key": "...",
  "api_secret": "...",
  "agent_id": "a1f0c8d2-...",
  "sender_id": "crm-sync" }

Response

{
  "token": "acp_chat_token_9f3b...",
  "expires_in": 3600,
  "expires_at": "2026-08-28T11:42:07.315Z",
  "agent": {
    "agent_id": "a1f0c8d2-...",
    "agent_name": "Support Agent",
    "agent_phone": "+14155550123"
  }
}

Using the token

Send it as a bearer credential on every other endpoint:

Authorization: Bearer acp_chat_token_9f3b...

Token lifetime

Valid forOne hour from issue.
Sliding expiryEvery successful authorized call resets the clock to a full hour. A token used regularly stays alive; an idle one expires an hour after its last use.
On expiryRequests return 401. Call /acai/auth again for a new token — there is no refresh flow.
Bound toOne agent and one sender_id. To act as a second agent, get a second token.

Revoking a token

POSThttps://acai-api.aquant.ai/acai/auth/revoke
POST https://acai-api.aquant.ai/acai/auth/revoke
{ "token": "acp_chat_token_9f3b..." }

# → { "revoked": true }

Idempotent: revoking a token that is missing, already revoked, or already expired returns revoked: false rather than an error, so a cleanup path never has to special-case it.

Don't authenticate per request. Hold the token for its lifetime and reuse it. Every /acai/auth call is a credential check against the database, and the call endpoints are rate limited per token — so minting a fresh token for each request is both slower and no more generous.

Placing a call

Makes the agent dial out. The agent calls from its own provisioned number and, when the call is answered, converses with whoever picked up exactly as it would on an inbound call.

POSThttps://acai-api.aquant.ai/acai/call/place

Request

FieldType
to_numberone of stringA single recipient in E.164 form, e.g. +14155550123. Spaces, dashes and parentheses are stripped, and a missing + is added.
group_idone of stringA caller group. Every active member's phone number is dialed. See Calling a group.
call_paramsoptional objectFlat key/value pairs handed to the agent for this call, so the conversation can open with context your system already has — a ticket number, a customer name. Values are stringified.
Exactly one of to_number or group_id. Sending both, or neither, is a 422. There is no way to pass an ad-hoc list of numbers in one request; use a group, or one request per number.

Single recipient

POST https://acai-api.aquant.ai/acai/call/place
Authorization: Bearer <token>

{ "to_number": "+14155550123",
  "call_params": { "ticket": "INC-4821", "customer": "Acme Corp" } }
{
  "success": true,
  "call_sid": "CA7f21c0e9b34d...",
  "error": null,
  "total": 1,
  "succeeded": 1,
  "failed": 0
}
success: true means the call was placed, not answered. This endpoint is fire-and-forget: it returns as soon as the telephony provider accepts the request. Ringing, no answer, busy, voicemail and the conversation itself all happen afterwards. Nothing about the outcome comes back on this response — read it from call details or the summary once the call has ended.

Calling a group

With group_id, the group's active members are resolved to phone numbers, deduplicated, and dialed concurrently. The response shape changes: a per-target results array replaces the single call_sid.

{ "group_id": "3d81..." }

# →
{
  "success": true,
  "total": 3,
  "succeeded": 2,
  "failed": 1,
  "results": [
    { "to_number": "+14155550123", "success": true,  "call_sid": "CA7f21..." },
    { "to_number": "+14155550188", "success": true,  "call_sid": "CAb90c..." },
    { "to_number": "+14155550199", "success": false, "error": "..." }
  ]
}

Group rules

Maximum targets10 per request. Each target is a full AI conversation with live audio, so calls are capped far harder than SMS or email. A larger group is rejected rather than truncated.
Active members onlyInactive members are skipped silently and are not counted in total.
Partial successsuccess is true if at least one target was placed. Always check failed and the per-target results — a 200 does not mean every call went out.
Rate-limit costOne unit per target, not per request — see Rate limits.
Empty or inactive group400, and nothing is dialed. A group id that doesn't exist in your organization is 404.
In practice a group is limited to 5 targets, not 10. The two limits interact: the request cap is 10 targets, but a fan-out spends one rate-limit unit per target against a budget of 5 calls per minute. So a group of 6 to 10 passes the cap and is then rejected with 429 — nothing is dialed. Keep groups at 5 or fewer, or split them across minutes.
Calls cost money and reach real people. A misconfigured retry loop against this endpoint dials actual phones. Treat a 429 as back-pressure to respect, not an error to retry immediately, and don't retry a request whose outcome you don't know — you have no way to tell a failed placement from a placement whose response you lost.

Call details

How long a call lasted and what was said on it. Both are produced after the call ends, so this endpoint is designed to be polled: the status field tells you whether to poll again or stop.

GEThttps://acai-api.aquant.ai/acai/call/{call_record_id}
GET https://acai-api.aquant.ai/acai/call/6cbd7ac0-33ab-4790-9c3c-bdfc64a4e079
Authorization: Bearer <token>

Response

{
  "call_record_id": "6cbd7ac0-33ab-4790-9c3c-bdfc64a4e079",
  "status": "Successful",
  "duration_s": 184,
  "transcript": "Caller: Hi, I'm calling about...\nAgent: Of course — can you..."
}
FieldType
call_record_idstringEchoed back, so a response can be matched to its request in a batch.
statusenumSuccessful, Pending, or Error. Describes the whole payload, not the transcript alone.
duration_sint · nullableCall length in whole seconds. Best-effort — see below.
transcriptstring · nullableSpeaker-attributed plain text, one Speaker: line per row, in conversation order.

Status

Successful Pending Error
StatusMeaningWhat to do
SuccessfulEverything this endpoint can give you for that call is in the response.Consume it. Stop polling.
PendingThe call is still in progress, or it has ended and the transcript is still being produced.Poll again shortly.
ErrorThe details are permanently incomplete. Transcription failed, or it reported finishing but produced nothing usable.Stop polling. Nothing further will arrive.
Successful with a null transcript means the call was not recorded. That is a normal, healthy answer, not a failure: recording is a per-agent setting, and when it is off no transcript was ever expected. This is exactly why you should branch on status rather than on whether transcript is present — a null transcript alone cannot tell you whether to wait, give up, or accept the result.

How status is decided

Included so polling logic can be written against the real behaviour rather than inferred from observation.

SituationStatus
Call is still in progressPending
Call ended; recording was off for the agent, so nothing was capturedSuccessful (transcript null)
Call ended; transcription is queued or runningPending
Transcription finished and the transcript was readSuccessful
Transcription finished but the transcript is missing or emptyError
Transcription finished but could not be read right nowPending
Transcription failedError
duration_s can be null on a perfectly successful call, and never affects status. Duration is written by the telephony provider's end-of-call notification; a call that ends by the audio stream stopping or the far end dropping can complete normally without one being recorded. Treat it as best-effort metadata: use it when present, don't infer failure from its absence, and don't use it to decide whether a call happened. It is returned whenever it is known, including alongside Pending and Error.
Polling guidance. Poll at a few seconds' interval, not in a tight loop; the endpoint allows 10 requests per minute per token. A long call stays Pending for its whole duration plus transcription time, so back off rather than hammering. A call whose end was never registered — a rare infrastructure failure mid-call — stops reporting Pending after six hours, so a poller cannot be stuck on one record forever.

Call summary

The AI-generated narrative summary of the conversation, produced by post-call analysis. Separate from call details because it comes from a later stage of the pipeline and has its own readiness vocabulary.

GEThttps://acai-api.aquant.ai/acai/call/{call_record_id}/summary
GET https://acai-api.aquant.ai/acai/call/6cbd7ac0-.../summary
Authorization: Bearer <token>

# →
{
  "call_record_id": "6cbd7ac0-33ab-4790-9c3c-bdfc64a4e079",
  "status": "Ready",
  "summary": "The caller reached out for assistance regarding..."
}
StatusMeaningWhat to do
ReadySummary is available in summary.Consume it.
PendingAnalysis is still running, or the text is mid-write.Poll again shortly.
FailedAnalysis ran and failed.Stop polling; no summary will arrive.
UnavailableAnalysis was never requested for this call — recording or analysis is off on the agent.Stop polling; enable the feature on the agent if you need summaries.
The two endpoints use different status vocabularies on purpose. Call details answers "is this whole payload final?" with Successful / Pending / Error. The summary answers "is the analysis stage done?" with Ready / Pending / Failed / Unavailable. Don't write one status handler for both — in particular, an unrecorded call is Successful on details but Unavailable on the summary, because there was nothing to summarize.

Finding a call id

Both read endpoints are addressed by call_record_id, the platform's own identifier for a call. This is not the same value as the telephony provider's call_sid, and knowing where each one comes from saves a lot of confusion.

call_record_idAquant's identifier for the call. A UUID. The value the details and summary endpoints expect in the path.
call_sidThe telephony provider's identifier, returned by call placement. Useful for correlating with your carrier's records. Not accepted by the read endpoints.

Where to get one

Source
SIP handoverA handed-over leg carries the call id in a User-to-User header as hex-encoded JSON; decode it to UTF-8, parse the JSON, and read the uri field. See the SIP reference. This is the primary programmatic source.
Console call historyEvery call is listed with its id, for looking one up by hand or during development.
Known gap: calls you place through this API don't hand you a call_record_id. /acai/call/place returns the provider's call_sid, and there is currently no endpoint that translates a call_sid into a call_record_id, nor one that lists an agent's recent calls. So an integration that places a call cannot yet fetch that same call's details on its own. If your use case needs this, raise it with your Aquant contact — it is a recognised limitation rather than a documented workflow.
Ids are scoped to the token's agent. A call handled by any other agent returns 404 — the same answer as an id that doesn't exist, deliberately, so the endpoint can't be used to discover which ids are real. Use the token belonging to the agent that handled the call.

Rate limits

Two limits apply to every request: a ceiling across the whole token, and a tighter per-endpoint budget. Both are per-token, per-minute. Exceeding either returns 429.

ScopeLimit
Any endpoint, per token30 / minThe overall ceiling. Every authorized request counts.
POST /acai/call/place5 / minCalls cost real money, so they are capped hard. A group consumes one unit per target.
GET /acai/call/{id}10 / minEach request reads the transcript from storage and can return a whole call's text.
GET /acai/call/{id}/summary20 / minCheaper and text-only, so more generous — but still below the token ceiling, so polling can't starve your sends.
The per-endpoint budgets sit under the token ceiling deliberately. No amount of polling can consume the whole 30-per-minute allowance and lock out call placement on the same token. If you need more headroom, use a separate token for polling.
Reading a call you don't own still costs you a unit. The limit is charged before the ownership check, so probing for call ids burns the prober's own budget. A 404 is not free.

Errors

Standard HTTP status codes. The body carries a detail string describing what went wrong.

CodeWhen
400A well-formed request that can't be carried out: recipient number isn't valid E.164, the agent has no phone number to call from, the caller group is inactive, or the group resolved to no active members.
401Bad API key or secret on /acai/auth; or a missing, malformed, expired, or revoked bearer token on any other endpoint. Re-authenticate.
404The call id doesn't exist or belongs to a different agent than the token's — the two are indistinguishable by design. Also returned for a caller group that isn't in your organization.
422The body failed schema validation: a required field is missing, or the exactly-one-of rule between to_number and group_id was broken by supplying both or neither.
429A rate limit was exceeded. Back off; limits reset on a one-minute boundary.
A retired agent's call history stays readable. The read endpoints deliberately don't require the bound agent to still be active, so deactivating an agent doesn't make its past calls inaccessible. Placing a call does require an active agent.

Full example

Authenticate, place a call, then poll for the transcript once it has ended.

  1. Get a token

    curl -X POST https://acai-api.aquant.ai/acai/auth \
      -H 'Content-Type: application/json' \
      -d '{"api_key":"...","api_secret":"...",
           "agent_id":"a1f0c8d2-...","sender_id":"crm-sync"}'
    
    # → { "token": "acp_chat_token_9f3b...", "expires_in": 3600, ... }
  2. Place the call

    curl -X POST https://acai-api.aquant.ai/acai/call/place \
      -H 'Authorization: Bearer acp_chat_token_9f3b...' \
      -H 'Content-Type: application/json' \
      -d '{"to_number":"+14155550123",
           "call_params":{"ticket":"INC-4821"}}'
    
    # → { "success": true, "call_sid": "CA7f21...", ... }
    # The call is now ringing. Nothing more arrives on this response.
  3. Read the details once the call has ended

    Using a call_record_id obtained as described in Finding a call id.

    curl https://acai-api.aquant.ai/acai/call/6cbd7ac0-... \
      -H 'Authorization: Bearer acp_chat_token_9f3b...'
    
    # → { "status": "Pending",    "duration_s": null, "transcript": null }  poll again
    # → { "status": "Successful", "duration_s": 184,  "transcript": "Caller: ..." }  done
  4. Branch on status, not on the fields

    # pseudocode
    while true:
        r = get(f"/acai/call/{call_record_id}")
        if r.status == "Successful": return r.transcript   # may be null: not recorded
        if r.status == "Error":      return None           # permanent; stop
        sleep(5)                                          # Pending; 10/min budget

Changelog

What changed in this revision of the reference. Behaviour that was already live but undocumented is marked clarified.

DateChange
28 Aug 2026 Initial reference. The Call API gathered into one page: session authentication and revocation, outbound call placement including group fan-out, call details, and the post-call summary.
Call details endpoint. New GET /acai/call/{id} returning duration and the speaker-attributed transcript, with a Successful / Pending / Error readiness status covering the whole payload.
Status derivation published. The full situation-to-status table for call details, so polling logic can be written against the actual behaviour.
Rate limits published. The token-wide ceiling and all per-endpoint budgets, including the per-target cost of a group fan-out.
Clarified: duration_s is best-effort and may be null on a successful call; Successful with a null transcript means the call was not recorded; the details and summary endpoints use different status vocabularies deliberately; a foreign call id returns 404 rather than 403 so ids cannot be enumerated; a rate-limit unit is charged before the ownership check; a retired agent's call history remains readable; calls placed through the API do not currently yield a call_record_id.