atteq API
  • Guides
  • Recipes
  • Cookbook
  • API Reference
Information
agents
    List AgentsgetCreate AgentpostDocument AttachmentsgetGet VersiongetEdit DraftpatchReplace Draft GraphputPublish VersionpostGet AgentgetDelete AgentdeleteCopy AgentpostGet DraftgetGet Agent MapgetPending ChangesgetList PlacementsgetRestore VersionpostList VersionsgetGet Mapget
voice_agent
    Open Browser CallpostEnd Browser CalldeleteGet Agent Test LinkgetGet Bridge CeilinggetSet Bridge Ceilingpatch
ai_profiles
    Get Ai Profile CapabilitiesgetList Live SettingsgetCreate Live SettingspostDelete Live SettingsdeleteUpdate Live SettingspatchList Llm SettingsgetCreate Llm SettingspostDelete Llm SettingsdeleteUpdate Llm SettingspatchList ProfilesgetCreate ProfilepostGet ProfilegetDelete ProfiledeleteUpdate ProfilepatchCopy ProfilepostSet Text SectionputSet Voice SectionputList Stt SettingsgetCreate Stt SettingspostDelete Stt SettingsdeleteUpdate Stt SettingspatchList Tts SettingsgetCreate Tts SettingspostDelete Tts SettingsdeleteUpdate Tts SettingspatchList Vad SettingsgetCreate Vad SettingspostDelete Vad SettingsdeleteUpdate Vad Settingspatch
analyst
    List Agent FindingsgetCall Back FindingpostDismiss FindingpostUndo Finding DismissaldeleteTeach FindingpostWatch FindingpostList Question ChartsgetList Tracked QuestionsgetCreate Tracked QuestionpostGet Question Search CostgetSearch For Tracked QuestionpostPropose Tracked QuestionpostDelete Tracked QuestiondeleteUpdate Tracked QuestionpatchGet Reading SwitchgetSet Reading SwitchputGet Agent Summaryget
analytics
    List ChartsgetCreate ChartpostPreview ChartpostGet ChartgetDelete ChartdeleteUpdate ChartpatchRun ChartpostList DatasetsgetList BoardsgetCreate BoardpostGet TraversalgetGet BoardgetDelete BoarddeleteUpdate Boardpatch
authentication
    List InvitationsgetInvite MemberpostRevoke InvitationdeleteResend InvitationpostList MembersgetRemove MemberdeleteGet Member Conversation AccessgetSet Member Conversation AccesspatchSet Member FlagspatchSet Member RolepatchGet OrganizationgetUpdate OrganizationpatchGet Role FlagsgetSet Role Flagspatch
automations
    List AutomationsgetCreate AutomationpostGet AutomationgetDelete AutomationdeleteUpdate AutomationpatchSet Automation EnabledputGet Automation MapgetRun AutomationpostList Automation RunsgetGet Automation Runget
billable-metrics
    List MetricsgetUsage SummarygetGet Metricget
outbound_calls
    Place CallpostList CampaignsgetCreate CampaignpostGet CampaigngetDelete CampaigndeleteUpdate CampaignpatchActivate CampaignpostLink CollectionpostAdd ContactspostAdd Contacts From CollectionpostRemove Contacts From CollectionpostSet Campaign HoursputPreview Campaign HourspostPause CampaignpostGet Campaign SummarygetExclude ContactpostInclude Contactpost
channels
    List ChannelsgetCreate ChannelpostList Thread SubjectsgetClose Channel ThreadpostList Thread DecisionsgetGet Thread Contact FilegetHand Thread To AgentpostList Thread MailgetGet Prepared ReplygetAsk Again For Prepared ReplypostDiscard Prepared ReplypostSend Prepared ReplypostReply On Channel ThreadpostGet ChannelgetDelete ChanneldeleteUpdate ChannelpatchSend On ChannelpostList Channel ThreadsgetList RostergetCreate Roster EntrypostGet Own Roster EntrygetGet Roster EntrygetDelete Roster EntrydeleteUpdate Roster EntrypatchAdd Roster AddresspostChange Roster AddressputRemove Roster Addressdelete
custom-data
    List CollectionsgetCreate CollectionpostList Collection NamesgetGet CollectiongetDelete CollectiondeleteUpdate CollectionpatchAdd FieldpostDelete FielddeleteUpdate FieldpatchImport RecordspostPlan ImportpostPropose ImportpostGet ImportgetList RecordsgetCreate RecordpostBulk Delete RecordspostBulk Set FieldpostCount RecordsgetList Records Of ContactgetDelete Records Of ContactdeleteGet RecordgetDelete RecorddeleteUpdate Recordpatch
connections
    List ConnectionsgetCreate ConnectionpostCreate Forwarded AddresspostCreate Mail ServerpostDiscover Mail ServerpostAuthorize Microsoft MailboxpostList ProvidersgetGet ConnectiongetDelete ConnectiondeleteUpdate ConnectionpatchGet Forwarded AddressgetCheck Forwarded AddresspostList Person GrantsgetSet Person GrantputRemove Person GrantdeleteGet Mail ServergetUpdate Mail ServerpatchList Mail Server FoldersgetEnter Mail Server PasswordputChange Mail Server ServersputTest Mail Server AgainpostGet Microsoft MailboxgetUpdate Microsoft MailboxpatchList Microsoft Mailbox FoldersgetConsent To Microsoft Mailbox WritespostWithdraw Microsoft Mailbox Writesdelete
custom-tools
    List Custom ToolsgetCreate Custom ToolpostGet Custom ToolgetDelete Custom TooldeleteGet Custom Tool DraftgetEdit Custom Tool DraftpatchPublish Custom ToolpostRestore Custom Tool VersionpostTry Custom ToolpostList Custom Tool Versionsget
rag
    List DocumentsgetUpload DocumentpostSearch DocumentspostGet DocumentgetDelete DocumentdeleteDownload DocumentgetReingest Documentpost
flows
    List FlowsgetCreate FlowpostGet FlowgetDelete FlowdeleteRename FlowpatchAdd Flow EdgepostDelete Flow EdgedeleteSet Entry SteppatchAdd SteppostDelete StepdeleteUpdate SteppatchTest FlowpostList Flow Testsget
dynamic_context
    Create GraphpostGet GraphgetDelete GraphdeleteAdd EdgepostDelete EdgedeleteAdd NodepostDelete NodedeleteUpdate NodepatchSet Rootput
inbox
    Get Inbox Open CountgetSet Inbox PreferencespatchGet Inbox RailgetCreate Inbox ViewpostDelete Inbox ViewdeleteUpdate Inbox Viewpatch
handoff
    Assign Threadpost
