Skip to content
Back to Crang

Developers

Connect your CRM or your own app to your Crang workspace with the REST API, get events as webhooks, or let an AI assistant work with it over MCP. Everything is secured with an API key you create in Settings, Integrations.

Overview

Your workspace has three ways in: a REST API to read and change data, webhooks that call you when something happens, and an MCP server for AI assistants. Each one belongs to a single workspace: the API key decides which.

REST base URL
https://api.crang.ai/v1
MCP server URL
https://mcp.crang.ai/mcp

Versioning

The version is in the path (/v1). New routes and new response fields are added within a version, so ignore fields you do not know. A breaking change ships as a new version, and the old one keeps working for a notice period.

Authentication

Create an API key in Settings, Integrations, and send it on every request as Authorization: Bearer <key>. The key is shown once: store it like a password.

A key belongs to one workspace and acts with the role of the person who created it. The workspace is never part of a request: there is no workspace id to send, and none that could be changed.

Each route needs one scope on the key. Without it the API answers 403. A missing, unknown, revoked or expired key answers 401.

curl "https://api.crang.ai/v1/usage" \
  -H "Authorization: Bearer $CRANG_API_KEY"

Scopes

You choose the scopes when you create a key. A key never gets more than its creator's role allows.

ScopeAllows
calls:readRead calls, transcripts and results
contacts:readRead contact lists and contacts
contacts:writeImport contacts and add numbers to the do-not-call list
campaigns:readRead campaigns and their status
campaigns:writeCreate and edit campaigns
campaigns:runStart, pause, resume and cancel campaigns
agents:readList agents and read their settings
agents:writeEdit the draft of an agent. Nothing goes live
agents:publishPublish an agent. Changes go live on real calls
knowledge:readList knowledge base documents
knowledge:writeAdd knowledge base documents. They join every agent, live versions included, so the assistant also needs agents:publish
numbers:readList phone numbers and the agent that answers each
analytics:readRead analytics and goal results
usage:readRead minutes left and usage
mcpConnect an AI assistant to the MCP server

Errors

Errors are JSON, { "error": { "code": "...", "details": ... } }. Branch on code, never on the HTTP text.

StatusCodeMeaning
400common.validationThe request is not valid. details lists the problems.
401api_keys.invalidThe key is missing, unknown, revoked or expired.
402tenant.subscription_suspendedThe workspace subscription is suspended.
403api_keys.scope_missingThe key lacks the scope the route needs (details.scope), or the integration is turned off for the workspace.
404common.not_foundNothing with that id in your workspace.
429common.rate_limitedToo many requests. Wait the Retry-After header's seconds.

Rate limits

By default 120 requests per minute per key and 600 per minute per workspace across all its keys, shared by the REST API and the MCP server. Beyond that the API answers 429 with a Retry-After header (seconds).

REST API

Requests and answers are JSON unless a route says otherwise. Fields marked required must be sent. In answers, ignore fields you do not know.

Contacts

GET/v1/contact-lists

List contact lists

Required scopecontacts:read

Response 200
FieldTypeDescription
idrequiredstring (uuid)
namerequiredstring
statusrequired"importing" | "ready" | "failed"
contactsrequiredinteger
reportrequiredobject | null
report.rowsrequiredinteger
report.importedrequiredinteger
report.invalidrequiredinteger
report.duplicatesrequiredinteger
report.suppressedrequiredinteger
report.invalidRowsrequiredobject[]
report.invalidRows[].rowrequiredinteger
report.invalidRows[].reasonrequired"phone_missing" | "phone_invalid" | "phone_premium" | "timezone_invalid" | "language_invalid"
variablesrequiredstring[]
lawfulBasisConfirmedAtrequiredstring (date-time)
createdAtrequiredstring (date-time)
Example request
curl "https://api.crang.ai/v1/contact-lists" \
  -H "Authorization: Bearer $CRANG_API_KEY"
POST/v1/contact-lists

Import a CSV into a new contact list

Answers 202 with the list in importing; poll the list for ready and its report, or subscribe to contact_list.imported.

Required scopecontacts:write

Request body · Multipart form
FieldTypeDescription
filerequiredstring (binary)CSV with a header row
namerequiredstring
mappingrequiredstringJSON: {"phone": "<column>", "name"?, "language"?, "timezone"?, "externalId"?, "variables"?: {"<call variable>": "<column>"}}
lawfulBasisrequired"true"Confirms the workspace may call these people
Response 202
FieldTypeDescription
idrequiredstring (uuid)
namerequiredstring
statusrequired"importing" | "ready" | "failed"
contactsrequiredinteger
reportrequiredobject | null
report.rowsrequiredinteger
report.importedrequiredinteger
report.invalidrequiredinteger
report.duplicatesrequiredinteger
report.suppressedrequiredinteger
report.invalidRowsrequiredobject[]
report.invalidRows[].rowrequiredinteger
report.invalidRows[].reasonrequired"phone_missing" | "phone_invalid" | "phone_premium" | "timezone_invalid" | "language_invalid"
variablesrequiredstring[]
lawfulBasisConfirmedAtrequiredstring (date-time)
createdAtrequiredstring (date-time)
Example request
curl -X POST "https://api.crang.ai/v1/contact-lists" \
  -H "Authorization: Bearer $CRANG_API_KEY" \
  -F file='@contacts.csv' \
  -F name='Spring customers' \
  -F mapping='{"phone": "Phone"}' \
  -F lawfulBasis='true'
GET/v1/contact-lists/{id}

Get a contact list

Required scopecontacts:read

Parameters
FieldTypeDescription
idrequiredstring (uuid) · path
Response 200
FieldTypeDescription
idrequiredstring (uuid)
namerequiredstring
statusrequired"importing" | "ready" | "failed"
contactsrequiredinteger
reportrequiredobject | null
report.rowsrequiredinteger
report.importedrequiredinteger
report.invalidrequiredinteger
report.duplicatesrequiredinteger
report.suppressedrequiredinteger
report.invalidRowsrequiredobject[]
report.invalidRows[].rowrequiredinteger
report.invalidRows[].reasonrequired"phone_missing" | "phone_invalid" | "phone_premium" | "timezone_invalid" | "language_invalid"
variablesrequiredstring[]
lawfulBasisConfirmedAtrequiredstring (date-time)
createdAtrequiredstring (date-time)
Example request
curl "https://api.crang.ai/v1/contact-lists/<id>" \
  -H "Authorization: Bearer $CRANG_API_KEY"
GET/v1/contact-lists/{id}/contacts

List the contacts of a list

Required scopecontacts:read

Parameters
FieldTypeDescription
idrequiredstring (uuid) · path
cursorstring (uuid) · query
limitinteger · query = 50
Response 200
FieldTypeDescription
itemsrequiredobject[]
items[].idrequiredstring (uuid)
items[].phonerequiredstring
items[].namerequiredstring | null
items[].languagerequiredstring | null
items[].timezonerequiredstring | null
items[].externalIdrequiredstring | null
items[].variablesrequiredobject<string, string>
items[].createdAtrequiredstring (date-time)
nextCursorrequiredstring (uuid) | null
Example request
curl "https://api.crang.ai/v1/contact-lists/<id>/contacts" \
  -H "Authorization: Bearer $CRANG_API_KEY"
POST/v1/dnc

Add numbers to the do-not-call list

400 with the numbers that are not valid in details.invalid.

Required scopecontacts:write

Request body · JSON
FieldTypeDescription
phonesrequiredstring[]
sourcerequired"manual" | "csv"
notestring
Response 200
FieldTypeDescription
addedrequiredinteger
Example request
curl -X POST "https://api.crang.ai/v1/dnc" \
  -H "Authorization: Bearer $CRANG_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"phones":["string"],"source":"manual"}'
POST/v1/contacts/{id}/erase

Erase a person (GDPR erasure request)

