Platform API reference
The Platform API (/v1) field by field: every endpoint, every field, its type, its allowed values and its limits. Generated from /openapi/v1.json; interactive version: API reference.
The rules every endpoint shares (base URL, keys, strict bodies, paging, ids) are in the Overview; what every error code means is in Errors. To get a task done, start from a guide: Send a text, Send a WhatsApp template, Place a call, Run a campaign, Receive webhooks or Test without spending.
Messages
Section titled “Messages”One SMS, MMS or WhatsApp template per request, read back by id, and pictures people text you: GET /v1/messages, POST /v1/messages, GET /v1/messages/{id}, POST /v1/media, GET /v1/media/{id}. Fields: Messages.
An AI agent calls one number; read the call and its recording: GET /v1/calls, POST /v1/calls, GET /v1/calls/{id}, GET /v1/calls/{id}/transcript, POST /v1/calls/{id}/end, GET /v1/calls/{id}/voicemail, GET /v1/recordings/{conversation_id}, GET /v1/voicemail-recordings, POST /v1/voicemail-recordings, GET /v1/voicemail-recordings/{id}, DELETE /v1/voicemail-recordings/{id}. Fields: Calls.
Campaigns
Section titled “Campaigns”One message or call to many people, drafted, previewed and started: GET /v1/campaigns, POST /v1/campaigns, GET /v1/campaigns/{id}, PATCH /v1/campaigns/{id}, DELETE /v1/campaigns/{id}, POST /v1/campaigns/{id}/start, POST /v1/campaigns/{id}/pause, POST /v1/campaigns/{id}/resume, POST /v1/campaigns/{id}/cancel. Fields: Campaigns.
Previews, recipients and usage
Section titled “Previews, recipients and usage”Who a draft reaches and what it costs, then what happened to each person and what it cost: POST /v1/campaigns/{id}/previews, GET /v1/campaigns/{id}/previews/{preview_id}, GET /v1/campaigns/{id}/recipients, GET /v1/campaigns/{id}/usage, GET /v1/campaigns/{id}/recipients/{recipient_id}. Fields: Campaigns → CampaignPreview, CampaignRecipient and CampaignUsage.
Rounds
Section titled “Rounds”How an AI-call campaign went, in counts, and its next round: GET /v1/campaigns/{id}/results, POST /v1/campaigns/{id}/clone. Fields: Campaigns → CampaignResults and CampaignClone.
Contacts and contact lists
Section titled “Contacts and contact lists”The people you text and call, and the lists a campaign sends to: GET /v1/contacts, POST /v1/contacts, GET /v1/contacts/{id}, PATCH /v1/contacts/{id}, DELETE /v1/contacts/{id}, POST /v1/contacts/{id}/tags, DELETE /v1/contacts/{id}/tags/{tag}, POST /v1/contacts/{id}/dnc, GET /v1/contacts/{id}/consent-events, POST /v1/contacts/{id}/consent-events, GET /v1/contacts/{id}/channels, POST /v1/contacts/{id}/channels, DELETE /v1/contacts/{id}/channels/{address}, GET /v1/contact-fields, GET /v1/lists, POST /v1/lists, GET /v1/lists/{id}, PATCH /v1/lists/{id}, DELETE /v1/lists/{id}, GET /v1/lists/{id}/contacts, POST /v1/lists/{id}/contacts, DELETE /v1/lists/{id}/contacts/{contact}. Fields: Contacts, Contact lists.
Insights
Section titled “Insights”What each call finds out, as typed fields; its answers are a call’s extracted: GET /v1/insights, POST /v1/insights, GET /v1/insights/{id}, PATCH /v1/insights/{id}, DELETE /v1/insights/{id}. Fields: Insights.
Senders, templates, agents, audiences, me
Section titled “Senders, templates, agents, audiences, me”The read-only lists you pick from, and the key a request runs as: GET /v1/senders, GET /v1/templates, GET /v1/templates/{id}, GET /v1/sms-templates, GET /v1/sms-templates/{id}, GET /v1/agents, GET /v1/agents/{id}, PATCH /v1/agents/{id}, GET /v1/agents/{id}/prompt-versions, GET /v1/agents/{id}/prompt-versions/{version}, POST /v1/agents/{id}/test-turns, GET /v1/agents/{id}/phrase-rules, POST /v1/agents/{id}/phrase-rules, PATCH /v1/agents/{id}/phrase-rules/{rule_id}, DELETE /v1/agents/{id}/phrase-rules/{rule_id}, GET /v1/segments, POST /v1/segments, GET /v1/segments/{id}, PATCH /v1/segments/{id}, DELETE /v1/segments/{id}, GET /v1/segments/{id}/contacts, GET /v1/me. Fields: Senders and lookups.
Balance and compliance
Section titled “Balance and compliance”The credit balance a send or a campaign start can be refused over, what the credits went on (usage), and the outbound compliance acknowledgment a campaign start can be refused over: GET /v1/balance, GET /v1/usage, GET /v1/usage/events, GET /v1/usage/export, GET /v1/compliance, POST /v1/compliance/accept. Fields: Balance and compliance.
Webhooks
Section titled “Webhooks”Where events are delivered, and how they are signed: GET /v1/webhooks, POST /v1/webhooks, GET /v1/webhooks/{id}, PATCH /v1/webhooks/{id}, DELETE /v1/webhooks/{id}, POST /v1/webhooks/{id}/rotate-secret, POST /v1/webhooks/{id}/test, GET /v1/webhooks/{id}/deliveries, GET /v1/webhooks/{id}/deliveries/{delivery_id}. Fields: Webhooks.
Events
Section titled “Events”What arrives at an endpoint or on the stream: GET /v1/events. Fields: Events.
Errors
Section titled “Errors”Every /v1 refusal is this envelope, with an HTTP status. What each code means and how to fix it: Errors.
V1Error
Section titled “V1Error”| field | type | description |
|---|---|---|
error | object, required | |
error.type | string, required | Follows the HTTP status: invalid_request_error (400, 405, 413, 415, 422), billing_error (402), permission_error (403), not_found_error (404), conflict_error (409), rate_limit_exceeded (429) or server_error (5xx). A missing or bad key is 401 with invalid_request_error and code invalid_api_key, as OpenAI answers it. Branch on code. |
error.code | string, required | Stable machine code. Branch on this. |
error.message | string, required | What went wrong, in a sentence. Show this. |
error.param | string or null, required | What the error is about, in /v1 names: a body field (to, audience.contacts.0.phone), a query parameter (status), a path segment (id) or a header (idempotency-key); null when it is about none of them. |
error.hint | string | What to do next. |
error.docs_url | string | |
error.request_id | string | Quote this when you contact support. |
error.fields | array of string | Every field the refusal is about, when there is more than one (unknown_variable, unmapped_variable): the first is param. |
error.field | string | unmapped_variable: the placeholder nothing fills. |
error.missing | array of string | launch_fields_missing: the /v1 fields to set before a start. |
error.status | string or null | A state refusal (campaign_not_pausable, campaign_not_cancellable, campaign_not_resumable, campaign_not_previewable, campaign_not_editable, campaign_not_deletable): the campaign’s status now. |
error.allowed | array of string | A state refusal (campaign_not_pausable, campaign_not_cancellable, campaign_not_resumable, campaign_not_previewable): the statuses the action takes. A cancel never lists draft: a draft is deleted instead. |
error.problems | array of objects | invalid_variable, unknown_variable, unmapped_variable: every variable the refusal is about, one entry each with its own message and hint, ordered by param; the first is the refusal’s own param, message and hint. field is the variable’s name (or the placeholder nothing fills). |
error.problems[].field | string, required | |
error.problems[].param | string, required | |
error.problems[].message | string, required | |
error.problems[].hint | string | |
error.problems[].suggestion | string | unknown_variable: the closest name the content has. |
error.part | stringtext, field, fallback | invalid_variable for a value over 200 characters: which part of the first one is too long (the plain text, the field name, or the fallback). |
error.template_status | string | template_not_approved: the template’s status. |
error.retry_after | number | Seconds to wait before retrying, as in Retry-After. |
error.short_credits | number | insufficient_credits: how many more credits the send needs. |
error.campaign_id | string | A refusal after the campaign was created: the campaign, kept as a draft. |
error.issues | array of objects | invalid_request: every problem the body has, one per entry. |
error.issues[].path | array of string, required | |
error.issues[].code | string, required | |
error.issues[].message | string, required | |
error.channel | string | field_not_for_channel: the campaign’s channel (fields names the fields of another channel). |
error.errors | array of any | An audience refusal (empty_audience_input, an import with no usable row): the rows that could not be read, one entry each ([] when there were none). |
error.received | number | An audience refusal: how many rows were sent. |
error.invalid_rows | number | An audience refusal: how many rows could not be used. |
error.sender_status | string | sender_not_cleared: the number’s sms.status in GET /v1/senders (receiving_only, in_review, rejected, …). |
error.skipped_by_reason | map of string to number | no_recipients: how many of the audience are left out, by reason (no_phone_number, opted_out, duplicate, …), accounting for every member. |
error.reason | string or null | Why, in a word, where a code has several causes: billing_not_eligible (no card on file, or a past-due or canceled subscription). |
error.message_id | string or null | A WhatsApp send refused after the message was stored (opted_out, send_failed): the stored message’s id, for GET /v1/messages/{id}. |
error.version | string or null | outbound_compliance_required: the acknowledgment version to accept (POST /v1/compliance/accept). |
error.current_version | string | version_mismatch (POST /v1/compliance/accept): the acknowledgment’s version now; read it with GET /v1/compliance and accept that one. |
error.candidates | array of string | ambiguous_contact: the ct-… ids of the contacts that hold the number; remove one by its id. |
error.campaigns | array of string | list_in_use: the ca-… ids of the campaigns not yet finished that use the list. |
error.contact | string | contact_exists: the ct-… id of the contact that already has the phone or email. |
error.channels | array of string | stopped_by_person, no_address (POST /v1/contacts/{id}/dnc): the channels the refusal is about. |
error.cap | number | daily_voice_minutes_exceeded: the workspace’s daily talk-minute cap. spend_cap_reached, spend_cap_raise_needs_person: the campaign’s spending limit, in credits. |
error.used | number | daily_voice_minutes_exceeded: what the day already owes: metered + held minutes. spend_cap_reached: the credits the campaign has been billed. |
error.remaining | number | daily_voice_minutes_exceeded: the minutes left on day. spend_cap_reached: the credits left under the limit after the calls under way (cap − used − in_flight). |
error.in_flight | number | spend_cap_reached: the projected credits of the campaign’s calls under way. |
error.next_call | number | spend_cap_reached: the projected credits of one more call (a median call on the campaign’s agent). |
error.dashboard_url | string | spend_cap_reached, spend_cap_raise_needs_person: the campaign’s page in the dashboard, where a person raises its spending limit. |
error.metered | number | daily_voice_minutes_exceeded: voice minutes already billed on day. |
error.held | number | daily_voice_minutes_exceeded: minutes held on day by queued or scheduled campaigns that start that day or earlier. |
error.held_by | array of objects | daily_voice_minutes_exceeded: the campaigns holding held, largest first. |
error.held_by[].campaign | string, required | The campaign’s ca-… id. |
error.held_by[].name | string, required | |
error.held_by[].minutes | number, required | |
error.held_by[].starts_at | string or null, required | |
error.day | string or null | daily_voice_minutes_exceeded: the budget day checked (YYYY-MM-DD in time_zone): the campaign’s start day. |
error.time_zone | string or null | daily_voice_minutes_exceeded: the zone the workspace’s budget day follows (UTC when none is set). |
error.per_call_minutes | number or null | daily_voice_minutes_exceeded: the minutes each call is projected at (this workspace’s average, at least 1.5). |
error.resets_at | string or null | When the daily allowance clears (ISO 8601): trial_daily_cap for the trial number’s calls, daily_voice_minutes_exceeded for the voice budget (midnight in time_zone). |
error.allowed_country | string or null | route_blocked: the country your calling rules let you call. |
error.closes_at | string or null | reply_window_closed: when Meta’s 24-hour window closed (ISO 8601), or null when the person never wrote. |