interactions
    List InteractionsgetCount InteractionsgetGet InteractiongetSet Conversation LabelspostGet TracegetGet TranscriptgetList LabelsgetCreate LabelpostDelete LabeldeleteUpdate Labelpatch
legal
    Agree To Processing AgreementpostAgree To TermspostLegal BoxesgetList Legal DocumentsgetRead Legal DocumentgetList Legal VersionsgetRead Legal VersiongetDownload Legal VersiongetOrganization Legal HistorygetOwn Legal HistorygetObject To SubprocessorspostWithdraw ObjectionpostLegal Pendingget
not-a-tool
    Agree To Processing AgreementpostAgree To TermspostObject To SubprocessorspostWithdraw Objectionpost
memory
    List MemoriesgetList Memory LinesgetCreate Memory LinepostDelete Memory LinedeleteUpdate Memory LinepatchList Memory PeoplegetGet Memory PersongetForget PersondeleteGet Memory SwitchgetSet Memory SwitchputWipe ContactdeleteGet Conversation MemorygetRecall MemoriespostGet MemorygetDelete MemorydeletePatch Memorypatch
notifications
    List InboxgetGet Unread CountgetMark ReadpostMark Unreaddelete
recipes
    List RecipesgetList Installed RecipesgetGet Installed RecipegetUninstall RecipedeleteSet Recipe EnabledputUpdate Recipe ParameterspatchUpdate RecipepostInstall Recipepost
data-retention
    Get RetentiongetSet Retentionpatch
text
    Open ConversationpostClose ConversationpostAnswer ConfirmationpostGet Sent FilegetSend MessagepostSet Modepatch
tools
    List ToolsgetCreate ToolpostList BuiltinsgetImport Mcp ServerpostImport OpenapipostPreview Openapi ImportpostSuggest Tool ExamplespostGet ToolgetDelete TooldeleteUpdate ToolpatchTest ToolpostList Tool Testsget
trunks
    List Transfer DirectorygetCreate Transfer Directory EntrypostGet Transfer Directory EntrygetDelete Transfer Directory EntrydeleteUpdate Transfer Directory EntrypatchList TrunksgetCreate TrunkpostGet TrunkgetDelete TrunkdeleteUpdate TrunkpatchList Forward Recordsget
Schemas
powered by Zudoku
attek API
attek API

outbound_calls


Place Call

POST
/api/v1/calls

Place one outbound call with an agent, through an outbound trunk. Use for a single call that is not part of a campaign — a one-off dial.

It takes its turn in the organization's rotation like a campaign does, and it is dialled once: one attempt, no retry, no calling-hours trim. Give at to name an instant instead — that jumps the rotation and is never brought forward into a free slot.

Place Call › Request Body

The primitive: place one outbound call. `docs/features/outbound-calls.md` — *"the atomic unit is place one outbound call = { agent, outbound trunk, destination number, context payload }"*.
CallIn
agent_id
​string · uuid · required
destination
​string · required
trunk_id
​string · uuid · required
​

When to place the call. Absent means it takes its turn in the organization's rotation, on the next free slot; a value is an instant somebody named, which jumps the rotation and is never brought forward into a free slot. A call that belongs to no campaign has no calling hours, so any instant is kept. Must carry a UTC offset (2026-09-10T11:00:00Z or 2026-09-10T13:00:00+02:00); without one the request is refused with 422 naming the field, so the call is never placed at a guessed-at moment.

​
​
​
told_fields
​string[]

Place Call › Responses

Successful Response

ContactOut
attempts_spent
​integer · required
​required
​required
created_at
​string · date-time · required
due_now
​boolean · required
​required
​required
id
​string · uuid · required
​required
​required
​required
status
​ContactStatus · enum · required

One contact's place in the state machine outbound-calls.md calls single-writer and idempotent.

EXHAUSTED is we are finished with this number — the attempt budget is gone, the provider confirmed the destination will never work, or the organization's own retention window destroyed the number while the contact was still waiting for a turn and there is nothing left to dial. EXCLUDED is the client said not to call this person, and only a client ever writes it. Keeping them apart is what lets "excluding a contact cancels its pending calls" be re-openable while a confirmed-invalid number is not.

There is no pending. A contact is dialable the moment it exists — "activating a campaign does not queue its contacts" — so it is written SCHEDULED and there is no earlier state for it to sit in. A member nothing writes is a state a reader has to ask about and nobody can reach.

Enum values:
scheduled
calling
done
exhausted
excluded
updated_at
​string · date-time · required
POST/api/v1/calls
curl /api/v1/calls \ --request POST \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer <token>' \ --data '{ "agent_id": "00000000-0000-0000-0000-000000000000", "at": "2024-08-25T15:00:00Z", "collection_id": "00000000-0000-0000-0000-000000000000", "context": { "key": "string" }, "destination": "destination", "record_id": "00000000-0000-0000-0000-000000000000", "told_fields": [ "string" ], "trunk_id": "00000000-0000-0000-0000-000000000000" }'
Example Request Body
{ "agent_id": "00000000-0000-0000-0000-000000000000", "at": "2024-08-25T15:00:00Z", "collection_id": "00000000-0000-0000-0000-000000000000", "context": { "key": "string" }, "destination": "destination", "record_id": "00000000-0000-0000-0000-000000000000", "told_fields": [ "string" ], "trunk_id": "00000000-0000-0000-0000-000000000000" }
json
Example Responses
{ "attempts_spent": 0, "campaign_id": "00000000-0000-0000-0000-000000000000", "collection_record_id": "00000000-0000-0000-0000-000000000000", "created_at": "2024-08-25T15:00:00Z", "due_now": true, "exact_at": "2024-08-25T15:00:00Z", "excluded_at": "2024-08-25T15:00:00Z", "id": "00000000-0000-0000-0000-000000000000", "last_outcome": "string", "next_at": "2024-08-25T15:00:00Z", "number": "string", "status": "scheduled", "updated_at": "2024-08-25T15:00:00Z" }
json
application/json

List Campaigns

GET
/api/v1/campaigns

List this organization's campaigns, newest first. Use when showing the catalogue of campaigns, or checking what already exists before creating another. Changes nothing.

List Campaigns › query Parameters

status
​

List Campaigns › Responses

Successful Response

​CampaignOut[]
CampaignOut
agent_id
​string · uuid · required
​required
​required
​AnswerField[] · required
​required
​required
​required
collection_told_fields
​string[] · required
contact_source
​ContactSourceKind · enum · required

Where a campaign's contacts come from.

COLLECTION names docs/features/custom-data.md's stored collection. This slice holds the id and nothing else: the read is an injected ContactSource, bound to nothing today.