Deletes every contact with this contact's number, in every list, and clears the number, transcript, summary, captured data and free-text analysis of their calls (outcome, minutes and goal results stay in the counts). A do-not-call entry for the number stays as a suppression record without the number, so the number is still not called. Only for a key whose creator is the workspace owner (403 otherwise). 409 contacts.erase_call_recent while a call with the person runs or ended less than 10 minutes ago.

Required scopecontacts:write

Parameters
FieldTypeDescription
idrequiredstring (uuid) · path
Response 200
FieldTypeDescription
contactsrequiredinteger
callsrequiredinteger
Example request
curl -X POST "https://api.crang.ai/v1/contacts/<id>/erase" \
  -H "Authorization: Bearer $CRANG_API_KEY"

Campaigns

GET/v1/campaigns

List campaigns

Required scopecampaigns:read

Response 200
FieldTypeDescription
namerequiredstring
agentIdrequiredstring (uuid)
listIdsrequiredstring (uuid)[]
phoneNumberIdsrequiredstring (uuid)[]
startsAtrequiredstring (date-time) | null
settingsrequiredobject
settings.timezonerequiredstring
settings.windowsrequiredobject[]
settings.windows[].daysrequiredinteger[]
settings.windows[].startrequiredstring
settings.windows[].endrequiredstring
settings.maxAttemptsrequiredinteger
settings.retryMinutesrequiredinteger
settings.retryOnrequired"no_answer" | "busy" | "failed" | "voicemail" | "cut_off"[]
settings.maxConcurrentrequiredinteger
settings.callsPerMinuterequiredinteger
settings.ringTimeoutSecondsrequiredinteger
settings.maxDurationSecondsrequiredinteger
settings.protectionobjectProtection overrides: pause automatically, minute limit, slow start and stop values
settings.protection.autoPausebooleanPause the campaign automatically when its recent calls look bad. Default false: the campaign only shows its metrics. The minute limit applies either way.
settings.protection.minuteLimitinteger | nullMost minutes this campaign may use (debited plus in progress); null or left out: no limit
settings.protection.warmupbooleanSlow start: the first calls run a few at a time. Default false.
settings.protection.stopsobject<string, integer | null>Per indicator, the percent of the last finished calls at which the campaign pauses (answer_rate: pauses when under it); null turns that stop off. technical_failures is between 5 and 60 and cannot be null. Left out: the workspace default.
idrequiredstring (uuid)
statusrequired"draft" | "scheduled" | "running" | "paused" | "completed" | "cancelled"
pauseReasonrequired"manual" | "minutes" | "protection" | null
createdAtrequiredstring (date-time)
startedAtrequiredstring (date-time) | null
endedAtrequiredstring (date-time) | null
progressrequiredobject<string, integer>
statsrequiredobject
stats.callsrequiredinteger
stats.answeredrequiredinteger
stats.successfulrequiredinteger
stats.secondsrequiredinteger
stats.hourlyrequiredobject[]
stats.hourly[].hourrequiredstring (date-time)
stats.hourly[].callsrequiredinteger
Example request
curl "https://api.crang.ai/v1/campaigns" \
  -H "Authorization: Bearer $CRANG_API_KEY"
GET/v1/campaigns/{id}

Get a campaign

Required scopecampaigns:read

Parameters
FieldTypeDescription
idrequiredstring (uuid) · path
Response 200
FieldTypeDescription
namerequiredstring
agentIdrequiredstring (uuid)
listIdsrequiredstring (uuid)[]
phoneNumberIdsrequiredstring (uuid)[]
startsAtrequiredstring (date-time) | null
settingsrequiredobject
settings.timezonerequiredstring
settings.windowsrequiredobject[]
settings.windows[].daysrequiredinteger[]
settings.windows[].startrequiredstring
settings.windows[].endrequiredstring
settings.maxAttemptsrequiredinteger
settings.retryMinutesrequiredinteger
settings.retryOnrequired"no_answer" | "busy" | "failed" | "voicemail" | "cut_off"[]
settings.maxConcurrentrequiredinteger
settings.callsPerMinuterequiredinteger
settings.ringTimeoutSecondsrequiredinteger
settings.maxDurationSecondsrequiredinteger
settings.protectionobjectProtection overrides: pause automatically, minute limit, slow start and stop values
settings.protection.autoPausebooleanPause the campaign automatically when its recent calls look bad. Default false: the campaign only shows its metrics. The minute limit applies either way.
settings.protection.minuteLimitinteger | nullMost minutes this campaign may use (debited plus in progress); null or left out: no limit
settings.protection.warmupbooleanSlow start: the first calls run a few at a time. Default false.
settings.protection.stopsobject<string, integer | null>Per indicator, the percent of the last finished calls at which the campaign pauses (answer_rate: pauses when under it); null turns that stop off. technical_failures is between 5 and 60 and cannot be null. Left out: the workspace default.
idrequiredstring (uuid)
statusrequired"draft" | "scheduled" | "running" | "paused" | "completed" | "cancelled"
pauseReasonrequired"manual" | "minutes" | "protection" | null
createdAtrequiredstring (date-time)
startedAtrequiredstring (date-time) | null
endedAtrequiredstring (date-time) | null
progressrequiredobject<string, integer>
statsrequiredobject
stats.callsrequiredinteger
stats.answeredrequiredinteger
stats.successfulrequiredinteger
stats.secondsrequiredinteger
stats.hourlyrequiredobject[]
stats.hourly[].hourrequiredstring (date-time)
stats.hourly[].callsrequiredinteger
Example request
curl "https://api.crang.ai/v1/campaigns/<id>" \
  -H "Authorization: Bearer $CRANG_API_KEY"
POST/v1/campaigns/{id}/start

Start a campaign

Required scopecampaigns:run

Parameters
FieldTypeDescription
idrequiredstring (uuid) · path
Response 200
FieldTypeDescription
namerequiredstring
agentIdrequiredstring (uuid)
listIdsrequiredstring (uuid)[]
phoneNumberIdsrequiredstring (uuid)[]
startsAtrequiredstring (date-time) | null
settingsrequiredobject
settings.timezonerequiredstring
settings.windowsrequiredobject[]
settings.windows[].daysrequiredinteger[]
settings.windows[].startrequiredstring
settings.windows[].endrequiredstring
settings.maxAttemptsrequiredinteger
settings.retryMinutesrequiredinteger
settings.retryOnrequired"no_answer" | "busy" | "failed" | "voicemail" | "cut_off"[]
settings.maxConcurrentrequiredinteger
settings.callsPerMinuterequiredinteger
settings.ringTimeoutSecondsrequiredinteger
settings.maxDurationSecondsrequiredinteger
settings.protectionobjectProtection overrides: pause automatically, minute limit, slow start and stop values
settings.protection.autoPausebooleanPause the campaign automatically when its recent calls look bad. Default false: the campaign only shows its metrics. The minute limit applies either way.
settings.protection.minuteLimitinteger | nullMost minutes this campaign may use (debited plus in progress); null or left out: no limit
settings.protection.warmupbooleanSlow start: the first calls run a few at a time. Default false.
settings.protection.stopsobject<string, integer | null>Per indicator, the percent of the last finished calls at which the campaign pauses (answer_rate: pauses when under it); null turns that stop off. technical_failures is between 5 and 60 and cannot be null. Left out: the workspace default.
idrequiredstring (uuid)
statusrequired"draft" | "scheduled" | "running" | "paused" | "completed" | "cancelled"
pauseReasonrequired"manual" | "minutes" | "protection" | null
createdAtrequiredstring (date-time)
startedAtrequiredstring (date-time) | null
endedAtrequiredstring (date-time) | null
progressrequiredobject<string, integer>
statsrequiredobject
stats.callsrequiredinteger
stats.answeredrequiredinteger
stats.successfulrequiredinteger
stats.secondsrequiredinteger
stats.hourlyrequiredobject[]
stats.hourly[].hourrequiredstring (date-time)
stats.hourly[].callsrequiredinteger
Example request
curl -X POST "https://api.crang.ai/v1/campaigns/<id>/start" \
  -H "Authorization: Bearer $CRANG_API_KEY"
