Download OpenAPI specification:
Embedded business communications for your platform.
Handset lets your SaaS product provision phone numbers, send compliant two-way SMS, and handle inbound calls (routing, voicemail-to-text, recording) for each of your customers — via API, under your brand.
tnt_…, num_…, msg_…, call_….limit, after → next_cursor).Idempotency-Key header; retries with the
same key return the original result.code, a human message, and a docs_url.Each API key may sustain 20 requests/second with bursts up to 60.
Beyond that, requests return 429 with code rate_limited and a
Retry-After header — back off for that many seconds and retry. Limits
are per key, so busy tenants can be isolated with per-tenant keys.
| Idempotency-Key | string <= 255 characters Retries with the same key within 24 h return the original response instead of repeating the action. |
| name required | string Display name, e.g. the business name. |
| external_ref | string or null Your own identifier for this customer. |
| timezone | string Default: "America/Phoenix" IANA timezone; used for business-hours routing. |
object (Metadata) <= 20 properties Your own key–value data, returned unchanged on the object and its events. |
{- "name": "string",
- "external_ref": "string",
- "timezone": "America/Phoenix",
- "metadata": {
- "property1": "string",
- "property2": "string"
}
}{- "name": "string",
- "external_ref": "string",
- "timezone": "America/Phoenix",
- "metadata": {
- "property1": "string",
- "property2": "string"
}, - "id": "tnt_01j8x2z",
- "created_at": "2019-08-24T14:15:22Z"
}| limit | integer [ 1 .. 100 ] Default: 25 |
| after | string Cursor from a previous page's |
| external_ref | string Filter by your own identifier for the tenant. |
{- "data": [
- {
- "name": "string",
- "external_ref": "string",
- "timezone": "America/Phoenix",
- "metadata": {
- "property1": "string",
- "property2": "string"
}, - "id": "tnt_01j8x2z",
- "created_at": "2019-08-24T14:15:22Z"
}
], - "has_more": true,
- "next_cursor": "string"
}{- "name": "string",
- "external_ref": "string",
- "timezone": "America/Phoenix",
- "metadata": {
- "property1": "string",
- "property2": "string"
}, - "id": "tnt_01j8x2z",
- "created_at": "2019-08-24T14:15:22Z"
}| tenant_id required | string |
| name | string |
| external_ref | string or null |
| timezone | string |
object (Metadata) <= 20 properties Your own key–value data, returned unchanged on the object and its events. |
{- "name": "string",
- "external_ref": "string",
- "timezone": "string",
- "metadata": {
- "property1": "string",
- "property2": "string"
}
}{- "name": "string",
- "external_ref": "string",
- "timezone": "America/Phoenix",
- "metadata": {
- "property1": "string",
- "property2": "string"
}, - "id": "tnt_01j8x2z",
- "created_at": "2019-08-24T14:15:22Z"
}Releases the tenant's phone numbers and deactivates its campaigns. Message and call history is retained per your data-retention settings.
| tenant_id required | string |
{- "error": {
- "code": "campaign_not_approved",
- "message": "string",
- "param": "string"
}
}| area_code | string^[0-9]{3}$ |
| locality | string City name, e.g. |
| contains | string Digit pattern the number should contain. |
| limit | integer [ 1 .. 100 ] Default: 25 |
{- "data": [
- {
- "phone_number": "+16025550134",
- "locality": "string",
- "region": "string",
- "capabilities": [
- "sms"
], - "monthly_price_usd": "1.50"
}
]
}| Idempotency-Key | string <= 255 characters Retries with the same key within 24 h return the original response instead of repeating the action. |
| tenant_id required | string (TenantIdField) The tenant this resource belongs to. |
| phone_number required | string E.164 number from a search result. |
| routing_config_id | string or null Routing to apply to inbound calls on this number. |
| campaign_id | string or null 10DLC campaign to attach. Outbound SMS is blocked until the number is attached to an approved campaign. |
{- "tenant_id": "tnt_01j8x2z",
- "phone_number": "+16025550134",
- "routing_config_id": "string",
- "campaign_id": "string"
}{- "id": "num_01j8x31",
- "tenant_id": "tnt_01j8x2z",
- "phone_number": "string",
- "status": "active",
- "capabilities": [
- "sms"
], - "routing_config_id": "string",
- "campaign_id": "string",
- "e911_address_id": "string",
- "messaging_ready": true,
- "created_at": "2019-08-24T14:15:22Z"
}| tenant_id | string Scope results to one tenant. |
| limit | integer [ 1 .. 100 ] Default: 25 |
| after | string Cursor from a previous page's |
{- "data": [
- {
- "id": "num_01j8x31",
- "tenant_id": "tnt_01j8x2z",
- "phone_number": "string",
- "status": "active",
- "capabilities": [
- "sms"
], - "routing_config_id": "string",
- "campaign_id": "string",
- "e911_address_id": "string",
- "messaging_ready": true,
- "created_at": "2019-08-24T14:15:22Z"
}
], - "has_more": true,
- "next_cursor": "string"
}{- "id": "num_01j8x31",
- "tenant_id": "tnt_01j8x2z",
- "phone_number": "string",
- "status": "active",
- "capabilities": [
- "sms"
], - "routing_config_id": "string",
- "campaign_id": "string",
- "e911_address_id": "string",
- "messaging_ready": true,
- "created_at": "2019-08-24T14:15:22Z"
}| number_id required | string |
| routing_config_id | string or null |
| campaign_id | string or null |
| e911_address_id | string or null |
{- "routing_config_id": "string",
- "campaign_id": "string",
- "e911_address_id": "string"
}{- "id": "num_01j8x31",
- "tenant_id": "tnt_01j8x2z",
- "phone_number": "string",
- "status": "active",
- "capabilities": [
- "sms"
], - "routing_config_id": "string",
- "campaign_id": "string",
- "e911_address_id": "string",
- "messaging_ready": true,
- "created_at": "2019-08-24T14:15:22Z"
}Sends from a tenant-owned number. Fails with campaign_not_approved if the number lacks an approved 10DLC campaign, and with recipient_opted_out if the recipient previously sent STOP.
| Idempotency-Key | string <= 255 characters Retries with the same key within 24 h return the original response instead of repeating the action. |
| from required | string A tenant number ID ( |
| to required | string Destination in E.164. |
| body | string <= 1600 characters |
| media_urls | Array of strings <uri> <= 10 items [ items <uri > ] Attach up to 10 publicly reachable |
object (Metadata) <= 20 properties Your own key–value data, returned unchanged on the object and its events. |
{- "from": "string",
- "to": "+14805550199",
- "body": "string",
- "metadata": {
- "property1": "string",
- "property2": "string"
}
}{- "id": "msg_01j8x3a",
- "tenant_id": "tnt_01j8x2z",
- "conversation_id": "string",
- "direction": "inbound",
- "from": "string",
- "to": "string",
- "body": "string",
- "status": "queued",
- "error_code": "string",
- "segments": 0,
- "metadata": {
- "property1": "string",
- "property2": "string"
}, - "created_at": "2019-08-24T14:15:22Z",
- "status_history": [
- {
- "status": "queued",
- "at": "2019-08-24T14:15:22Z"
}
]
}| tenant_id | string Scope results to one tenant. |
| conversation_id | string |
| direction | string Enum: "inbound" "outbound" |
| limit | integer [ 1 .. 100 ] Default: 25 |
| after | string Cursor from a previous page's |
{- "data": [
- {
- "id": "msg_01j8x3a",
- "tenant_id": "tnt_01j8x2z",
- "conversation_id": "string",
- "direction": "inbound",
- "from": "string",
- "to": "string",
- "body": "string",
- "status": "queued",
- "error_code": "string",
- "segments": 0,
- "metadata": {
- "property1": "string",
- "property2": "string"
}, - "created_at": "2019-08-24T14:15:22Z",
- "status_history": [
- {
- "status": "queued",
- "at": "2019-08-24T14:15:22Z"
}
]
}
], - "has_more": true,
- "next_cursor": "string"
}{- "id": "msg_01j8x3a",
- "tenant_id": "tnt_01j8x2z",
- "conversation_id": "string",
- "direction": "inbound",
- "from": "string",
- "to": "string",
- "body": "string",
- "status": "queued",
- "error_code": "string",
- "segments": 0,
- "metadata": {
- "property1": "string",
- "property2": "string"
}, - "created_at": "2019-08-24T14:15:22Z",
- "status_history": [
- {
- "status": "queued",
- "at": "2019-08-24T14:15:22Z"
}
]
}A conversation is the thread between one tenant number and one external number, ordered by most recent activity.
| tenant_id | string Scope results to one tenant. |
| phone_number_id | string |
| limit | integer [ 1 .. 100 ] Default: 25 |
| after | string Cursor from a previous page's |
{- "data": [
- {
- "id": "cnv_01j8x3f",
- "tenant_id": "tnt_01j8x2z",
- "phone_number_id": "string",
- "external_number": "string",
- "last_activity_at": "2019-08-24T14:15:22Z",
- "last_message_preview": "string",
- "opted_out": true
}
], - "has_more": true,
- "next_cursor": "string"
}{- "id": "cnv_01j8x3f",
- "tenant_id": "tnt_01j8x2z",
- "phone_number_id": "string",
- "external_number": "string",
- "last_activity_at": "2019-08-24T14:15:22Z",
- "last_message_preview": "string",
- "opted_out": true
}Recipients who sent STOP to a tenant's numbers. Handset blocks sends to them automatically; this endpoint exists so your UI can show why.
| tenant_id | string Scope results to one tenant. |
| limit | integer [ 1 .. 100 ] Default: 25 |
| after | string Cursor from a previous page's |
{- "data": [
- {
- "tenant_id": "tnt_01j8x2z",
- "external_number": "string",
- "opted_out_at": "2019-08-24T14:15:22Z",
- "source": "stop_keyword"
}
], - "has_more": true,
- "next_cursor": "string"
}Registers your platform (or a tenant, for tenants with their own EIN) with The Campaign Registry. Vetting typically takes minutes to days; track via brand.status_changed.
| Idempotency-Key | string <= 255 characters Retries with the same key within 24 h return the original response instead of repeating the action. |
| legal_name required | string |
| dba | string or null |
| ein required | string US tax ID. |
| entity_type required | string Enum: "private_company" "public_company" "non_profit" "sole_proprietor" |
| website | string or null <uri> |
| contact_email required | string <email> |
| phone required | string Business contact number, E.164 — carrier registration requires it. |
| street required | string |
| city required | string |
| state required | string Two-letter US state code. |
| postal_code required | string 5-digit ZIP (ZIP+4 allowed). |
| tenant_id | string or null Set when the brand belongs to a tenant with its own EIN; omit for your platform-level brand. |
{- "legal_name": "string",
- "dba": "string",
- "ein": "string",
- "entity_type": "private_company",
- "contact_email": "user@example.com",
- "phone": "string",
- "street": "string",
- "city": "string",
- "state": "string",
- "postal_code": "string",
- "tenant_id": "string"
}{- "legal_name": "string",
- "dba": "string",
- "ein": "string",
- "entity_type": "private_company",
- "contact_email": "user@example.com",
- "phone": "string",
- "street": "string",
- "city": "string",
- "state": "string",
- "postal_code": "string",
- "tenant_id": "string",
- "id": "brd_01j8x3k",
- "status": "pending_vetting",
- "rejection_reason": "string",
- "created_at": "2019-08-24T14:15:22Z"
}| limit | integer [ 1 .. 100 ] Default: 25 |
| after | string Cursor from a previous page's |
{- "data": [
- {
- "legal_name": "string",
- "dba": "string",
- "ein": "string",
- "entity_type": "private_company",
- "contact_email": "user@example.com",
- "phone": "string",
- "street": "string",
- "city": "string",
- "state": "string",
- "postal_code": "string",
- "tenant_id": "string",
- "id": "brd_01j8x3k",
- "status": "pending_vetting",
- "rejection_reason": "string",
- "created_at": "2019-08-24T14:15:22Z"
}
], - "has_more": true,
- "next_cursor": "string"
}{- "legal_name": "string",
- "dba": "string",
- "ein": "string",
- "entity_type": "private_company",
- "contact_email": "user@example.com",
- "phone": "string",
- "street": "string",
- "city": "string",
- "state": "string",
- "postal_code": "string",
- "tenant_id": "string",
- "id": "brd_01j8x3k",
- "status": "pending_vetting",
- "rejection_reason": "string",
- "created_at": "2019-08-24T14:15:22Z"
}Registers a messaging use case under an approved brand for a tenant. Carrier review typically takes 1–3 business days; sending is blocked until status is approved. Track via campaign.status_changed.
| Idempotency-Key | string <= 255 characters Retries with the same key within 24 h return the original response instead of repeating the action. |
| tenant_id required | string (TenantIdField) The tenant this resource belongs to. |
| brand_id required | string |
| use_case required | string Enum: "customer_care" "appointment_reminders" "marketing" "two_factor" "mixed" |
| description required | string What the tenant sends and why recipients expect it. |
| sample_messages required | Array of strings [ 2 .. 5 ] items |
| opt_in_description required | string >= 40 characters How recipients consent to receive these messages. Carriers review this text; at least 40 characters describing the consent flow. |
{- "tenant_id": "tnt_01j8x2z",
- "brand_id": "string",
- "use_case": "customer_care",
- "description": "string",
- "sample_messages": [
- "string",
- "string"
], - "opt_in_description": "stringstringstringstringstringstringstri"
}{- "tenant_id": "tnt_01j8x2z",
- "brand_id": "string",
- "use_case": "customer_care",
- "description": "string",
- "sample_messages": [
- "string",
- "string"
], - "opt_in_description": "stringstringstringstringstringstringstri",
- "id": "cmp_01j8x3p",
- "status": "draft",
- "rejection_reason": "string",
- "throughput": {
- "messages_per_minute": 0,
- "daily_cap": 0
}, - "created_at": "2019-08-24T14:15:22Z"
}| tenant_id | string Scope results to one tenant. |
| status | string (CampaignStatus) Enum: "draft" "pending_review" "approved" "rejected" "suspended" |
| limit | integer [ 1 .. 100 ] Default: 25 |
| after | string Cursor from a previous page's |
{- "data": [
- {
- "tenant_id": "tnt_01j8x2z",
- "brand_id": "string",
- "use_case": "customer_care",
- "description": "string",
- "sample_messages": [
- "string",
- "string"
], - "opt_in_description": "stringstringstringstringstringstringstri",
- "id": "cmp_01j8x3p",
- "status": "draft",
- "rejection_reason": "string",
- "throughput": {
- "messages_per_minute": 0,
- "daily_cap": 0
}, - "created_at": "2019-08-24T14:15:22Z"
}
], - "has_more": true,
- "next_cursor": "string"
}{- "tenant_id": "tnt_01j8x2z",
- "brand_id": "string",
- "use_case": "customer_care",
- "description": "string",
- "sample_messages": [
- "string",
- "string"
], - "opt_in_description": "stringstringstringstringstringstringstri",
- "id": "cmp_01j8x3p",
- "status": "draft",
- "rejection_reason": "string",
- "throughput": {
- "messages_per_minute": 0,
- "daily_cap": 0
}, - "created_at": "2019-08-24T14:15:22Z"
}Validates a dispatchable location and registers it for use with a tenant's numbers. Returns e911_address_invalid with correction suggestions when validation fails.
| Idempotency-Key | string <= 255 characters Retries with the same key within 24 h return the original response instead of repeating the action. |
| tenant_id required | string (TenantIdField) The tenant this resource belongs to. |
| street required | string |
| unit | string or null |
| city required | string |
| state required | string = 2 characters |
| postal_code required | string |
{- "tenant_id": "tnt_01j8x2z",
- "street": "123 N Central Ave",
- "unit": "Suite 400",
- "city": "string",
- "state": "st",
- "postal_code": "string"
}{- "tenant_id": "tnt_01j8x2z",
- "street": "123 N Central Ave",
- "unit": "Suite 400",
- "city": "string",
- "state": "st",
- "postal_code": "string",
- "id": "e911_01j8x3t",
- "status": "validated",
- "created_at": "2019-08-24T14:15:22Z"
}| tenant_id | string Scope results to one tenant. |
| limit | integer [ 1 .. 100 ] Default: 25 |
| after | string Cursor from a previous page's |
{- "data": [
- {
- "tenant_id": "tnt_01j8x2z",
- "street": "123 N Central Ave",
- "unit": "Suite 400",
- "city": "string",
- "state": "st",
- "postal_code": "string",
- "id": "e911_01j8x3t",
- "status": "validated",
- "created_at": "2019-08-24T14:15:22Z"
}
], - "has_more": true,
- "next_cursor": "string"
}Ask, per number, whether it can be ported in. Free and side-effect-free.
| phone_numbers required | Array of strings US numbers in E.164, up to 100. |
{- "phone_numbers": [
- "string"
]
}{- "data": [
- {
- "phone_number": "string",
- "portable": true,
- "reason": "string"
}
]
}Opens a draft port-in carrying the numbers and the account details as
they appear at the losing carrier. Fails with numbers_not_portable
if any number can't be ported. Call submit to start carrier review.
| tenant_id required | string The tenant the ported numbers will belong to. |
| phone_numbers required | Array of strings US numbers in E.164, up to 100, all from one losing carrier. |
| entity_name required | string Account holder name exactly as the losing carrier has it. |
| authorized_person required | string Person authorized to move the numbers. |
| billing_phone_number required | string The losing account's main billing number, E.164. |
| account_number required | string Account number at the losing carrier. Encrypted at rest. |
| pin | string or null Port-out PIN, when the losing carrier uses one. Encrypted at rest. |
required | object (PortInAddress) |
{- "tenant_id": "string",
- "phone_numbers": [
- "string"
], - "entity_name": "string",
- "authorized_person": "string",
- "billing_phone_number": "string",
- "account_number": "string",
- "pin": "string",
- "service_address": {
- "street": "string",
- "city": "string",
- "state": "string",
- "postal_code": "string"
}
}{- "id": "string",
- "tenant_id": "string",
- "phone_numbers": [
- "string"
], - "status": "draft",
- "status_detail": "string",
- "entity_name": "string",
- "authorized_person": "string",
- "billing_phone_number": "string",
- "account_number": "string",
- "service_address": {
- "street": "string",
- "city": "string",
- "state": "string",
- "postal_code": "string"
}, - "foc_date": "2019-08-24T14:15:22Z",
- "created_at": "2019-08-24T14:15:22Z"
}| tenant_id | string Scope results to one tenant. |
| status | string Enum: "draft" "in_review" "action_needed" "foc_confirmed" "completed" "cancelled" |
| limit | integer [ 1 .. 100 ] Default: 25 |
| after | string Cursor from a previous page's |
{- "data": [
- {
- "id": "string",
- "tenant_id": "string",
- "phone_numbers": [
- "string"
], - "status": "draft",
- "status_detail": "string",
- "entity_name": "string",
- "authorized_person": "string",
- "billing_phone_number": "string",
- "account_number": "string",
- "service_address": {
- "street": "string",
- "city": "string",
- "state": "string",
- "postal_code": "string"
}, - "foc_date": "2019-08-24T14:15:22Z",
- "created_at": "2019-08-24T14:15:22Z"
}
], - "has_more": true,
- "next_cursor": "string"
}{- "id": "string",
- "tenant_id": "string",
- "phone_numbers": [
- "string"
], - "status": "draft",
- "status_detail": "string",
- "entity_name": "string",
- "authorized_person": "string",
- "billing_phone_number": "string",
- "account_number": "string",
- "service_address": {
- "street": "string",
- "city": "string",
- "state": "string",
- "postal_code": "string"
}, - "foc_date": "2019-08-24T14:15:22Z",
- "created_at": "2019-08-24T14:15:22Z"
}Moves a draft (or corrected action_needed) port-in into
in_review. Status changes arrive as port_in.status_changed
webhooks. In test mode the simulated carrier completes the whole
lifecycle in under a minute.
| port_in_id required | string |
{- "id": "string",
- "tenant_id": "string",
- "phone_numbers": [
- "string"
], - "status": "draft",
- "status_detail": "string",
- "entity_name": "string",
- "authorized_person": "string",
- "billing_phone_number": "string",
- "account_number": "string",
- "service_address": {
- "street": "string",
- "city": "string",
- "state": "string",
- "postal_code": "string"
}, - "foc_date": "2019-08-24T14:15:22Z",
- "created_at": "2019-08-24T14:15:22Z"
}{- "id": "string",
- "tenant_id": "string",
- "phone_numbers": [
- "string"
], - "status": "draft",
- "status_detail": "string",
- "entity_name": "string",
- "authorized_person": "string",
- "billing_phone_number": "string",
- "account_number": "string",
- "service_address": {
- "street": "string",
- "city": "string",
- "state": "string",
- "postal_code": "string"
}, - "foc_date": "2019-08-24T14:15:22Z",
- "created_at": "2019-08-24T14:15:22Z"
}| Idempotency-Key | string <= 255 characters Retries with the same key within 24 h return the original response instead of repeating the action. |
| tenant_id required | string (TenantIdField) The tenant this resource belongs to. |
| name required | string |
object or null Weekly schedule in the tenant's timezone. Omit for 24/7 | |
required | object (RingBehavior) |
VoicemailBehavior (object) or RingBehavior (object) Applied outside business hours. Defaults to voicemail. | |
object |
{- "tenant_id": "tnt_01j8x2z",
- "name": "Main line",
- "business_hours": {
- "schedule": [
- {
- "days": [
- "mon"
], - "open": "08:00",
- "close": "17:30"
}
]
}, - "open_behavior": {
- "type": "ring",
- "targets": [
- "+14805550142"
], - "strategy": "simultaneous",
- "timeout_seconds": 25,
- "no_answer": {
- "type": "voicemail",
- "greeting_text": "string",
- "transcribe": true
}
}, - "closed_behavior": {
- "type": "voicemail",
- "greeting_text": "string",
- "transcribe": true
}, - "recording": {
- "enabled": false,
- "consent_announcement": true
}
}{- "tenant_id": "tnt_01j8x2z",
- "name": "Main line",
- "business_hours": {
- "schedule": [
- {
- "days": [
- "mon"
], - "open": "08:00",
- "close": "17:30"
}
]
}, - "open_behavior": {
- "type": "ring",
- "targets": [
- "+14805550142"
], - "strategy": "simultaneous",
- "timeout_seconds": 25,
- "no_answer": {
- "type": "voicemail",
- "greeting_text": "string",
- "transcribe": true
}
}, - "closed_behavior": {
- "type": "voicemail",
- "greeting_text": "string",
- "transcribe": true
}, - "recording": {
- "enabled": false,
- "consent_announcement": true
}, - "id": "rtc_01j8x3w",
- "created_at": "2019-08-24T14:15:22Z"
}| tenant_id | string Scope results to one tenant. |
| limit | integer [ 1 .. 100 ] Default: 25 |
| after | string Cursor from a previous page's |
{- "data": [
- {
- "tenant_id": "tnt_01j8x2z",
- "name": "Main line",
- "business_hours": {
- "schedule": [
- {
- "days": [
- "mon"
], - "open": "08:00",
- "close": "17:30"
}
]
}, - "open_behavior": {
- "type": "ring",
- "targets": [
- "+14805550142"
], - "strategy": "simultaneous",
- "timeout_seconds": 25,
- "no_answer": {
- "type": "voicemail",
- "greeting_text": "string",
- "transcribe": true
}
}, - "closed_behavior": {
- "type": "voicemail",
- "greeting_text": "string",
- "transcribe": true
}, - "recording": {
- "enabled": false,
- "consent_announcement": true
}, - "id": "rtc_01j8x3w",
- "created_at": "2019-08-24T14:15:22Z"
}
], - "has_more": true,
- "next_cursor": "string"
}| routing_config_id required | string |
{- "tenant_id": "tnt_01j8x2z",
- "name": "Main line",
- "business_hours": {
- "schedule": [
- {
- "days": [
- "mon"
], - "open": "08:00",
- "close": "17:30"
}
]
}, - "open_behavior": {
- "type": "ring",
- "targets": [
- "+14805550142"
], - "strategy": "simultaneous",
- "timeout_seconds": 25,
- "no_answer": {
- "type": "voicemail",
- "greeting_text": "string",
- "transcribe": true
}
}, - "closed_behavior": {
- "type": "voicemail",
- "greeting_text": "string",
- "transcribe": true
}, - "recording": {
- "enabled": false,
- "consent_announcement": true
}, - "id": "rtc_01j8x3w",
- "created_at": "2019-08-24T14:15:22Z"
}Changes apply to new calls immediately.
| routing_config_id required | string |
| tenant_id required | string (TenantIdField) The tenant this resource belongs to. |
| name required | string |
object or null Weekly schedule in the tenant's timezone. Omit for 24/7 | |
required | object (RingBehavior) |
VoicemailBehavior (object) or RingBehavior (object) Applied outside business hours. Defaults to voicemail. | |
object |
{- "tenant_id": "tnt_01j8x2z",
- "name": "Main line",
- "business_hours": {
- "schedule": [
- {
- "days": [
- "mon"
], - "open": "08:00",
- "close": "17:30"
}
]
}, - "open_behavior": {
- "type": "ring",
- "targets": [
- "+14805550142"
], - "strategy": "simultaneous",
- "timeout_seconds": 25,
- "no_answer": {
- "type": "voicemail",
- "greeting_text": "string",
- "transcribe": true
}
}, - "closed_behavior": {
- "type": "voicemail",
- "greeting_text": "string",
- "transcribe": true
}, - "recording": {
- "enabled": false,
- "consent_announcement": true
}
}{- "tenant_id": "tnt_01j8x2z",
- "name": "Main line",
- "business_hours": {
- "schedule": [
- {
- "days": [
- "mon"
], - "open": "08:00",
- "close": "17:30"
}
]
}, - "open_behavior": {
- "type": "ring",
- "targets": [
- "+14805550142"
], - "strategy": "simultaneous",
- "timeout_seconds": 25,
- "no_answer": {
- "type": "voicemail",
- "greeting_text": "string",
- "transcribe": true
}
}, - "closed_behavior": {
- "type": "voicemail",
- "greeting_text": "string",
- "transcribe": true
}, - "recording": {
- "enabled": false,
- "consent_announcement": true
}, - "id": "rtc_01j8x3w",
- "created_at": "2019-08-24T14:15:22Z"
}Dials connect_to (the agent) from the tenant's number; when they
answer, dials to (the customer) showing the same tenant number,
and bridges the two. Track progress via call.completed webhooks or
by polling: dialing → ringing → in_progress → completed
(failed if either side never answers). In test mode the simulated
parties answer within seconds and the call auto-completes.
A small set of US rural exchanges known for access stimulation
(traffic pumping) can't be dialed from any leg — such requests
return destination_not_supported.
| from required | string A tenant number id ( |
| to required | string The customer's number, E.164. |
| connect_to required | string The agent's number, E.164 — rings first. |
| transcribe | boolean Default: false Stream live speech-to-text: each final utterance arrives
as a |
{- "from": "string",
- "to": "string",
- "connect_to": "string",
- "transcribe": false
}{- "id": "call_01j8x42",
- "tenant_id": "tnt_01j8x2z",
- "phone_number_id": "string",
- "direction": "inbound",
- "from": "string",
- "to": "string",
- "connect_to": "string",
- "status": "dialing",
- "answered_by": "string",
- "duration_seconds": 0,
- "recording_id": "string",
- "voicemail_id": "string",
- "summary": "string",
- "started_at": "2019-08-24T14:15:22Z",
- "ended_at": "2019-08-24T14:15:22Z",
- "events": [
- {
- "type": "initiated",
- "at": "2019-08-24T14:15:22Z",
- "detail": { }
}
]
}| tenant_id | string Scope results to one tenant. |
| phone_number_id | string |
| limit | integer [ 1 .. 100 ] Default: 25 |
| after | string Cursor from a previous page's |
{- "data": [
- {
- "id": "call_01j8x42",
- "tenant_id": "tnt_01j8x2z",
- "phone_number_id": "string",
- "direction": "inbound",
- "from": "string",
- "to": "string",
- "connect_to": "string",
- "status": "dialing",
- "answered_by": "string",
- "duration_seconds": 0,
- "recording_id": "string",
- "voicemail_id": "string",
- "summary": "string",
- "started_at": "2019-08-24T14:15:22Z",
- "ended_at": "2019-08-24T14:15:22Z",
- "events": [
- {
- "type": "initiated",
- "at": "2019-08-24T14:15:22Z",
- "detail": { }
}
]
}
], - "has_more": true,
- "next_cursor": "string"
}{- "id": "call_01j8x42",
- "tenant_id": "tnt_01j8x2z",
- "phone_number_id": "string",
- "direction": "inbound",
- "from": "string",
- "to": "string",
- "connect_to": "string",
- "status": "dialing",
- "answered_by": "string",
- "duration_seconds": 0,
- "recording_id": "string",
- "voicemail_id": "string",
- "summary": "string",
- "started_at": "2019-08-24T14:15:22Z",
- "ended_at": "2019-08-24T14:15:22Z",
- "events": [
- {
- "type": "initiated",
- "at": "2019-08-24T14:15:22Z",
- "detail": { }
}
]
}The transcript so far — callable mid-call. Segments are final
utterances in order; text is the full conversation joined.
| call_id required | string |
{- "call_id": "string",
- "text": "string",
- "segments": [
- {
- "speaker": "string",
- "text": "string",
- "occurred_at": "2019-08-24T14:15:22Z"
}
]
}Plays digits to the call's remote party — dial an extension, enter a
conference PIN, navigate a phone tree. The call must be in progress.
On outbound calls tones reach the to party; on inbound calls the
original caller.
| call_id required | string |
| digits required | string <= 32 characters 0-9, A-D, *, #, plus pause characters w (0.5 s) and W (1 s). Example: "wwww4512#". |
{- "digits": "string"
}{- "call_id": "string",
- "digits": "string",
- "status": "sent"
}Speaks prompt as text-to-speech and collects keypresses from the
call's remote party. Each keypress fires a call.dtmf webhook; the
collected result arrives as a call.gather webhook with digits
and a reason of completed, timeout, or hangup. The call must
be in progress. In test mode the simulated party presses 1 about
1.5 s after the prompt (calls to +15005550007 never press anything
and time out).
| call_id required | string |
| prompt required | string <= 500 characters Spoken to the remote party as TTS. |
| max_digits | integer [ 1 .. 32 ] Default: 1 Collection ends once this many digits arrive. |
| terminator | string <= 1 characters A digit (0-9, *, |
| timeout_ms | integer [ 1000 .. 60000 ] Default: 10000 How long to wait for input. |
{- "prompt": "string",
- "max_digits": 1,
- "terminator": "s",
- "timeout_ms": 10000
}{- "call_id": "string",
- "status": "listening"
}Turns on live speech-to-text for an in-progress call, either
direction — each final utterance arrives as a call.transcript
webhook and accumulates on GET /calls/{call_id}/transcript, and
an AI summary generates after hangup (call.summary). This is the
agent-assist switch for inbound calls; transcribe: true at
creation remains the click-to-call shortcut. Idempotent — starting
an already-transcribing call is a no-op. Runs until hangup; billed
per transcribed minute on the call's connected time.
| call_id required | string |
{- "call_id": "string",
- "status": "transcribing"
}Forks the call's audio to a WebSocket on the media gateway
(media.handset.dev) as ~20 ms G.711 μ-law frames. Connect to the
returned url with the returned token (?token=… or an
Authorization: Bearer header) — the token is shown exactly once.
Frames arrive as JSON: {"event":"media","track":"inbound","seq":1, "timestamp_ms":840,"payload":"<base64 pcmu>"}.
direction: bidirectional also plays audio you send on the same
socket into the call ({"event":"media","payload":…}; send
{"event":"clear"} to flush queued playback) — the substrate for
AI voice agents. The call must be ringing or in progress; one
active stream per call. Billed per connected minute
(stream_minute). In test mode the simulated carrier streams a
pulsing 440 Hz tone on the inbound track and echoes your playback
on the outbound track; calls to +15005550008 fail to stream.
| call_id required | string |
| direction | string Default: "fork" Enum: "fork" "bidirectional" fork receives audio; bidirectional also plays your audio into the call. |
| track | string Default: "both" Enum: "inbound" "outbound" "both" Which side(s) of the conversation to receive. |
{- "direction": "fork",
- "track": "inbound"
}{- "id": "stm_01j8x4b",
- "call_id": "string",
- "tenant_id": "tnt_01j8x2z",
- "direction": "fork",
- "track": "inbound",
- "status": "starting",
- "url": "string",
- "token": "string",
- "stop_reason": "string",
- "created_at": "2019-08-24T14:15:22Z",
- "started_at": "2019-08-24T14:15:22Z",
- "stopped_at": "2019-08-24T14:15:22Z"
}{- "data": [
- {
- "id": "stm_01j8x4b",
- "call_id": "string",
- "tenant_id": "tnt_01j8x2z",
- "direction": "fork",
- "track": "inbound",
- "status": "starting",
- "url": "string",
- "token": "string",
- "stop_reason": "string",
- "created_at": "2019-08-24T14:15:22Z",
- "started_at": "2019-08-24T14:15:22Z",
- "stopped_at": "2019-08-24T14:15:22Z"
}
]
}Stops forking audio and settles billing. Idempotent — deleting an already-stopped stream returns its final state. Streams also end on their own when the call ends.
| call_id required | string |
| stream_id required | string |
{- "id": "stm_01j8x4b",
- "call_id": "string",
- "tenant_id": "tnt_01j8x2z",
- "direction": "fork",
- "track": "inbound",
- "status": "starting",
- "url": "string",
- "token": "string",
- "stop_reason": "string",
- "created_at": "2019-08-24T14:15:22Z",
- "started_at": "2019-08-24T14:15:22Z",
- "stopped_at": "2019-08-24T14:15:22Z"
}| tenant_id | string Scope results to one tenant. |
| limit | integer [ 1 .. 100 ] Default: 25 |
| after | string Cursor from a previous page's |
{- "data": [
- {
- "id": "vm_01j8x45",
- "tenant_id": "tnt_01j8x2z",
- "call_id": "string",
- "from": "string",
- "duration_seconds": 0,
- "transcript": "string",
- "created_at": "2019-08-24T14:15:22Z"
}
], - "has_more": true,
- "next_cursor": "string"
}{- "id": "vm_01j8x45",
- "tenant_id": "tnt_01j8x2z",
- "call_id": "string",
- "from": "string",
- "duration_seconds": 0,
- "transcript": "string",
- "created_at": "2019-08-24T14:15:22Z"
}{- "id": "rec_01j8x48",
- "tenant_id": "tnt_01j8x2z",
- "call_id": "string",
- "duration_seconds": 0,
- "consent_announced": true,
- "created_at": "2019-08-24T14:15:22Z"
}| Idempotency-Key | string <= 255 characters Retries with the same key within 24 h return the original response instead of repeating the action. |
| url required | string <uri> |
| enabled_events | Array of strings Event types to deliver; omit for all. |
| description | string or null |
{- "enabled_events": [
- "message.received"
], - "description": "string"
}{- "enabled_events": [
- "message.received"
], - "description": "string",
- "id": "whe_01j8x4b",
- "status": "active",
- "created_at": "2019-08-24T14:15:22Z",
- "secret": "string"
}| limit | integer [ 1 .. 100 ] Default: 25 |
| after | string Cursor from a previous page's |
{- "data": [
- {
- "enabled_events": [
- "message.received"
], - "description": "string",
- "id": "whe_01j8x4b",
- "status": "active",
- "created_at": "2019-08-24T14:15:22Z"
}
], - "has_more": true,
- "next_cursor": "string"
}| endpoint_id required | string |
| url required | string <uri> |
| enabled_events | Array of strings Event types to deliver; omit for all. |
| description | string or null |
{- "enabled_events": [
- "message.received"
], - "description": "string"
}{- "enabled_events": [
- "message.received"
], - "description": "string",
- "id": "whe_01j8x4b",
- "status": "active",
- "created_at": "2019-08-24T14:15:22Z"
}{- "error": {
- "code": "campaign_not_approved",
- "message": "string",
- "param": "string"
}
}| endpoint_id required | string |
| event_type | string Default: "message.received" |
{- "event_type": "message.received"
}{- "delivered": true,
- "response_status": 0,
- "error": "string"
}| id required | string |
| type required | string |
| event_version required | string |
| created_at required | string <date-time> |
| tenant_id | string (TenantIdField) The tenant this resource belongs to. |
| data required | object The full affected resource (Message, Call, Voicemail, …). |
{- "id": "evt_01j8x4e",
- "type": "message.received",
- "event_version": "2026-08-11",
- "created_at": "2019-08-24T14:15:22Z",
- "tenant_id": "tnt_01j8x2z",
- "data": { }
}| id required | string |
| type required | string |
| event_version required | string |
| created_at required | string <date-time> |
| tenant_id | string (TenantIdField) The tenant this resource belongs to. |
| data required | object The full affected resource (Message, Call, Voicemail, …). |
{- "id": "evt_01j8x4e",
- "type": "message.received",
- "event_version": "2026-08-11",
- "created_at": "2019-08-24T14:15:22Z",
- "tenant_id": "tnt_01j8x2z",
- "data": { }
}| id required | string |
| type required | string |
| event_version required | string |
| created_at required | string <date-time> |
| tenant_id | string (TenantIdField) The tenant this resource belongs to. |
| data required | object The full affected resource (Message, Call, Voicemail, …). |
{- "id": "evt_01j8x4e",
- "type": "message.received",
- "event_version": "2026-08-11",
- "created_at": "2019-08-24T14:15:22Z",
- "tenant_id": "tnt_01j8x2z",
- "data": { }
}| id required | string |
| type required | string |
| event_version required | string |
| created_at required | string <date-time> |
| tenant_id | string (TenantIdField) The tenant this resource belongs to. |
| data required | object The full affected resource (Message, Call, Voicemail, …). |
{- "id": "evt_01j8x4e",
- "type": "message.received",
- "event_version": "2026-08-11",
- "created_at": "2019-08-24T14:15:22Z",
- "tenant_id": "tnt_01j8x2z",
- "data": { }
}| id required | string |
| type required | string |
| event_version required | string |
| created_at required | string <date-time> |
| tenant_id | string (TenantIdField) The tenant this resource belongs to. |
| data required | object The full affected resource (Message, Call, Voicemail, …). |
{- "id": "evt_01j8x4e",
- "type": "message.received",
- "event_version": "2026-08-11",
- "created_at": "2019-08-24T14:15:22Z",
- "tenant_id": "tnt_01j8x2z",
- "data": { }
}| id required | string |
| type required | string |
| event_version required | string |
| created_at required | string <date-time> |
| tenant_id | string (TenantIdField) The tenant this resource belongs to. |
| data required | object The full affected resource (Message, Call, Voicemail, …). |
{- "id": "evt_01j8x4e",
- "type": "message.received",
- "event_version": "2026-08-11",
- "created_at": "2019-08-24T14:15:22Z",
- "tenant_id": "tnt_01j8x2z",
- "data": { }
}| id required | string |
| type required | string |
| event_version required | string |
| created_at required | string <date-time> |
| tenant_id | string (TenantIdField) The tenant this resource belongs to. |
| data required | object The full affected resource (Message, Call, Voicemail, …). |
{- "id": "evt_01j8x4e",
- "type": "message.received",
- "event_version": "2026-08-11",
- "created_at": "2019-08-24T14:15:22Z",
- "tenant_id": "tnt_01j8x2z",
- "data": { }
}| id required | string |
| type required | string |
| event_version required | string |
| created_at required | string <date-time> |
| tenant_id | string (TenantIdField) The tenant this resource belongs to. |
| data required | object The full affected resource (Message, Call, Voicemail, …). |
{- "id": "evt_01j8x4e",
- "type": "message.received",
- "event_version": "2026-08-11",
- "created_at": "2019-08-24T14:15:22Z",
- "tenant_id": "tnt_01j8x2z",
- "data": { }
}| id required | string |
| type required | string |
| event_version required | string |
| created_at required | string <date-time> |
| tenant_id | string (TenantIdField) The tenant this resource belongs to. |
| data required | object The full affected resource (Message, Call, Voicemail, …). |
{- "id": "evt_01j8x4e",
- "type": "message.received",
- "event_version": "2026-08-11",
- "created_at": "2019-08-24T14:15:22Z",
- "tenant_id": "tnt_01j8x2z",
- "data": { }
}| id required | string |
| type required | string |
| event_version required | string |
| created_at required | string <date-time> |
| tenant_id | string (TenantIdField) The tenant this resource belongs to. |
| data required | object The full affected resource (Message, Call, Voicemail, …). |
{- "id": "evt_01j8x4e",
- "type": "message.received",
- "event_version": "2026-08-11",
- "created_at": "2019-08-24T14:15:22Z",
- "tenant_id": "tnt_01j8x2z",
- "data": { }
}| id required | string |
| type required | string |
| event_version required | string |
| created_at required | string <date-time> |
| tenant_id | string (TenantIdField) The tenant this resource belongs to. |
| data required | object The full affected resource (Message, Call, Voicemail, …). |
{- "id": "evt_01j8x4e",
- "type": "message.received",
- "event_version": "2026-08-11",
- "created_at": "2019-08-24T14:15:22Z",
- "tenant_id": "tnt_01j8x2z",
- "data": { }
}| id required | string |
| type required | string |
| event_version required | string |
| created_at required | string <date-time> |
| tenant_id | string (TenantIdField) The tenant this resource belongs to. |
| data required | object The full affected resource (Message, Call, Voicemail, …). |
{- "id": "evt_01j8x4e",
- "type": "message.received",
- "event_version": "2026-08-11",
- "created_at": "2019-08-24T14:15:22Z",
- "tenant_id": "tnt_01j8x2z",
- "data": { }
}| id required | string |
| type required | string |
| event_version required | string |
| created_at required | string <date-time> |
| tenant_id | string (TenantIdField) The tenant this resource belongs to. |
| data required | object The full affected resource (Message, Call, Voicemail, …). |
{- "id": "evt_01j8x4e",
- "type": "message.received",
- "event_version": "2026-08-11",
- "created_at": "2019-08-24T14:15:22Z",
- "tenant_id": "tnt_01j8x2z",
- "data": { }
}| id required | string |
| type required | string |
| event_version required | string |
| created_at required | string <date-time> |
| tenant_id | string (TenantIdField) The tenant this resource belongs to. |
| data required | object The full affected resource (Message, Call, Voicemail, …). |
{- "id": "evt_01j8x4e",
- "type": "message.received",
- "event_version": "2026-08-11",
- "created_at": "2019-08-24T14:15:22Z",
- "tenant_id": "tnt_01j8x2z",
- "data": { }
}| id required | string |
| type required | string |
| event_version required | string |
| created_at required | string <date-time> |
| tenant_id | string (TenantIdField) The tenant this resource belongs to. |
| data required | object The full affected resource (Message, Call, Voicemail, …). |
{- "id": "evt_01j8x4e",
- "type": "message.received",
- "event_version": "2026-08-11",
- "created_at": "2019-08-24T14:15:22Z",
- "tenant_id": "tnt_01j8x2z",
- "data": { }
}Provisions a browser softphone endpoint (one SIP credential). Create
one per agent seat — credentials must not be shared across concurrent
devices. The browser never sees the credential: mint short-lived
login tokens server-side via POST /web_clients/{id}/tokens and hand
only the token to the page. Freshly created clients can take a few
seconds to accept their first login.
| tenant_id required | string |
| name | string Label for your own bookkeeping, e.g. an agent seat. |
{- "tenant_id": "string",
- "name": "string"
}{- "id": "string",
- "tenant_id": "string",
- "name": "string",
- "sip_username": "string",
- "status": "active",
- "created_at": "2019-08-24T14:15:22Z"
}| tenant_id | string Scope results to one tenant. |
| limit | integer [ 1 .. 100 ] Default: 25 |
| after | string Cursor from a previous page's |
{- "data": [
- {
- "id": "string",
- "tenant_id": "string",
- "name": "string",
- "sip_username": "string",
- "status": "active",
- "created_at": "2019-08-24T14:15:22Z"
}
], - "has_more": true,
- "next_cursor": "string"
}Deletes the underlying credential — outstanding login tokens die with it and any registered browser session disconnects. Idempotent.
| web_client_id required | string |
{- "id": "string",
- "tenant_id": "string",
- "name": "string",
- "sip_username": "string",
- "status": "active",
- "created_at": "2019-08-24T14:15:22Z"
}Returns a short-lived browser login token. Call this from your backend when a signed-in agent opens the softphone, and pass the token to the browser SDK. Mint a fresh token per session; tokens expire on their own and die early if the client is revoked.
| web_client_id required | string |
{- "token": "string",
- "expires_at": "2019-08-24T14:15:22Z"
}Totals the account's billable usage by kind over [start, end) for the key's mode. Live and test ledgers are separate; only live usage is invoiced.
| start | string RFC 3339 timestamp or |
| end | string RFC 3339 timestamp or |
| tenant_id | string Scope results to one tenant. |
{- "object": "usage_summary",
- "mode": "live",
- "start": "2019-08-24T14:15:22Z",
- "end": "2019-08-24T14:15:22Z",
- "data": [
- {
- "kind": "sms_segment_outbound",
- "quantity": 0
}
]
}Returns a short-lived browser-safe token and the WebSocket url to
connect it to (wss://media.handset.dev/v1/events?token=…). The
socket pushes your account's events — the same envelopes your
webhook endpoints receive — the moment they happen; tenant-scoped
keys get only their tenant's events. Mint from your backend (your
API key never reaches the browser) and re-mint on expiry; treat the
stream as a low-latency refresh signal, with webhooks as the
durable channel.
{- "token": "hsrt_eyJhIjo…",
- "expires_at": "2019-08-24T14:15:22Z"
}