Enum values:
inline
collection
​object · required
created_at
​string · date-time · required
​required
​HoursSlot[] · required
id
​string · uuid · required
​required
name
​string · required
org_id
​string · uuid · required
​required
​required
​required
provider_failure_streak
​integer · required
​required
status
​CampaignStatus · enum · required

Where a campaign is in its life.

PAUSED is one state whoever caused it — see PauseOrigin. There is no separate "stopped by the provider" status, because resuming is the same operation either way and a second status would double every read of "is this campaign dialling".

Enum values:
draft
active
paused
completed
timezone
​string · required
trunk_id
​string · uuid · required
updated_at
​string · date-time · required
warnings
​string[] · required
GET/api/v1/campaigns
curl /api/v1/campaigns \ --header 'Authorization: Bearer <token>'
Example Responses
[ { "agent_id": "00000000-0000-0000-0000-000000000000", "answers_tool_id": "00000000-0000-0000-0000-000000000000", "board_id": "00000000-0000-0000-0000-000000000000", "collection_answer_fields": [ { "key": "key", "question": "question" } ], "collection_id": "00000000-0000-0000-0000-000000000000", "collection_name_field": "string", "collection_number_field": "string", "collection_told_fields": [ "string" ], "contact_source": "inline", "contacts_by_status": { "key": 0 }, "created_at": "2024-08-25T15:00:00Z", "description": "string", "hours": [ { "ends_at": "15:00:00", "starts_at": "15:00:00", "weekday": 0 } ], "id": "00000000-0000-0000-0000-000000000000", "max_attempts": 0, "name": "name", "org_id": "00000000-0000-0000-0000-000000000000", "pause_origin": "operator", "paused_at": "2024-08-25T15:00:00Z", "paused_reason": "string", "provider_failure_streak": 0, "retry_delay_minutes": 0, "status": "draft", "timezone": "timezone", "trunk_id": "00000000-0000-0000-0000-000000000000", "updated_at": "2024-08-25T15:00:00Z", "warnings": [ "string" ] } ]
json
application/json

Create Campaign

POST
/api/v1/campaigns

Open a campaign: an agent, an outbound trunk, a retry policy and calling hours. Use when many contacts need the same agent to call them under one policy.

It opens as a draft with sensible values already filled in, which you can see and change. Nothing is dialled until you activate it. A campaign drawn from a custom-data collection imports that collection's rows as it is created.

Create Campaign › Request Body

What a client sends to open a campaign. Every field that decides how it dials is here and pre-filled, except the ones that cannot be guessed -- and the retry policy, which is the client's to write (rule 7).
CampaignIn
agent_id
​string · uuid · required
name
​string · minLength: 1 · maxLength: 200 · required
timezone
​string · required
trunk_id
​string · uuid · required
​AnswerField[]
​
​
​
collection_told_fields
​string[]
contact_source
​ContactSourceKind · enum

Where a campaign's contacts come from.

COLLECTION names docs/features/custom-data.md's stored collection. This slice holds the id and nothing else: the read is an injected ContactSource, bound to nothing today.

Enum values:
inline
collection
Default: inline
​
​
​
​

Create Campaign › Responses

Successful Response

CampaignOut
agent_id
​string · uuid · required
​required
​required
​AnswerField[] · required
​required
​required
​required
collection_told_fields
​string[] · required
contact_source
​ContactSourceKind · enum · required

Where a campaign's contacts come from.

COLLECTION names docs/features/custom-data.md's stored collection. This slice holds the id and nothing else: the read is an injected ContactSource, bound to nothing today.

Enum values:
inline
collection
​object · required
created_at
​string · date-time · required
​required
​HoursSlot[] · required
id
​string · uuid · required
​required
name
​string · required
org_id
​string · uuid · required
​required
​required
​required
provider_failure_streak
​integer · required
​required
status
​CampaignStatus · enum · required

Where a campaign is in its life.

PAUSED is one state whoever caused it — see PauseOrigin. There is no separate "stopped by the provider" status, because resuming is the same operation either way and a second status would double every read of "is this campaign dialling".

Enum values:
draft
active
paused
completed
timezone
​string · required
trunk_id
​string · uuid · required
updated_at
​string · date-time · required
warnings
​string[] · required
POST/api/v1/campaigns
curl /api/v1/campaigns \ --request POST \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer <token>' \ --data '{ "agent_id": "00000000-0000-0000-0000-000000000000", "collection_answer_fields": [ { "key": "key", "question": "question" } ], "collection_id": "00000000-0000-0000-0000-000000000000", "collection_name_field": "string", "collection_number_field": "string", "collection_told_fields": [ "string" ], "contact_source": "inline", "description": "string", "hours": [ { "ends_at": "15:00:00", "starts_at": "15:00:00", "weekday": 0 } ], "max_attempts": 1, "name": "name", "retry_delay_minutes": 5, "timezone": "timezone", "trunk_id": "00000000-0000-0000-0000-000000000000" }'
Example Request Body
{ "agent_id": "00000000-0000-0000-0000-000000000000", "collection_answer_fields": [ { "key": "key", "question": "question" } ], "collection_id": "00000000-0000-0000-0000-000000000000", "collection_name_field": "string", "collection_number_field": "string", "collection_told_fields": [ "string" ], "contact_source": "inline", "description": "string", "hours": [ { "ends_at": "15:00:00", "starts_at": "15:00:00", "weekday": 0 } ], "max_attempts": 1, "name": "name", "retry_delay_minutes": 5, "timezone": "timezone", "trunk_id": "00000000-0000-0000-0000-000000000000" }
json
Example Responses
{ "agent_id": "00000000-0000-0000-0000-000000000000", "answers_tool_id": "00000000-0000-0000-0000-000000000000", "board_id": "00000000-0000-0000-0000-000000000000", "collection_answer_fields": [ { "key": "key", "question": "question" } ], "collection_id": "00000000-0000-0000-0000-000000000000", "collection_name_field": "string", "collection_number_field": "string", "collection_told_fields": [ "string" ], "contact_source": "inline", "contacts_by_status": { "key": 0 }, "created_at": "2024-08-25T15:00:00Z", "description": "string", "hours": [ { "ends_at": "15:00:00", "starts_at": "15:00:00", "weekday": 0 } ], "id": "00000000-0000-0000-0000-000000000000", "max_attempts": 0, "name": "name", "org_id": "00000000-0000-0000-0000-000000000000", "pause_origin": "operator", "paused_at": "2024-08-25T15:00:00Z", "paused_reason": "string", "provider_failure_streak": 0, "retry_delay_minutes": 0, "status": "draft", "timezone": "timezone", "trunk_id": "00000000-0000-0000-0000-000000000000", "updated_at": "2024-08-25T15:00:00Z", "warnings": [ "string" ] }
json
application/json