POST/v1/campaigns/{id}/pause

Pause a campaign

Required scopecampaigns:run

Parameters
FieldTypeDescription
idrequiredstring (uuid) · path
Response 200
FieldTypeDescription
namerequiredstring
agentIdrequiredstring (uuid)
listIdsrequiredstring (uuid)[]
phoneNumberIdsrequiredstring (uuid)[]
startsAtrequiredstring (date-time) | null
settingsrequiredobject
settings.timezonerequiredstring
settings.windowsrequiredobject[]
settings.windows[].daysrequiredinteger[]
settings.windows[].startrequiredstring
settings.windows[].endrequiredstring
settings.maxAttemptsrequiredinteger
settings.retryMinutesrequiredinteger
settings.retryOnrequired"no_answer" | "busy" | "failed" | "voicemail" | "cut_off"[]
settings.maxConcurrentrequiredinteger
settings.callsPerMinuterequiredinteger
settings.ringTimeoutSecondsrequiredinteger
settings.maxDurationSecondsrequiredinteger
settings.protectionobjectProtection overrides: pause automatically, minute limit, slow start and stop values
settings.protection.autoPausebooleanPause the campaign automatically when its recent calls look bad. Default false: the campaign only shows its metrics. The minute limit applies either way.
settings.protection.minuteLimitinteger | nullMost minutes this campaign may use (debited plus in progress); null or left out: no limit
settings.protection.warmupbooleanSlow start: the first calls run a few at a time. Default false.
settings.protection.stopsobject<string, integer | null>Per indicator, the percent of the last finished calls at which the campaign pauses (answer_rate: pauses when under it); null turns that stop off. technical_failures is between 5 and 60 and cannot be null. Left out: the workspace default.
idrequiredstring (uuid)
statusrequired"draft" | "scheduled" | "running" | "paused" | "completed" | "cancelled"
pauseReasonrequired"manual" | "minutes" | "protection" | null
createdAtrequiredstring (date-time)
startedAtrequiredstring (date-time) | null
endedAtrequiredstring (date-time) | null
progressrequiredobject<string, integer>
statsrequiredobject
stats.callsrequiredinteger
stats.answeredrequiredinteger
stats.successfulrequiredinteger
stats.secondsrequiredinteger
stats.hourlyrequiredobject[]
stats.hourly[].hourrequiredstring (date-time)
stats.hourly[].callsrequiredinteger
Example request
curl -X POST "https://api.crang.ai/v1/campaigns/<id>/pause" \
  -H "Authorization: Bearer $CRANG_API_KEY"
POST/v1/campaigns/{id}/resume

Resume a paused campaign

Required scopecampaigns:run

Parameters
FieldTypeDescription
idrequiredstring (uuid) · path
Response 200
FieldTypeDescription
namerequiredstring
agentIdrequiredstring (uuid)
listIdsrequiredstring (uuid)[]
phoneNumberIdsrequiredstring (uuid)[]
startsAtrequiredstring (date-time) | null
settingsrequiredobject
settings.timezonerequiredstring
settings.windowsrequiredobject[]
settings.windows[].daysrequiredinteger[]
settings.windows[].startrequiredstring
settings.windows[].endrequiredstring
settings.maxAttemptsrequiredinteger
settings.retryMinutesrequiredinteger
settings.retryOnrequired"no_answer" | "busy" | "failed" | "voicemail" | "cut_off"[]
settings.maxConcurrentrequiredinteger
settings.callsPerMinuterequiredinteger
settings.ringTimeoutSecondsrequiredinteger
settings.maxDurationSecondsrequiredinteger
settings.protectionobjectProtection overrides: pause automatically, minute limit, slow start and stop values
settings.protection.autoPausebooleanPause the campaign automatically when its recent calls look bad. Default false: the campaign only shows its metrics. The minute limit applies either way.
settings.protection.minuteLimitinteger | nullMost minutes this campaign may use (debited plus in progress); null or left out: no limit
settings.protection.warmupbooleanSlow start: the first calls run a few at a time. Default false.
settings.protection.stopsobject<string, integer | null>Per indicator, the percent of the last finished calls at which the campaign pauses (answer_rate: pauses when under it); null turns that stop off. technical_failures is between 5 and 60 and cannot be null. Left out: the workspace default.
idrequiredstring (uuid)
statusrequired"draft" | "scheduled" | "running" | "paused" | "completed" | "cancelled"
pauseReasonrequired"manual" | "minutes" | "protection" | null
createdAtrequiredstring (date-time)
startedAtrequiredstring (date-time) | null
endedAtrequiredstring (date-time) | null
progressrequiredobject<string, integer>
statsrequiredobject
stats.callsrequiredinteger
stats.answeredrequiredinteger
stats.successfulrequiredinteger
stats.secondsrequiredinteger
stats.hourlyrequiredobject[]
stats.hourly[].hourrequiredstring (date-time)
stats.hourly[].callsrequiredinteger
Example request
curl -X POST "https://api.crang.ai/v1/campaigns/<id>/resume" \
  -H "Authorization: Bearer $CRANG_API_KEY"

Calls

GET/v1/calls

List calls

Required scopecalls:read

Parameters
FieldTypeDescription
cursorstring (uuid) · query
limitinteger · query = 50
direction"inbound" | "outbound" · query
agentIdstring (uuid) · query
campaignIdstring (uuid) · query
contactIdstring (uuid) · query
phoneNumberIdstring (uuid) · query
outcome"answered" | "voicemail" | "no_answer" | "busy" | "failed" · query
success"all_met" | "any_failed" | "unknown" · query
fromstring (date-time) · query
tostring (date-time) · query
minSecondsinteger · query
maxSecondsinteger · query
qstring · query
transferredstring · query
cutOffstring · query
Response 200
FieldTypeDescription
itemsrequiredobject[]
items[].idrequiredstring (uuid)
items[].directionrequired"inbound" | "outbound"
items[].statusrequired"active" | "ended"
items[].outcomerequired"answered" | "voicemail" | "no_answer" | "busy" | "failed" | null
items[].successrequired"all_met" | "any_failed" | "unknown" | null
items[].agentrequiredobject | null
items[].agent.idrequiredstring (uuid)
items[].agent.namerequiredstring
items[].campaignrequiredobject | null
items[].campaign.idrequiredstring (uuid)
items[].campaign.namerequiredstring
items[].contactrequiredobject | null
items[].contact.idrequiredstring (uuid)
items[].contact.namerequiredstring | null
items[].phonerequiredstring
items[].linerequiredstring | null
items[].secondsrequiredinteger | null
items[].startedAtrequiredstring (date-time)
items[].endedAtrequiredstring (date-time) | null
items[].transferredrequiredboolean
items[].cutOffrequiredbooleanTrue when an answered call ended without the agent closing it or handing it off: the other side hung up or the line dropped.
nextCursorrequiredstring (uuid) | null
Example request
curl "https://api.crang.ai/v1/calls" \
  -H "Authorization: Bearer $CRANG_API_KEY"
GET/v1/calls/{id}

Get a call

Required scopecalls:read

Parameters
FieldTypeDescription
idrequiredstring (uuid) · path
Response 200
FieldTypeDescription
idrequiredstring (uuid)
directionrequired"inbound" | "outbound"
statusrequired"active" | "ended"
outcomerequired"answered" | "voicemail" | "no_answer" | "busy" | "failed" | null
successrequired"all_met" | "any_failed" | "unknown" | null
agentrequiredobject | null
agent.idrequiredstring (uuid)
agent.namerequiredstring
campaignrequiredobject | null
campaign.idrequiredstring (uuid)
campaign.namerequiredstring
contactrequiredobject | null
contact.idrequiredstring (uuid)
contact.namerequiredstring | null
phonerequiredstring
linerequiredstring | null
secondsrequiredinteger | null
startedAtrequiredstring (date-time)
endedAtrequiredstring (date-time) | null
transferredrequiredboolean
cutOffrequiredbooleanTrue when an answered call ended without the agent closing it or handing it off: the other side hung up or the line dropped.
summaryrequiredstring | null
transcriptrequiredobject[]
transcript[].rolerequired"user" | "agent"
transcript[].textrequiredstring
transcript[].atSecondsrequirednumber
capturedDatarequiredobject[]
capturedData[].namerequiredstring
capturedData[].valuerequiredany
criteriarequiredobject[]
criteria[].namerequiredstring
criteria[].resultrequired"success" | "failure" | "unknown"
criteria[].rationalerequiredstring
goalsrequiredobject[]
goals[].namerequiredstring
goals[].statusrequired"reached" | "not_reached" | "unknown" | "not_rated"
goals[].valuerequiredstring | number | boolean
hasAudiorequiredboolean
Example request
curl "https://api.crang.ai/v1/calls/<id>" \
  -H "Authorization: Bearer $CRANG_API_KEY"
GET/v1/calls/{id}/transcript

Download the transcript (txt or json)

Required scopecalls:read

Parameters
FieldTypeDescription
idrequiredstring (uuid) · path
format"txt" | "json" · query = "txt"
Example request
curl "https://api.crang.ai/v1/calls/<id>/transcript" \
  -H "Authorization: Bearer $CRANG_API_KEY"

Agents

GET/v1/agents

List agents

Required scopeagents:read

Response 200
FieldTypeDescription
slotsrequiredobject
slots.usedrequiredinteger
slots.totalrequiredinteger
agentsrequiredobject[]
agents[].idrequiredstring (uuid)
agents[].namerequiredstring
agents[].directionrequired"inbound" | "outbound" | "both"
agents[].statusrequired"draft_only" | "published" | "unpublished_changes"
agents[].voiceIdrequiredstring (uuid) | null
agents[].languagesrequiredstring[]
agents[].currentVersionrequiredinteger | null
agents[].publishedAtrequiredstring (date-time) | null
Example request
curl "https://api.crang.ai/v1/agents" \
  -H "Authorization: Bearer $CRANG_API_KEY"
GET/v1/agents/{id}

Get an agent

Required scopeagents:read

Parameters
FieldTypeDescription
idrequiredstring (uuid) · path
Response 200
FieldTypeDescription
idrequiredstring (uuid)
namerequiredstring
directionrequired"inbound" | "outbound" | "both"
statusrequired"draft_only" | "published" | "unpublished_changes"
voiceIdrequiredstring (uuid) | null
languagesrequiredstring[]
currentVersionrequiredinteger | null
publishedAtrequiredstring (date-time) | null
templateKeyrequiredstring | null
createdAtrequiredstring (date-time)
configrequiredobject
config.basicsrequiredobject
config.basics.voiceIdrequiredstring (uuid) | null
config.basics.stabilityrequirednumber
config.basics.speedrequirednumber
config.basics.languagesrequiredobject[]
config.basics.callVariablesrequiredobject[]
config.rulesrequiredobject
config.rules.purposerequiredstring
config.rules.tonerequiredstring
config.rules.alwaysSayrequiredstring[]
config.rules.neverSayrequiredstring[]
config.rules.transfersrequiredobject[]
config.rules.endCallRulesrequiredstring[]
config.rules.dataToCapturerequiredobject[]
config.rules.goalsrequiredobject[]
config.rules.callHandlingrequiredobject
config.rules.voicemailMessagerequiredstring
config.rules.additionalInstructionsrequiredstring
config.workflowrequiredobject
config.workflow.stepsrequiredobject[]
config.openingHoursobject
config.openingHours.enabledrequiredboolean
config.openingHours.timezonerequiredstring
config.openingHours.windowsrequiredobject[]
config.openingHours.outsiderequired"message" | "transfer" | "reject"
config.openingHours.transferNumberrequiredstring | null
knowledgeBaserequiredstring (uuid)[]
revisionrequiredinteger
draftUpdatedAtrequiredstring (date-time)
draftBasedOnVersionrequiredinteger | null
dataCollectionLimitrequiredinteger
inboundPersonalizationrequiredboolean
Example request
curl "https://api.crang.ai/v1/agents/<id>" \
  -H "Authorization: Bearer $CRANG_API_KEY"
PATCH/v1/agents/{id}

Edit the agent's draft

Changes land in the draft only; callers keep talking to the published version until a publish. The exception is inboundPersonalization (off by default), which applies from the next inbound call: when on, a caller whose number the carrier fully verified gets the call variables of their matching contact. Send the revision from the last read (409 on a stale one).

Required scopeagents:write

Parameters
FieldTypeDescription
idrequiredstring (uuid) · path
Request body · JSON
FieldTypeDescription
revisionrequiredinteger
namestring
configobject
config.basicsrequiredobject
config.basics.voiceIdrequiredstring (uuid) | null
config.basics.stabilityrequirednumber
config.basics.speedrequirednumber
config.basics.languagesrequiredobject[]
config.basics.callVariablesrequiredobject[]
config.rulesrequiredobject
config.rules.purposerequiredstring
config.rules.tonerequiredstring
config.rules.alwaysSayrequiredstring[]
config.rules.neverSayrequiredstring[]
config.rules.transfersrequiredobject[]
config.rules.endCallRulesrequiredstring[]
config.rules.dataToCapturerequiredobject[]
config.rules.goalsrequiredobject[]
config.rules.callHandlingrequiredobject
config.rules.voicemailMessagerequiredstring
config.rules.additionalInstructionsrequiredstring
config.workflowrequiredobject
config.workflow.stepsrequiredobject[]
config.openingHoursobject
config.openingHours.enabledrequiredboolean
config.openingHours.timezonerequiredstring
config.openingHours.windowsrequiredobject[]
config.openingHours.outsiderequired"message" | "transfer" | "reject"
config.openingHours.transferNumberrequiredstring | null
knowledgeBasestring (uuid)[]
inboundPersonalizationboolean
Response 200
FieldTypeDescription
idrequiredstring (uuid)
namerequiredstring
directionrequired"inbound" | "outbound" | "both"
statusrequired"draft_only" | "published" | "unpublished_changes"
voiceIdrequiredstring (uuid) | null
languagesrequiredstring[]
currentVersionrequiredinteger | null
publishedAtrequiredstring (date-time) | null
templateKeyrequiredstring | null
createdAtrequiredstring (date-time)
configrequiredobject
config.basicsrequiredobject
config.basics.voiceIdrequiredstring (uuid) | null
config.basics.stabilityrequirednumber
config.basics.speedrequirednumber
config.basics.languagesrequiredobject[]
config.basics.callVariablesrequiredobject[]
config.rulesrequiredobject
config.rules.purposerequiredstring
config.rules.tonerequiredstring
config.rules.alwaysSayrequiredstring[]
config.rules.neverSayrequiredstring[]
config.rules.transfersrequiredobject[]
config.rules.endCallRulesrequiredstring[]
config.rules.dataToCapturerequiredobject[]
config.rules.goalsrequiredobject[]
config.rules.callHandlingrequiredobject
config.rules.voicemailMessagerequiredstring
config.rules.additionalInstructionsrequiredstring
config.workflowrequiredobject
config.workflow.stepsrequiredobject[]
config.openingHoursobject
config.openingHours.enabledrequiredboolean
config.openingHours.timezonerequiredstring
config.openingHours.windowsrequiredobject[]
config.openingHours.outsiderequired"message" | "transfer" | "reject"
config.openingHours.transferNumberrequiredstring | null
knowledgeBaserequiredstring (uuid)[]
revisionrequiredinteger
draftUpdatedAtrequiredstring (date-time)
draftBasedOnVersionrequiredinteger | null
dataCollectionLimitrequiredinteger
inboundPersonalizationrequiredboolean
Example request
curl -X PATCH "https://api.crang.ai/v1/agents/<id>" \
  -H "Authorization: Bearer $CRANG_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"revision":0}'
POST/v1/agents/{id}/publish

Publish the draft: live calls use it from now on

Required scopeagents:publish