Get Campaign

GET
/api/v1/campaigns/{campaign_id}

Read one campaign, its calling hours, and anything they were warned about. Use when opening a specific campaign by id. Changes nothing.

Get Campaign › path Parameters

campaign_id
​string · uuid · required

Get Campaign › Responses

Successful Response

CampaignOut
agent_id
​string · uuid · required
​required
​required
​AnswerField[] · required
​required
​required
​required
collection_told_fields
​string[] · required
contact_source
​ContactSourceKind · enum · required

Where a campaign's contacts come from.

COLLECTION names docs/features/custom-data.md's stored collection. This slice holds the id and nothing else: the read is an injected ContactSource, bound to nothing today.

Enum values:
inline
collection
​object · required
created_at
​string · date-time · required
​required
​HoursSlot[] · required
id
​string · uuid · required
​required
name
​string · required
org_id
​string · uuid · required
​required
​required
​required
provider_failure_streak
​integer · required
​required
status
​CampaignStatus · enum · required

Where a campaign is in its life.

PAUSED is one state whoever caused it — see PauseOrigin. There is no separate "stopped by the provider" status, because resuming is the same operation either way and a second status would double every read of "is this campaign dialling".

Enum values:
draft
active
paused
completed
timezone
​string · required
trunk_id
​string · uuid · required
updated_at
​string · date-time · required
warnings
​string[] · required
GET/api/v1/campaigns/{campaign_id}
curl /api/v1/campaigns/:campaign_id \ --header 'Authorization: Bearer <token>'
Example Responses
{ "agent_id": "00000000-0000-0000-0000-000000000000", "answers_tool_id": "00000000-0000-0000-0000-000000000000", "board_id": "00000000-0000-0000-0000-000000000000", "collection_answer_fields": [ { "key": "key", "question": "question" } ], "collection_id": "00000000-0000-0000-0000-000000000000", "collection_name_field": "string", "collection_number_field": "string", "collection_told_fields": [ "string" ], "contact_source": "inline", "contacts_by_status": { "key": 0 }, "created_at": "2024-08-25T15:00:00Z", "description": "string", "hours": [ { "ends_at": "15:00:00", "starts_at": "15:00:00", "weekday": 0 } ], "id": "00000000-0000-0000-0000-000000000000", "max_attempts": 0, "name": "name", "org_id": "00000000-0000-0000-0000-000000000000", "pause_origin": "operator", "paused_at": "2024-08-25T15:00:00Z", "paused_reason": "string", "provider_failure_streak": 0, "retry_delay_minutes": 0, "status": "draft", "timezone": "timezone", "trunk_id": "00000000-0000-0000-0000-000000000000", "updated_at": "2024-08-25T15:00:00Z", "warnings": [ "string" ] }
json
application/json

Delete Campaign

DELETE
/api/v1/campaigns/{campaign_id}

Delete a campaign and its contact list. Use when the campaign should no longer exist at all — there is no undo. Pause it first if it is dialling.

Delete Campaign › path Parameters

campaign_id
​string · uuid · required

Delete Campaign › Responses

Successful Response

No data returned
DELETE/api/v1/campaigns/{campaign_id}
curl /api/v1/campaigns/:campaign_id \ --request DELETE \ --header 'Authorization: Bearer <token>'
Example Responses
No example specified for this content type

Update Campaign

PATCH
/api/v1/campaigns/{campaign_id}

Change a campaign's name, agent, trunk, timezone or retry policy. Use when a campaign's own settings need to change — only the fields given.

It also takes the collection columns the agent is told about and the questions each call answers — and the questions need the collection read, because their kinds are the collection's. Hence contacts here: without it every save of a campaign that asks anything is refused as a deployment that cannot read a collection.

Update Campaign › path Parameters

campaign_id
​string · uuid · required

Update Campaign › Request Body

A partial edit. `contact_source` and `collection_id` are absent on purpose: where a campaign's list comes from is decided when it is created, and changing it under a half-dialled list would leave the attempt ledger describing rows that no longer exist.
CampaignUpdate
​
​
​
​
​
​
​
​
​
​

Update Campaign › Responses

Successful Response

CampaignOut
agent_id
​string · uuid · required
​required
​required
​AnswerField[] · required
​required
​required
​required
collection_told_fields
​string[] · required
contact_source
​ContactSourceKind · enum · required

Where a campaign's contacts come from.

COLLECTION names docs/features/custom-data.md's stored collection. This slice holds the id and nothing else: the read is an injected ContactSource, bound to nothing today.

Enum values:
inline
collection
​object · required
created_at
​string · date-time · required
​required
​HoursSlot[] · required
id
​string · uuid · required
​required
name
​string · required
org_id
​string · uuid · required
​required
​required
​required
provider_failure_streak
​integer · required
​required
status
​CampaignStatus · enum · required

Where a campaign is in its life.

PAUSED is one state whoever caused it — see PauseOrigin. There is no separate "stopped by the provider" status, because resuming is the same operation either way and a second status would double every read of "is this campaign dialling".

Enum values:
draft
active
paused
completed
timezone
​string · required
trunk_id
​string · uuid · required
updated_at
​string · date-time · required
warnings
​string[] · required
PATCH/api/v1/campaigns/{campaign_id}
curl /api/v1/campaigns/:campaign_id \ --request PATCH \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer <token>' \ --data '{ "agent_id": "00000000-0000-0000-0000-000000000000", "board_id": "00000000-0000-0000-0000-000000000000", "collection_answer_fields": [ { "key": "key", "question": "question" } ], "collection_told_fields": [ "string" ], "description": "string", "max_attempts": 1, "name": "string", "retry_delay_minutes": 5, "timezone": "string", "trunk_id": "00000000-0000-0000-0000-000000000000" }'
Example Request Body
{ "agent_id": "00000000-0000-0000-0000-000000000000", "board_id": "00000000-0000-0000-0000-000000000000", "collection_answer_fields": [ { "key": "key", "question": "question" } ], "collection_told_fields": [ "string" ], "description": "string", "max_attempts": 1, "name": "string", "retry_delay_minutes": 5, "timezone": "string", "trunk_id": "00000000-0000-0000-0000-000000000000" }
json
Example Responses
{ "agent_id": "00000000-0000-0000-0000-000000000000", "answers_tool_id": "00000000-0000-0000-0000-000000000000", "board_id": "00000000-0000-0000-0000-000000000000", "collection_answer_fields": [ { "key": "key", "question": "question" } ], "collection_id": "00000000-0000-0000-0000-000000000000", "collection_name_field": "string", "collection_number_field": "string", "collection_told_fields": [ "string" ], "contact_source": "inline", "contacts_by_status": { "key": 0 }, "created_at": "2024-08-25T15:00:00Z", "description": "string", "hours": [ { "ends_at": "15:00:00", "starts_at": "15:00:00", "weekday": 0 } ], "id": "00000000-0000-0000-0000-000000000000", "max_attempts": 0, "name": "name", "org_id": "00000000-0000-0000-0000-000000000000", "pause_origin": "operator", "paused_at": "2024-08-25T15:00:00Z", "paused_reason": "string", "provider_failure_streak": 0, "retry_delay_minutes": 0, "status": "draft", "timezone": "timezone", "trunk_id": "00000000-0000-0000-0000-000000000000", "updated_at": "2024-08-25T15:00:00Z", "warnings": [ "string" ] }
json
application/json