Parameters
FieldTypeDescription
idrequiredstring (uuid) · path
Request body · JSON
FieldTypeDescription
notestring
revisioninteger
Response 200
FieldTypeDescription
idrequiredstring (uuid)
namerequiredstring
directionrequired"inbound" | "outbound" | "both"
statusrequired"draft_only" | "published" | "unpublished_changes"
voiceIdrequiredstring (uuid) | null
languagesrequiredstring[]
currentVersionrequiredinteger | null
publishedAtrequiredstring (date-time) | null
templateKeyrequiredstring | null
createdAtrequiredstring (date-time)
configrequiredobject
config.basicsrequiredobject
config.basics.voiceIdrequiredstring (uuid) | null
config.basics.stabilityrequirednumber
config.basics.speedrequirednumber
config.basics.languagesrequiredobject[]
config.basics.callVariablesrequiredobject[]
config.rulesrequiredobject
config.rules.purposerequiredstring
config.rules.tonerequiredstring
config.rules.alwaysSayrequiredstring[]
config.rules.neverSayrequiredstring[]
config.rules.transfersrequiredobject[]
config.rules.endCallRulesrequiredstring[]
config.rules.dataToCapturerequiredobject[]
config.rules.goalsrequiredobject[]
config.rules.callHandlingrequiredobject
config.rules.voicemailMessagerequiredstring
config.rules.additionalInstructionsrequiredstring
config.workflowrequiredobject
config.workflow.stepsrequiredobject[]
config.openingHoursobject
config.openingHours.enabledrequiredboolean
config.openingHours.timezonerequiredstring
config.openingHours.windowsrequiredobject[]
config.openingHours.outsiderequired"message" | "transfer" | "reject"
config.openingHours.transferNumberrequiredstring | null
knowledgeBaserequiredstring (uuid)[]
revisionrequiredinteger
draftUpdatedAtrequiredstring (date-time)
draftBasedOnVersionrequiredinteger | null
dataCollectionLimitrequiredinteger
inboundPersonalizationrequiredboolean
Example request
curl -X POST "https://api.crang.ai/v1/agents/<id>/publish" \
  -H "Authorization: Bearer $CRANG_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'

Usage

GET/v1/usage

Get usage

Required scopeusage:read

Response 200
FieldTypeDescription
statusrequired"pending_payment" | "setup" | "active" | "past_due" | "suspended" | "cancelled" | "pending_deletion"
billingCyclerequired"monthly" | "annual" | null
monthrequiredobject | null
month.leftSecondsrequiredinteger
month.grantedSecondsrequiredinteger
month.endsAtrequiredstring (date-time)
packSecondsrequiredinteger
overdraftSecondsrequiredinteger
liveCallsrequiredinteger
callLimitrequiredinteger
packPricerequiredobject | null
packPrice.amountrequiredinteger
packPrice.currencyrequiredstring
Example request
curl "https://api.crang.ai/v1/usage" \
  -H "Authorization: Bearer $CRANG_API_KEY"

Webhooks

A webhook is an HTTPS address of yours. Crang sends it a POST when something happens in the workspace. Add endpoints in Settings, Integrations, choose their events and copy the signing secret.

The request

Every event has the same body, sent as JSON with the headers Content-Type, User-Agent: Crang-Webhooks/1.0 and Crang-Signature. Only the fields of data differ.

FieldTypeDescription
idrequiredstringEvent id. The same on every retry and resend: drop duplicates by it.
typerequiredstringThe event type.
createdAtrequiredstring (date-time)When the event was recorded.
datarequiredobjectThe fields of the event, listed below.

Verify the signature

Crang-Signature is t={unix seconds},v1={hex}, where v1 is the HMAC-SHA256 of {t}.{raw body} with your endpoint's signing secret (whsec_...). During a secret rotation there are two v1 values for 24 hours: accept any match.

Compute it on the raw body, compare in constant time and reject a t older than 5 minutes.

import { createHmac, timingSafeEqual } from "node:crypto";

export function verify(rawBody, header, secret) {
  const parts = header.split(",").map((p) => p.split("="));
  const t = parts.find(([k]) => k === "t")?.[1];
  if (!t || Math.abs(Date.now() / 1000 - Number(t)) > 300) return false;
  const expected = createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
  return parts
    .filter(([k]) => k === "v1")
    .some(([, v]) => v?.length === expected.length && timingSafeEqual(Buffer.from(v), Buffer.from(expected)));
}

Delivery and retries

Answer with any 2xx within 10 seconds; only the status is read and redirects are not followed. Otherwise Crang retries 6 times, after 1 min, 5 min, 30 min, 2 h, 6 h, 15 h, about 24 hours in all, and then marks the delivery failed. An endpoint whose deliveries keep failing is turned off and the owners are emailed. Delivery is at least once.

While the workspace is suspended nothing is delivered, except the one tenant.suspended event.

Settings, Integrations shows the recent deliveries with their status, attempts and payload. You can resend one, or send a test event (webhook.test) to a single endpoint.

Events

Pick the events each endpoint receives. webhook.test goes only to the endpoint you test.

call.completed

A call was answered or reached voicemail, and its result is stored.

FieldTypeDescription
callIdrequiredstring (uuid)The call. Use it with GET /v1/calls/{id}
directionrequired"inbound" | "outbound"
outcomerequired"answered" | "voicemail" | "no_answer" | "busy" | "failed"How the call ended
secondsrequiredintegerLength of the call in seconds
billedSecondsrequiredintegerSeconds taken from your minutes
startedAtrequiredstring (date-time)
cutOffrequiredbooleanTrue when an answered call ended without the agent closing it or handing it off: the other side hung up or the line dropped.
Example payload
{
  "id": "evt_0f1e2d3c4b5a69788796a5b4c3d2e1f0",
  "type": "call.completed",
  "createdAt": "2026-03-02T14:05:09.000Z",
  "data": {
    "callId": "7b0e5f1c-3a52-4d0e-9a55-2f1d6f0c9a10",
    "direction": "outbound",
    "outcome": "answered",
    "seconds": 94,
    "billedSeconds": 94,
    "startedAt": "2026-03-02T14:05:09.000Z",
    "cutOff": false
  }
}

call.failed

A call ended without being answered: no answer, busy or failed.

FieldTypeDescription
callIdrequiredstring (uuid)The call. Use it with GET /v1/calls/{id}
directionrequired"inbound" | "outbound"
outcomerequired"answered" | "voicemail" | "no_answer" | "busy" | "failed"How the call ended
secondsrequiredintegerLength of the call in seconds
billedSecondsrequiredintegerSeconds taken from your minutes
startedAtrequiredstring (date-time)
cutOffrequiredbooleanTrue when an answered call ended without the agent closing it or handing it off: the other side hung up or the line dropped.
Example payload
{
  "id": "evt_0f1e2d3c4b5a69788796a5b4c3d2e1f0",
  "type": "call.failed",
  "createdAt": "2026-03-02T14:05:09.000Z",
  "data": {
    "callId": "7b0e5f1c-3a52-4d0e-9a55-2f1d6f0c9a10",
    "direction": "outbound",
    "outcome": "no_answer",
    "seconds": 0,
    "billedSeconds": 0,
    "startedAt": "2026-03-02T14:05:09.000Z",
    "cutOff": false
  }
}

campaign.completed

A campaign has nothing left to dial or to wait for.

FieldTypeDescription
campaignIdrequiredstring (uuid)The campaign. Use it with GET /v1/campaigns/{id}
Example payload
{
  "id": "evt_0f1e2d3c4b5a69788796a5b4c3d2e1f0",
  "type": "campaign.completed",
  "createdAt": "2026-03-02T14:05:09.000Z",
  "data": {
    "campaignId": "7b0e5f1c-3a52-4d0e-9a55-2f1d6f0c9a10"
  }
}

contact_list.imported

A CSV import finished, ready or failed.

FieldTypeDescription
listIdrequiredstring (uuid)The list. Use it with GET /v1/contact-lists/{id}
statusrequired"ready" | "failed"
importedrequiredintegerContacts added
invalidrequiredintegerRows without a usable phone number
duplicatesrequiredintegerRows dropped as duplicates
suppressedrequiredintegerRows dropped because the number is on the do-not-call list
Example payload
{
  "id": "evt_0f1e2d3c4b5a69788796a5b4c3d2e1f0",
  "type": "contact_list.imported",
  "createdAt": "2026-03-02T14:05:09.000Z",
  "data": {
    "listId": "7b0e5f1c-3a52-4d0e-9a55-2f1d6f0c9a10",
    "status": "ready",
    "imported": 940,
    "invalid": 12,
    "duplicates": 31,
    "suppressed": 17
  }
}

usage.threshold_reached

The monthly minutes passed 80, 95 or 100 percent.

FieldTypeDescription
thresholdrequired"80" | "95" | "100" | "zero"Share of the monthly minutes used, or zero left
Example payload
{
  "id": "evt_0f1e2d3c4b5a69788796a5b4c3d2e1f0",
  "type": "usage.threshold_reached",
  "createdAt": "2026-03-02T14:05:09.000Z",
  "data": {
    "threshold": "80"
  }
}

usage.exhausted

The monthly minutes ran out.

FieldTypeDescription
thresholdrequired"80" | "95" | "100" | "zero"Share of the monthly minutes used, or zero left
Example payload
{
  "id": "evt_0f1e2d3c4b5a69788796a5b4c3d2e1f0",
  "type": "usage.exhausted",
  "createdAt": "2026-03-02T14:05:09.000Z",
  "data": {
    "threshold": "zero"
  }
}

tenant.suspended

The workspace was suspended: calls stop and nothing else is delivered.

FieldTypeDescription
statusrequired"pending_payment" | "setup" | "active" | "past_due" | "suspended" | "cancelled" | "pending_deletion"
previousStatusrequired"pending_payment" | "setup" | "active" | "past_due" | "suspended" | "cancelled" | "pending_deletion"
Example payload
{
  "id": "evt_0f1e2d3c4b5a69788796a5b4c3d2e1f0",
  "type": "tenant.suspended",
  "createdAt": "2026-03-02T14:05:09.000Z",
  "data": {
    "status": "suspended",
    "previousStatus": "active"
  }
}

tenant.reactivated

The workspace is active again.

FieldTypeDescription
statusrequired"pending_payment" | "setup" | "active" | "past_due" | "suspended" | "cancelled" | "pending_deletion"
previousStatusrequired"pending_payment" | "setup" | "active" | "past_due" | "suspended" | "cancelled" | "pending_deletion"
Example payload
{
  "id": "evt_0f1e2d3c4b5a69788796a5b4c3d2e1f0",
  "type": "tenant.reactivated",
  "createdAt": "2026-03-02T14:05:09.000Z",
  "data": {
    "status": "active",
    "previousStatus": "suspended"
  }
}

webhook.test

Sent by Send test event, to that one endpoint only.

FieldTypeDescription
endpointIdrequiredstring (uuid)The endpoint the test was sent to
Example payload
{
  "id": "evt_0f1e2d3c4b5a69788796a5b4c3d2e1f0",
  "type": "webhook.test",
  "createdAt": "2026-03-02T14:05:09.000Z",
  "data": {
    "endpointId": "7b0e5f1c-3a52-4d0e-9a55-2f1d6f0c9a10"
  }
}

MCP

The MCP server lets an AI assistant work with your workspace as the API key allows, and nothing more. Every action is recorded in the audit log. It is Streamable HTTP, without sessions.

Authentication

Send the key as Authorization: Bearer <key>. The key needs the mcp scope plus the scopes of the tools you want to use. The assistant sees only the tools its key allows; any other is answered as not found. Requests count against the same rate limits as the REST API.

Connect an AI assistant

Add the server to the assistant's MCP configuration and replace YOUR_API_KEY with your key. Agent edits land in the draft; nothing goes live until publish_agent.

{
  "mcpServers": {
    "crang": {
      "url": "https://mcp.crang.ai/mcp",
      "headers": {
        "Authorization": "Bearer YOUR_API_KEY"
      }
    }
  }
}

Tools

41 tools. Each needs all the scopes listed.

Calls

list_calls

Read

Find calls

Read only. Lists calls, newest first, with outcome, goal result, agent, campaign, contact and duration. Filter by direction, agent, campaign, contact, phone number, outcome, goal result (success), time range (from/to, ISO date-time), length in seconds, transfers, cut-off calls (cutOff) or a phone/name search (q). Paginate with cursor.

Scopescalls:read

Arguments
FieldTypeDescription
cursorstring (uuid)nextCursor of the previous page
direction"inbound" | "outbound"
agentIdstring (uuid)
campaignIdstring (uuid)
contactIdstring (uuid)
phoneNumberIdstring (uuid)
outcome"answered" | "voicemail" | "no_answer" | "busy" | "failed"
success"all_met" | "any_failed" | "unknown"
fromstring (date-time)
tostring (date-time)
minSecondsinteger
maxSecondsinteger
qstring
limitinteger = 50
transferredbooleanHanded to a person or another agent
cutOffbooleanTrue when an answered call ended without the agent closing it or handing it off: the other side hung up or the line dropped.

get_call

Read

Get a call

Read only. One call with its summary, full transcript (caller and agent turns), captured data and goal results.

Scopescalls:read

Arguments
FieldTypeDescription
callIdrequiredstring (uuid)

Contacts

list_contact_lists

Read

List contact lists

Read only. Contact lists with their status (importing, ready, failed), contact count and import report.

Scopescontacts:read

Arguments

No arguments.

get_contact_list

Read

Get a contact list

Read only. One contact list with its status and import report (imported, invalid, duplicate and do-not-call rows). Use it to follow an import.

Scopescontacts:read

Arguments
FieldTypeDescription
listIdrequiredstring (uuid)

list_contacts

Read

List contacts

Read only. The contacts of one list, newest first. Paginate with cursor.

Scopescontacts:read

Arguments
FieldTypeDescription
listIdrequiredstring (uuid)
cursorstring (uuid)nextCursor of the previous page
limitinteger = 50

search_do_not_call

Read

Search the do-not-call list

Read only. Numbers the workspace never calls, newest first; query matches phone digits.

Scopescontacts:read

Arguments
FieldTypeDescription
querystring
cursorstring (uuid)nextCursor of the previous page
limitinteger = 50

import_contacts

Changes data

Import contacts

Changes data. Creates a new contact list from rows (objects of column name to value), with the same checks as a CSV import in the app: phone numbers are normalized, invalid and duplicate rows are skipped and do-not-call numbers suppressed. mapping names the column for each contact field and for each call variable; other columns are ignored. At most 5,000 rows and 100,000 values (rows × mapped columns) per call, and 30 call variables: for more rows, call it again (each call makes its own list). Only for people the workspace may lawfully call (lawfulBasis: true records that confirmation). Returns the list in importing; call get_contact_list for the report.

Scopescontacts:write

Arguments
FieldTypeDescription
namerequiredstring
rowsrequiredobject<string, string | number | boolean | null>[]
mappingrequiredobject
mapping.phonerequiredstring
mapping.namestring
mapping.languagestring
mapping.timezonestring
mapping.externalIdstring
mapping.variablesobject<string, string> = {}
lawfulBasisrequiredtrue

add_to_do_not_call

Changes data

Add numbers to do-not-call

Changes data. Adds phone numbers to the do-not-call list; campaigns skip them from now on. Any invalid number refuses the whole request (details.invalid).

Scopescontacts:write

Arguments
FieldTypeDescription
phonesrequiredstring[]
notestring

Campaigns

list_campaigns

Read

List campaigns

Read only. Outbound campaigns with status, progress by contact state and call stats.

Scopescampaigns:read

Arguments

No arguments.

get_campaign

Read

Get campaign status

Read only. One campaign: status, pause reason, settings, progress and calls in the last 24 hours.

Scopescampaigns:read

Arguments
FieldTypeDescription
campaignIdrequiredstring (uuid)