Activate Campaign

POST
/api/v1/campaigns/{campaign_id}/activate

Start dialling this campaign, or resume one that stopped. Use once the campaign's agent, list and hours are right and it should actually call people — this makes the platform phone somebody.

It queues nothing: the campaign joins the organization's rotation and takes the next free slot. Resuming keeps the record of why it stopped.

Refused, saying which, for a campaign already dialling, a completed one (copy it instead), one whose retry policy is blank, one whose agent no arrangement of its profile can hold, and one with no calling hours at all.

Activate Campaign › path Parameters

campaign_id
​string · uuid · required

Activate Campaign › Responses

Successful Response

CampaignOut
agent_id
​string · uuid · required
​required
​required
​AnswerField[] · required
​required
​required
​required
collection_told_fields
​string[] · required
contact_source
​ContactSourceKind · enum · required

Where a campaign's contacts come from.

COLLECTION names docs/features/custom-data.md's stored collection. This slice holds the id and nothing else: the read is an injected ContactSource, bound to nothing today.

Enum values:
inline
collection
​object · required
created_at
​string · date-time · required
​required
​HoursSlot[] · required
id
​string · uuid · required
​required
name
​string · required
org_id
​string · uuid · required
​required
​required
​required
provider_failure_streak
​integer · required
​required
status
​CampaignStatus · enum · required

Where a campaign is in its life.

PAUSED is one state whoever caused it — see PauseOrigin. There is no separate "stopped by the provider" status, because resuming is the same operation either way and a second status would double every read of "is this campaign dialling".

Enum values:
draft
active
paused
completed
timezone
​string · required
trunk_id
​string · uuid · required
updated_at
​string · date-time · required
warnings
​string[] · required
POST/api/v1/campaigns/{campaign_id}/activate
curl /api/v1/campaigns/:campaign_id/activate \ --request POST \ --header 'Authorization: Bearer <token>'
Example Responses
{ "agent_id": "00000000-0000-0000-0000-000000000000", "answers_tool_id": "00000000-0000-0000-0000-000000000000", "board_id": "00000000-0000-0000-0000-000000000000", "collection_answer_fields": [ { "key": "key", "question": "question" } ], "collection_id": "00000000-0000-0000-0000-000000000000", "collection_name_field": "string", "collection_number_field": "string", "collection_told_fields": [ "string" ], "contact_source": "inline", "contacts_by_status": { "key": 0 }, "created_at": "2024-08-25T15:00:00Z", "description": "string", "hours": [ { "ends_at": "15:00:00", "starts_at": "15:00:00", "weekday": 0 } ], "id": "00000000-0000-0000-0000-000000000000", "max_attempts": 0, "name": "name", "org_id": "00000000-0000-0000-0000-000000000000", "pause_origin": "operator", "paused_at": "2024-08-25T15:00:00Z", "paused_reason": "string", "provider_failure_streak": 0, "retry_delay_minutes": 0, "status": "draft", "timezone": "timezone", "trunk_id": "00000000-0000-0000-0000-000000000000", "updated_at": "2024-08-25T15:00:00Z", "warnings": [ "string" ] }
json
application/json

Link Collection

POST
/api/v1/campaigns/{campaign_id}/collection

Give a campaign that has no list one, and optionally take records from it. Use once, when a campaign should draw its contacts from a custom-data collection rather than numbers added by hand.

Which column holds the number comes with it and is chosen once. Refused for a campaign that already draws from a list: a campaign that needs a different one is another campaign.

Link Collection › path Parameters

campaign_id
​string · uuid · required

Link Collection › Request Body

Which list a campaign is being given, and which of its columns is dialled. **The two arrive together and neither is editable afterwards.** Which column holds the number is chosen once for the life of the campaign — every contact that enters freezes its number from it — so it is asked at the one moment there is a list in front of the person to choose it from. `records` is the same question the records screen asks everywhere else: the filters it was showing and the rows unticked afterwards, never a list of rows. Absent means the list is attached and nobody is taken from it yet.
CollectionLink
collection_id
​string · uuid · required
number_field
​string · minLength: 1 · required
​
​

Link Collection › Responses

Successful Response

ImportResult
added
​integer · required
​RejectedRow[] · required
​WarnedRow[]
POST/api/v1/campaigns/{campaign_id}/collection
curl /api/v1/campaigns/:campaign_id/collection \ --request POST \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer <token>' \ --data '{ "collection_id": "00000000-0000-0000-0000-000000000000", "name_field": "string", "number_field": "number_field", "records": { "exclude_ids": [ "00000000-0000-0000-0000-000000000000" ], "filters": [ { "field": "field", "op": "eq", "value": "string", "values": [ "string" ] } ], "ids": [ "00000000-0000-0000-0000-000000000000" ] } }'
Example Request Body
{ "collection_id": "00000000-0000-0000-0000-000000000000", "name_field": "string", "number_field": "number_field", "records": { "exclude_ids": [ "00000000-0000-0000-0000-000000000000" ], "filters": [ { "field": "field", "op": "eq", "value": "string", "values": [ "string" ] } ], "ids": [ "00000000-0000-0000-0000-000000000000" ] } }
json
Example Responses
{ "added": 0, "rejected": [ { "number": "number", "reason": "reason" } ], "warnings": [ { "number": "number", "warning": "warning" } ] }
json
application/json

Add Contacts

POST
/api/v1/campaigns/{campaign_id}/contacts

Add numbers to a campaign's list. Use to hand-add specific phone numbers to a campaign, rather than importing from a collection.

Rows this campaign already holds, or that are not a phone number under the organization's default country, come back in rejected with the reason; nothing is silently dropped.

Add Contacts › path Parameters

campaign_id
​string · uuid · required

Add Contacts › Request Body

A paste or a CSV, already split into rows. Rejected rows are reported and never silently dropped. **The list is bounded by the cap a campaign holds**, so a paste no campaign could ever accept is refused at the edge, before ten thousand savepoints are opened to find that out one row at a time.
ContactsIn
​ContactIn[] · maxItems: 10000 · required

Add Contacts › Responses

Successful Response

ImportResult
added
​integer · required
​RejectedRow[] · required
​WarnedRow[]
POST/api/v1/campaigns/{campaign_id}/contacts
curl /api/v1/campaigns/:campaign_id/contacts \ --request POST \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer <token>' \ --data '{ "contacts": [ { "collection_record_id": "00000000-0000-0000-0000-000000000000", "exact_at": "2024-08-25T15:00:00Z", "name": "string", "number": "number" } ] }'
Example Request Body
{ "contacts": [ { "collection_record_id": "00000000-0000-0000-0000-000000000000", "exact_at": "2024-08-25T15:00:00Z", "name": "string", "number": "number" } ] }
json
Example Responses
{ "added": 0, "rejected": [ { "number": "number", "reason": "reason" } ], "warnings": [ { "number": "number", "warning": "warning" } ] }
json
application/json

Add Contacts From Collection

POST
/api/v1/campaigns/{campaign_id}/contacts/from-collection

Add the records a filter finds to a campaign's list. Use after a campaign is already linked to a collection, to pull in records matching a filter.

The body is the question the client was looking at — the collection's own filters, and the records they picked or unticked — never a list of rows. Records with no usable phone number come back in rejected with the reason.

Add Contacts From Collection › path Parameters

campaign_id
​string · uuid · required

Add Contacts From Collection › Request Body

Which of a collection's records a campaign is being asked to take, or to let go of — **the question, never a list of rows**. `custom-data.md`'s ruling of 2026-09-09 in this slice's terms: a client ticking "select all" over a filtered collection of five hundred means the five hundred, not the two hundred their browser had drawn. So what crosses the wire is the filters the screen was showing, plus the rows they unticked afterwards; this side re-runs it against the store. An empty question is the whole collection, which is what a campaign created from a collection already takes.
CollectionQuestion
exclude_ids
​string[] · maxItems: 10000
​SourceFilter[] · maxItems: 32
ids
​string[] · maxItems: 10000

Add Contacts From Collection › Responses

Successful Response

ImportResult
added
​integer · required
​RejectedRow[] · required
​WarnedRow[]
POST/api/v1/campaigns/{campaign_id}/contacts/from-collection
curl /api/v1/campaigns/:campaign_id/contacts/from-collection \ --request POST \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer <token>' \ --data '{ "exclude_ids": [ "00000000-0000-0000-0000-000000000000" ], "filters": [ { "field": "field", "op": "eq", "value": "string", "values": [ "string" ] } ], "ids": [ "00000000-0000-0000-0000-000000000000" ] }'
Example Request Body
{ "exclude_ids": [ "00000000-0000-0000-0000-000000000000" ], "filters": [ { "field": "field", "op": "eq", "value": "string", "values": [ "string" ] } ], "ids": [ "00000000-0000-0000-0000-000000000000" ] }
json
Example Responses
{ "added": 0, "rejected": [ { "number": "number", "reason": "reason" } ], "warnings": [ { "number": "number", "warning": "warning" } ] }
json
application/json

Remove Contacts From Collection

POST
/api/v1/campaigns/{campaign_id}/contacts/remove-from-collection

Take the records a filter finds back out of a campaign's list. Use when records matching a filter should stop being part of the campaign entirely — not merely excluded from calling.

A record the campaign never held is not an error. A call already placed is not cancelled by this — that is excluding the contact.

Remove Contacts From Collection › path Parameters

campaign_id
​string · uuid · required

Remove Contacts From Collection › Request Body

Which of a collection's records a campaign is being asked to take, or to let go of — **the question, never a list of rows**. `custom-data.md`'s ruling of 2026-09-09 in this slice's terms: a client ticking "select all" over a filtered collection of five hundred means the five hundred, not the two hundred their browser had drawn. So what crosses the wire is the filters the screen was showing, plus the rows they unticked afterwards; this side re-runs it against the store. An empty question is the whole collection, which is what a campaign created from a collection already takes.
CollectionQuestion
exclude_ids
​string[] · maxItems: 10000
​SourceFilter[] · maxItems: 32
ids
​string[] · maxItems: 10000

Remove Contacts From Collection › Responses

Successful Response

How many of a campaign's contacts a question took back out. Contacts, not records: a record the campaign never held is not an error and is not counted — the client asked for a state, not for a transaction.
RemovedFromCampaign
removed
​integer · required
POST/api/v1/campaigns/{campaign_id}/contacts/remove-from-collection
curl /api/v1/campaigns/:campaign_id/contacts/remove-from-collection \ --request POST \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer <token>' \ --data '{ "exclude_ids": [ "00000000-0000-0000-0000-000000000000" ], "filters": [ { "field": "field", "op": "eq", "value": "string", "values": [ "string" ] } ], "ids": [ "00000000-0000-0000-0000-000000000000" ] }'
Example Request Body
{ "exclude_ids": [ "00000000-0000-0000-0000-000000000000" ], "filters": [ { "field": "field", "op": "eq", "value": "string", "values": [ "string" ] } ], "ids": [ "00000000-0000-0000-0000-000000000000" ] }
json
Example Responses
{ "removed": 0 }
json
application/json

Set Campaign Hours

PUT
/api/v1/campaigns/{campaign_id}/hours

Replace a campaign's calling hours: slots per weekday, in its own timezone. Use once the hours are decided; preview_campaign_hours first to see what the change does to calls already waiting.

No slot may cross midnight, no two may overlap, and a schedule that opens on no day at all is rejected. Hours outside the Spanish commercial-calling window are warned about and saved as asked. Waiting calls that keep to the hours (a timed retry, or a callback the client asked to keep to them) and that the new hours do not hold are moved to the first later day whose hours hold that same local time (or, when no day does, to the next opening); every other time somebody named stays where it is and is counted in warnings. preview_campaign_hours gives both counts before you save.

Set Campaign Hours › path Parameters

campaign_id
​string · uuid · required

Set Campaign Hours › Request Body

A whole schedule, replaced at once. Slots are validated together — no two on one weekday may overlap — so a schedule is never half-saved.
HoursIn
​HoursSlot[] · required

Set Campaign Hours › Responses

Successful Response

CampaignOut
agent_id
​string · uuid · required
​required
​required
​AnswerField[] · required
​required
​required
​required
collection_told_fields
​string[] · required
contact_source
​ContactSourceKind · enum · required

Where a campaign's contacts come from.