create_campaign

Changes data

Create a campaign

Changes data. Creates a draft outbound campaign (nothing is dialed until start_campaign). Settings default to weekday business hours in the workspace time zone.

Scopescampaigns:write

Arguments
FieldTypeDescription
namerequiredstring
agentIdrequiredstring (uuid)An agent with a published version
listIdsrequiredstring (uuid)[]Contact lists to call
phoneNumberIdsrequiredstring (uuid)[]Workspace numbers to call from
startsAtstring (date-time) | nullWhen dialing may start after start_campaign; null or left out: at once
settingsrequiredobjectDialing settings; anything left out keeps its current or default value
settings.timezonestring
settings.windowsobject[]
settings.windows[].daysrequiredinteger[]
settings.windows[].startrequiredstring
settings.windows[].endrequiredstring
settings.maxAttemptsinteger
settings.retryMinutesinteger
settings.retryOn"no_answer" | "busy" | "failed" | "voicemail" | "cut_off"[]
settings.maxConcurrentinteger
settings.callsPerMinuteinteger
settings.ringTimeoutSecondsinteger
settings.maxDurationSecondsinteger
settings.protectionobjectProtection overrides: pause automatically, minute limit, slow start and stop values
settings.protection.autoPausebooleanPause the campaign automatically when its recent calls look bad. Default false: the campaign only shows its metrics. The minute limit applies either way.
settings.protection.minuteLimitinteger | nullMost minutes this campaign may use (debited plus in progress); null or left out: no limit
settings.protection.warmupbooleanSlow start: the first calls run a few at a time. Default false.
settings.protection.stopsobject<string, integer | null>Per indicator, the percent of the last finished calls at which the campaign pauses (answer_rate: pauses when under it); null turns that stop off. technical_failures is between 5 and 60 and cannot be null. Left out: the workspace default.

update_campaign

Changes data

Edit a campaign

Changes data. Edits a draft, scheduled or paused campaign; fields left out keep their value. Scheduled and paused campaigns keep their agent and lists.

Scopescampaigns:write

Arguments
FieldTypeDescription
campaignIdrequiredstring (uuid)
namestring
agentIdstring (uuid)An agent with a published version
listIdsstring (uuid)[]Contact lists to call
phoneNumberIdsstring (uuid)[]Workspace numbers to call from
startsAtstring (date-time) | nullWhen dialing may start after start_campaign; null or left out: at once
settingsobjectDialing settings; anything left out keeps its current or default value
settings.timezonestring
settings.windowsobject[]
settings.windows[].daysrequiredinteger[]
settings.windows[].startrequiredstring
settings.windows[].endrequiredstring
settings.maxAttemptsinteger
settings.retryMinutesinteger
settings.retryOn"no_answer" | "busy" | "failed" | "voicemail" | "cut_off"[]
settings.maxConcurrentinteger
settings.callsPerMinuteinteger
settings.ringTimeoutSecondsinteger
settings.maxDurationSecondsinteger
settings.protectionobjectProtection overrides: pause automatically, minute limit, slow start and stop values
settings.protection.autoPausebooleanPause the campaign automatically when its recent calls look bad. Default false: the campaign only shows its metrics. The minute limit applies either way.
settings.protection.minuteLimitinteger | nullMost minutes this campaign may use (debited plus in progress); null or left out: no limit
settings.protection.warmupbooleanSlow start: the first calls run a few at a time. Default false.
settings.protection.stopsobject<string, integer | null>Per indicator, the percent of the last finished calls at which the campaign pauses (answer_rate: pauses when under it); null turns that stop off. technical_failures is between 5 and 60 and cannot be null. Left out: the workspace default.

start_campaign

Goes live

Start a campaign

Changes data. Starts a draft campaign: real calls go out (now, or at startsAt) and use minutes. Needs a published agent, a phone number and contacts.

Scopescampaigns:run

Arguments
FieldTypeDescription
campaignIdrequiredstring (uuid)

pause_campaign

Changes data

Pause a campaign

Changes data. Pauses a running or scheduled campaign; calls in progress finish.

Scopescampaigns:run

Arguments
FieldTypeDescription
campaignIdrequiredstring (uuid)

resume_campaign

Goes live

Resume a campaign

Changes data. Resumes a paused campaign: dialing continues.

Scopescampaigns:run

Arguments
FieldTypeDescription
campaignIdrequiredstring (uuid)

cancel_campaign

Changes data

Cancel a campaign

Changes data, cannot be undone. Cancels a campaign for good; it never dials again.

Scopescampaigns:run

Arguments
FieldTypeDescription
campaignIdrequiredstring (uuid)

duplicate_campaign

Changes data

Duplicate a campaign

Changes data. A new draft with the same agent, numbers and settings: the same lists (all) or a new list of the contacts not reached yet (not_reached, named listName).

Scopescampaigns:write

Arguments
FieldTypeDescription
campaignIdrequiredstring (uuid)
contactsrequired"all" | "not_reached"
listNamestring

Agents

list_agents

Read

List agents

Read only. Agents with direction, voice, languages, phone numbers and status (draft only, published, unpublished changes), plus used and total agent slots.

Scopesagents:read

Arguments

No arguments.

get_agent

Read

Get an agent

Read only. One agent's draft: config (basics, rules with goals, workflow), knowledge documents and the revision every edit tool needs. Live calls use the latest published version until the draft is published.

Scopesagents:read

Arguments
FieldTypeDescription
agentIdrequiredstring (uuid)

list_agent_versions

Read

List agent versions

Read only. Published versions of an agent, newest first, with who published and the note. rollback_agent takes a version id from here.

Scopesagents:read

Arguments
FieldTypeDescription
agentIdrequiredstring (uuid)

list_voices

Read

List voices