COLLECTION names docs/features/custom-data.md's stored collection. This slice holds the id and nothing else: the read is an injected ContactSource, bound to nothing today.

Enum values:
inline
collection
​object · required
created_at
​string · date-time · required
​required
​HoursSlot[] · required
id
​string · uuid · required
​required
name
​string · required
org_id
​string · uuid · required
​required
​required
​required
provider_failure_streak
​integer · required
​required
status
​CampaignStatus · enum · required

Where a campaign is in its life.

PAUSED is one state whoever caused it — see PauseOrigin. There is no separate "stopped by the provider" status, because resuming is the same operation either way and a second status would double every read of "is this campaign dialling".

Enum values:
draft
active
paused
completed
timezone
​string · required
trunk_id
​string · uuid · required
updated_at
​string · date-time · required
warnings
​string[] · required
PUT/api/v1/campaigns/{campaign_id}/hours
curl /api/v1/campaigns/:campaign_id/hours \ --request PUT \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer <token>' \ --data '{ "slots": [ { "ends_at": "15:00:00", "starts_at": "15:00:00", "weekday": 0 } ] }'
Example Request Body
{ "slots": [ { "ends_at": "15:00:00", "starts_at": "15:00:00", "weekday": 0 } ] }
json
Example Responses
{ "agent_id": "00000000-0000-0000-0000-000000000000", "answers_tool_id": "00000000-0000-0000-0000-000000000000", "board_id": "00000000-0000-0000-0000-000000000000", "collection_answer_fields": [ { "key": "key", "question": "question" } ], "collection_id": "00000000-0000-0000-0000-000000000000", "collection_name_field": "string", "collection_number_field": "string", "collection_told_fields": [ "string" ], "contact_source": "inline", "contacts_by_status": { "key": 0 }, "created_at": "2024-08-25T15:00:00Z", "description": "string", "hours": [ { "ends_at": "15:00:00", "starts_at": "15:00:00", "weekday": 0 } ], "id": "00000000-0000-0000-0000-000000000000", "max_attempts": 0, "name": "name", "org_id": "00000000-0000-0000-0000-000000000000", "pause_origin": "operator", "paused_at": "2024-08-25T15:00:00Z", "paused_reason": "string", "provider_failure_streak": 0, "retry_delay_minutes": 0, "status": "draft", "timezone": "timezone", "trunk_id": "00000000-0000-0000-0000-000000000000", "updated_at": "2024-08-25T15:00:00Z", "warnings": [ "string" ] }
json
application/json

Preview Campaign Hours

POST
/api/v1/campaigns/{campaign_id}/hours/preview

Count what these hours would do to the calls set for an exact time, without saving them: how many that keep to the hours would move, and how many named times would stay outside them. Use before set_campaign_hours, to show the effect before committing to it.

Ask before set_campaign_hours so whoever is saving can be told both counts, and confirm. Only calls still to come are counted: one already due rings while the hours are open and moves while they are closed. Nothing is written.

Preview Campaign Hours › path Parameters

campaign_id
​string · uuid · required

Preview Campaign Hours › Request Body

A whole schedule, replaced at once. Slots are validated together — no two on one weekday may overlap — so a schedule is never half-saved.
HoursIn
​HoursSlot[] · required

Preview Campaign Hours › Responses

Successful Response

What saving a schedule would do to the calls already set for an exact time (`outbound-calls.md` rule 41), read before it is saved. Both counts are over calls still to come; one already due is the dial's (rule 22).
HoursChange
moves
​integer · required

How many waiting calls that keep to the calling hours -- a timed retry, or a callback the client asked to keep to them -- these hours would not hold. Saving the hours moves each to the first later day whose hours hold the same local time, or, when no day's hours hold it, to the next opening.

stays_outside
​integer · required

How many waiting calls set for a time a person, an integration or a caller named these hours would not hold. Saving the hours leaves each at its time: this is a warning, not a move.

POST/api/v1/campaigns/{campaign_id}/hours/preview
curl /api/v1/campaigns/:campaign_id/hours/preview \ --request POST \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer <token>' \ --data '{ "slots": [ { "ends_at": "15:00:00", "starts_at": "15:00:00", "weekday": 0 } ] }'
Example Request Body
{ "slots": [ { "ends_at": "15:00:00", "starts_at": "15:00:00", "weekday": 0 } ] }
json
Example Responses
{ "moves": 0, "stays_outside": 0 }
json
application/json

Pause Campaign

POST
/api/v1/campaigns/{campaign_id}/pause

Stop dialling this campaign. Use when a running campaign needs to stop without losing it — resume later with activate_campaign. A call already in progress is never cut.

Pause Campaign › path Parameters

campaign_id
​string · uuid · required

Pause Campaign › Responses

Successful Response

CampaignOut
agent_id
​string · uuid · required
​required
​required
​AnswerField[] · required
​required
​required
​required
collection_told_fields
​string[] · required
contact_source
​ContactSourceKind · enum · required

Where a campaign's contacts come from.

COLLECTION names docs/features/custom-data.md's stored collection. This slice holds the id and nothing else: the read is an injected ContactSource, bound to nothing today.

Enum values:
inline
collection
​object · required
created_at
​string · date-time · required
​required
​HoursSlot[] · required
id
​string · uuid · required
​required
name
​string · required
org_id
​string · uuid · required
​required
​required
​required
provider_failure_streak
​integer · required
​required
status
​CampaignStatus · enum · required

Where a campaign is in its life.

PAUSED is one state whoever caused it — see PauseOrigin. There is no separate "stopped by the provider" status, because resuming is the same operation either way and a second status would double every read of "is this campaign dialling".

Enum values:
draft
active
paused
completed
timezone
​string · required
trunk_id
​string · uuid · required
updated_at
​string · date-time · required
warnings
​string[] · required
POST/api/v1/campaigns/{campaign_id}/pause
curl /api/v1/campaigns/:campaign_id/pause \ --request POST \ --header 'Authorization: Bearer <token>'
Example Responses
{ "agent_id": "00000000-0000-0000-0000-000000000000", "answers_tool_id": "00000000-0000-0000-0000-000000000000", "board_id": "00000000-0000-0000-0000-000000000000", "collection_answer_fields": [ { "key": "key", "question": "question" } ], "collection_id": "00000000-0000-0000-0000-000000000000", "collection_name_field": "string", "collection_number_field": "string", "collection_told_fields": [ "string" ], "contact_source": "inline", "contacts_by_status": { "key": 0 }, "created_at": "2024-08-25T15:00:00Z", "description": "string", "hours": [ { "ends_at": "15:00:00", "starts_at": "15:00:00", "weekday": 0 } ], "id": "00000000-0000-0000-0000-000000000000", "max_attempts": 0, "name": "name", "org_id": "00000000-0000-0000-0000-000000000000", "pause_origin": "operator", "paused_at": "2024-08-25T15:00:00Z", "paused_reason": "string", "provider_failure_streak": 0, "retry_delay_minutes": 0, "status": "draft", "timezone": "timezone", "trunk_id": "00000000-0000-0000-0000-000000000000", "updated_at": "2024-08-25T15:00:00Z", "warnings": [ "string" ] }
json
application/json

Get Campaign Summary

GET
/api/v1/campaigns/{campaign_id}/summary

How far a campaign has got: contacts by status, attempts by outcome, connect rate, and whether the organization's spend gate is what is stopping it from dialling right now. Use when showing campaign progress. Changes nothing.

Get Campaign Summary › path Parameters

campaign_id
​string · uuid · required

Get Campaign Summary › Responses

Successful Response

What a campaign's screen reads, and all of it comes from this slice's own tables. There is no money and no minutes here, and that is deliberate: a per-campaign euro figure is `analytics`' shape and needs an aggregate on `interactions` that does not exist. A join from here would be two features reading one table.
CampaignSummaryOut
as_of
​string · date-time · required
​object · required
attempts_spent
​integer · required
campaign_id
​string · uuid · required
​required
​object · required
contacts_without_number
​integer · required
due_later
​integer · required
due_now
​integer · required
status
​CampaignStatus · enum · required

Where a campaign is in its life.

PAUSED is one state whoever caused it — see PauseOrigin. There is no separate "stopped by the provider" status, because resuming is the same operation either way and a second status would double every read of "is this campaign dialling".

Enum values:
draft
active
paused
completed
unclassified_pickups
​integer · required
​
GET/api/v1/campaigns/{campaign_id}/summary
curl /api/v1/campaigns/:campaign_id/summary \ --header 'Authorization: Bearer <token>'
Example Responses
{ "as_of": "2024-08-25T15:00:00Z", "attempts_by_outcome": { "key": 0 }, "attempts_spent": 0, "campaign_id": "00000000-0000-0000-0000-000000000000", "connect_rate": 0, "contacts_by_status": { "key": 0 }, "contacts_without_number": 0, "due_later": 0, "due_now": 0, "status": "draft", "unclassified_pickups": 0, "waiting_for_capacity": true }
json
application/json

Exclude Contact

POST
/api/v1/contacts/{contact_id}/exclude

Stop calling this contact. Use when one person should be left out of the rotation without touching the rest of the campaign's list. A call already in progress is never cut. Refused for a contact already done or exhausted: there is nothing left to stop.

Exclude Contact › path Parameters

contact_id
​string · uuid · required

Exclude Contact › Responses

Successful Response

ContactOut
attempts_spent
​integer · required
​required
​required
created_at
​string · date-time · required
due_now
​boolean · required
​required
​required
id
​string · uuid · required
​required
​required
​required
status
​ContactStatus · enum · required

One contact's place in the state machine outbound-calls.md calls single-writer and idempotent.

EXHAUSTED is we are finished with this number — the attempt budget is gone, the provider confirmed the destination will never work, or the organization's own retention window destroyed the number while the contact was still waiting for a turn and there is nothing left to dial. EXCLUDED is the client said not to call this person, and only a client ever writes it. Keeping them apart is what lets "excluding a contact cancels its pending calls" be re-openable while a confirmed-invalid number is not.

There is no pending. A contact is dialable the moment it exists — "activating a campaign does not queue its contacts" — so it is written SCHEDULED and there is no earlier state for it to sit in. A member nothing writes is a state a reader has to ask about and nobody can reach.

Enum values:
scheduled
calling
done
exhausted
excluded
updated_at
​string · date-time · required
POST/api/v1/contacts/{contact_id}/exclude
curl /api/v1/contacts/:contact_id/exclude \ --request POST \ --header 'Authorization: Bearer <token>'
Example Responses
{ "attempts_spent": 0, "campaign_id": "00000000-0000-0000-0000-000000000000", "collection_record_id": "00000000-0000-0000-0000-000000000000", "created_at": "2024-08-25T15:00:00Z", "due_now": true, "exact_at": "2024-08-25T15:00:00Z", "excluded_at": "2024-08-25T15:00:00Z", "id": "00000000-0000-0000-0000-000000000000", "last_outcome": "string", "next_at": "2024-08-25T15:00:00Z", "number": "string", "status": "scheduled", "updated_at": "2024-08-25T15:00:00Z" }
json
application/json

Include Contact

POST
/api/v1/contacts/{contact_id}/include

Put an excluded contact back. Use when a previously excluded contact should be called again. It rejoins the rotation immediately. Refused for a contact that was never excluded or has since finished, and when the list has been filled back up to its bound while this contact was off it.

Include Contact › path Parameters

contact_id
​string · uuid · required

Include Contact › Responses

Successful Response

ContactOut
attempts_spent
​integer · required
​required
​required
created_at
​string · date-time · required
due_now
​boolean · required
​required
​required
id
​string · uuid · required
​required
​required
​required
status
​ContactStatus · enum · required

One contact's place in the state machine outbound-calls.md calls single-writer and idempotent.

EXHAUSTED is we are finished with this number — the attempt budget is gone, the provider confirmed the destination will never work, or the organization's own retention window destroyed the number while the contact was still waiting for a turn and there is nothing left to dial. EXCLUDED is the client said not to call this person, and only a client ever writes it. Keeping them apart is what lets "excluding a contact cancels its pending calls" be re-openable while a confirmed-invalid number is not.

There is no pending. A contact is dialable the moment it exists — "activating a campaign does not queue its contacts" — so it is written SCHEDULED and there is no earlier state for it to sit in. A member nothing writes is a state a reader has to ask about and nobody can reach.

Enum values:
scheduled
calling
done
exhausted
excluded
updated_at
​string · date-time · required
POST/api/v1/contacts/{contact_id}/include
curl /api/v1/contacts/:contact_id/include \ --request POST \ --header 'Authorization: Bearer <token>'
Example Responses
{ "attempts_spent": 0, "campaign_id": "00000000-0000-0000-0000-000000000000", "collection_record_id": "00000000-0000-0000-0000-000000000000", "created_at": "2024-08-25T15:00:00Z", "due_now": true, "exact_at": "2024-08-25T15:00:00Z", "excluded_at": "2024-08-25T15:00:00Z", "id": "00000000-0000-0000-0000-000000000000", "last_outcome": "string", "next_at": "2024-08-25T15:00:00Z", "number": "string", "status": "scheduled", "updated_at": "2024-08-25T15:00:00Z" }
json
application/json

billable-metricschannels