Read only. Voices an agent can use, with languages, gender and accent; kind is catalog or custom (this workspace's own ready voices). basics.voiceId takes an id from here.

Scopesagents:read

Arguments

No arguments.

list_agent_templates

Read

List agent templates

Read only. The templates a new agent can start from: key, name, description and direction. create_agent takes a key from here.

Scopesagents:read

Arguments

No arguments.

list_custom_voices

Read

List the workspace's own voices

Read only. The workspace's own voices with status (processing, ready, failed, disabled), the agents using each, and how many more it may have.

Scopesagents:read

Arguments

No arguments.

create_agent

Changes data

Create an agent

Changes data. Creates a new agent in the workspace from a template (list_agent_templates), as a draft: nothing is live or answers calls until publish_agent. Uses one of the workspace's free agent slots and is refused when none is left; it never buys a slot.

Scopesagents:write

Arguments
FieldTypeDescription
namerequiredstring
templateKeyrequiredstring

update_agent_basics

Draft only

Edit agent basics

Changes the draft only (live calls are unchanged until publish_agent). Replaces the agent's name and/or basics: voice, expressiveness (stability), speed, languages with their first message, call variables.

Scopesagents:write

Arguments
FieldTypeDescription
agentIdrequiredstring (uuid)
revisionrequiredintegerThe agent's revision from get_agent. A stale one is refused: read the agent again.
namestring
basicsobject
basics.voiceIdrequiredstring (uuid) | null
basics.stabilityrequirednumber
basics.speedrequirednumber
basics.languagesrequiredobject[]
basics.languages[].coderequiredstring
basics.languages[].firstMessagerequiredstring
basics.callVariablesrequiredobject[]
basics.callVariables[].namerequiredstring
basics.callVariables[].examplerequiredstring

update_agent_rules

Draft only

Edit agent rules

Changes the draft only. Replaces the agent's rules: purpose and tone, always/never say, transfers, end-call rules, data to capture, goals, call handling, voicemail message and additional instructions. Send the whole rules object from get_agent with your changes.

Scopesagents:write

Arguments
FieldTypeDescription
agentIdrequiredstring (uuid)
revisionrequiredintegerThe agent's revision from get_agent. A stale one is refused: read the agent again.
rulesrequiredobject
rules.purposerequiredstring
rules.tonerequiredstring
rules.alwaysSayrequiredstring[]
rules.neverSayrequiredstring[]
rules.transfersrequiredobject[]
rules.transfers[].torequired"number"
rules.transfers[].destinationrequiredstring
rules.transfers[].conditionrequiredstring
rules.transfers[].transferTyperequired"conference" | "blind"
rules.transfers[].hoursrequiredstring
rules.transfers[].outsideHoursrequiredstring
rules.transfers[].callerMessagerequiredstring
rules.transfers[].summaryrequiredstring
rules.transfers[].agentIdrequiredstring (uuid)
rules.endCallRulesrequiredstring[]
rules.dataToCapturerequiredobject[]
rules.dataToCapture[].namerequiredstring
rules.dataToCapture[].typerequired"string" | "boolean" | "integer" | "number"
rules.dataToCapture[].descriptionrequiredstring
rules.goalsrequiredobject[]
rules.goals[].namerequiredstring
rules.goals[].descriptionrequiredstring
rules.goals[].valueobject
rules.callHandlingrequiredobject
rules.callHandling.languageDetectionrequiredboolean
rules.callHandling.voicemailDetectionrequiredboolean
rules.callHandling.keypadTonesrequiredboolean
rules.callHandling.skipTurnrequiredboolean
rules.voicemailMessagerequiredstring
rules.additionalInstructionsrequiredstring

update_agent_goals

Draft only

Edit agent goals

Changes the draft only. Replaces the agent's goals (rules.goals): what counts as a result of a call, each with "reached when" and an optional value to capture. Evaluated after every call.

Scopesagents:write

Arguments
FieldTypeDescription
agentIdrequiredstring (uuid)
revisionrequiredintegerThe agent's revision from get_agent. A stale one is refused: read the agent again.
goalsrequiredobject[]
goals[].namerequiredstring
goals[].descriptionrequiredstring
goals[].valueobject
goals[].value.typerequired"text" | "number" | "date" | "yes_no"
goals[].value.namerequiredstring
goals[].value.unitstring

update_agent_workflow

Draft only

Edit agent workflow

Changes the draft only. Replaces the agent's workflow (steps and the paths between them). Problems in the workflow are allowed in the draft but block publishing.

Scopesagents:write

Arguments
FieldTypeDescription
agentIdrequiredstring (uuid)
revisionrequiredintegerThe agent's revision from get_agent. A stale one is refused: read the agent again.
workflowrequiredobject
workflow.stepsrequiredobject[]
workflow.steps[].kindrequired"start"
workflow.steps[].idrequiredstring
workflow.steps[].labelrequiredstring
workflow.steps[].positionrequiredobject
workflow.steps[].pathsrequiredobject[]
workflow.steps[].instructionsrequiredstring
workflow.steps[].collectrequiredstring[]
workflow.steps[].documentIdsrequiredstring (uuid)[]
workflow.steps[].voiceIdrequiredstring (uuid) | null
workflow.steps[].messagesrequiredobject<string, string>
workflow.steps[].actionrequired"save_data"
workflow.steps[].fieldrequiredstring | ""
workflow.steps[].destinationrequiredstring | ""
workflow.steps[].transferTyperequired"conference" | "blind"
workflow.steps[].agentIdrequiredstring (uuid) | null
workflow.steps[].messagerequiredstring

attach_knowledge_document

Draft only

Attach a knowledge document

Changes the draft only. Lets the agent use a knowledge base document (ids from list_knowledge_documents).

Scopesagents:write

Arguments
FieldTypeDescription
agentIdrequiredstring (uuid)
revisionrequiredintegerThe agent's revision from get_agent. A stale one is refused: read the agent again.
documentIdrequiredstring (uuid)

detach_knowledge_document

Draft only

Detach a knowledge document

Changes the draft only. The agent stops using one of its own knowledge base documents once published; the document stays in the workspace. Company documents cannot be detached (remove them in Settings).

Scopesagents:write

Arguments
FieldTypeDescription
agentIdrequiredstring (uuid)
revisionrequiredintegerThe agent's revision from get_agent. A stale one is refused: read the agent again.
documentIdrequiredstring (uuid)

discard_agent_draft

Draft only

Discard the agent draft

Changes data, cannot be undone. Throws away every unpublished change: the draft becomes the live version again.

Scopesagents:write

Arguments
FieldTypeDescription
agentIdrequiredstring (uuid)
revisionrequiredintegerThe agent's revision from get_agent. A stale one is refused: read the agent again.

publish_agent

Goes live

Publish an agent

Goes live. The draft becomes the next version and every new real call (inbound and campaigns) uses it at once. Needs a voice, a language with a first message and a workflow without problems. Confirm with the user first.

Scopesagents:publish

Arguments
FieldTypeDescription
agentIdrequiredstring (uuid)
revisionrequiredintegerThe agent's revision from get_agent. A stale one is refused: read the agent again.
notestring

rollback_agent

Goes live

Roll back an agent

Goes live. Publishes a copy of an earlier version (from list_agent_versions) as the next version; real calls use it at once. The draft is kept. Confirm with the user first.

Scopesagents:publish

Arguments
FieldTypeDescription
agentIdrequiredstring (uuid)
versionIdrequiredstring (uuid)
notestring

assign_phone_number_agent

Goes live

Set the agent of a phone number

Goes live. The agent answers inbound calls on this number at once (agentId null: nobody answers). The agent needs a published version. Confirm with the user first.

Scopesagents:publish

Arguments
FieldTypeDescription
phoneNumberIdrequiredstring (uuid)
agentIdrequiredstring (uuid) | null

Knowledge base

list_knowledge_documents

Read

List knowledge documents

Read only. The workspace knowledge base: documents with status (processing, ready, failed) and size.

Scopesknowledge:read

Arguments

No arguments.

add_knowledge_text

Goes live

Add a text document

Changes data, also on live calls. Adds a text document to the knowledge base. Once processed it joins every agent, the published versions included.

Scopesknowledge:writeagents:publish

Arguments
FieldTypeDescription
namerequiredstring
textrequiredstring

add_knowledge_url

Goes live

Add a web page

Changes data, also on live calls. Adds the content of one public web page (https) to the knowledge base. Once processed it joins every agent, the published versions included.

Scopesknowledge:writeagents:publish

Arguments
FieldTypeDescription
urlrequiredstring (uri)

Phone numbers

list_phone_numbers

Read

List phone numbers

Read only. The workspace phone numbers with the agent that answers each one, open campaigns using it and inbound calls of the last 7 days.

Scopesnumbers:read

Arguments

No arguments.

Analytics

get_analytics

Read

Get analytics

Read only. Call results for a period in the workspace time zone: totals (calls, answer rate, goals reached, minutes, transfers, opt-outs) with the previous period (compare), calls over time, outcomes, call lengths, best hours to call, results by agent, campaign and number, and top captured values. custom needs from and to (local days). Set agent for one agent.

Scopesanalytics:read

Arguments
FieldTypeDescription
period"today" | "yesterday" | "7d" | "30d" | "90d" | "this_month" | "this_year" | "custom" = "7d"
fromstring (date)
tostring (date)
direction"inbound" | "outbound"
agentIdstring (uuid)Only this agent (the per-agent view)
campaignIdstring (uuid)
compareboolean = trueAlso return the previous period

get_goal_results

Read

Get goal results

Read only. How often each goal was reached in a period: totals with the previous period's rate, by agent, campaign and number, over time, and captured values. Same period options as get_analytics; set agent for one agent.

Scopesanalytics:read

Arguments
FieldTypeDescription
period"today" | "yesterday" | "7d" | "30d" | "90d" | "this_month" | "this_year" | "custom" = "7d"
fromstring (date)
tostring (date)
direction"inbound" | "outbound"
agentIdstring (uuid)Only this agent (the per-agent view)
campaignIdstring (uuid)
compareboolean = trueAlso return the previous period

Usage

get_usage

Read

Get minutes and usage

Read only. Minutes left and used in the current period.

Scopesusage:read

Arguments

No arguments.