{"openapi":"3.0.3","info":{"title":"TextKit","description":"Programmable SMS and phone numbers for developers. Send and receive texts, verify phone numbers, and provision numbers with one REST API. Test keys (ssms_sk_test_...) work instantly against the sandbox; live keys are enabled after a short live-access review.","version":"1.0.0","contact":{"url":"https://jointextkit.com"}},"servers":[{"url":"https://api.jointextkit.com/v1"}],"security":[{"apiKey":[]}],"paths":{"/messages":{"post":{"operationId":"sendMessage","summary":"Send an SMS","description":"Sends an SMS from one of your numbers. In test mode the message is simulated: it reaches `delivered` but no text is sent (`simulated: true`, an X-TextKit-Simulated header, and a `notice` when `to` is a real-looking number). The one exception is a send to your own verified phone, which is really delivered when a verification sender is available (`simulated: false`). `from` may be omitted when the account has exactly one number for the key's mode (the sandbox number with a test key, the one live sending number with a live key); with none or several the request is refused and the error lists the choices under `numbers`. Supports the Idempotency-Key header, including with scheduled_at. A live send from a US local number that is not yet linked to an approved registration (sender.state other than active) is delivered only to your verified numbers; to anyone else it answers 403 sender_not_registered with an X-TextKit-Sender-State header (test_only | pending | action_needed). The same holds for a toll-free number until its toll_free.status is verified. An account without live access that owns a live toll-free number may use a live key here: until the account is live, a live send goes only from that toll-free number to your verified numbers (billed like any live send); any other live send answers 403 sender_not_registered.","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","maxLength":255},"description":"Makes retries safe for 24 hours: the same key and body replays the original successful response (with Idempotent-Replayed: true); a different body returns 409. Failed requests release the key."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["to","body"],"properties":{"to":{"type":"string","example":"+15005550006"},"from":{"type":"string","example":"+15005550100","description":"One of your numbers, or a sender pool id (pool_...): TextKit picks a pool member, the same one for each recipient every time. Optional when the account has exactly one number for the key's mode."},"body":{"type":"string","maxLength":1600},"customer_id":{"type":"string","description":"Optional. Attribute to one of your customers. Defaults to the customer the from-number is assigned to; a number assigned to another customer is rejected (400)."},"scheduled_at":{"type":"string","description":"Optional. Future ISO timestamp (or epoch ms), up to 30 days out. The message is queued and the response is a scheduled_message object instead of a message. A marketing message scheduled into the recipient's quiet hours is refused now (403 quiet_hours) or, with quiet_hours defer, moved to their next 8:00am; any other message is moved."},"category":{"type":"string","enum":["marketing","transactional","otp"],"description":"Optional. It can make a message stricter (`marketing`), never looser: what a message is comes from the sending number's registration and its wording ([which messages are marketing](/docs/compliance-checks#which-messages-are-marketing)). A message outside its registration's use case is refused with 403 campaign_mismatch."},"quiet_hours":{"type":"string","enum":["block","defer"],"description":"Optional. What to do with a message in the recipient's quiet hours: `block` refuses it (403 quiet_hours), `defer` holds it until their next 8:00am. Omitted: the account default (a single marketing send is refused; every other message is held). One-time codes are never held."},"shorten_links":{"type":"boolean","description":"Optional. Rewrite every http(s) link in `body` to a short link on your active link domain (POST /link-domains), with clicks tracked (`links` on the message, `message.link_clicked` events). The links are checked as you wrote them; segments are counted on the shortened text. Without an active link domain the request is refused with 400 link_domain_required: short links are never put on a shared domain. Works with scheduled_at."},"tags":{"type":"array","maxItems":10,"items":{"type":"string","maxLength":32},"example":["spring-sale"],"description":"Optional. Your own labels, up to 10 (letters, digits, space _ . : -; up to 32 characters each; lowercased), for example a campaign or a flow. Stored on the message, sent on its webhook events, and reported by GET /v1/deliverability?group_by=tag. Filter with GET /v1/messages?tag=."},"consent":{"type":"object","description":"Optional. How the recipient agreed to your messages. A recurring non-marketing message from a number linked to a registration needs an opt-in on file for that registration; when none is, this records it and the message is sent. Never counts for marketing (use POST /v1/consent with proof). Not with scheduled_at.","required":["source","collected_at"],"properties":{"source":{"type":"string","enum":["web_form","keyword","paper","checkout","verbal"]},"collected_at":{"type":"string","description":"When they agreed: ISO timestamp or epoch ms, not in the future."},"page_url":{"type":"string","format":"uri","description":"Required for web_form: the page the form was on."}}},"media":{"type":"array","items":{"type":"string","format":"uri"},"description":"Reserved for MMS; currently returns 400 mms_not_enabled."},"status_callback":{"type":"string","format":"uri","example":"https://example.com/sms/status","description":"Optional. Also send this message's status events (message.sent, message.delivered, message.failed, message.blocked) to this https URL, signed with your callback secret (POST /v1/webhooks/callback_secret; 400 callback_secret_required until it exists). Logged and retried like a webhook endpoint, under endpoint `status_callback`. Immediate sends only."}}}}}},"responses":{"201":{"description":"Message accepted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Message"}}}},"202":{"description":"Accepted and not yet sent. Either a Message with status queued (a temporary carrier failure is being retried automatically, next_attempt_at set), or a ScheduledMessage with a `deferred` object (a message held for quiet hours, set for the recipient's next 8:00am).","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/Message"},{"$ref":"#/components/schemas/ScheduledMessage"}]}}}},"400":{"description":"Invalid request, or link_domain_required (`shorten_links` with no active link domain)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Refused by a send check: campaign_mismatch (the message is outside the sending number's registration: promotional wording or a marketing label on a non-marketing registration, anything but a one-time code from a two-factor registration, or a kind of message our registration check found does not match; with registration_id, use_case and mismatch, plus checked_by consistency_check and fingerprint when the check refused it), recipient_opted_out, quiet_hours (with recipient_local_time, timezone, send_after), consent_required (a marketing message, or a recurring message from a registered number, to a number with no opt-in on file: for the registration of the sending number when it has one, with registration_id and consent_scope), reconfirmation_pending / reconfirmation_expired (a marketing recipient not texted in 30 days is asked to confirm first; see /docs/compliance-checks), campaign_paused (marketing paused on the registration of the sending number, for a high opt-out rate), held_for_review (a new kind of message from an account in its first 30 days of live sending, or one the registration check could not place, held for our team to review; with held, fingerprint, registration_id and expires_at), review_rejected (a kind of message our team did not approve for the registration; with fingerprint), sender_not_registered, recipient_not_verified, spend_limit_reached, or forbidden (content, destination). The attempt is stored as a message with status blocked; its id is `message_id` in the error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Idempotency conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"The carrier permanently rejected the message (carrier_error)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The content screen could not check the message (screen_unavailable), so it was not sent or charged; retry after Retry-After. Stored as a blocked message; its id is `message_id` in the error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"get":{"operationId":"listMessages","summary":"List messages","parameters":[{"name":"limit","in":"query","schema":{"type":"integer","maximum":100,"default":25}},{"name":"cursor","in":"query","schema":{"type":"string"}},{"name":"number","in":"query","schema":{"type":"string"},"description":"Filter to messages to or from this number"},{"name":"to","in":"query","schema":{"type":"string"},"description":"Exact destination number (E.164)"},{"name":"from","in":"query","schema":{"type":"string"},"description":"Exact sending number (E.164)"},{"name":"status","in":"query","schema":{"type":"string","enum":["queued","sent","delivered","failed","blocked","received"]}},{"name":"blocked_reason","in":"query","schema":{"type":"string","enum":["account_closed","account_suspended","account_paused","opted_out","destination_not_allowed","sender_not_registered","campaign_mismatch","rate_limited","recipient_not_verified","quiet_hours","consent_required","reconfirmation_pending","reconfirmation_expired","campaign_paused","sender_rotation","content_blocked","screen_unavailable","held_for_review","review_rejected","review_expired","quota_exceeded","spend_limit_reached","insufficient_credits","topic_unsubscribed"]},"description":"Blocked messages with this reason. Implies status=blocked."},{"name":"failure_code","in":"query","schema":{"type":"string"},"description":"Failed or blocked messages whose failure.code is this."},{"name":"direction","in":"query","schema":{"type":"string","enum":["outbound","inbound"]}},{"name":"failure_code","in":"query","schema":{"type":"string","enum":["carrier_filtered","unreachable","invalid_number","opted_out","campaign_mismatch","content_blocked","screen_unavailable","rate_limited","landline","carrier_rejected","spend_limit_reached","insufficient_credits","sender_not_registered","quiet_hours","consent_required","reconfirmation_pending","reconfirmation_expired","held_for_review","review_rejected","review_expired","campaign_paused","sender_rotation","recipient_not_verified","quota_exceeded","destination_not_allowed","account_closed","account_suspended","account_paused","topic_unsubscribed","unknown"]},"description":"Filter to messages that failed for this reason (the `failure.code` on a message)"},{"name":"created_after","in":"query","schema":{"type":"string"},"description":"Inclusive. ISO timestamp or epoch ms."},{"name":"created_before","in":"query","schema":{"type":"string"},"description":"Exclusive. ISO timestamp or epoch ms."},{"name":"customer_id","in":"query","schema":{"type":"string"},"description":"Filter to one customer's messages"},{"name":"schedule_id","in":"query","schema":{"type":"string"},"description":"Filter to the occurrences of one recurring schedule (sch_...)"},{"name":"tag","in":"query","schema":{"type":"string"},"description":"Filter to messages sent with this tag"}],"responses":{"200":{"description":"Messages, newest first","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Message"}},"has_more":{"type":"boolean"},"next_cursor":{"type":"string","nullable":true}}}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/messages/{id}":{"get":{"operationId":"getMessage","summary":"Retrieve a message","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The message","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Message"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"operationId":"deleteMessage","summary":"Delete a message","description":"Deletes a finished message (delivered, failed, received, or sent with `receipt_status: \"missing\"`) and every copy of its text: the events about it, pending webhook retries, the request logs that carried it, and the conversation preview. Opt-out and consent records, usage and billing are kept, and a batch's results keep the message's outcome. Queued messages are refused (409 `invalid_state`): cancel a scheduled one with DELETE /scheduled_messages/{id}. Also refused: blocked messages, a message that is the proof of an opt-out or opt-in, and messages from a registration our team is reviewing. Recorded in the audit log as `message.deleted`. A key deletes only its own mode's messages.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"object":{"type":"string","enum":["message"]},"deleted":{"type":"boolean"}}}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Not finished, blocked, consent evidence, or under review","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/messages/{id}/redact":{"post":{"operationId":"redactMessage","summary":"Redact a message","description":"Empties a finished message's `body` and `media` everywhere we keep them (the message, its events, pending webhook retries, request logs, the conversation preview, the carrier's receipt text) and keeps the record: status, timeline, segments, price and failure stay, and `redacted_at` is set. The same messages as DELETE /messages/{id} are refused. Safe to repeat. Recorded in the audit log as `message.redacted`.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The redacted message","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Message"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Not finished, blocked, consent evidence, or under review","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/batches":{"post":{"operationId":"createBatch","summary":"Send a batch","description":"One body to up to 10,000 recipients, each with its own merge variables (or to contacts by tag), sent as individual messages through the normal pipeline. Numbers are normalized, deduplicated and checked against opt-outs before queueing; rejected rows are returned with reasons. Set dry_run to validate without sending.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["body"],"properties":{"from":{"type":"string","example":"+15005550100","description":"One of your numbers. Optional while the key's mode has one number."},"body":{"type":"string","maxLength":1600,"example":"Hi {{first_name}}, your order {{order_id}} has shipped."},"name":{"type":"string","maxLength":80},"recipients":{"type":"array","maxItems":10000,"items":{"type":"object","required":["to"],"properties":{"to":{"type":"string","example":"+14155550132"},"variables":{"type":"object","additionalProperties":{"type":"string"},"example":{"first_name":"Jane","order_id":"A-1042"}}}}},"tags":{"type":"array","items":{"type":"string"},"description":"Send to contacts carrying any of these tags (instead of recipients)."},"contact_ids":{"type":"array","items":{"type":"string"}},"segment_id":{"type":"string","description":"Send to the contacts in this saved segment, evaluated when the batch is created (instead of recipients)."},"list_id":{"type":"string","description":"Send to the contacts in this list, as it is when the batch is created (instead of recipients)."},"category":{"type":"string","enum":["marketing","transactional","otp"],"description":"Optional. It can make the batch stricter (`marketing`), never looser: the sending number's registration and the wording decide ([which messages are marketing](/docs/compliance-checks#which-messages-are-marketing)). From a number with no registration, a batch is marketing."},"quiet_hours":{"type":"string","enum":["block","defer"],"description":"Optional. What to do with a message in the recipient's quiet hours: `block` refuses it (403 quiet_hours), `defer` holds it until their next 8:00am. Omitted: each recipient in quiet hours is held until their 8:00am."},"topic_id":{"type":"string","example":"tp_marketing","description":"Subscription topic. Recipients unsubscribed from it are removed at validation (`topic_unsubscribed`) and skipped at send time, like opt-outs. Omit to apply opt-outs only."},"scheduled_at":{"type":"string","description":"Future ISO timestamp or epoch ms, up to 30 days out."},"dry_run":{"type":"boolean","description":"Validate only: returns a BatchValidation and sends nothing."},"check_line_types":{"type":"boolean","description":"Reject landlines (first 500 unique numbers are checked)."}}}}}},"responses":{"200":{"description":"Dry run result","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchValidation"}}}},"201":{"description":"Batch created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Batch"}}}},"400":{"description":"Invalid request or no valid recipients","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`from` not owned, or key mode does not match the number","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"get":{"operationId":"listBatches","summary":"List batches","parameters":[{"name":"limit","in":"query","schema":{"type":"integer","maximum":100,"default":25}},{"name":"cursor","in":"query","schema":{"type":"string"}},{"name":"status","in":"query","schema":{"type":"string","enum":["scheduled","sending","paused","complete","canceled"]}}],"responses":{"200":{"description":"Batches, newest first","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Batch"}},"has_more":{"type":"boolean"},"next_cursor":{"type":"string","nullable":true}}}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/batches/{id}":{"get":{"operationId":"getBatch","summary":"Retrieve a batch","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The batch with live counts","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Batch"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/batches/{id}/recipients":{"get":{"operationId":"listBatchRecipients","summary":"List a batch’s recipients","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"status","in":"query","schema":{"type":"string","enum":["queued","paused","sent","failed","skipped","canceled"]}},{"name":"limit","in":"query","schema":{"type":"integer","maximum":500,"default":100}},{"name":"cursor","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Recipients in upload order","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/BatchRecipient"}},"has_more":{"type":"boolean"},"next_cursor":{"type":"string","nullable":true}}}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/batches/{id}/results":{"get":{"operationId":"getBatchResults","summary":"Results of a batch","description":"What happened after the queue: `attempted = delivered + failed + pending`. `delivered` and `failed` come from carrier delivery receipts; `pending` was accepted by the carrier with no final receipt (`no_receipt` of those never got one). `replies` are inbound messages from recipients to the sending number within `reply_window_hours` of the batch finishing (opt-out keywords are not counted); `stops` are recipients who opted out in that time and are still opted out. `spent_usd` sums the recorded price of each message. `failures` groups failed recipients by reason. Derived from stored messages and cached for up to a minute while numbers can still move; `partial: true` means a read bound was hit and delivered / replies are floors.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Results","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchResults"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/batches/{id}/pause":{"post":{"operationId":"pauseBatch","summary":"Pause a batch","description":"Holds every message not yet handed to the carrier. Messages already in flight still send.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The updated batch, plus affected and sweep_complete","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Batch"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Not allowed in the batch’s current status (or a retry is already running)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/batches/{id}/resume":{"post":{"operationId":"resumeBatch","summary":"Resume a paused batch","description":"Re-queues held messages, paced from now (or from scheduled_at if still in the future).","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The updated batch, plus affected and sweep_complete","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Batch"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Not allowed in the batch’s current status (or a retry is already running)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/batches/{id}/cancel":{"post":{"operationId":"cancelBatch","summary":"Cancel a batch","description":"Cancels a scheduled batch, or stops a sending/paused one: every unsent message is canceled and counted. If sweep_complete is false (very large batches), call again - it is idempotent.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The updated batch, plus affected and sweep_complete","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Batch"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Not allowed in the batch’s current status (or a retry is already running)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/batches/{id}/retry":{"post":{"operationId":"retryBatch","summary":"Retry failed recipients","description":"Re-queues failures that can succeed on a retry (rate limits, quota, carrier errors), up to 3 attempts per recipient. Opt-outs, invalid numbers and recipients this batch already messaged are never retried. Only for complete batches.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The updated batch, plus affected and sweep_complete","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Batch"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Not allowed in the batch’s current status (or a retry is already running)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/scheduled_messages":{"get":{"operationId":"listScheduledMessages","summary":"List scheduled messages","description":"Pending 1:1 messages created with POST /messages scheduled_at. Batches list their own recipients.","responses":{"200":{"description":"Pending scheduled messages, soonest first","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ScheduledMessage"}},"has_more":{"type":"boolean"},"next_cursor":{"type":"string","nullable":true}}}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/scheduled_messages/{id}":{"get":{"operationId":"getScheduledMessage","summary":"Retrieve a pending scheduled message","description":"404 once it has sent (it is a message then: GET /messages) or was canceled.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The scheduled message","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScheduledMessage"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, already sent, or canceled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"operationId":"cancelScheduledMessage","summary":"Cancel a scheduled message","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Canceled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScheduledMessage"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found or already sent","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/verify":{"post":{"operationId":"sendVerification","summary":"Send a verification code","description":"Generates a one-time code and sends it by text (`channel: \"sms\"`, the default) or by email (`channel: \"email\"` with `email`). Enforces expiry, attempt limits and anti-pumping controls. You do NOT need to own a phone number; TextKit sends from its own verification pool. If the pool is unavailable, a live code is sent from one of your own numbers that can already text anyone (sender status `active`, or a verified toll-free number, whose codes always name its verified business); a number still being registered is never used, and with none ready the request is refused (400) and nothing is sent. Nothing is billed here; a verification is charged only when the code is checked successfully. Supports the Idempotency-Key header, so a retried request does not text a second code. By email: the same template, limits per address and per domain, disposable addresses refused (403 `verification_blocked`), and test keys never send (the code is 111111).","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","maxLength":255},"description":"Makes retries safe for 24 hours: the same key and body replays the original successful response (with Idempotent-Replayed: true); a different body returns 409. Failed requests release the key."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"channel":{"type":"string","enum":["sms","email"],"default":"sms","description":"How the code goes out. `sms` needs `phone`; `email` needs `email`."},"phone":{"type":"string","example":"+14155550132","description":"The number to text the code to (channel `sms`). `to` works too."},"email":{"type":"string","example":"ada@example.com","description":"The address to email the code to (channel `email`). Disposable addresses are refused."},"app_name":{"type":"string","maxLength":24,"description":"Your product name, shown in the message"},"user_ip":{"type":"string","description":"Optional. Your end user's IP address, as your server saw it. With it, one person requesting codes for many numbers or addresses is limited (20 an hour per IP on your account, texts and emails together); without it there is no per-IP limit, since requests come from your server."},"from":{"type":"string","description":"Optional, channel `sms` only: send from a number you own instead of the TextKit pool"},"customer_id":{"type":"string","description":"Optional: attribute the verification to one of your customers"}}}}}},"responses":{"201":{"description":"Verification created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Verification"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Blocked by Shield, or recipient_opted_out (the number opted out, or texted STOP to the number the code comes from; they text START to get codes again). charged: false","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Idempotency conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"channel_unavailable: email codes cannot go out right now. Send a text instead (charged: false)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/verify/{id}/fallback":{"post":{"operationId":"fallbackVerification","summary":"Email the code instead","description":"The text did not arrive: email a code for the same verification. One verification id, so one charge when the code is checked. The email carries a new code that replaces the texted one; Check it by `email`: it proves the address, not the phone (a check by `phone` answers 409 `invalid_state`). The try count carries over and the new code gets a fresh 10 minutes. Once per verification, while it is pending (409 `invalid_state` otherwise). The email limits of POST /verify apply. Test keys send nothing; the code stays 111111. Supports the Idempotency-Key header.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","maxLength":255},"description":"Makes retries safe for 24 hours: the same key and body replays the original successful response (with Idempotent-Replayed: true); a different body returns 409. Failed requests release the key."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email"],"properties":{"email":{"type":"string","example":"ada@example.com","description":"The address to email the code to"},"channel":{"type":"string","enum":["email"],"default":"email","description":"The channel to fall back to. Only `email` today."},"user_ip":{"type":"string","description":"Optional. Your end user's IP address, counted in the same per-IP limit as POST /verify."}}}}}},"responses":{"200":{"description":"The verification, now on channel `email`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Verification"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Blocked by Shield (charged: false)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"invalid_state: already moved to email, or no longer pending; or an idempotency conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"channel_unavailable: email codes cannot go out right now (charged: false)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/verify/check":{"post":{"operationId":"checkVerification","summary":"Check a verification code","description":"The only billable moment in Verify. `charged` tells you whether this call was billed.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["code"],"properties":{"phone":{"type":"string","description":"The number the code was texted to. Send this or `email`."},"email":{"type":"string","description":"The address the code was emailed to. After a fallback, check by `email`: the code proves the address, not the phone, and a check by `phone` answers 409 `invalid_state`."},"code":{"type":"string","example":"482193","description":"The code last sent"}}}}}},"responses":{"200":{"description":"Check result","content":{"application/json":{"schema":{"type":"object","properties":{"verified":{"type":"boolean"},"channel":{"type":"string","enum":["sms","email"],"description":"The channel of the code checked"},"phone":{"type":"string","nullable":true,"description":"Channel `sms`: the number the code proved"},"email":{"type":"string","nullable":true,"description":"Channel `email`: the address the code proved (after a fallback, the email, never the phone)"},"status":{"type":"string","enum":["approved","pending","expired","max_attempts"]},"attempts":{"type":"integer"},"charged":{"type":"boolean"}}}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No active verification","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/verify/{id}":{"get":{"operationId":"getVerification","summary":"Retrieve a verification","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The verification","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Verification"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/numbers/available":{"get":{"operationId":"searchAvailableNumbers","summary":"Search available numbers","parameters":[{"name":"area_code","in":"query","schema":{"type":"string","example":"415"}}],"responses":{"200":{"description":"Available numbers","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AvailableNumber"}},"has_more":{"type":"boolean"},"next_cursor":{"type":"string","nullable":true}}}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/numbers":{"get":{"operationId":"listNumbers","summary":"List your numbers","parameters":[{"name":"customer_id","in":"query","schema":{"type":"string"},"description":"Only this customer's numbers"},{"name":"tag","in":"query","schema":{"type":"string"}},{"name":"pool","in":"query","schema":{"type":"string"},"description":"Pool id"},{"name":"mode","in":"query","schema":{"type":"string","enum":["test","live"]}},{"name":"q","in":"query","schema":{"type":"string"},"description":"Matches digits of the number or its label"}],"responses":{"200":{"description":"Your active numbers","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Number"}},"has_more":{"type":"boolean"},"next_cursor":{"type":"string","nullable":true}}}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"purchaseNumber","summary":"Purchase a number","description":"Supports the Idempotency-Key header: a retried purchase replays the original 201.","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","maxLength":255},"description":"Makes retries safe for 24 hours: the same key and body replays the original successful response (with Idempotent-Replayed: true); a different body returns 409. Failed requests release the key."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["phone_number"],"properties":{"phone_number":{"type":"string","example":"+15005550132"},"customer_id":{"type":"string","description":"Optional: assign the number to one of your customers"},"registration_id":{"type":"string","description":"Optional (live numbers): the registration this number should send for. Without it the number follows your account's registration."}}}}}},"responses":{"201":{"description":"Number purchased","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Number"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Idempotency conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/numbers/{id}/registration":{"post":{"operationId":"attachNumberRegistration","summary":"Attach a number to a registration","description":"Ties a live US local number to one of your registrations. Needed only when you have more than one approved registration, or to retry a link that failed; otherwise numbers follow your registration on their own. If the registration is approved the number is linked with the carriers (sender.state pending, then active); a registration holds at most 49 numbers. Emits number.sender_updated.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"The E.164 number"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["registration_id"],"properties":{"registration_id":{"type":"string","example":"reg_a1B2c3D4e5F6"}}}}}},"responses":{"200":{"description":"The number with its new sender","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Number"}}}},"400":{"description":"Unknown registration, or a number that is not registered (sandbox, toll-free)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The registration already has 49 numbers, or the number is active elsewhere and this registration is not approved yet","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/numbers/{id}":{"get":{"operationId":"getNumber","summary":"Retrieve a number","description":"One of your active numbers, with `sender`: how it stands with the carriers, and `throughput`: the pace TextKit holds it to.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"The E.164 number"}],"responses":{"200":{"description":"The number","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Number"},{"type":"object","properties":{"throughput":{"type":"object","nullable":true,"description":"The messages-a-minute pace of the registration this number is linked to, shared by every number linked to it: direct sends over it get 429 rate_limited, batches and scheduled messages wait for the next minute. Null when no TextKit pace applies (not linked to a registration); carrier limits still apply.","properties":{"per_minute":{"type":"integer","example":240},"shared_by":{"type":"string","enum":["registration"]},"registration_id":{"type":"string"}}}}}]}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"operationId":"updateNumber","summary":"Assign a number to a customer","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"The E.164 number"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["customer_id"],"properties":{"customer_id":{"type":"string","nullable":true,"description":"A customer id, or null to unassign"}}}}}},"responses":{"200":{"description":"The updated number","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Number"}}}},"400":{"description":"Unknown customer","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"operationId":"releaseNumber","summary":"Release a number","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"The E.164 number"}],"responses":{"200":{"description":"Number released","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Number"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/contacts":{"post":{"operationId":"upsertContact","summary":"Create or update a contact","description":"One contact per phone number. Creates the contact (201) or updates the one that already has this number (200). Values you leave out are kept and tags are added, never removed; use PATCH to replace them.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactInput"}}}},"responses":{"200":{"description":"The existing contact, updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Contact"}}}},"201":{"description":"The new contact","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Contact"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"get":{"operationId":"listContacts","summary":"List contacts","parameters":[{"name":"limit","in":"query","schema":{"type":"integer","maximum":100,"default":25}},{"name":"cursor","in":"query","schema":{"type":"string"}},{"name":"tag","in":"query","schema":{"type":"string"},"description":"Only contacts carrying this tag"},{"name":"phone_number","in":"query","schema":{"type":"string"},"description":"Find the contact with this number (0 or 1 results)"},{"name":"segment_id","in":"query","schema":{"type":"string"},"description":"Only contacts in this saved segment, evaluated now"},{"name":"list_id","in":"query","schema":{"type":"string"},"description":"Only contacts in this list"},{"name":"q","in":"query","schema":{"type":"string"},"description":"Matches name, email, company, or digits of the number"}],"responses":{"200":{"description":"Contacts, newest first","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Contact"}},"has_more":{"type":"boolean"},"next_cursor":{"type":"string","nullable":true}}}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/contacts/import/sources":{"get":{"operationId":"importSources","summary":"Ways to bring an audience in","description":"Where an audience can come from and every way to bring it here. Without `source`: the questions to ask (where the people are, which group, what you can sign in to, whether consent was recorded, once or synced) and the kinds of source we know. With `source` (free text, e.g. \"Mixpanel\" or \"our Postgres users table\"): the ways in, best first: an integration already syncing it, tools you already have for it, our 6-hourly sync, connected with your key here or in the console, a push to an ingest URL, a script against its API with a key from your environment, its export screen, and the console upload. Also lists the integrations already connected. Reads nothing else and changes nothing.","parameters":[{"name":"source","in":"query","schema":{"type":"string"},"description":"Where the people are, in your words. Omit to get the questions first."}],"responses":{"200":{"description":"The import plan","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","enum":["import_plan"]},"source":{"type":"object","nullable":true,"properties":{"key":{"type":"string"},"label":{"type":"string"},"kind":{"type":"string"},"synced":{"type":"boolean","description":"We have our own sync for it"},"consent":{"type":"string","nullable":true,"description":"Where it records SMS consent"}}},"questions":{"type":"array","items":{"type":"string"}},"connected":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"provider":{"type":"string"},"name":{"type":"string"},"status":{"type":"string"},"list_id":{"type":"string"}}}},"paths":{"type":"array","items":{"type":"object","properties":{"method":{"type":"string","enum":["connected","your_tools","integration","push","api","export","upload"]},"title":{"type":"string"},"when":{"type":"string"},"steps":{"type":"array","items":{"type":"string"}},"console_url":{"type":"string","description":"The console page for this way in"}}}},"consent":{"type":"string","description":"How consent carries over"}}}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/contacts/import":{"post":{"operationId":"importContacts","summary":"Bulk create or update contacts","description":"Up to 500 per request, keyed on phone number, with the same merge rule as POST /contacts. Rows that cannot be imported come back in skipped; the rest still land. `dry_run: true` returns the summary (found, valid, malformed, duplicates, landlines, already opted out, will create / update, eligible) and writes nothing. A row with `opt_in.status: true` (or a date or source) stores opt-in evidence in the consent ledger; `opt_in.status: false` records the number as opted out. An import never opts anyone back in.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["contacts"],"properties":{"contacts":{"type":"array","maxItems":500,"items":{"$ref":"#/components/schemas/ContactInput"}},"campaign_id":{"type":"string","example":"reg_a1B2c3D4e5F6","description":"Applies to every opt_in row that does not name its own campaign_id"},"dry_run":{"type":"boolean"},"check_line_types":{"type":"boolean","description":"dry_run only. Looks up line types to count landlines; live lookups are paid, so only the first 500 numbers are checked."},"import_id":{"type":"string","description":"From a previous response, to group several requests as one import."},"name":{"type":"string","description":"A label for the import (first request only)."}}}}}},"responses":{"200":{"description":"Import result, or the dry-run preview","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","enum":["contact_import","contact_import_preview"]},"created":{"type":"integer"},"updated":{"type":"integer"},"skipped":{"type":"array","items":{"type":"object","properties":{"index":{"type":"integer","description":"Position of the row in the request"},"value":{"type":"string"},"reason":{"type":"string"}}}},"import_id":{"type":"string","example":"imp_a1B2c3D4e5"},"opted_out":{"type":"integer","description":"Rows recorded as opted out because they were marked unsubscribed."},"summary":{"type":"object","description":"dry_run only.","properties":{"found":{"type":"integer"},"valid":{"type":"integer"},"malformed":{"type":"integer"},"duplicates":{"type":"integer"},"landlines":{"type":"integer","nullable":true,"description":"null unless check_line_types was set"},"already_opted_out":{"type":"integer"},"marked_unsubscribed":{"type":"integer"},"will_create":{"type":"integer"},"will_update":{"type":"integer"},"eligible":{"type":"integer","description":"Would receive a broadcast today"}}},"rejected":{"type":"array","description":"dry_run only.","items":{"type":"object","properties":{"index":{"type":"integer"},"phone_number":{"type":"string"},"reason":{"type":"string","enum":["malformed","duplicate","landline"]},"detail":{"type":"string"}}}}}}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/contacts/bulk":{"post":{"operationId":"bulkContacts","summary":"Tag, untag, or delete many contacts","description":"One action for up to 500 contact ids. Ids that do not exist are counted in missing; the rest are applied.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["action","ids"],"properties":{"action":{"type":"string","enum":["add_tags","remove_tags","delete"]},"ids":{"type":"array","maxItems":500,"items":{"type":"string"}},"tags":{"type":"array","items":{"type":"string","maxLength":40},"description":"Required for add_tags and remove_tags"}}}}}},"responses":{"200":{"description":"Result","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","enum":["contact_bulk"]},"action":{"type":"string"},"updated":{"type":"integer"},"deleted":{"type":"integer"},"missing":{"type":"integer"}}}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/contacts/{id}":{"get":{"operationId":"getContact","summary":"Retrieve a contact","description":"Includes `consent_status`, each topic's subscription, and the saved segments the contact is in right now. `id` may also be the contact's phone number.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The contact","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Contact"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"operationId":"updateContact","summary":"Update a contact","description":"Only the fields sent change. tags and fields replace what is stored; null clears name or notes, and null or an empty string clears any other optional property.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"phone_number":{"type":"string","description":"E.164, US or Canada"},"name":{"type":"string","maxLength":120,"nullable":true},"tags":{"type":"array","maxItems":20,"items":{"type":"string","maxLength":40}},"fields":{"type":"object","additionalProperties":{"type":"string","maxLength":500},"description":"Up to 20 string values"},"notes":{"type":"string","maxLength":2000,"nullable":true},"first_name":{"type":"string","maxLength":80,"nullable":true},"last_name":{"type":"string","maxLength":80,"nullable":true},"email":{"type":"string","maxLength":254,"nullable":true},"company":{"type":"string","maxLength":120,"nullable":true},"state":{"type":"string","maxLength":40,"nullable":true},"source":{"type":"string","maxLength":60,"nullable":true}}}}}},"responses":{"200":{"description":"The contact","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Contact"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"phone_number already used by another contact","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"operationId":"deleteContact","summary":"Delete a contact","description":"Messages, consent records and topic preferences for the number are untouched.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"object":{"type":"string","enum":["contact"]},"deleted":{"type":"boolean"}}}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/customers":{"post":{"operationId":"createCustomer","summary":"Create a customer","description":"A business you send on behalf of. Assign numbers to it and its messages, verifications, events and usage carry its id. Optional; nothing requires customers.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","maxLength":200,"nullable":true},"external_id":{"type":"string","maxLength":200,"nullable":true,"description":"Your id for them; unique per account"},"metadata":{"type":"object","additionalProperties":{"type":"string","maxLength":500},"description":"Up to 50 string values"}}}}}},"responses":{"201":{"description":"The customer","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Customer"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"external_id already used","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"get":{"operationId":"listCustomers","summary":"List customers","parameters":[{"name":"limit","in":"query","schema":{"type":"integer","maximum":100,"default":25}},{"name":"cursor","in":"query","schema":{"type":"string"}},{"name":"external_id","in":"query","schema":{"type":"string"},"description":"Find the customer with this external_id (0 or 1 results)"}],"responses":{"200":{"description":"Customers, newest first","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Customer"}},"has_more":{"type":"boolean"},"next_cursor":{"type":"string","nullable":true}}}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/customers/{id}":{"get":{"operationId":"getCustomer","summary":"Retrieve a customer","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The customer, with phone_numbers assigned to it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Customer"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"operationId":"updateCustomer","summary":"Update a customer","description":"Only the fields sent change. metadata replaces the whole object; null clears a field.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","maxLength":200,"nullable":true},"external_id":{"type":"string","maxLength":200,"nullable":true,"description":"Your id for them; unique per account"},"metadata":{"type":"object","additionalProperties":{"type":"string","maxLength":500},"description":"Up to 50 string values"}}}}}},"responses":{"200":{"description":"The customer","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Customer"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"external_id already used","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"operationId":"deleteCustomer","summary":"Delete a customer","description":"Unassigns its numbers (they stay on your account). Messages, events and usage already attributed keep the id.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"object":{"type":"string","enum":["customer"]},"deleted":{"type":"boolean"}}}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/customers/{id}/usage":{"get":{"operationId":"getCustomerUsage","summary":"A customer's usage","description":"UTC days, inclusive, up to 366. Follows the key: test keys report sandbox traffic, live keys live traffic.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"start","in":"query","schema":{"type":"string","format":"date"},"description":"Default: first day of this month"},{"name":"end","in":"query","schema":{"type":"string","format":"date"},"description":"Default: today"}],"responses":{"200":{"description":"Usage","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomerUsage"}}}},"400":{"description":"Invalid range","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/numbers/bulk":{"post":{"operationId":"bulkUpdateNumbers","summary":"Act on many numbers at once","description":"Tag, untag, label, pool, unpool or release up to 1000 numbers (100 for release). Returns 200 with a result per id even when some fail. Release requires `confirm: \"RELEASE <count of ids>\"`; test keys cannot release live numbers.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["ids","action"],"properties":{"ids":{"type":"array","items":{"type":"string"},"maxItems":1000,"example":["+15005550132","+15005550154"]},"action":{"type":"string","enum":["add_to_pool","remove_from_pool","add_tags","remove_tags","set_label","release"]},"pool_id":{"type":"string","description":"add_to_pool / remove_from_pool"},"tags":{"type":"array","items":{"type":"string"},"description":"add_tags / remove_tags. Lowercased; up to 20 per number."},"label":{"type":"string","nullable":true,"maxLength":64,"description":"set_label; null clears"},"confirm":{"type":"string","example":"RELEASE 2","description":"release only"}}}}}},"responses":{"200":{"description":"Per-id results","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkResult"}}}},"400":{"description":"Malformed request or missing release confirmation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/numbers/bulk-purchase":{"post":{"operationId":"bulkPurchaseNumbers","summary":"Buy many numbers in an area code","description":"Quota is checked for the whole quantity up front (429, nothing bought). After that, carrier refusals are reported per number and backfilled from spare inventory; `shortfall` counts any the area code could not supply. Test keys mint sandbox numbers. Supports the Idempotency-Key header: a retried request replays the original response instead of buying again.","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","maxLength":255},"description":"Makes retries safe for 24 hours: the same key and body replays the original successful response (with Idempotent-Replayed: true); a different body returns 409. Failed requests release the key."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["area_code","quantity"],"properties":{"area_code":{"type":"string","example":"415"},"quantity":{"type":"integer","minimum":1,"maximum":50},"pool_id":{"type":"string","description":"Add the new numbers to this pool"},"tags":{"type":"array","items":{"type":"string"}}}}}}},"responses":{"201":{"description":"At least one number purchased","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","enum":["bulk_purchase"]},"requested":{"type":"integer"},"purchased":{"type":"array","items":{"$ref":"#/components/schemas/Number"}},"failed":{"type":"array","items":{"type":"object","properties":{"phone_number":{"type":"string"},"error":{"type":"string"},"message":{"type":"string"}}}},"shortfall":{"type":"integer"}}}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Idempotency conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/numbers/requests":{"get":{"operationId":"listNumberRequests","summary":"List open short-code and toll-free requests","description":"Requests you filed (here or in the console) that our team has not closed yet. Scope: numbers:read.","responses":{"200":{"description":"Open requests","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","enum":["list"]},"data":{"type":"array","items":{"type":"object","properties":{"object":{"type":"string","enum":["number_request"]},"id":{"type":"string","example":"short_code"},"type":{"type":"string","enum":["short_code","toll_free"]},"business_name":{"type":"string"},"monthly_volume":{"type":"string"},"use_case":{"type":"string"},"status":{"type":"string","enum":["requested"]},"created_at":{"type":"string","format":"date-time"}}}},"has_more":{"type":"boolean"},"next_cursor":{"type":"string","nullable":true}}}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"requestNumber","summary":"Request a short code (or a toll-free number set up by our team)","description":"Files a request with our team; it provisions nothing by itself. We set the number up with you by email. Carriers take 8 to 12 weeks to approve a short code, and its carrier fees are passed through at cost. One open request per type: asking again updates it. 5 a day per key. Scope: numbers:write.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["type","business_name","monthly_volume","use_case"],"properties":{"type":{"type":"string","enum":["short_code","toll_free"]},"business_name":{"type":"string","maxLength":120},"monthly_volume":{"type":"string","enum":["under_1k","1k_10k","10k_100k","over_100k"]},"use_case":{"type":"string","minLength":10,"maxLength":500,"description":"What you will send, in a sentence or two."}}}}}},"responses":{"201":{"description":"The request","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","enum":["number_request"]},"id":{"type":"string","example":"short_code"},"type":{"type":"string","enum":["short_code","toll_free"]},"business_name":{"type":"string"},"monthly_volume":{"type":"string"},"use_case":{"type":"string"},"status":{"type":"string","enum":["requested"]},"created_at":{"type":"string","format":"date-time"}}}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/numbers/export":{"get":{"operationId":"exportNumbers","summary":"Export numbers as CSV","parameters":[{"name":"tag","in":"query","schema":{"type":"string"}},{"name":"pool","in":"query","schema":{"type":"string"}},{"name":"mode","in":"query","schema":{"type":"string","enum":["test","live"]}},{"name":"q","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"phone_number,mode,label,tags,pools,pool_ids,created_at","content":{"text/csv":{"schema":{"type":"string"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/numbers/pools":{"get":{"operationId":"listPools","summary":"List sender pools","responses":{"200":{"description":"Your pools","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Pool"}},"has_more":{"type":"boolean"},"next_cursor":{"type":"string","nullable":true}}}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"createPool","summary":"Create a sender pool","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string","maxLength":64},"description":{"type":"string","maxLength":280},"routing":{"type":"string","enum":["sticky","local"],"default":"sticky","description":"How a recipient gets a number. sticky: the same member every time. local: a member in the recipient's area code, else one in their time zone, else any; still the same one each time."}}}}}},"responses":{"201":{"description":"Pool created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Pool"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/numbers/pools/{id}":{"get":{"operationId":"getPool","summary":"Retrieve a pool","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The pool","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Pool"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"operationId":"updatePool","summary":"Rename a pool","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"routing":{"type":"string","enum":["sticky","local"],"default":"sticky","description":"How a recipient gets a number. sticky: the same member every time. local: a member in the recipient's area code, else one in their time zone, else any; still the same one each time."}}}}}},"responses":{"200":{"description":"The pool","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Pool"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"operationId":"deletePool","summary":"Delete a pool","description":"Its numbers stay on your account; sends that name this pool start failing.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Deleted"},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/numbers/pools/{id}/clone":{"post":{"operationId":"clonePool","summary":"Clone a pool","description":"New pool with the same description and members. Numbers can belong to several pools.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"}}}}}},"responses":{"201":{"description":"The new pool","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Pool"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/lookup/{phone}":{"get":{"operationId":"lookupPhone","summary":"Look up a phone number","description":"Carrier, line type, and caller name for any US/Canada number.","parameters":[{"name":"phone","in":"path","required":true,"schema":{"type":"string","example":"+14155550132"}}],"responses":{"200":{"description":"Lookup result","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Lookup"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/contacts/{id}/topics":{"get":{"operationId":"getContactTopics","summary":"A contact's topic subscriptions","description":"`id` is a contact id or a phone number (preferences belong to the number, so a number that is not a contact still answers). `consent_status` is the global opt-out state: when it is `opted_out` nothing is sent, whatever the topics say.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Each topic and whether this number is subscribed","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","enum":["topic_subscriptions"]},"phone":{"type":"string"},"contact_id":{"type":"string","nullable":true},"consent_status":{"type":"string","enum":["opted_out","opted_in","no_record"]},"data":{"type":"array","items":{"$ref":"#/components/schemas/TopicSubscription"}}}}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/contacts/{id}/topics/{topic}":{"put":{"operationId":"setContactTopic","summary":"Subscribe or unsubscribe a contact from a topic","description":"`topic` is a topic id or name. Recorded in the consent ledger and the audit log. No record means subscribed, so only an unsubscribe narrows who is sent to. Subscribing to a topic does not lift an opt-out.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Contact id or phone number"},{"name":"topic","in":"path","required":true,"schema":{"type":"string"},"description":"Topic id (tp_...) or name"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["subscribed"],"properties":{"subscribed":{"type":"boolean"},"source":{"type":"string","description":"Where the change came from, e.g. \"preference_page\". Default \"api\"."}}}}}},"responses":{"200":{"description":"The new state; `changed` is false when it already was","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","enum":["topic_subscription"]},"phone":{"type":"string"},"contact_id":{"type":"string","nullable":true},"topic_id":{"type":"string"},"name":{"type":"string"},"subscribed":{"type":"boolean"},"changed":{"type":"boolean"}}}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such contact, number, or topic","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/lists":{"post":{"operationId":"createList","summary":"Create a list","description":"A list is a fixed group of contacts you add and remove on purpose (a segment is the rule-based kind). Add people with `POST /lists/{id}/contacts`, with `list_id` on `POST /contacts/import`, or from the console. Up to 200 lists.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListInput"}}}},"responses":{"201":{"description":"The list","content":{"application/json":{"schema":{"$ref":"#/components/schemas/List"}}}},"400":{"description":"Missing name, or the list limit is reached","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"get":{"operationId":"listLists","summary":"List lists","responses":{"200":{"description":"Every list, by name, with `contact_count`","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/List"}},"has_more":{"type":"boolean"},"next_cursor":{"type":"string","nullable":true}}}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/lists/{id}":{"get":{"operationId":"getList","summary":"Retrieve a list","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","example":"lst_a1B2c3D4e5F6"}}],"responses":{"200":{"description":"The list with `contact_count`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/List"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"operationId":"updateList","summary":"Update a list","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListInput"}}}},"responses":{"200":{"description":"The list","content":{"application/json":{"schema":{"$ref":"#/components/schemas/List"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"operationId":"deleteList","summary":"Delete a list","description":"Deletes the list, not the contacts.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"object":{"type":"string","enum":["list"]},"deleted":{"type":"boolean"}}}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/lists/{id}/contacts":{"get":{"operationId":"listListContacts","summary":"List a list's contacts","description":"Most recently added first.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":25}},{"name":"cursor","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"A page of contacts","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Contact"}},"has_more":{"type":"boolean"},"next_cursor":{"type":"string","nullable":true}}}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"addListContacts","summary":"Add contacts to a list","description":"Up to 1,000 per request between `contact_ids` and `phone_numbers`. Numbers must already be contacts; any that are not come back in `not_found`.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListMembersInput"}}}},"responses":{"200":{"description":"What changed","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","enum":["list_membership_change"]},"list_id":{"type":"string"},"added":{"type":"integer"},"unchanged":{"type":"integer"},"not_found":{"type":"array","items":{"type":"string"}}}}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such list","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"operationId":"removeListContacts","summary":"Remove contacts from a list","description":"Same body as adding. The contacts stay contacts.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListMembersInput"}}}},"responses":{"200":{"description":"What changed","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","enum":["list_membership_change"]},"list_id":{"type":"string"},"removed":{"type":"integer"},"unchanged":{"type":"integer"},"not_found":{"type":"array","items":{"type":"string"}}}}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such list","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/segments":{"post":{"operationId":"createSegment","summary":"Create a segment","description":"A segment is a saved, named filter over contacts (an audience; not an SMS message part). It is evaluated when used, so it always reflects current contacts. Up to 100 segments, 10 rules each.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SegmentInput"}}}},"responses":{"201":{"description":"The segment","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Segment"}}}},"400":{"description":"Invalid rules, or the segment limit is reached","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"get":{"operationId":"listSegments","summary":"List segments","responses":{"200":{"description":"Every segment, by name","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Segment"}},"has_more":{"type":"boolean"},"next_cursor":{"type":"string","nullable":true}}}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/segments/{id}":{"get":{"operationId":"getSegment","summary":"Retrieve a segment","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","example":"seg_a1B2c3D4e5F6"}}],"responses":{"200":{"description":"The segment","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Segment"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"operationId":"updateSegment","summary":"Update a segment","description":"Send `name` / `description` alone, or `rules` (and `match`) to replace the whole rule list.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SegmentInput"}}}},"responses":{"200":{"description":"The segment","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Segment"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"operationId":"deleteSegment","summary":"Delete a segment","description":"Deletes the filter, not the contacts.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"object":{"type":"string","enum":["segment"]},"deleted":{"type":"boolean"}}}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/segments/preview":{"post":{"operationId":"queryAudience","summary":"Query an audience without saving it","description":"Runs a rule list now and answers with the counts `GET /segments/{id}/preview` gives plus the first `limit` matching contacts. Use it to ask cohort questions (who replied in the last 7 days, who got a batch and did not reply, who did one event and not another) before saving the rules as a segment to page through or send to. `eligible + opted_out + topic_unsubscribed = matched`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"match":{"type":"string","enum":["all","any"],"default":"all"},"rules":{"type":"array","maxItems":10,"items":{"$ref":"#/components/schemas/SegmentRule"},"example":[{"field":"last_reply_at","op":"after","value":"7d"},{"field":"tag","op":"is","value":"vip"}]},"topic_id":{"type":"string","description":"Topic id or name: eligible also removes contacts unsubscribed from it"},"limit":{"type":"integer","minimum":0,"maximum":100,"default":25,"description":"How many matching contacts to return"}}}}}},"responses":{"200":{"description":"Counts and the first contacts","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","enum":["segment_preview"]},"segment_id":{"type":"string","nullable":true},"topic_id":{"type":"string","nullable":true},"contacts_total":{"type":"integer"},"matched":{"type":"integer"},"opted_out":{"type":"integer"},"topic_unsubscribed":{"type":"integer"},"eligible":{"type":"integer"},"contacts":{"type":"array","items":{"$ref":"#/components/schemas/Contact"}}}}}}},"400":{"description":"Invalid rules","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/segments/{id}/preview":{"get":{"operationId":"previewSegment","summary":"Count a segment","description":"How many contacts the segment matches now and how many would receive a broadcast: opted-out contacts are removed, then (with `topic_id`) those unsubscribed from that topic. `eligible + opted_out + topic_unsubscribed = matched`.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"topic_id","in":"query","schema":{"type":"string"},"description":"Topic id or name"}],"responses":{"200":{"description":"Counts","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","enum":["segment_preview"]},"segment_id":{"type":"string"},"topic_id":{"type":"string","nullable":true},"contacts_total":{"type":"integer"},"matched":{"type":"integer"},"opted_out":{"type":"integer"},"topic_unsubscribed":{"type":"integer"},"eligible":{"type":"integer"}}}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api_keys":{"get":{"operationId":"listApiKeys","summary":"List API keys","description":"Active keys of the calling key's mode (a test key lists test keys). Never the secrets.","responses":{"200":{"description":"Keys","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ApiKey"}},"has_more":{"type":"boolean"},"next_cursor":{"type":"string","nullable":true}}}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"createApiKey","summary":"Create an API key","description":"A key for an app you build, to keep as a secret (for example TEXTKIT_API_KEY). Same mode as the calling key. A key with limited scopes can only create keys with scopes inside its own. The secret is returned once. The owner and admins are emailed. 20 per hour.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string","maxLength":60,"description":"What the key is for, e.g. \"Booking app\""},"scopes":{"type":"array","items":{"type":"string"},"description":"Limit the key to these scopes (see /docs/authentication). Omit for full access (full-access callers only)."}}}}}},"responses":{"201":{"description":"The key, with its secret (shown once)","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiKey"}],"properties":{"secret":{"type":"string"}}}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api_keys/{id}":{"delete":{"operationId":"revokeApiKey","summary":"Revoke an API key","description":"Immediate and permanent. Only keys of the calling key's mode.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Revoked","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"object":{"type":"string"},"revoked":{"type":"boolean"}}}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/topics":{"get":{"operationId":"listTopics","summary":"List subscription topics","description":"Every account starts with Marketing (tp_marketing), Account alerts (tp_account_alerts) and Product updates (tp_product_updates). Up to 20 topics.","responses":{"200":{"description":"Topics, oldest first","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Topic"}},"has_more":{"type":"boolean"},"next_cursor":{"type":"string","nullable":true}}}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"createTopic","summary":"Create a topic","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string","maxLength":60},"description":{"type":"string","maxLength":200,"nullable":true}}}}}},"responses":{"201":{"description":"The topic","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Topic"}}}},"400":{"description":"Invalid request, or the topic limit is reached","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"A topic with that name exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/topics/{id}":{"get":{"operationId":"getTopic","summary":"Retrieve a topic","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","example":"tp_marketing"}}],"responses":{"200":{"description":"The topic","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Topic"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"operationId":"updateTopic","summary":"Rename or describe a topic","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","maxLength":60},"description":{"type":"string","maxLength":200,"nullable":true}}}}}},"responses":{"200":{"description":"The topic","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Topic"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"A topic with that name exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"operationId":"deleteTopic","summary":"Delete a topic","description":"Recipients' recorded preferences for the topic are kept as consent history, and batches already created with it keep honoring them.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"object":{"type":"string","enum":["topic"]},"deleted":{"type":"boolean"}}}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/consent":{"get":{"operationId":"listConsent","summary":"List opted-out numbers (suppression list)","parameters":[{"name":"limit","in":"query","schema":{"type":"integer","maximum":100,"default":25}},{"name":"cursor","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Suppressions in numeric order","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Consent"}},"has_more":{"type":"boolean"},"next_cursor":{"type":"string","nullable":true}}}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/consent/{phone}":{"get":{"operationId":"getConsent","summary":"Consent state and append-only history for one number","parameters":[{"name":"phone","in":"path","required":true,"schema":{"type":"string","example":"+14155550132"}}],"responses":{"200":{"description":"Consent record","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsentDetail"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"setConsent","summary":"Set consent state from your own system (CRM sync)","description":"Opting in a number that opted out needs `proof` with `collected_at` at or after the opt-out (a STOP cancels earlier consent); otherwise 409 `invalid_request` with `param` `proof` or `proof.collected_at`, and nothing changes.","parameters":[{"name":"phone","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["status"],"properties":{"status":{"type":"string","enum":["opted_out","opted_in"]},"note":{"type":"string","maxLength":200},"campaign_id":{"type":"string","example":"reg_a1B2c3D4e5F6","description":"Only with opted_in: the registration the person opted in to. A marketing send from a number linked to that registration looks for this record. Proof is required when its use case is marketing or mixed (400 otherwise). An opt-out is always account-wide."},"proof":{"$ref":"#/components/schemas/OptInProof"}}}}}},"responses":{"200":{"description":"Updated consent state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Consent"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The number opted out, and the opt-in carries no proof collected at or after the opt-out","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/consent/import":{"post":{"operationId":"importConsent","summary":"Bulk-import a suppression list (up to 500 numbers)","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["phone_numbers"],"properties":{"phone_numbers":{"type":"array","items":{"type":"string"},"maxItems":500}}}}}},"responses":{"200":{"description":"Import counts","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","example":"consent_import"},"imported":{"type":"integer"},"skipped":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"reason":{"type":"string"}}}}}}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/consent/export":{"get":{"operationId":"exportConsent","summary":"Export the suppression list, or the full consent ledger with opt-in proof, as CSV","parameters":[{"name":"type","in":"query","schema":{"type":"string","enum":["suppressions","ledger"],"default":"suppressions"}}],"responses":{"200":{"description":"suppressions: phone,status,updated_at,via,method,detected. ledger: phone,at,type,via,method,detected,note,proof_source,proof_collected_at,proof_page_url,proof_disclosure,proof_ip,proof_user_agent,proof_registration_id,proof_campaign_id","content":{"text/csv":{"schema":{"type":"string"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/registrations":{"get":{"operationId":"listRegistrations","summary":"List brand + campaign registrations","responses":{"200":{"description":"Registrations, newest first","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Registration"}},"has_more":{"type":"boolean"},"next_cursor":{"type":"string","nullable":true}}}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"createRegistration","summary":"Create a registration and run the website disclosure check","description":"Fetches the website, privacy policy, terms (discovered from homepage links when not given) and opt-in page, and checks them against carrier requirements. Also checks your example messages (sample_messages). Returns status ready or checks_failed, with findings, guidance, and generated copy. A registration is ready only when the website check passes, the example check passes, and the examples are yours (samples_source = customer): without sample_messages you get starter drafts that must be edited or confirmed first.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegistrationInput"}}}},"responses":{"201":{"description":"Registration","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Registration"}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/registrations/prefill":{"post":{"operationId":"prefillRegistration","summary":"Suggest a registration from a website","description":"Reads the public pages of the website (homepage plus the sign-up, contact, privacy and terms pages it links to) and returns suggestions for a registration: business name, legal name when the site states one, use case, description, opt-in / privacy / terms URLs with ranked candidates, support email, contact phone, address and industry. Creates and files nothing. EIN and entity type are never suggested. Names, contact details and URLs are read with fixed rules; where drafting is available (ai: true) we draft the description, use case, industry and three starter example messages from the same public page text, and every field it returns is validated. Review the fields, then send them to POST /registrations (drafted examples as starter_messages, or as sample_messages once they are yours). One read per website per account every 10 minutes; 10 websites a minute.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["website"],"properties":{"website":{"type":"string","format":"uri","example":"https://acmeplumbing.com"}}}}}},"responses":{"200":{"description":"Suggestions (reachable: false when the site could not be read)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegistrationPrefill"}}}},"400":{"description":"Invalid website","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/registrations/{id}":{"get":{"operationId":"getRegistration","summary":"Get a registration","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","example":"reg_a1B2c3D4e5F6"}}],"responses":{"200":{"description":"Registration","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Registration"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/registrations/{id}/recheck":{"post":{"operationId":"recheckRegistration","summary":"Re-run the checks (optionally correcting input fields)","description":"Allowed in draft, checks_failed, ready and rejected. Any RegistrationInput field may be sent to correct it, including sample_messages and description; samples_confirmed: true adopts the starter examples as yours. A body that only changes the examples re-runs the example check and keeps the website verdict.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegistrationInput"}}}},"responses":{"200":{"description":"Registration","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Registration"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"invalid_state: not recheckable now (message says why)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/registrations/{id}/submit":{"post":{"operationId":"submitRegistration","summary":"Submit a ready registration for carrier review (or resubmit after a rejection)","description":"The body must carry `fees_acknowledged: true`: your acceptance of the registration fee terms (Terms §4.3). Carrier registration fees (brand, vetting, the monthly campaign fee) are separate from the per-message rate and billed at twice what the registry or carrier charges us; a campaign is billed for a minimum of 3 months; the fees are non-refundable, including if the registration is rejected. The acknowledgment is recorded on the filed copy with its wording, the time and your IP; without it the request is refused with 400 invalid_request (param fees_acknowledged) and nothing is filed. The first submit for a business also carries the signed business attestation; without one on file the submit answers 400 attestation_required (param attestation). The submission is then held while our team verifies the business (guidance.next_actor delivered), and filed once it is verified. Later submits for the same business details need no attestation. A business we could not verify returns the registration as rejected with reason code business_unverified; the next submit is signed again.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["fees_acknowledged"],"properties":{"fees_acknowledged":{"type":"boolean","enum":[true],"description":"Must be true. Confirms the registration fee terms above."},"attestation":{"$ref":"#/components/schemas/BusinessAttestation"}}}}}},"responses":{"200":{"description":"Registration, status submitted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Registration"}}}},"400":{"description":"invalid_request: fees_acknowledged missing or not true (param fees_acknowledged), or a missing business detail or attestation field; attestation_required: the business has no signature on file for these details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"invalid_state: not ready (message says what to do)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/registrations/{id}/submissions":{"get":{"operationId":"listRegistrationSubmissions","summary":"List the filed copies of a registration","description":"One entry per submission, oldest first: exactly what was sent for carrier review (business details, example messages, description, opt-in story) and how the checks stood. Read-only. A copy is written when you submit and never changed; a resubmission adds a new one.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","example":"reg_a1B2c3D4e5F6"}}],"responses":{"200":{"description":"Filed copies, oldest first","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/RegistrationSubmission"}},"has_more":{"type":"boolean"},"next_cursor":{"type":"string","nullable":true}}}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/toll-free-verifications":{"get":{"operationId":"listTollFreeVerifications","summary":"List toll-free verifications","description":"Your toll-free verifications, newest first. Each lists its numbers, status, the form, what would get it sent back (problems) and the carriers' answer.","parameters":[{"name":"status","in":"query","schema":{"type":"string","enum":["draft","in_review","submitted","verified","rejected"]}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":25}},{"name":"cursor","in":"query","schema":{"type":"string"},"description":"next_cursor from the previous page"}],"responses":{"200":{"description":"Toll-free verifications, newest first","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/TollFreeVerification"}},"has_more":{"type":"boolean"},"next_cursor":{"type":"string","nullable":true}}}}}},"400":{"description":"invalid_request: an unknown status or cursor","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"createTollFreeVerification","summary":"Start a toll-free verification","description":"Starts the carriers' toll-free verification form for one or more of your live toll-free numbers, filled in from what we already have: your registration, hosted opt-in page, account and website. Any field you send replaces what we filled in. Returns a draft with `problems`: what the carriers would send it back for. Nothing is sent anywhere until you submit it. A number can be on one verification at a time.","requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TollFreeVerificationInput"}}}},"responses":{"201":{"description":"Toll-free verification, status draft","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TollFreeVerification"}}}},"400":{"description":"invalid_request: a field of the wrong type or value (param names it)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"invalid_state: a number is already on another verification, or another form is still open","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/toll-free-verifications/{id}":{"get":{"operationId":"getTollFreeVerification","summary":"Get a toll-free verification","description":"Its status, the form, what would get it sent back (problems), its business, and the carriers' answer (rejection.reason) when it was sent back.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","example":"tfv_a1B2c3D4e5F6"}}],"responses":{"200":{"description":"Toll-free verification","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TollFreeVerification"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"operationId":"updateTollFreeVerification","summary":"Change a toll-free verification's answers","description":"Send only the fields to change. Allowed while draft, in_review (until our team sends it to the carriers) and rejected. After a rejection, fix what it asks for here, then submit again.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","example":"tfv_a1B2c3D4e5F6"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TollFreeVerificationInput"}}}},"responses":{"200":{"description":"Toll-free verification","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TollFreeVerification"}}}},"400":{"description":"invalid_request: a field of the wrong type or value (param names it)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"invalid_state: with the carriers or verified, so it can't change; or a number is on another verification","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/toll-free-verifications/{id}/submit":{"post":{"operationId":"submitTollFreeVerification","summary":"Submit a toll-free verification for review","description":"Sends a draft, or a rejected form you fixed, to our team: it comes back `in_review`. A person on our team checks every form by hand, usually within a business day, then sends it to the carriers (status `submitted`; usually days, sometimes weeks). Nothing goes to the carriers straight from this call. The first submit for a business also carries the signed business attestation; without one on file it answers 400 attestation_required (param attestation). Later forms for the same business details need none. While anything the carriers require is missing it answers 409 invalid_state with `problems`. Toll-free verification has no fee.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","example":"tfv_a1B2c3D4e5F6"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"attestation":{"$ref":"#/components/schemas/BusinessAttestation"}}}}}},"responses":{"200":{"description":"Toll-free verification, status in_review","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TollFreeVerification"}}}},"400":{"description":"invalid_request: a missing business detail or attestation field; attestation_required: the business has no signature on file for these details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"invalid_state: already in review or further along, or not complete (problems lists every missing answer)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/events":{"get":{"operationId":"listEvents","summary":"List events","parameters":[{"name":"limit","in":"query","schema":{"type":"integer","maximum":100,"default":25}},{"name":"cursor","in":"query","schema":{"type":"string"}},{"name":"type","in":"query","schema":{"type":"string","example":"message.delivered"}}],"responses":{"200":{"description":"Events, newest first","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Event"}},"has_more":{"type":"boolean"},"next_cursor":{"type":"string","nullable":true}}}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/events/wait":{"get":{"operationId":"waitForEvents","summary":"Long-poll for new events","description":"Holds the request until an event newer than `cursor` exists or `timeout` seconds pass, then returns the new events oldest first and the cursor to send next. Call once without `cursor` to get one that means \"from now\" (answers immediately, empty). An event can be returned more than once: de-duplicate by `id`. Reads the event log only: no webhook endpoint is called and no delivery is recorded. This is what `textkit listen` runs on. Rate limit: 600 requests per 5 minutes, separate from other calls. Scope: webhooks.","parameters":[{"name":"cursor","in":"query","schema":{"type":"string"},"description":"The `cursor` of the previous response. Omit on the first call."},{"name":"timeout","in":"query","schema":{"type":"integer","minimum":0,"maximum":25,"default":20},"description":"Seconds to hold the request."}],"responses":{"200":{"description":"New events, oldest first","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","enum":["event_wait"]},"data":{"type":"array","items":{"$ref":"#/components/schemas/Event"}},"cursor":{"type":"string","description":"Send as `cursor` on the next call."},"has_more":{"type":"boolean","description":"More events are already waiting: call again without pausing."}}}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/events/{id}/replay":{"post":{"operationId":"replayEvent","summary":"Replay an event to one webhook endpoint","description":"Sends the stored event to the endpoint you name, now, and returns its answer. Not retried. Works for paused endpoints and ones not subscribed to the event type.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","example":"evt_a1B2c3D4e5F6g7H8"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["endpoint_id"],"properties":{"endpoint_id":{"type":"string","example":"we_a1B2c3D4e5F6"}}}}}},"responses":{"200":{"description":"What the endpoint answered","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","enum":["webhook_replay"]},"event_id":{"type":"string"},"endpoint_id":{"type":"string"},"delivery_id":{"type":"string"},"ok":{"type":"boolean"},"status_code":{"type":"integer","nullable":true},"error":{"type":"string","nullable":true,"enum":["timeout","unreachable","blocked"]},"latency_ms":{"type":"integer"}}}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such event or endpoint","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/integrations":{"get":{"operationId":"listIntegrations","summary":"List integrations","description":"The tools connected to this account (Shopify, HubSpot, Salesforce, Mixpanel, Amplitude, webhook): status, settings, last sync, list. Credentials are never returned. Scope: contacts.","responses":{"200":{"description":"Integrations","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"connectIntegration","summary":"Connect a tool","description":"Connects a tool with its API key or service account, the same as the console's Integrations page: the credentials are proved with one read, then sealed and never returned. Its people sync into a list of their own every 6 hours (and on POST /v1/integrations/{id}/sync). Consent comes across only from the field or opt-in event you name. A push integration (provider webhook, or Mixpanel / Amplitude with mode \"push\") answers ingest_url and ingest_token once. Scope: contacts.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["provider"],"properties":{"provider":{"type":"string","enum":["shopify","hubspot","salesforce","mixpanel","amplitude","webhook"]},"mode":{"type":"string","enum":["pull","push"],"description":"Mixpanel / Amplitude: pull with credentials every 6 hours (default when credentials are sent), or push: no credentials, the tool's cohort sync posts to the ingest URL."},"name":{"type":"string","description":"What to call it (default: the tool and its source)."},"sourceId":{"type":"string","description":"Mixpanel or Amplitude cohort id; HubSpot list id; Salesforce campaign id; a Shopify customer search. Blank = everyone (HubSpot, Shopify, Salesforce)."},"projectId":{"type":"string","description":"Mixpanel project id."},"region":{"type":"string","enum":["us","eu"],"description":"Mixpanel / Amplitude data residency."},"username":{"type":"string","description":"Mixpanel service account username."},"secret":{"type":"string","description":"Mixpanel service account secret."},"apiKey":{"type":"string","description":"Amplitude API key."},"secretKey":{"type":"string","description":"Amplitude secret key."},"accessToken":{"type":"string","description":"HubSpot private app token, or Shopify Admin API access token."},"shopDomain":{"type":"string","description":"Shopify store, like acme.myshopify.com."},"instanceUrl":{"type":"string","description":"Salesforce My Domain URL."},"sfObject":{"type":"string","enum":["Contact","Lead"]},"clientId":{"type":"string","description":"Salesforce consumer key, or Shopify client id."},"clientSecret":{"type":"string","description":"Salesforce consumer secret, or Shopify client secret."},"phoneField":{"type":"string","description":"The tool's property holding the mobile number, when not the usual one ($phone, phone, mobilephone)."},"consentField":{"type":"string","description":"The tool's property that records SMS consent (e.g. sms_opt_in)."},"consentEvent":{"type":"string","description":"Mixpanel / Amplitude: the opt-in event the cohort is built on (e.g. SMS Opt In). Everyone in the cohort is recorded as opted in, from that event."},"phoneEvent":{"type":"string","description":"Mixpanel: an event that carries the number when profiles do not (e.g. Verification Check Success). A member with no phone on the profile gets it from their latest such event."},"phoneEventProperty":{"type":"string","description":"Mixpanel: that event's property holding the number (default phone)."}}}}}},"responses":{"201":{"description":"The integration (with ingest_url and ingest_token once, for push)","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/integrations/{id}":{"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","example":"int_a1B2c3D4e5F6"}}],"get":{"operationId":"getIntegration","summary":"Get an integration","description":"One integration, without credentials. Scope: contacts.","responses":{"200":{"description":"The integration","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"operationId":"updateIntegration","summary":"Update an integration","description":"Rename it, pause or resume it (status), or change its settings or credentials (credentials left out keep the stored ones). Scope: contacts.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"status":{"type":"string","enum":["active","paused"]},"sourceId":{"type":"string"},"phoneField":{"type":"string"},"phoneEvent":{"type":"string"},"phoneEventProperty":{"type":"string"},"consentField":{"type":"string"},"consentEvent":{"type":"string"},"username":{"type":"string"},"secret":{"type":"string"},"apiKey":{"type":"string"},"secretKey":{"type":"string"},"accessToken":{"type":"string"}}}}}},"responses":{"200":{"description":"The integration","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"operationId":"disconnectIntegration","summary":"Disconnect an integration","description":"Deletes its credentials and its list. The contacts stay, with their consent records. Scope: contacts.","responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/integrations/{id}/sync":{"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","example":"int_a1B2c3D4e5F6"}}],"post":{"operationId":"syncIntegration","summary":"Sync an integration now","description":"Reads the source now (pull integrations), up to 6 times an hour. Answers found, added, updated, left the list, opted out. Scope: contacts.","responses":{"200":{"description":"The sync summary","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/webhooks":{"get":{"operationId":"listWebhookEndpoints","summary":"List webhook endpoints","description":"Your endpoints. Signing secrets are never listed: one is returned once, by the POST that creates the endpoint. Scope: webhooks.","responses":{"200":{"description":"Endpoints","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/WebhookEndpoint"}},"has_more":{"type":"boolean"},"next_cursor":{"type":"string","nullable":true}}}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"createWebhookEndpoint","summary":"Create a webhook endpoint","description":"Registers a URL to receive events. Same rules as the console: https only, a publicly reachable host, known event types, at most 5 endpoints per account. The response includes `secret`, the signing secret: this is the only time the API returns it. Recorded in the audit log as webhook.created. Scope: webhooks:write.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["url"],"properties":{"url":{"type":"string","format":"uri","example":"https://example.com/webhooks/textkit"},"events":{"description":"Event types to receive. Omit, or send \"*\" or [], for every event. An unknown type is a 400.","oneOf":[{"type":"string","enum":["*"]},{"type":"array","items":{"type":"string"},"example":["message.received","message.delivered"]}]},"description":{"type":"string","maxLength":120},"numbers":{"type":"array","maxItems":100,"items":{"type":"string","example":"+14155550132"},"description":"Optional. Only events about these of your numbers (E.164): message events by their from/to, number events by phone_number. Events about no number (batches, credits, registrations) go only to endpoints without a filter. Omit or [] for every number."}}}}}},"responses":{"201":{"description":"The endpoint, with its signing secret (shown once)","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEndpoint"},{"type":"object","properties":{"secret":{"type":"string","example":"whsec_a1B2c3D4e5F6g7H8a1B2c3D4e5F6g7H8"}}}]}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/webhooks/{id}":{"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","example":"we_a1B2c3D4e5F6"}}],"get":{"operationId":"getWebhookEndpoint","summary":"Get a webhook endpoint","description":"One endpoint, without its signing secret. Scope: webhooks:write.","responses":{"200":{"description":"The endpoint","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpoint"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"operationId":"updateWebhookEndpoint","summary":"Update a webhook endpoint","description":"Change the URL, the subscribed events, or pause and resume delivery with `active`. Send only the fields to change. The signing secret is unchanged (POST /v1/webhooks/{id}/rotate_secret replaces it). Recorded in the audit log as webhook.updated. Scope: webhooks:write.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"events":{"oneOf":[{"type":"string","enum":["*"]},{"type":"array","items":{"type":"string"}}]},"active":{"type":"boolean","description":"false pauses delivery; pending retries for a paused endpoint are dropped."},"description":{"type":"string","maxLength":120,"nullable":true},"numbers":{"type":"array","maxItems":100,"nullable":true,"items":{"type":"string"},"description":"Replace the number filter; [] or null clears it."}}}}}},"responses":{"200":{"description":"The updated endpoint","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpoint"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"operationId":"deleteWebhookEndpoint","summary":"Delete a webhook endpoint","description":"Removes the endpoint and stops delivery to it. Recorded in the audit log as webhook.deleted. Scope: webhooks:write.","responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"object":{"type":"string","enum":["webhook_endpoint"]},"deleted":{"type":"boolean","enum":[true]}}}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/webhooks/callback_secret":{"post":{"operationId":"rotateCallbackSecret","summary":"Create or rotate the status callback secret","description":"The signing secret for `status_callback` requests (POST /v1/messages), returned once. The first call creates it (201); a later call replaces it (200) and the old secret keeps signing for 24 hours, as a second `v1=` in the signature header. Callbacks are signed exactly like webhook endpoints (`textkit-signature`). Recorded in the audit log as webhook.secret_rotated. Scope: webhooks:write.","responses":{"200":{"description":"Rotated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CallbackSecret"}}}},"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CallbackSecret"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/webhooks/{id}/rotate_secret":{"post":{"operationId":"rotateWebhookSecret","summary":"Rotate a webhook signing secret","description":"Replaces the signing secret and returns the new one: the only time the API shows it. With `expire_previous_in`, the old secret keeps signing for that many seconds (up to 7 days) as a second `v1=` in the signature header, so you can deploy the new secret without dropping events; every TextKit SDK accepts either. 0 (the default) retires it now. Recorded in the audit log as webhook.secret_rotated. Scope: webhooks:write.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","example":"we_a1B2c3D4e5F6"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"expire_previous_in":{"type":"integer","minimum":0,"maximum":604800,"default":0,"description":"Seconds the old secret keeps signing alongside the new one.","example":86400}}}}}},"responses":{"200":{"description":"The endpoint with its new secret (shown once)","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEndpoint"},{"type":"object","properties":{"secret":{"type":"string","example":"whsec_a1B2c3D4e5F6g7H8a1B2c3D4e5F6g7H8"},"previous_secret_expires_at":{"type":"string","format":"date-time","nullable":true,"description":"When the old secret stops signing; null when it already has."}}}]}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/webhooks/{id}/test":{"post":{"operationId":"testWebhookEndpoint","summary":"Send a test event to a webhook endpoint","description":"Sends a signed `test.ping` event now and returns what the endpoint answered. Test pings are not stored as events (they cannot be replayed) but the attempt shows in the delivery log. Scope: webhooks:write.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","example":"we_a1B2c3D4e5F6"}}],"responses":{"200":{"description":"What the endpoint answered","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","enum":["webhook_test"]},"endpoint_id":{"type":"string"},"ok":{"type":"boolean"},"status_code":{"type":"integer","nullable":true},"error":{"type":"string","nullable":true,"enum":["timeout","unreachable","blocked"]},"latency_ms":{"type":"integer"},"response_body":{"type":"string","nullable":true,"description":"First 2 KB of the response."}}}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/webhooks/{id}/replay":{"post":{"operationId":"replayFailedWebhooks","summary":"Replay every failed delivery to an endpoint","description":"Queues each event whose deliveries to this endpoint all failed between `since` and `until` (default: the last 24 hours; at most 7 days and 50 events a call). Queued events go out within 5 minutes, with the normal retry schedule. `more: true` means call again. The endpoint must be active. To send one event now, use POST /v1/events/{id}/replay. Scope: webhooks:write.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","example":"we_a1B2c3D4e5F6"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"since":{"type":"string","format":"date-time","description":"ISO date or epoch milliseconds. Default: 24 hours before `until`."},"until":{"type":"string","format":"date-time","description":"ISO date or epoch milliseconds. Default: now."}}}}}},"responses":{"200":{"description":"What was queued","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","enum":["webhook_replay"]},"endpoint_id":{"type":"string"},"since":{"type":"string","format":"date-time"},"until":{"type":"string","format":"date-time"},"queued":{"type":"integer"},"missing":{"type":"array","items":{"type":"string"},"description":"Event ids past the 30-day event retention."},"more":{"type":"boolean"},"limit":{"type":"integer","example":50}}}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The endpoint is paused","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/webhooks/deliveries":{"get":{"operationId":"listWebhookDeliveries","summary":"List webhook delivery attempts","description":"Every attempt to push an event to your endpoints, newest first, kept 30 days: status code, latency, attempt number and the first 2 KB of the response body.","parameters":[{"name":"endpoint_id","in":"query","schema":{"type":"string"},"description":"One endpoint (we_...), or status_callback for the attempts to send messages' own status callbacks."},{"name":"event_id","in":"query","schema":{"type":"string"},"description":"Every attempt for one event"},{"name":"limit","in":"query","schema":{"type":"integer","maximum":100,"default":25}},{"name":"cursor","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Deliveries, newest first","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/WebhookDelivery"}},"has_more":{"type":"boolean"},"next_cursor":{"type":"string","nullable":true}}}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such endpoint","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/deliverability":{"get":{"operationId":"getDeliverability","summary":"Delivery rates by day, carrier, number, country, category, customer, or error code","description":"Final outcomes (delivered/failed) aggregated over a UTC date range of up to 90 days. Test keys report sandbox traffic, live keys live traffic. `country` is the destination (US or CA), `category` the message category (`none` when unset), `customer` your platform customer id (`none` when unset), `error_code` our failure code (failed messages only, most frequent first), and `tag` the tags you sent with each message (one row per tag, `_untagged` for messages without; a message with two tags counts in both rows, so rows can add up to more than the totals). Those five are recorded from 2026-10-24. Scope: messages:read.","parameters":[{"name":"group_by","in":"query","schema":{"type":"string","enum":["day","carrier","number","country","category","customer","error_code","tag"],"default":"day"}},{"name":"start_date","in":"query","schema":{"type":"string","format":"date"},"description":"Default: 6 days before end_date"},{"name":"end_date","in":"query","schema":{"type":"string","format":"date"},"description":"Default: today (UTC)"}],"responses":{"200":{"description":"Deliverability report","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Deliverability"}}}},"400":{"description":"Invalid range or group_by","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/balance":{"get":{"operationId":"getBalance","summary":"Get the prepaid credit balance and auto-recharge setting","description":"Billable live calls draw the balance down; a call it cannot cover answers 402 insufficient_credits and is not charged. Amounts ending in _micro_usd are millionths of a dollar. Scope: billing.","responses":{"200":{"description":"Balance","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","enum":["balance"]},"balance_micro_usd":{"type":"integer","example":12340000},"balance_usd":{"type":"number","example":12.34},"currency":{"type":"string","enum":["usd"]},"low_balance_usd":{"type":"number","example":5,"description":"credits.low fires when the balance falls below this"},"auto_recharge":{"type":"object","properties":{"enabled":{"type":"boolean"},"threshold_usd":{"type":"number","example":10},"amount_usd":{"type":"number","example":20}}},"top_up_url":{"type":"string"}}}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/spend-limit":{"get":{"operationId":"getSpendLimit","summary":"Get the monthly spend limit and this month's estimated spend","description":"Scope: billing.","responses":{"200":{"description":"Spend limit","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpendLimit"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"operationId":"updateSpendLimit","summary":"Set or remove the monthly spend limit and alert thresholds","description":"Live outbound messages that would exceed the cap are rejected with 403 spend_limit_reached and not charged. Thresholds fire spend.threshold_reached once each per month. Scope: billing.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"monthly_limit_usd":{"type":"number","nullable":true,"minimum":1,"maximum":1000000,"description":"null removes the cap"},"alert_thresholds":{"type":"array","maxItems":5,"items":{"type":"integer","minimum":1,"maximum":100},"example":[50,80,100]}}}}}},"responses":{"200":{"description":"Updated spend limit","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpendLimit"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/usage":{"get":{"operationId":"getUsage","summary":"Get account usage by month, today's counters, and plan limits","description":"Counts for the whole account, so test and live keys read the same numbers. Months and days are UTC; the current month comes first and is month-to-date. Scope: billing.","parameters":[{"name":"months","in":"query","schema":{"type":"integer","minimum":1,"maximum":6,"default":3},"description":"How many months to return, counting back from the current one"}],"responses":{"200":{"description":"Usage","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Usage"}}}},"400":{"description":"Invalid months","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/audit-logs":{"get":{"operationId":"listAuditLogs","summary":"List audit log entries","description":"Account changes (keys, team, webhooks, spend limit, numbers, consent, settings) with actor and IP, newest first. Filters apply before paging. One request scans at most 5,000 entries, so a rarely matching filter can return a short or empty page with has_more true: follow next_cursor until has_more is false. Scope: audit_logs:read.","parameters":[{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":25}},{"name":"cursor","in":"query","schema":{"type":"string"}},{"name":"action","in":"query","description":"One action, or a group prefix with a trailing dot (team., api_key.)","schema":{"type":"string","example":"api_key.created"}},{"name":"actor","in":"query","description":"Actor id (exact) or part of the actor email (case-insensitive)","schema":{"type":"string","example":"dev@example.com"}},{"name":"target_type","in":"query","description":"Target type, e.g. api_key, member, invite, webhook, number, phone","schema":{"type":"string","example":"webhook"}},{"name":"created_after","in":"query","description":"Entries at or after this time (ISO 8601)","schema":{"type":"string","format":"date-time","example":"2026-09-01T00:00:00Z"}},{"name":"created_before","in":"query","description":"Entries at or before this time (ISO 8601); a bare date means the end of that day (UTC)","schema":{"type":"string","format":"date-time","example":"2026-09-30T23:59:59Z"}}],"responses":{"200":{"description":"Audit log entries, newest first","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AuditLog"}},"has_more":{"type":"boolean"},"next_cursor":{"type":"string","nullable":true}}}}}},"400":{"description":"Invalid filter","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/exports/{kind}":{"get":{"operationId":"exportCsv","summary":"Export messages, delivery results, events, opt-outs, webhook deliveries, usage, credit history or the audit log as CSV","description":"Streams a CSV (UTF-8 with BOM). At most 50,000 rows per export (header X-Export-Row-Cap); narrow the date range if you hit it. Cells that would be spreadsheet formulas are prefixed with a single quote.","parameters":[{"name":"kind","in":"path","required":true,"schema":{"type":"string","enum":["messages","deliveries","events","opt_outs","webhook_deliveries","usage","usage_monthly","audit_log","credit_history"]}},{"name":"from","in":"query","description":"ISO date/time or epoch ms (messages, deliveries, events, webhook_deliveries)","schema":{"type":"string","example":"2026-01-01"}},{"name":"to","in":"query","description":"Inclusive; a bare date means the end of that day (UTC)","schema":{"type":"string","example":"2026-01-31"}},{"name":"status","in":"query","description":"messages, deliveries","schema":{"type":"string","enum":["queued","sent","delivered","failed","blocked","received"]}},{"name":"blocked_reason","in":"query","description":"messages, deliveries: blocked messages with this reason","schema":{"type":"string"}},{"name":"direction","in":"query","description":"messages","schema":{"type":"string","enum":["outbound","inbound"]}},{"name":"number","in":"query","description":"messages, deliveries: digits matched against either side","schema":{"type":"string"}},{"name":"to_number","in":"query","description":"messages, deliveries: exact recipient (E.164). `to`/`from` are the date range here.","schema":{"type":"string"}},{"name":"from_number","in":"query","description":"messages, deliveries: exact sender (E.164)","schema":{"type":"string"}},{"name":"customer_id","in":"query","description":"messages, deliveries","schema":{"type":"string"}},{"name":"failure_code","in":"query","description":"messages, deliveries: failed for this reason (the `failure.code` on a message)","schema":{"type":"string","enum":["carrier_filtered","unreachable","invalid_number","opted_out","campaign_mismatch","content_blocked","screen_unavailable","rate_limited","landline","carrier_rejected","spend_limit_reached","insufficient_credits","sender_not_registered","quiet_hours","consent_required","reconfirmation_pending","reconfirmation_expired","held_for_review","review_rejected","review_expired","campaign_paused","sender_rotation","recipient_not_verified","quota_exceeded","destination_not_allowed","account_closed","account_suspended","account_paused","topic_unsubscribed","unknown"]}},{"name":"batch","in":"query","description":"messages, deliveries: batch id (bc_...)","schema":{"type":"string"}},{"name":"q","in":"query","description":"messages: body/failure text; events: id/payload text","schema":{"type":"string"}},{"name":"type","in":"query","description":"events, webhook_deliveries","schema":{"type":"string","example":"message.failed"}},{"name":"endpoint_id","in":"query","description":"webhook_deliveries (default: all endpoints)","schema":{"type":"string"}},{"name":"ok","in":"query","description":"webhook_deliveries","schema":{"type":"boolean"}},{"name":"months","in":"query","description":"usage: daily rows for the last N months","schema":{"type":"integer","minimum":1,"maximum":12,"default":3}},{"name":"action","in":"query","description":"audit_log: one action, or a group prefix with a trailing dot (team.)","schema":{"type":"string"}},{"name":"actor","in":"query","description":"audit_log: actor id or part of the actor email","schema":{"type":"string"}},{"name":"target_type","in":"query","description":"audit_log","schema":{"type":"string"}},{"name":"created_after","in":"query","description":"audit_log: same as on GET /audit-logs (from is accepted too)","schema":{"type":"string"}},{"name":"created_before","in":"query","description":"audit_log: same as on GET /audit-logs (to is accepted too)","schema":{"type":"string"}}],"responses":{"200":{"description":"CSV body","content":{"text/csv":{"schema":{"type":"string"}}}},"400":{"description":"Unknown kind or invalid filter","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/track":{"post":{"operationId":"trackEvent","summary":"Track a customer event","description":"Records something a person did in your app and starts or advances the automations listening for it. Pass phone, user_id, or both; a call with both links them so later events need only user_id. Test keys reach sandbox automations, live keys live ones. Supports Idempotency-Key. 100 calls per 10 seconds per key. Scope: automations. (GET /events is the webhook event log, a different thing.)","parameters":[{"name":"Idempotency-Key","in":"header","schema":{"type":"string","maxLength":255}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["event"],"properties":{"event":{"type":"string","maxLength":64,"example":"trial_started","description":"Letters, digits, and _ . : -"},"user_id":{"type":"string","maxLength":128,"example":"123"},"phone":{"type":"string","example":"+14155550132"},"properties":{"type":"object","description":"Up to 20 scalar values; usable as merge fields and in conditions.","example":{"plan":"pro"}}}}}}},"responses":{"201":{"description":"Recorded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrackedEvent"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/schedules":{"get":{"operationId":"listSchedules","summary":"List recurring schedules (at most 100 per account)","description":"Scope: messages:read.","responses":{"200":{"description":"Schedules, newest first","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Schedule"}},"has_more":{"type":"boolean"},"next_cursor":{"type":"string","nullable":true}}}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"createSchedule","summary":"Create a recurring schedule","description":"Sends one message to one number on a repeating wall-clock time in an IANA time zone, correct across daylight saving. Each occurrence goes through the same checks as POST /messages at the moment it fires (opt-out, sender registration, quota, spend limit, content screening) and is a normal message with `schedule_id`. A local time that does not exist (spring forward) runs after the gap that day; one that occurs twice (fall back) runs once, the first time. An occurrence reached more than 1 hour late is skipped (`skipped_missed`), never sent late. Limits: 25 active and 100 total schedules per account, at most one message per local day per schedule. Supports the Idempotency-Key header. Scope: messages:send.","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","maxLength":255},"description":"Makes retries safe for 24 hours: the same key and body replays the original successful response (with Idempotent-Replayed: true); a different body returns 409. Failed requests release the key."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScheduleInput"}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Schedule"}}}},"400":{"description":"Invalid request, or a limit reached","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Idempotency conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/schedules/{id}":{"get":{"operationId":"getSchedule","summary":"Retrieve a schedule, with its recent occurrences","description":"Scope: messages:read.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The schedule","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Schedule"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"operationId":"updateSchedule","summary":"Change, pause or resume a schedule","description":"`active: false` pauses; `active: true` resumes from now (nothing that came due while paused is sent). Any of to, from, body, repeat (the whole object), start_date, end_date, max_occurrences can be changed in the same call. Scope: messages:send.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ScheduleInput"}],"properties":{"active":{"type":"boolean","description":"false pauses, true resumes."}},"required":[]}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Schedule"}}}},"400":{"description":"Invalid request, or the active-schedule limit reached","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Cannot resume: no occurrences left (invalid_state)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"operationId":"deleteSchedule","summary":"Delete a schedule","description":"Stops it for good and deletes its occurrence records. Messages it already sent stay in the message log. Scope: messages:send.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"object":{"type":"string","enum":["schedule"]},"deleted":{"type":"boolean"}}}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/automations":{"get":{"operationId":"listAutomations","summary":"List automations (at most 50 per account)","description":"Scope: automations.","responses":{"200":{"description":"Automations, newest first","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Automation"}},"has_more":{"type":"boolean"},"next_cursor":{"type":"string","nullable":true}}}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"createAutomation","summary":"Create an automation (as a draft)","description":"The response lists `issues`: everything that stops the flow from being activated. Scope: automations.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AutomationInput"}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Automation"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/automations/{id}":{"get":{"operationId":"getAutomation","summary":"Retrieve an automation","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Automation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Automation"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"operationId":"updateAutomation","summary":"Edit an automation","description":"Editing an active automation publishes a new version at once and must leave it valid; runs in progress finish on the version they started with. Pause first to save work in progress.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AutomationInput"}}}},"responses":{"200":{"description":"Updated automation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Automation"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"operationId":"deleteAutomation","summary":"Delete an automation","description":"Runs in progress end with end_reason flow_deleted the next time they wake; nothing more is sent.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"object":{"type":"string","enum":["automation"]},"deleted":{"type":"boolean"}}}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/automations/{id}/activate":{"post":{"operationId":"activateAutomation","summary":"Activate an automation","description":"Answers 400 with `issues` when the flow is not ready. At most 20 automations can be active at once.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Active automation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Automation"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/automations/{id}/pause":{"post":{"operationId":"pauseAutomation","summary":"Pause an automation","description":"Stops new runs and holds runs in progress where they are until it is activated again.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Paused automation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Automation"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/automations/identities/{user_id}":{"delete":{"operationId":"unlinkAutomationIdentity","summary":"Forget the phone number linked to a user_id","description":"Removes the user_id to phone link that a tracked event carrying both taught us (see POST /v1/track). Later events with only that user_id start nothing until a call with both links them again. Runs, tracked events and consent records are not changed. Audit-logged as automation.identity_unlinked. Use it to honour a deletion request for one person.","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","maxLength":128},"description":"Your own id for the person, as passed to POST /v1/track."}],"responses":{"200":{"description":"Unlinked","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","enum":["automation_identity"]},"user_id":{"type":"string"},"unlinked":{"type":"boolean","enum":[true]}},"required":["object","user_id","unlinked"]}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/automations/{id}/runs":{"get":{"operationId":"listAutomationRuns","summary":"List an automation's runs, newest first","description":"Runs are kept for 30 days after they end.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":25}},{"name":"cursor","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Runs","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AutomationRun"}},"has_more":{"type":"boolean"},"next_cursor":{"type":"string","nullable":true}}}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/automations/{id}/runs/{run_id}":{"get":{"operationId":"getAutomationRun","summary":"Retrieve a run with its timeline","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"run_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Run","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AutomationRun"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"operationId":"cancelAutomationRun","summary":"Cancel a run in progress","description":"The run stays readable, ended with end_reason canceled. 409 if it is advancing at this moment.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"run_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Canceled run","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AutomationRun"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/templates":{"get":{"operationId":"listTemplates","summary":"List message templates (at most 100 per account)","responses":{"200":{"description":"Templates by name","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Template"}},"has_more":{"type":"boolean"},"next_cursor":{"type":"string","nullable":true}}}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"createTemplate","summary":"Create a template with {{merge}} fields","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name","body"],"properties":{"name":{"type":"string","maxLength":60},"body":{"type":"string","maxLength":1600,"example":"Hi {{first_name}}, your order shipped."}}}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Template"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/templates/{id}":{"get":{"operationId":"getTemplate","summary":"Retrieve a template","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Template","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Template"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"operationId":"updateTemplate","summary":"Rename or edit a template","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","maxLength":60},"body":{"type":"string","maxLength":1600}}}}}},"responses":{"200":{"description":"Updated template","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Template"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"operationId":"deleteTemplate","summary":"Delete a template","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"object":{"type":"string"},"deleted":{"type":"boolean"}}}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/link-domains":{"get":{"operationId":"listLinkDomains","summary":"List link domains","description":"Your short-link domains (at most 3) and where each stands. Scope: links.","responses":{"200":{"description":"Your link domains","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/LinkDomain"}},"has_more":{"type":"boolean"},"next_cursor":{"type":"string","nullable":true}}}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"createLinkDomain","summary":"Add a link domain","description":"Add a subdomain of your own (links.example.com) to carry short links (`shorten_links` on POST /messages). Carriers filter shared shorteners, so short links only ever go on your own domain. The response lists two DNS records to add: a CNAME to our link host and a TXT that proves the domain is yours. Then call POST /link-domains/{id}/verify. Once both records check out, verify turns it on (`active`). A bare domain, one of ours, a public shortener or a look-alike of a known brand is refused. Recorded in the audit log as link_domain.created. Scope: links.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["domain"],"properties":{"domain":{"type":"string","example":"links.example.com"}}}}}},"responses":{"201":{"description":"Added, with the DNS records to create","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinkDomain"}}}},"400":{"description":"Not a usable domain, or 3 already","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Already on this account or another (invalid_state)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/link-domains/{id}":{"get":{"operationId":"getLinkDomain","summary":"Retrieve a link domain","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The link domain","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinkDomain"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"operationId":"deleteLinkDomain","summary":"Delete a link domain","description":"Its short links stop opening at once (404); sent messages keep their text and click counts. Recorded in the audit log as link_domain.deleted. Scope: links.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Deleted"},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/link-domains/{id}/verify":{"post":{"operationId":"verifyLinkDomain","summary":"Check a link domain DNS records","description":"Looks up the CNAME and TXT records now. Both found: the domain is turned on (`active`); the certificate can take a minute or two. Always 200 with the domain; `verification.problem` says what is still missing (DNS changes can take a few minutes). A record proxied by its DNS host shows no CNAME: turn the proxy off for it. Scope: links.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The domain, with what this check found","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/LinkDomain"},{"type":"object","properties":{"verification":{"type":"object","properties":{"cname":{"type":"boolean"},"txt":{"type":"boolean"},"problem":{"type":"string","nullable":true}}}}}]}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/test/inbound":{"post":{"operationId":"simulateInbound","summary":"Simulate an inbound SMS (test keys only)","description":"Runs through the real inbound pipeline and emits `message.received` with `simulated: true`. Once the account has live access, STOP, START, YES and HELP change consent only when `from` is a +1 500 555 number or one of your verified numbers; from any other number the message is handled as an ordinary one and the response carries a `notice`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["from","body"],"properties":{"to":{"type":"string","description":"One of your sandbox numbers (never a live number). Optional while the account has one sandbox number."},"from":{"type":"string"},"body":{"type":"string"}}}}}},"responses":{"201":{"description":"Simulated inbound message, with `notice` when consent was left unchanged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Message"}}}},"401":{"description":"Missing, malformed, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Live keys not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"Your API key, e.g. Authorization: Bearer ssms_sk_test_.... Keys may be restricted to scopes (messages:send, messages:read, numbers:read, numbers:write, verify, consent, lookup, webhooks, webhooks:write, customers, contacts, registrations, billing, automations, audit_logs:read, links, keys); a restricted key outside its scopes gets 403 insufficient_scope. Keys without scopes have full access."}},"schemas":{"Message":{"type":"object","properties":{"id":{"type":"string","example":"msg_a1B2c3D4e5F6g7H8"},"object":{"type":"string","enum":["message"]},"to":{"type":"string","example":"+15005550006"},"from":{"type":"string","example":"+15005550100"},"body":{"type":"string"},"direction":{"type":"string","enum":["outbound","inbound"]},"status":{"type":"string","enum":["queued","sent","delivered","failed","blocked","received"],"description":"`blocked`: refused by one of our checks before any carrier saw it (opt-out, quiet hours, unregistered sender, content, allowance, spend limit, credits). Final, never charged; `failure` says why. `failed`: accepted, then not delivered."},"test":{"type":"boolean"},"simulated":{"type":"boolean","description":"True when nothing was handed to a carrier: every test-key message except one sent to your own verified phone. Always false on live messages. A simulated message still reaches `delivered`."},"notice":{"type":"string","description":"Only on the response to POST /messages. Present when a test-key send to a real-looking number was simulated: says nothing was sent and how to send for real."},"schedule_id":{"type":"string","description":"The recurring schedule this message is an occurrence of. Present only when set."},"tags":{"type":"array","items":{"type":"string"},"example":["spring-sale"],"description":"The labels sent with the message (`tags` on POST /messages), lowercased. Present only when set."},"category":{"type":"string","enum":["marketing","transactional","otp"],"description":"How the message was classified. Present when you sent one, or when it was classified as marketing."},"disclosures_added":{"type":"array","items":{"type":"string","enum":["brand","opt_out"]},"description":"Wording we added to a message from a number linked to a registration ([what we add](/docs/compliance-checks#what-we-add)): `brand`, your business name in front of a person's first message; `opt_out`, \" Reply STOP to opt out.\" on marketing that had none. `body` is the text as sent. Present only when we added something."},"deferred":{"type":"object","description":"Present when the message was held for quiet hours and sent when the window opened.","properties":{"reason":{"type":"string","enum":["quiet_hours"]},"held_at":{"type":"string","format":"date-time"}}},"redacted_at":{"type":"string","format":"date-time","description":"When the message was redacted (POST /messages/{id}/redact): `body` is empty and `media` is gone. Present only then."},"links":{"type":"array","description":"Links shortened with `shorten_links`, in the order they appear. On the send response and GET /messages/{id} (with clicks so far); not on list pages. `body` is the text as sent, with the short links.","items":{"type":"object","properties":{"url":{"type":"string","description":"The link as you wrote it."},"short_url":{"type":"string","example":"https://links.example.com/Ab3dEf9h"},"clicks":{"type":"integer","description":"Opens by people. Link previews (a messaging app fetching the link to draw a preview) are not counted."},"first_clicked_at":{"type":"string","format":"date-time","nullable":true},"last_clicked_at":{"type":"string","format":"date-time","nullable":true}}}},"created_at":{"type":"string","format":"date-time"},"media":{"type":"array","items":{"type":"string","format":"uri"},"description":"MMS attachments (inbound today)."},"sent_by":{"type":"string","description":"Console user or API key that composed it."},"failure_reason":{"type":"string","description":"The carrier's raw text for a failed send. Kept for compatibility; see `failure`."},"attempts":{"type":"integer","description":"Live sends: carrier submissions so far. More than 1 means it was retried."},"next_attempt_at":{"type":"string","format":"date-time","description":"Present while queued for an automatic carrier retry."},"receipt_status":{"type":"string","enum":["missing"],"description":"The carrier accepted the message but sent no delivery report within 72 hours. status stays sent."},"customer_id":{"type":"string","description":"The customer this is attributed to. Present only when set."},"timeline":{"type":"array","description":"Every status transition, oldest first. Inbound messages have a single `received`.","items":{"type":"object","properties":{"status":{"type":"string","enum":["accepted","validated","queued","sent_to_carrier","carrier_accepted","retry_scheduled","delivered","failed","blocked","received"]},"at":{"type":"string","format":"date-time"},"attempt":{"type":"integer","description":"Carrier attempt this step belongs to (from sent_to_carrier on)."}}}},"segments":{"type":"integer","description":"Parts the body is split into (GSM-7: 160, then 153 each; UCS-2: 70, then 67).","example":1},"encoding":{"type":"string","enum":["gsm7","ucs2"]},"price":{"type":"object","nullable":true,"description":"What this message costs, in USD: the outbound rate per segment, plus the recipient's carrier fee per segment, at cost, as its own line. Null on messages sent before prices were recorded.","properties":{"total":{"type":"number","example":0.0082},"currency":{"type":"string","enum":["usd"]},"breakdown":{"type":"array","items":{"type":"object","properties":{"label":{"type":"string"},"amount":{"type":"number"}}}}}},"destination_carrier":{"type":"string","nullable":true,"description":"The recipient's mobile carrier, when a cached lookup knows it."},"failure":{"allOf":[{"$ref":"#/components/schemas/Failure"}],"nullable":true,"description":"Null unless status is `failed`."}}},"Batch":{"type":"object","properties":{"id":{"type":"string","example":"bc_a1B2c3D4e5F6"},"object":{"type":"string","enum":["batch"]},"name":{"type":"string"},"status":{"type":"string","enum":["scheduled","sending","paused","complete","canceled"]},"from":{"type":"string"},"body":{"type":"string"},"source":{"type":"string","enum":["api","csv","contacts"]},"test":{"type":"boolean"},"scheduled_at":{"type":"string","format":"date-time","nullable":true},"created_at":{"type":"string","format":"date-time"},"completed_at":{"type":"string","format":"date-time","nullable":true},"counts":{"type":"object","description":"total = queued + sent + failed + opted_out + canceled, always. \"sent\" = accepted by the carrier; what happened next (delivered, failed, replies) is GET /v1/batches/{id}/results. \"opted_out\" also counts recipients unsubscribed from the batch topic.","properties":{"total":{"type":"integer"},"queued":{"type":"integer"},"sent":{"type":"integer"},"failed":{"type":"integer"},"opted_out":{"type":"integer"},"canceled":{"type":"integer"}}},"rejected":{"type":"object","nullable":true,"description":"Rows dropped at validation (not part of total), by reason."},"variables":{"type":"array","items":{"type":"string"}},"retries":{"type":"integer"}}},"BatchRecipient":{"type":"object","properties":{"id":{"type":"string","example":"r00042"},"object":{"type":"string","enum":["batch_recipient"]},"to":{"type":"string"},"status":{"type":"string","enum":["queued","paused","sent","failed","skipped","canceled"]},"error":{"type":"string","nullable":true,"example":"carrier_error"},"detail":{"type":"string","nullable":true},"retryable":{"type":"boolean"},"attempts":{"type":"integer"},"message_id":{"type":"string","nullable":true},"variables":{"type":"object","additionalProperties":{"type":"string"}}}},"BatchValidation":{"type":"object","properties":{"object":{"type":"string","enum":["batch_validation"]},"counts":{"type":"object","properties":{"rows":{"type":"integer"},"valid":{"type":"integer"},"invalid":{"type":"integer"},"duplicates":{"type":"integer"},"opted_out":{"type":"integer"},"topic_unsubscribed":{"type":"integer","description":"Unsubscribed from the batch topic_id; 0 without a topic"},"missing_variable":{"type":"integer"},"landline":{"type":"integer"},"empty_after_merge":{"type":"integer"}}},"unknown_variables":{"type":"array","items":{"type":"string"}},"segments":{"type":"object","properties":{"total":{"type":"integer"},"max":{"type":"integer"},"encoding":{"type":"string","enum":["GSM-7","UCS-2"]}}},"estimated_cost_usd":{"type":"number"},"preview":{"type":"array","items":{"type":"object","properties":{"to":{"type":"string"},"body":{"type":"string"},"segments":{"type":"integer"}}}},"rejected":{"type":"array","items":{"type":"object","properties":{"row":{"type":"integer","description":"0-based index into recipients"},"to":{"type":"string"},"reason":{"type":"string","enum":["invalid","duplicate","opted_out","topic_unsubscribed","missing_variable","landline","empty_after_merge"]},"detail":{"type":"string"}}}},"consent":{"type":"object","description":"Dry run only. What the consent gate would decide at send time for the first 1,000 accepted recipients: `no_opt_in` would be refused `consent_required` (marketing without an opt-in on file, or a recurring message from a registered number). Informational; every message is still checked when it sends.","properties":{"checked":{"type":"integer"},"unchecked":{"type":"integer","description":"Accepted recipients past the first 1,000, not checked"},"no_opt_in":{"type":"integer"},"no_opt_in_sample":{"type":"array","items":{"type":"string"},"description":"Up to 20 of them"},"unknown":{"type":"integer","description":"Consent could not be read; the send retries these"}}}}},"ApiKey":{"type":"object","properties":{"id":{"type":"string"},"object":{"type":"string","enum":["api_key"]},"name":{"type":"string"},"mode":{"type":"string","enum":["test","live"]},"prefix":{"type":"string","description":"The start of the secret, for recognizing it"},"scopes":{"type":"array","items":{"type":"string"},"description":"Empty = full access"},"status":{"type":"string","enum":["active","paused","revoked"]},"created_at":{"type":"string","format":"date-time"},"last_used_at":{"type":"string","format":"date-time","nullable":true}}},"ScheduledMessage":{"type":"object","properties":{"id":{"type":"string","example":"job_1759400000000a1B2c3D4e5"},"object":{"type":"string","enum":["scheduled_message"]},"to":{"type":"string"},"from":{"type":"string"},"body":{"type":"string"},"scheduled_at":{"type":"string","format":"date-time","description":"When it will send. The same name as the request field."},"run_at":{"type":"string","format":"date-time","deprecated":true,"description":"Deprecated alias of scheduled_at (same value), kept for existing integrations."},"deferred":{"type":"object","description":"Present on the response when the message was held for quiet hours.","properties":{"reason":{"type":"string","enum":["quiet_hours"]},"recipient_local_time":{"type":"string","example":"22:41","description":"24-hour wall clock in `timezone`."},"timezone":{"type":"string","example":"America/New_York","description":"IANA zone read from the area code of the number."},"send_after":{"type":"string","format":"date-time"},"category":{"type":"string","enum":["marketing"]},"category_basis":{"type":"string","enum":["request","broadcast","sender_registration"]}}},"status":{"type":"string","enum":["queued","canceled"]},"batch_id":{"type":"string","nullable":true}}},"ScheduleRepeat":{"type":"object","required":["frequency","time","timezone"],"properties":{"frequency":{"type":"string","enum":["daily","weekly","monthly"]},"time":{"type":"string","example":"08:00","description":"24-hour wall-clock time, HH:MM, in `timezone`."},"timezone":{"type":"string","example":"America/New_York","description":"IANA time zone."},"days_of_week":{"type":"array","items":{"type":"string","enum":["sun","mon","tue","wed","thu","fri","sat"]},"description":"Weekly only, required: which weekdays."},"day_of_month":{"type":"integer","minimum":1,"maximum":31,"description":"Monthly only, required. Months without that day run on their last day."}}},"ScheduleInput":{"type":"object","required":["to","body","repeat"],"properties":{"to":{"type":"string","example":"+14155550132"},"from":{"type":"string","description":"One of your numbers. Optional when the account has exactly one number for the key's mode."},"body":{"type":"string","maxLength":1600},"repeat":{"$ref":"#/components/schemas/ScheduleRepeat"},"start_date":{"type":"string","format":"date","nullable":true,"description":"First local date it may run. Defaults to today."},"end_date":{"type":"string","format":"date","nullable":true,"description":"Last local date it may run (inclusive)."},"max_occurrences":{"type":"integer","minimum":1,"nullable":true,"description":"The schedule completes after this many messages have been sent."},"category":{"type":"string","nullable":true,"enum":["marketing","transactional","otp"],"description":"Optional. It can make each occurrence stricter (`marketing`), never looser: the sending number's registration and the wording decide ([which messages are marketing](/docs/compliance-checks#which-messages-are-marketing))."},"quiet_hours":{"type":"string","nullable":true,"enum":["block","defer"],"description":"Optional. What to do with a message in the recipient's quiet hours: `block` refuses it (403 quiet_hours), `defer` holds it until their next 8:00am. Omitted: an occurrence in quiet hours is held until the recipient's 8:00am and recorded as `deferred`."}}},"Schedule":{"type":"object","properties":{"id":{"type":"string","example":"sch_a1B2c3D4e5F6g7H8"},"object":{"type":"string","enum":["schedule"]},"to":{"type":"string"},"from":{"type":"string"},"body":{"type":"string"},"repeat":{"$ref":"#/components/schemas/ScheduleRepeat"},"start_date":{"type":"string","format":"date","nullable":true},"end_date":{"type":"string","format":"date","nullable":true},"max_occurrences":{"type":"integer","nullable":true},"category":{"type":"string","nullable":true,"enum":["marketing","transactional","otp",null]},"quiet_hours":{"type":"string","nullable":true,"enum":["block","defer",null]},"active":{"type":"boolean"},"status":{"type":"string","enum":["active","paused","completed"]},"paused_reason":{"type":"string","nullable":true,"enum":["manual","recipient_opted_out","repeated_failures","sender_unavailable",null]},"next_run_at":{"type":"string","format":"date-time","nullable":true,"description":"The next occurrence (UTC). Null unless active."},"last_run_at":{"type":"string","format":"date-time","nullable":true},"last_status":{"type":"string","nullable":true,"enum":["sent","skipped_opted_out","skipped_missed","deferred","failed",null]},"last_error":{"type":"string","nullable":true,"description":"Why the last occurrence was skipped or failed."},"last_message_id":{"type":"string","nullable":true},"counts":{"type":"object","properties":{"sent":{"type":"integer"},"skipped":{"type":"integer"},"failed":{"type":"integer"}}},"test":{"type":"boolean","description":"Made with a test key: only ever sends in the sandbox."},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"recent_occurrences":{"type":"array","description":"Only on GET /schedules/{id}: the last 10 occurrences, newest first.","items":{"type":"object","properties":{"date":{"type":"string","format":"date","description":"The local calendar date of the occurrence."},"due_at":{"type":"string","format":"date-time","nullable":true},"status":{"type":"string","enum":["sent","skipped_opted_out","skipped_missed","deferred","failed"]},"message_id":{"type":"string","nullable":true},"error":{"type":"string","nullable":true}}}}}},"Failure":{"type":"object","description":"Why a message was not delivered, readable. See /docs/errors#delivery-failures.","properties":{"code":{"type":"string","enum":["carrier_filtered","unreachable","invalid_number","opted_out","campaign_mismatch","content_blocked","screen_unavailable","rate_limited","landline","carrier_rejected","spend_limit_reached","insufficient_credits","sender_not_registered","quiet_hours","consent_required","reconfirmation_pending","reconfirmation_expired","held_for_review","review_rejected","review_expired","campaign_paused","sender_rotation","recipient_not_verified","quota_exceeded","destination_not_allowed","account_closed","account_suspended","account_paused","topic_unsubscribed","unknown"]},"title":{"type":"string","example":"Filtered as spam"},"explanation":{"type":"string"},"action":{"type":"string","description":"What to do next; starts with \"Recommended:\"."},"carrier_code":{"type":"string","nullable":true,"description":"The carrier's own error code, when it sent one."}}},"Verification":{"type":"object","properties":{"id":{"type":"string","example":"ver_a1B2c3D4e5F6g7H8"},"object":{"type":"string","enum":["verification"]},"channel":{"type":"string","enum":["sms","email"],"description":"The channel the current code went out on"},"phone":{"type":"string","nullable":true,"description":"null on an email verification"},"email":{"type":"string","nullable":true,"description":"null until a code is emailed"},"status":{"type":"string","enum":["pending","approved","expired","max_attempts","blocked"]},"attempts":{"type":"integer"},"test":{"type":"boolean"},"charged":{"type":"boolean"},"created_at":{"type":"string","format":"date-time"},"expires_at":{"type":"string","format":"date-time"},"fallback_at":{"type":"string","format":"date-time","description":"When the verification moved from text to email. Present only then."},"customer_id":{"type":"string","description":"The customer this is attributed to. Present only when set."}}},"Error":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["invalid_api_key","key_paused","number_purchase_held","tenant_suspended","account_closed","email_unconfirmed","account_paused","live_access_required","test_mode_only","forbidden","not_found","invalid_request","rate_limited","quota_exceeded","idempotency_conflict","idempotency_in_progress","verification_blocked","verification_not_found","verification_already_approved","channel_unavailable","insufficient_scope","spend_limit_reached","insufficient_credits","sender_not_registered","recipient_opted_out","recipient_not_verified","quiet_hours","consent_required","reconfirmation_pending","reconfirmation_expired","campaign_paused","campaign_mismatch","sender_rotation","held_for_review","review_rejected","screen_unavailable","carrier_error","invalid_state","attestation_required","internal_error"]},"message":{"type":"string"},"param":{"type":"string","description":"The offending parameter, when there is one"},"required_scope":{"type":"string","description":"insufficient_scope only: the scope this endpoint needs"},"balance_micro_usd":{"type":"integer","description":"insufficient_credits only: the credit balance, in millionths of a dollar"},"required_micro_usd":{"type":"integer","description":"insufficient_credits only: what the call costs, in millionths of a dollar"},"top_up_url":{"type":"string","description":"insufficient_credits only: where to add credits"},"held":{"type":"boolean","description":"held_for_review and reconfirmation_pending: whether this message is held to be sent later (false: it was not kept)"},"fingerprint":{"type":"string","description":"held_for_review and review_rejected: the id of this kind of message (fp_…); every message of the same kind shares it"},"request_id":{"type":"string","description":"Id of this request (req_…), also returned in the X-Request-Id header on every response. Find it in the console under Logs, and quote it to support."}}}}},"Number":{"type":"object","properties":{"id":{"type":"string","example":"+15005550132"},"object":{"type":"string","enum":["number"]},"phone_number":{"type":"string"},"status":{"type":"string","enum":["active","released"]},"mode":{"type":"string","enum":["test","live"]},"created_at":{"type":"string","format":"date-time"},"customer_id":{"type":"string","nullable":true,"description":"The customer this number is assigned to"},"label":{"type":"string","nullable":true},"tags":{"type":"array","items":{"type":"string"}},"pools":{"type":"array","items":{"type":"string"},"description":"Pool ids"},"sender":{"type":"object","nullable":true,"description":"How the number stands with the carriers. null on sandbox numbers and on numbers that need no registration (toll-free, Canadian). Only an active number can text anyone; in every other state it can text only your verified numbers.","properties":{"state":{"type":"string","enum":["test_only","pending","active","action_needed"],"description":"test_only: no registration submitted. pending: a registration is in review, or approved and being linked. active: linked to an approved registration. action_needed: see reason."},"registration_id":{"type":"string","nullable":true,"description":"The registration it is (being) linked to"},"reason":{"type":"string","nullable":true,"description":"What to do, in plain words, when state is action_needed"},"updated_at":{"type":"string","format":"date-time"}}},"toll_free":{"type":"object","nullable":true,"description":"Toll-free verification, on live toll-free numbers only (null on every other number). Only a verified number texts anyone; its texts and STOP/HELP replies name `business`. Until the account has live access it texts only your verified numbers, verified or not; the carriers verifying it also verifies its business with us and gives a sandbox account live access.","properties":{"status":{"type":"string","enum":["not_started","pending","verified","rejected"]},"business":{"type":"string","nullable":true,"description":"The business the verification names"},"use_case":{"type":"string","nullable":true},"verified_at":{"type":"string","format":"date-time","nullable":true}}}}},"ContactInput":{"type":"object","required":["phone_number"],"properties":{"phone_number":{"type":"string","description":"E.164, US or Canada"},"name":{"type":"string","maxLength":120,"description":"Display name. Defaults to first + last name."},"tags":{"type":"array","maxItems":20,"items":{"type":"string","maxLength":40}},"fields":{"type":"object","additionalProperties":{"type":"string","maxLength":500},"description":"Up to 20 string values (account id, plan, location...), usable in segments and as {{field:key}} merge fields"},"notes":{"type":"string","maxLength":2000},"first_name":{"type":"string","maxLength":80},"last_name":{"type":"string","maxLength":80},"email":{"type":"string","maxLength":254},"company":{"type":"string","maxLength":120},"state":{"type":"string","maxLength":40,"example":"TN"},"source":{"type":"string","maxLength":60,"description":"Where the contact came from. Defaults to \"api\"."},"opt_in":{"type":"object","description":"Import only (POST /v1/contacts/import). Stored as consent evidence; never lifts an opt-out.","properties":{"status":{"type":"boolean","description":"true = opted in; false = unsubscribed (recorded as an opt-out)"},"at":{"type":"string","description":"When they opted in: ISO timestamp, date, or epoch ms"},"source":{"type":"string","description":"web_form, keyword, paper, checkout or verbal are stored as proof; anything else is kept as a note"},"campaign_id":{"type":"string","example":"reg_a1B2c3D4e5F6","description":"The registration the opt-in is for. A marketing or mixed registration needs at, a recognised source, and page_url for a web form, or the row is skipped"},"page_url":{"type":"string","format":"uri","description":"The page a web-form opt-in was collected on"}}}}},"Contact":{"type":"object","properties":{"id":{"type":"string","example":"ct_a1B2c3D4e5F6"},"object":{"type":"string","enum":["contact"]},"phone_number":{"type":"string","example":"+14155550132"},"name":{"type":"string","nullable":true},"tags":{"type":"array","items":{"type":"string"},"description":"Free-form labels; they double as broadcast audiences"},"fields":{"type":"object","additionalProperties":{"type":"string"}},"notes":{"type":"string","nullable":true},"first_name":{"type":"string","nullable":true},"last_name":{"type":"string","nullable":true},"email":{"type":"string","nullable":true},"company":{"type":"string","nullable":true},"state":{"type":"string","nullable":true},"source":{"type":"string","nullable":true},"import_id":{"type":"string","nullable":true,"description":"The last import that touched this contact"},"opt_in":{"type":"object","nullable":true,"properties":{"source":{"type":"string","nullable":true},"collected_at":{"type":"string","format":"date-time","nullable":true}}},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"consent_status":{"type":"string","enum":["opted_out","opted_in","no_record"],"description":"Only on retrieve"},"topics":{"type":"array","items":{"$ref":"#/components/schemas/TopicSubscription"},"description":"Only on retrieve"},"segments":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}}},"description":"Only on retrieve"}}},"TopicSubscription":{"type":"object","properties":{"topic_id":{"type":"string","example":"tp_marketing"},"name":{"type":"string","example":"Marketing"},"subscribed":{"type":"boolean","description":"true unless an unsubscribe is recorded"},"updated_at":{"type":"string","format":"date-time","nullable":true},"source":{"type":"string","nullable":true}}},"Topic":{"type":"object","properties":{"id":{"type":"string","example":"tp_marketing"},"object":{"type":"string","enum":["topic"]},"name":{"type":"string"},"description":{"type":"string","nullable":true},"builtin":{"type":"boolean"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"List":{"type":"object","properties":{"id":{"type":"string","example":"lst_a1B2c3D4e5F6"},"object":{"type":"string","enum":["list"]},"name":{"type":"string","example":"Spring promo"},"description":{"type":"string","nullable":true},"integration_id":{"type":"string","nullable":true,"description":"Set when an integration sync owns the list"},"contact_count":{"type":"integer"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"ListInput":{"type":"object","properties":{"name":{"type":"string","maxLength":80,"example":"Spring promo"},"description":{"type":"string","maxLength":200,"nullable":true}}},"ListMembersInput":{"type":"object","properties":{"contact_ids":{"type":"array","items":{"type":"string"},"example":["ct_a1B2c3D4e5F6"]},"phone_numbers":{"type":"array","items":{"type":"string"},"example":["+14155550132"]}}},"SegmentRule":{"type":"object","required":["field"],"properties":{"field":{"type":"string","enum":["tag","first_name","last_name","name","email","company","state","source","field","import","status","topic","created_at","last_message_at","last_reply_at","batch","event","list"],"description":"Attributes: tag, the name and profile fields, source, field (a custom field), import, status (opt-out), topic, list (in a static list), created_at. Behaviour: last_message_at (last text either way), last_reply_at (last text from them), batch (what happened to them in one batch), event (a tracked event; the newest 20,000 events are searched)."},"key":{"type":"string","description":"The custom field name (field), topic id (topic), batch id (batch), or event name (event)"},"op":{"type":"string","enum":["is","is_not","contains","not_contains","exists","not_exists","before","after"],"description":"Text fields take is / is_not / contains / not_contains / exists / not_exists; tag takes is / is_not / exists / not_exists; status, topic, import, batch and list take is / is_not; created_at takes before / after; last_message_at, last_reply_at and event take after (at or after the time), before (it happened, and the last time was before it), exists (ever) and not_exists (never). Comparisons ignore case."},"value":{"type":"string","description":"status: subscribed or opted_out. topic: subscribed or unsubscribed. import: an import id. list: a list id (lst_...). batch: included, sent, failed, skipped or replied (sent, then texted the batch's number after it was created). Times (created_at, last_message_at, last_reply_at, event): an ISO date, or a window back from now such as 7d or 24h, read each time the segment is used."}}},"SegmentInput":{"type":"object","properties":{"name":{"type":"string","maxLength":80},"description":{"type":"string","maxLength":200,"nullable":true},"match":{"type":"string","enum":["all","any"],"default":"all"},"rules":{"type":"array","maxItems":10,"items":{"$ref":"#/components/schemas/SegmentRule"},"example":[{"field":"tag","op":"is","value":"vip"},{"field":"state","op":"is","value":"TN"},{"field":"field","key":"plan","op":"is","value":"pro"},{"field":"topic","key":"tp_marketing","op":"is","value":"subscribed"}]}}},"Segment":{"type":"object","properties":{"id":{"type":"string","example":"seg_a1B2c3D4e5F6"},"object":{"type":"string","enum":["segment"]},"name":{"type":"string"},"description":{"type":"string","nullable":true},"match":{"type":"string","enum":["all","any"]},"rules":{"type":"array","items":{"$ref":"#/components/schemas/SegmentRule"}},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"BatchResults":{"type":"object","properties":{"object":{"type":"string","enum":["batch_results"]},"batch_id":{"type":"string"},"recipients":{"type":"integer"},"attempted":{"type":"integer"},"delivered":{"type":"integer"},"failed":{"type":"integer"},"pending":{"type":"integer"},"no_receipt":{"type":"integer"},"skipped":{"type":"object","properties":{"opted_out":{"type":"integer"},"topic_unsubscribed":{"type":"integer"}}},"canceled":{"type":"integer"},"queued":{"type":"integer"},"replies":{"type":"integer"},"repliers":{"type":"integer"},"stops":{"type":"integer"},"spent_usd":{"type":"number"},"failures":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"title":{"type":"string"},"explanation":{"type":"string"},"action":{"type":"string"},"count":{"type":"integer"}}}},"reply_window_hours":{"type":"integer"},"window_open":{"type":"boolean"},"partial":{"type":"boolean"},"test":{"type":"boolean"},"computed_at":{"type":"string","format":"date-time"}}},"Customer":{"type":"object","properties":{"id":{"type":"string","example":"cus_a1B2c3D4e5F6g7"},"object":{"type":"string","enum":["customer"]},"name":{"type":"string","nullable":true},"external_id":{"type":"string","nullable":true},"metadata":{"type":"object","additionalProperties":{"type":"string"}},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"phone_numbers":{"type":"array","items":{"type":"string"},"description":"Only on retrieve"}}},"CustomerUsage":{"type":"object","properties":{"object":{"type":"string","enum":["customer_usage"]},"customer_id":{"type":"string"},"mode":{"type":"string","enum":["test","live"]},"start":{"type":"string","format":"date"},"end":{"type":"string","format":"date"},"messages_sent":{"type":"integer"},"messages_failed":{"type":"integer"},"messages_received":{"type":"integer"},"verifications_sent":{"type":"integer"},"verifications_approved":{"type":"integer"},"numbers":{"type":"integer","description":"Numbers assigned now"},"phone_numbers":{"type":"array","items":{"type":"string"}},"daily":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string","format":"date"},"messages_sent":{"type":"integer"},"messages_failed":{"type":"integer"},"messages_received":{"type":"integer"},"verifications_sent":{"type":"integer"},"verifications_approved":{"type":"integer"}}}}}},"AvailableNumber":{"type":"object","properties":{"object":{"type":"string","enum":["available_number"]},"phone_number":{"type":"string"},"locality":{"type":"string"},"region":{"type":"string"}}},"Lookup":{"type":"object","properties":{"phone_number":{"type":"string"},"valid":{"type":"boolean"},"line_type":{"type":"string","nullable":true,"example":"mobile"},"carrier":{"type":"object","properties":{"name":{"type":"string","nullable":true},"type":{"type":"string","nullable":true}}},"caller_name":{"type":"string","nullable":true}}},"Consent":{"type":"object","properties":{"object":{"type":"string","enum":["consent"]},"phone":{"type":"string","example":"+14155550132"},"status":{"type":"string","enum":["opted_out","opted_in","no_record"]},"updated_at":{"type":"string","format":"date-time"},"via":{"type":"string","example":"sms_phrase"},"method":{"type":"string","enum":["keyword","phrase","ai","api","import","hosted_page"]},"detected":{"type":"string","description":"The keyword or phrase that triggered detection"},"confidence":{"type":"number","description":"Intent-tier detections only, 0 to 1"},"campaign_id":{"type":"string","description":"On the response to an opt-in recorded for one registration"},"proof":{"$ref":"#/components/schemas/OptInProof"}}},"ConsentDetail":{"allOf":[{"$ref":"#/components/schemas/Consent"},{"type":"object","properties":{"campaigns":{"type":"array","description":"The registrations this number has an opt-in on file for (empty while opted out: STOP clears every scope)","items":{"type":"object","properties":{"campaign_id":{"type":"string","example":"reg_a1B2c3D4e5F6"},"opted_in_at":{"type":"string","format":"date-time"},"via":{"type":"string","example":"api_set"},"proof_source":{"type":"string","enum":["web_form","keyword","paper","checkout","verbal"]}}}},"history":{"type":"array","description":"Append-only consent history, newest first","items":{"type":"object","properties":{"at":{"type":"string","format":"date-time"},"type":{"type":"string","enum":["opt_out","opt_in","exempt_send","import","api_set","topic_unsubscribe","topic_subscribe","blocked_send","help","opt_in_request"],"description":"`import` is opt-in evidence from a contact import; `topic_*` are per-topic preferences; `opt_in_request` is a sign-up on your hosted opt-in page from a number that had opted out (kept as evidence; they stay opted out until they text START); `blocked_send` and `help` record a refused send and a HELP request. None of these changes the opt-out status."},"via":{"type":"string"},"method":{"type":"string"},"detected":{"type":"string"},"confidence":{"type":"number"},"note":{"type":"string"},"proof":{"$ref":"#/components/schemas/OptInProof"},"campaign_id":{"type":"string","description":"The registration an opt-in was given for; absent on account-level opt-ins and on every opt-out"}}}}}}]},"CallbackSecret":{"type":"object","properties":{"object":{"type":"string","enum":["callback_secret"]},"secret":{"type":"string","example":"whsec_a1B2c3D4e5F6g7H8a1B2c3D4e5F6g7H8"},"previous_secret_expires_at":{"type":"string","format":"date-time","nullable":true,"description":"When the replaced secret stops signing; null on the first call."}}},"WebhookEndpoint":{"type":"object","properties":{"id":{"type":"string","example":"we_a1B2c3D4e5F6"},"object":{"type":"string","enum":["webhook_endpoint"]},"url":{"type":"string","format":"uri"},"events":{"oneOf":[{"type":"string","enum":["*"]},{"type":"array","items":{"type":"string"}}]},"numbers":{"type":"array","items":{"type":"string"},"description":"The number filter: only events about these numbers. Empty: every number."},"active":{"type":"boolean"},"mode":{"type":"string","enum":["test","live"],"description":"Which events it receives: the mode of the key that created it (or chosen in the console). A key lists and changes only its own mode's endpoints."},"description":{"type":"string","nullable":true},"created_at":{"type":"string","format":"date-time"}}},"LinkDomain":{"type":"object","properties":{"id":{"type":"string","example":"ld_a1B2c3D4e5F6"},"object":{"type":"string","enum":["link_domain"]},"domain":{"type":"string","example":"links.example.com"},"status":{"type":"string","enum":["pending_dns","dns_verified","active"],"description":"`pending_dns`: add the records and verify. `dns_verified`: the records check out and it is being turned on. `active`: `shorten_links` uses it."},"dns_records":{"type":"array","description":"The records to create at your DNS host.","items":{"type":"object","properties":{"type":{"type":"string","enum":["CNAME","TXT"]},"name":{"type":"string","example":"links.example.com"},"value":{"type":"string","example":"links.jointextkit.com"},"purpose":{"type":"string"}}}},"created_at":{"type":"string","format":"date-time"},"verified_at":{"type":"string","format":"date-time","nullable":true},"activated_at":{"type":"string","format":"date-time","nullable":true},"last_check":{"type":"object","nullable":true,"properties":{"at":{"type":"string","format":"date-time"},"cname":{"type":"boolean"},"txt":{"type":"boolean"}}}}},"Pool":{"type":"object","properties":{"id":{"type":"string","example":"pool_a1B2c3D4e5F6"},"object":{"type":"string","enum":["pool"]},"name":{"type":"string"},"routing":{"type":"string","enum":["sticky","local"]},"description":{"type":"string","nullable":true},"numbers":{"type":"array","items":{"type":"string"},"description":"Active member numbers (E.164)"},"cloned_from":{"type":"string","nullable":true},"created_at":{"type":"string","format":"date-time"}}},"BulkResult":{"type":"object","properties":{"object":{"type":"string","enum":["bulk_result"]},"action":{"type":"string"},"requested":{"type":"integer"},"succeeded":{"type":"integer"},"failed":{"type":"integer"},"results":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"ok":{"type":"boolean"},"error":{"type":"string","enum":["invalid_id","duplicate","not_found","too_many_tags","mode_mismatch","carrier_error"]},"message":{"type":"string"}}}}}},"WebhookDelivery":{"type":"object","properties":{"id":{"type":"string"},"object":{"type":"string","enum":["webhook_delivery"]},"endpoint_id":{"type":"string"},"event_id":{"type":"string"},"event_type":{"type":"string"},"attempt":{"type":"integer","description":"1 is the first push; up to 6 with retries"},"ok":{"type":"boolean"},"status_code":{"type":"integer","nullable":true},"error":{"type":"string","nullable":true,"enum":["timeout","unreachable","blocked"]},"latency_ms":{"type":"integer"},"response_body":{"type":"string","nullable":true,"description":"First 2 KB of what your endpoint answered"},"replay":{"type":"boolean","description":"Sent by a manual replay"},"created_at":{"type":"string","format":"date-time"}}},"OptInProof":{"type":"object","description":"Opt-in evidence (Messaging Policy 1.4). Accepted only with status opted_in.","required":["source"],"properties":{"source":{"type":"string","enum":["web_form","keyword","paper","checkout","verbal"]},"collected_at":{"type":"string","format":"date-time","description":"When the person consented; defaults to now"},"page_url":{"type":"string","format":"uri","description":"Required for web_form"},"disclosure":{"type":"string","maxLength":2000,"description":"Exact disclosure wording shown"},"ip":{"type":"string"},"user_agent":{"type":"string","maxLength":500},"registration_id":{"type":"string","example":"reg_a1B2c3D4e5F6"},"campaign_id":{"type":"string"},"message_id":{"type":"string","description":"The inbound message that was the opt-in. Required for keyword opt-ins recorded for a marketing registration"}}},"RegistrationInput":{"type":"object","required":["business_name","website","use_case"],"properties":{"business_name":{"type":"string","maxLength":120},"website":{"type":"string","format":"uri"},"use_case":{"type":"string","enum":["2fa","account_notification","customer_care","delivery_notification","fraud_alert","higher_education","marketing","mixed","polling_voting","public_service_announcement","security_alert","low_volume"]},"opt_in_url":{"type":"string","format":"uri","description":"Page where people enter their number"},"privacy_url":{"type":"string","format":"uri","description":"Discovered from homepage links when omitted"},"terms_url":{"type":"string","format":"uri","description":"Discovered from homepage links when omitted"},"support_email":{"type":"string","format":"email","description":"Used in the generated HELP reply"},"sample_messages":{"type":"array","nullable":true,"minItems":2,"maxItems":5,"items":{"type":"string","minLength":20,"maxLength":320},"description":"Real examples of the texts you will send, as a recipient would get them (no template variables). Carriers compare them with your traffic. Each must name your business; marketing and mixed use cases need opt-out wording in at least one. More than 5, or one over 320 characters, is a 400; too few or too short comes back as findings. Omit to get starter drafts; null or [] goes back to them."},"starter_messages":{"type":"array","nullable":true,"maxItems":5,"items":{"type":"string","maxLength":320},"description":"Draft examples suggested for this business (fields.sample_messages from POST /registrations/prefill). Shown instead of the template starter drafts; samples_source stays starter and they are never filed until you edit or confirm them. Drafts that would not pass the example check for the business name and use case are ignored."},"samples_confirmed":{"type":"boolean","description":"true = the starter drafts are right as they are; they become your examples. Ignored when sample_messages is sent."},"description":{"type":"string","nullable":true,"maxLength":500,"description":"What you send and to whom (40 to 500 characters). Omit or null to file the generated one."},"opt_in_flow":{"type":"string","nullable":true,"minLength":40,"maxLength":500,"description":"How people opt in, in your own words. It leads the filed opt-in account; the opt-in page, the disclosure and the START keyword are added after it. Omit or null to file the standard sentence."},"stop_reply":{"type":"string","nullable":true,"minLength":20,"maxLength":320,"description":"Your wording of the confirmation sent when someone opts out. It must name your business, say they are unsubscribed, and carry no link; anything else is a 400 naming what is missing. Omit or null for the standard wording in your business name. Filed with the registration and sent by its numbers."},"help_reply":{"type":"string","nullable":true,"minLength":20,"maxLength":320,"description":"Your wording of the HELP reply. It must name your business, give a way to reach you (email, phone or web page) and keep the STOP instruction. Omit or null for the standard wording."},"includes_links":{"type":"boolean","description":"Your messages include web links. Also true whenever an example contains one."},"includes_phone_numbers":{"type":"boolean","description":"Your messages include phone numbers. Also true whenever an example contains one."},"age_gated":{"type":"boolean","description":"Your messages are for adults only (alcohol, cannabis, gambling)."},"direct_lending":{"type":"boolean","description":"Your messages are about loans you make yourself."},"brand":{"type":"object","properties":{"legal_name":{"type":"string"},"entity_type":{"type":"string","enum":["PRIVATE_PROFIT","PUBLIC_PROFIT","NON_PROFIT","GOVERNMENT"],"description":"SOLE_PROPRIETOR is a 400: local numbers cannot be registered for a sole proprietor yet; use toll-free verification."},"ein":{"type":"string","example":"12-3456789"},"street":{"type":"string"},"city":{"type":"string"},"state":{"type":"string"},"postal_code":{"type":"string"},"country":{"type":"string","description":"US only."},"vertical":{"type":"string"},"contact_name":{"type":"string","description":"The authorized representative: a person who works for the business and can act for it. Required to submit. Kept with the registration for business verification; not sent to the registry."},"contact_title":{"type":"string","description":"The representative's job title, like Owner or Operations Manager. Required to submit."},"contact_email":{"type":"string","description":"The representative's own email at the business's domain. A free mailbox (gmail.com, outlook.com, ...) or a shared inbox (info@, sales@, support@, ...) is refused with 400 (param brand.contact_email). Carriers verify the business by writing to it."},"contact_phone":{"type":"string","description":"A 10-digit US number the representative answers. Required to submit: the carrier registry refuses a brand without one."}},"description":"The business as the carriers register it. street must be a street address, not a P.O. box; state a 2-letter US state or territory code; postal_code a 5-digit ZIP (optionally +4). Anything else is refused with 400 naming the field."}}},"BusinessAttestation":{"type":"object","required":["signer_name","signer_title","accepted"],"description":"Signed by someone authorized to act for the business, once per business: that they are authorized, that the details match the business's IRS records, and that the business will text only people who agreed to it and will follow the Messaging Policy. Kept as signed, with the exact wording, who sent it (the API key), the IP address, the user agent and the date of our legal documents.","properties":{"signer_name":{"type":"string","minLength":2,"maxLength":120,"description":"The full name of the person signing."},"signer_title":{"type":"string","minLength":2,"maxLength":120,"description":"Their job title."},"accepted":{"type":"boolean","description":"Must be true: the person signing agrees to the statement."}}},"TollFreeVerificationInput":{"type":"object","description":"The carriers' toll-free verification form, field for field. Every field is optional on each call: what you leave out keeps what we filled in. The HELP and STOP replies, example messages, program and opt-in descriptions and advertising answer follow your business name and use case until you set them yourself. Unknown fields are ignored.","properties":{"numbers":{"type":"array","items":{"type":"string"},"example":["+18335550100"],"description":"Your live toll-free numbers this program texts from, E.164. One per business unless multiple_numbers_reason says why."},"multiple_numbers_reason":{"type":"string","description":"Why the program needs more than one number (one per store, say). Required with more than one number."},"business_name":{"type":"string","example":"Acme Plumbing LLC d/b/a Acme","description":"The legal name, as on the IRS EIN letter, optionally with \"d/b/a\" and the brand your messages carry."},"ein":{"type":"string","example":"12-3456789","description":"The EIN (US), matching the legal name; elsewhere the business registration number."},"entity_type":{"type":"string","enum":["PRIVATE_PROFIT","PUBLIC_PROFIT","NON_PROFIT","GOVERNMENT","SOLE_PROPRIETOR"]},"street":{"type":"string"},"city":{"type":"string"},"state":{"type":"string"},"postal_code":{"type":"string"},"country":{"type":"string","example":"United States"},"contact_first_name":{"type":"string"},"contact_last_name":{"type":"string"},"contact_phone":{"type":"string"},"brand_email":{"type":"string","description":"The business's own email, for its customers: the HELP reply and the hosted opt-in page name it."},"website":{"type":"string","format":"uri"},"use_case":{"type":"string","enum":["2fa","account_notification","customer_care","delivery_notification","fraud_alert","higher_education","marketing","mixed","polling_voting","public_service_announcement","security_alert","low_volume"]},"use_case_summary":{"type":"string","maxLength":1500,"description":"What the program sends, to whom and why. At least 40 characters."},"sample_messages":{"type":"array","maxItems":8,"items":{"type":"string","maxLength":640},"description":"At least two example messages."},"help_reply":{"type":"string"},"stop_reply":{"type":"string"},"opt_in_description":{"type":"string","maxLength":2000,"description":"How people opt in, step by step. At least 40 characters."},"opt_in_url":{"type":"string","format":"uri","description":"The opt-in page. With none of your own we make you a hosted one."},"monthly_volume":{"type":"string","example":"About 1,000 messages per month"},"volume_basis":{"type":"string","enum":["existing","projecting"]},"advertising":{"type":"string","enum":["yes","no","unsure"],"description":"Whether the messages advertise a product or service."},"previous_service":{"type":"string","enum":["new","short_code","long_number"],"description":"Where this traffic ran before."},"previous_content":{"type":"string","maxLength":2000,"description":"Sample messages from the previous service (not new only)."},"previous_numbers":{"type":"string","description":"The numbers it used (not new only)."},"number_published":{"type":"string","description":"Where people see this number."},"fortune_500":{"type":"boolean"},"urls_in_messages":{"type":"string","description":"Links your messages include, or \"None\"."},"phones_in_messages":{"type":"string","description":"Phone numbers your messages include, or \"None\"."},"terms_url":{"type":"string","format":"uri"},"privacy_url":{"type":"string","format":"uri"}}},"TollFreeVerification":{"type":"object","properties":{"id":{"type":"string","example":"tfv_a1B2c3D4e5F6"},"object":{"type":"string","enum":["toll_free_verification"]},"status":{"type":"string","enum":["draft","in_review","submitted","verified","rejected"],"description":"draft: not submitted. in_review: our team is checking it by hand; nothing has gone to the carriers yet. submitted: we sent it to the carriers; waiting on their answer. verified: its numbers text anyone who opted in. rejected: sent back; rejection.reason says why."},"numbers":{"type":"array","items":{"type":"string"},"example":["+18335550100"]},"business_name":{"type":"string"},"ready":{"type":"boolean","description":"Nothing the carriers require is missing (problems is empty)."},"problems":{"type":"array","description":"What the carriers would send it back for, one entry per answer to fix.","items":{"type":"object","properties":{"field":{"type":"string","example":"ein"},"message":{"type":"string"}}}},"editable":{"type":"boolean","description":"Whether PATCH may change it now (draft, in_review and rejected)."},"next_actor":{"type":"string","enum":["you","textkit","carriers","nobody"],"description":"Who acts next."},"form":{"allOf":[{"$ref":"#/components/schemas/TollFreeVerificationInput"}],"description":"The form as it stands, every field. contact_email is always our support address: the carriers write to us, and we forward what only you can answer."},"business":{"type":"object","nullable":true,"description":"The business on the form, as business verification sees it.","properties":{"status":{"type":"string","enum":["not_started","pending","verified","rejected"]},"attestation_needed":{"type":"boolean","description":"Submit needs an attestation for these business details."},"signed":{"type":"object","nullable":true,"properties":{"signer_name":{"type":"string"},"signer_title":{"type":"string"},"signed_at":{"type":"string","format":"date-time"}}},"rejection":{"type":"object","nullable":true,"properties":{"code":{"type":"string"},"note":{"type":"string","nullable":true}}}}},"rejection":{"type":"object","nullable":true,"description":"Set while rejected: what the carriers or our team asked for.","properties":{"reason":{"type":"string"},"rejected_at":{"type":"string","format":"date-time"}}},"carrier_reference":{"type":"string","nullable":true,"description":"The carriers' case number, once they give one."},"registration_id":{"type":"string","nullable":true,"description":"The registration it was filled in from, if any."},"history":{"type":"array","items":{"type":"object","properties":{"at":{"type":"string","format":"date-time"},"status":{"type":"string","enum":["draft","in_review","submitted","verified","rejected"]},"actor":{"type":"string","enum":["customer","staff","system","carrier"]},"note":{"type":"string","nullable":true}}}},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"Finding":{"type":"object","properties":{"id":{"type":"string","enum":["site_reachable","business_identity","privacy_policy_exists","privacy_no_sharing","terms_exist","terms_sms_program","optin_reachable","optin_phone_field","optin_brand","optin_message_types","optin_frequency","optin_rates","optin_stop","optin_help","optin_links","optin_not_prechecked"]},"title":{"type":"string"},"status":{"type":"string","enum":["pass","fail","warn"]},"evidence":{"type":"object","properties":{"url":{"type":"string","nullable":true},"snippet":{"type":"string","nullable":true}}},"why":{"type":"string"},"fix":{"type":"object","nullable":true,"properties":{"summary":{"type":"string"},"copy":{"type":"string","nullable":true,"description":"Exact text to paste"}}},"flagged_by_rejection":{"type":"boolean"}}},"SampleFinding":{"type":"object","description":"A finding of the example-message check. Same shape as Finding.","properties":{"id":{"type":"string","enum":["samples_confirmed","samples_count","samples_length","samples_brand","samples_opt_out","samples_links","samples_placeholders","samples_distinct","samples_use_case","samples_description","samples_consent","samples_content"]},"title":{"type":"string"},"status":{"type":"string","enum":["pass","fail","warn"]},"evidence":{"type":"object","properties":{"url":{"type":"string","nullable":true},"snippet":{"type":"string","nullable":true}}},"why":{"type":"string"},"fix":{"type":"object","nullable":true,"properties":{"summary":{"type":"string"},"copy":{"type":"string","nullable":true,"description":"Exact text to paste"}}}}},"RegistrationPrefill":{"type":"object","properties":{"object":{"type":"string","enum":["registration_prefill"]},"reachable":{"type":"boolean","description":"false = the website could not be read; fields then holds only the website"},"error":{"type":"string","nullable":true,"description":"Why the site could not be read"},"fields":{"type":"object","description":"Suggestions, shaped like RegistrationInput. Only fields we found are present.","properties":{"website":{"type":"string","format":"uri"},"business_name":{"type":"string"},"use_case":{"type":"string"},"description":{"type":"string","maxLength":500},"opt_in_url":{"type":"string","format":"uri"},"privacy_url":{"type":"string","format":"uri"},"terms_url":{"type":"string","format":"uri"},"support_email":{"type":"string","format":"email"},"sample_messages":{"type":"array","items":{"type":"string"},"description":"Starter drafts written for this business (drafting only). Send them as starter_messages."},"brand":{"type":"object","description":"Never includes ein or entity_type.","properties":{"legal_name":{"type":"string"},"street":{"type":"string"},"city":{"type":"string"},"state":{"type":"string"},"postal_code":{"type":"string"},"country":{"type":"string"},"vertical":{"type":"string","enum":["PROFESSIONAL","REAL_ESTATE","HEALTHCARE","HUMAN_RESOURCES","ENERGY","ENTERTAINMENT","RETAIL","TRANSPORTATION","AGRICULTURE","INSURANCE","POSTAL","EDUCATION","HOSPITALITY","FINANCIAL","LEGAL","CONSTRUCTION","NGO","MANUFACTURING","GOVERNMENT","TECHNOLOGY","COMMUNICATION"]},"contact_email":{"type":"string"},"contact_phone":{"type":"string","description":"A 10-digit US number for the business contact. Required to submit."}}}}},"suggested":{"type":"array","items":{"type":"string"},"description":"Dotted names of every field filled in, e.g. business_name, brand.city"},"sources":{"type":"object","additionalProperties":{"type":"string","enum":["page","ai"]},"description":"Where each suggested field came from: page = read from your pages by fixed rules; ai = drafted for you"},"candidates":{"type":"object","description":"Pages that could be the opt-in, privacy policy and terms, best first","properties":{"opt_in":{"type":"array","items":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"confidence":{"type":"number","minimum":0,"maximum":1},"reason":{"type":"string"}}}},"privacy":{"type":"array","items":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"confidence":{"type":"number","minimum":0,"maximum":1},"reason":{"type":"string"}}}},"terms":{"type":"array","items":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"confidence":{"type":"number","minimum":0,"maximum":1},"reason":{"type":"string"}}}}}},"about":{"type":"string","nullable":true,"description":"What the site says the business is (its meta description)"},"ai":{"type":"boolean","description":"true when any of these suggestions were drafted rather than read from your pages"},"policy_checks":{"type":"object","description":"Whether the suggested privacy policy and terms pages pass the website check's rules for them (privacy: says mobile opt-in data is not shared; terms: SMS program terms with STOP and HELP). Fixed rules, never the model; false when that page was not suggested.","properties":{"privacy":{"type":"boolean"},"terms":{"type":"boolean"}}}}},"RegistrationSubmission":{"type":"object","description":"An immutable copy of what one submission filed.","properties":{"object":{"type":"string","enum":["registration_submission"]},"registration_id":{"type":"string","example":"reg_a1B2c3D4e5F6"},"number":{"type":"integer","description":"1 for the first submission, 2 for the first resubmission, and so on"},"submitted_at":{"type":"string","format":"date-time"},"filed_by":{"type":"string","enum":["delivered_compliance","registry"]},"reference":{"type":"string"},"sample_messages":{"type":"array","items":{"type":"string"}},"samples_source":{"type":"string","enum":["starter","customer"]},"description":{"type":"string"},"brand":{"type":"object","description":"The business details as filed"},"campaign":{"type":"object","description":"Use case, description, opt-in story, example messages and keyword replies as filed"},"website_check":{"type":"object","nullable":true,"properties":{"checked_at":{"type":"string","format":"date-time"},"passed":{"type":"integer"},"failed":{"type":"integer"},"warnings":{"type":"integer"},"urls":{"type":"object"},"not_passing":{"type":"array","items":{"type":"string"},"description":"Ids of findings that were warnings (a failing finding blocks submission)"}}},"sample_check":{"type":"object","properties":{"passed":{"type":"integer"},"failed":{"type":"integer"},"warnings":{"type":"integer"},"not_passing":{"type":"array","items":{"type":"string"}}}},"fee_acknowledgment":{"type":"object","nullable":true,"description":"The registration fee terms accepted at this submit: the exact wording shown, when, and from which IP. Null on copies filed before the acknowledgment existed.","properties":{"statement":{"type":"string"},"accepted_at":{"type":"string","format":"date-time"},"ip":{"type":"string","nullable":true}}}}},"Registration":{"type":"object","properties":{"id":{"type":"string","example":"reg_a1B2c3D4e5F6"},"object":{"type":"string","enum":["registration"]},"status":{"type":"string","enum":["draft","checks_failed","ready","submitted","in_review","approved","rejected"]},"business_name":{"type":"string"},"website":{"type":"string"},"use_case":{"type":"string"},"opt_in_url":{"type":"string","nullable":true},"privacy_url":{"type":"string","nullable":true},"terms_url":{"type":"string","nullable":true},"support_email":{"type":"string","nullable":true},"brand":{"type":"object","description":"The business details as saved (the brand object of RegistrationInput, including the authorized representative: contact_name, contact_title, contact_email, contact_phone)."},"sample_messages":{"type":"array","items":{"type":"string"},"description":"The examples as they stand: yours, or starter drafts (see samples_source). These are what a submission files."},"samples_source":{"type":"string","enum":["starter","customer"],"description":"starter: our drafts, not yet edited or confirmed; the registration cannot be ready. customer: yours."},"description":{"type":"string","nullable":true,"description":"Your own description; null when the generated one (generated.campaign.description) is filed"},"opt_in_flow":{"type":"string","nullable":true,"description":"Your own account of how people opt in; null when the standard sentence is filed"},"stop_reply":{"type":"string","nullable":true,"description":"Your wording of the opt-out confirmation; null when the standard wording (generated.stop_reply) is filed and sent"},"help_reply":{"type":"string","nullable":true,"description":"Your wording of the HELP reply; null when the standard wording (generated.help_reply) is filed and sent"},"includes_links":{"type":"boolean"},"includes_phone_numbers":{"type":"boolean"},"age_gated":{"type":"boolean"},"direct_lending":{"type":"boolean"},"marketing_paused":{"type":"object","nullable":true,"description":"Set while marketing from this registration is paused because more than 4% of recipients opted out in 24 hours; our team resumes it. Transactional messages are not paused.","properties":{"paused_at":{"type":"string","format":"date-time"},"opt_out_rate":{"type":"number","description":"Opt-outs divided by messages sent, over the 24 hours that tripped it"},"sent":{"type":"integer"},"opt_outs":{"type":"integer"}}},"sample_check":{"type":"object","description":"The example-message check. Fixed rules. Recomputed on every read.","properties":{"passed":{"type":"integer"},"failed":{"type":"integer"},"warnings":{"type":"integer"},"ok":{"type":"boolean"},"findings":{"type":"array","items":{"$ref":"#/components/schemas/SampleFinding"}}}},"guidance":{"type":"object","description":"The five answers every state gives","properties":{"what_happened":{"type":"string"},"why":{"type":"string"},"next_actor":{"type":"string","enum":["you","delivered","carriers","nobody"]},"next_step":{"type":"string"},"after":{"type":"string"}}},"check":{"type":"object","nullable":true,"properties":{"checked_at":{"type":"string","format":"date-time"},"passed":{"type":"integer"},"failed":{"type":"integer"},"warnings":{"type":"integer"},"urls":{"type":"object"},"findings":{"type":"array","items":{"$ref":"#/components/schemas/Finding"}}}},"rejection":{"type":"object","nullable":true,"properties":{"code":{"type":"string"},"title":{"type":"string"},"explanation":{"type":"string"},"fix":{"type":"string"},"note":{"type":"string","nullable":true},"related_findings":{"type":"array","items":{"type":"string"}},"rejected_at":{"type":"string","format":"date-time"}}},"generated":{"type":"object","description":"Deterministic compliant copy and the registry draft","properties":{"opt_in_cta":{"type":"string"},"privacy_clause":{"type":"string"},"sms_terms":{"type":"string"},"sample_messages":{"type":"array","items":{"type":"string"},"minItems":2,"maxItems":5,"description":"Starter drafts from templates. What is filed is the top-level sample_messages (also in generated.campaign.sample_messages)."},"help_reply":{"type":"string"},"stop_reply":{"type":"string"},"opt_in_confirmation":{"type":"string"},"brand":{"type":"object","properties":{"missing":{"type":"array","items":{"type":"string"}}}},"campaign":{"type":"object","properties":{"use_case":{"type":"string"},"description":{"type":"string"},"message_flow":{"type":"string"},"sample_messages":{"type":"array","items":{"type":"string"}},"opt_in_keywords":{"type":"array","items":{"type":"string"}},"opt_in_message":{"type":"string"},"opt_out_keywords":{"type":"array","items":{"type":"string"}},"opt_out_message":{"type":"string"},"help_keywords":{"type":"array","items":{"type":"string"}},"help_message":{"type":"string"},"embedded_link":{"type":"boolean"},"embedded_phone":{"type":"boolean"},"number_pooling":{"type":"boolean"},"direct_lending":{"type":"boolean"},"age_gated":{"type":"boolean"},"affiliate_marketing":{"type":"boolean"},"privacy_policy_url":{"type":"string"},"terms_url":{"type":"string"}}}}},"submissions":{"type":"integer"},"history":{"type":"array","items":{"type":"object","properties":{"at":{"type":"string","format":"date-time"},"status":{"type":"string"},"actor":{"type":"string","enum":["customer","delivered","system"]},"label":{"type":"string"},"reason_code":{"type":"string"},"note":{"type":"string"}}}},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"DeliverabilityRow":{"type":"object","properties":{"key":{"type":"string","description":"Date (YYYY-MM-DD), carrier key, or 10-digit sending number","example":"2026-10-01"},"delivered":{"type":"integer"},"failed":{"type":"integer"},"total":{"type":"integer"},"delivery_rate":{"type":"number","nullable":true,"example":0.9907},"failure_reasons":{"type":"object","additionalProperties":{"type":"integer"}}}},"Deliverability":{"type":"object","properties":{"object":{"type":"string","enum":["deliverability"]},"mode":{"type":"string","enum":["test","live"]},"start_date":{"type":"string","format":"date"},"end_date":{"type":"string","format":"date"},"group_by":{"type":"string","enum":["day","carrier","number","country","category","customer","error_code","tag"]},"totals":{"$ref":"#/components/schemas/DeliverabilityRow"},"data":{"type":"array","items":{"$ref":"#/components/schemas/DeliverabilityRow"}}}},"SpendLimit":{"type":"object","properties":{"object":{"type":"string","enum":["spend_limit"]},"monthly_limit_usd":{"type":"number","nullable":true},"alert_thresholds":{"type":"array","items":{"type":"integer"}},"applies_to":{"type":"array","items":{"type":"string","enum":["outbound_sms"]}},"estimated_cost_per_message_usd":{"type":"number"},"current_month":{"type":"object","properties":{"month":{"type":"string","example":"2026-10"},"spent_usd":{"type":"number"},"remaining_usd":{"type":"number","nullable":true}}},"updated_at":{"type":"string","format":"date-time","nullable":true}}},"UsageCounters":{"type":"object","properties":{"messages":{"type":"integer","example":412,"description":"Live outbound messages accepted for sending; a carrier rejection is taken back off the count"},"messages_test":{"type":"integer","example":37,"description":"Sandbox messages; never counted toward a limit"},"lookups":{"type":"integer","example":18,"description":"Lookups, live and sandbox together"},"verifications":{"type":"integer","example":9,"description":"Approved live verifications"},"api_requests":{"type":"integer","example":1280,"description":"Authenticated requests to /v1"}}},"Usage":{"type":"object","properties":{"object":{"type":"string","enum":["usage"]},"months":{"type":"array","description":"Newest first; a month with no traffic is listed with zeros","items":{"allOf":[{"type":"object","properties":{"month":{"type":"string","example":"2026-10"}}},{"$ref":"#/components/schemas/UsageCounters"}]}},"today":{"description":"The current UTC day so far","allOf":[{"type":"object","properties":{"date":{"type":"string","format":"date","example":"2026-10-05"}}},{"$ref":"#/components/schemas/UsageCounters"}]},"limits":{"type":"object","description":"Caps applied to live traffic on your plan; null means no cap","properties":{"messages_per_day":{"type":"integer","example":10000},"messages_per_month":{"type":"integer","nullable":true,"example":null},"lookups_per_day":{"type":"integer","example":5000,"description":"0 when live lookups are not available on your plan"},"verifications_per_day":{"type":"integer","example":2000},"verifications_per_month":{"type":"integer","nullable":true,"example":null}}}}},"AuditLog":{"type":"object","properties":{"id":{"type":"string","example":"aud_a1B2c3D4e5F6g7H8"},"object":{"type":"string","enum":["audit_log"]},"action":{"type":"string","enum":["api_key.created","api_key.revoked","api_key.rolled","cli_login.approved","cli_login.denied","team.invite_created","team.invite_accepted","team.invite_revoked","team.member_left","team.member_removed","team.role_changed","webhook.created","webhook.updated","webhook.deleted","webhook.secret_rotated","webhook.secret_viewed","spend_limit.updated","credits.topped_up","credits.settings_updated","number.purchased","number.released","number.assigned","number.attached","number.toll_free_updated","number.registration_set","number.sender_linked","number.sender_unlinked","registration.approved","registration.approval_undone","business.attested","business.verified","business.rejected","registration.marketing_paused","registration.marketing_resumed","opt_in_page.enabled","opt_in_page.disabled","live_access.approved","live_access.denied","consent.updated","consent.imported","migration.opt_outs_attested","quiet_hours.updated","reconfirmation.updated","settings.updated","contacts.imported","contact.topic_updated","segment.created","segment.updated","segment.deleted","topic.created","topic.updated","topic.deleted","automation.created","automation.updated","automation.activated","automation.paused","automation.deleted","automation.identity_unlinked","privacy.phone_erased","privacy.email_erased","message.deleted","message.redacted","schedule.created","schedule.updated","schedule.paused","schedule.resumed","schedule.deleted","account.closed","account.reopened","link_domain.created","link_domain.deleted","link_domain.activated","link_domain.deactivated"]},"actor":{"type":"object","properties":{"type":{"type":"string","enum":["user","api_key","system"]},"id":{"type":"string"},"email":{"type":"string"},"name":{"type":"string"},"mode":{"type":"string","enum":["test","live"],"description":"API keys: which mode of key acted."}}},"target":{"type":"object","properties":{"type":{"type":"string"},"id":{"type":"string"}}},"metadata":{"type":"object"},"ip":{"type":"string"},"created_at":{"type":"string","format":"date-time"}}},"Event":{"type":"object","properties":{"id":{"type":"string","example":"evt_a1B2c3D4e5F6g7H8"},"object":{"type":"string","enum":["event"]},"type":{"type":"string","enum":["message.sent","message.delivered","message.failed","message.received","message.opted_out","message.opted_in","message.help_requested","message.blocked","message.deferred","message.link_clicked","broadcast.complete","number.purchased","number.released","number.sender_updated","verification.sent","verification.approved","verification.failed","verification.blocked","verification.sent_to_opted_out","registration.updated","deliverability.degraded","compliance.flagged","compliance.paused","compliance.resumed","spend.threshold_reached","credits.low","credits.depleted","credits.topped_up","credits.auto_recharge_failed","automation.run.started","automation.run.completed","automation.run.failed","schedule.occurrence_skipped","schedule.paused","batch.created","batch.paused","batch.resumed","batch.canceled","batch.retried","batch.complete"]},"mode":{"type":"string","enum":["test","live"],"description":"Sandbox or live traffic. A key sees only its own mode's events, and an endpoint receives only its own mode's."},"created_at":{"type":"string","format":"date-time"},"data":{"type":"object","description":"The subject of the event. Events about one message (message.sent, .delivered, .failed, .received, .blocked) include `message_id`, its `price` (the same object GET /v1/messages/{id} shows) and its `tags` when it has any."}}},"TrackedEvent":{"type":"object","properties":{"id":{"type":"string","example":"tev_a1B2c3D4e5F6g7H8"},"object":{"type":"string","enum":["tracked_event"]},"event":{"type":"string"},"user_id":{"type":"string","nullable":true},"phone":{"type":"string","nullable":true,"description":"Null when user_id is not linked to a number yet (nothing could start)."},"properties":{"type":"object"},"test":{"type":"boolean"},"runs_started":{"type":"array","items":{"type":"string"},"description":"Ids of the runs this event started."},"runs_notified":{"type":"integer","description":"Runs in progress for this person that recorded the event."},"created_at":{"type":"string","format":"date-time"}}},"AutomationStep":{"type":"object","required":["type"],"description":"One of: send_sms {body, from?}; wait {seconds}; wait_for_event {event, timeout_seconds, on_received, on_timeout}; condition {check, on_met, on_not_met}; end. Branches are \"continue\", \"end\", or \"goto:<id of a later step>\".","properties":{"id":{"type":"string","description":"Stable handle for goto branches; assigned when omitted."},"type":{"type":"string","enum":["send_sms","wait","wait_for_event","condition","end"]},"body":{"type":"string","maxLength":1600,"description":"send_sms. Merge fields: event properties by name, {{first_name}}, {{name}}, {{phone}}, {{field:<custom>}}."},"from":{"type":"string","description":"send_sms: overrides the flow sender."},"seconds":{"type":"integer","minimum":60,"maximum":2592000},"event":{"type":"string"},"timeout_seconds":{"type":"integer","minimum":60,"maximum":2592000},"on_received":{"type":"string","example":"end"},"on_timeout":{"type":"string","example":"continue"},"check":{"type":"object","description":"{kind: event_received, event} | {kind: property, property, op, value?} | {kind: contact_field, field, op, value?}. op: eq, neq, contains, gt, lt, exists, not_exists."},"on_met":{"type":"string","example":"continue"},"on_not_met":{"type":"string","example":"end"}}},"AutomationInput":{"type":"object","properties":{"name":{"type":"string","maxLength":80},"from":{"type":"string","nullable":true,"description":"One of your numbers. A sandbox number makes it a sandbox flow."},"topic_id":{"type":"string","nullable":true,"description":"Skip recipients unsubscribed from this topic."},"trigger":{"type":"object","properties":{"event":{"type":"string","example":"trial_started"},"filters":{"type":"array","maxItems":5,"items":{"type":"object","properties":{"property":{"type":"string"},"op":{"type":"string"},"value":{}}}}}},"steps":{"type":"array","maxItems":20,"items":{"$ref":"#/components/schemas/AutomationStep"}}}},"Automation":{"type":"object","properties":{"id":{"type":"string","example":"auto_a1B2c3D4e5F6"},"object":{"type":"string","enum":["automation"]},"name":{"type":"string"},"status":{"type":"string","enum":["draft","active","paused"]},"trigger":{"type":"object"},"steps":{"type":"array","items":{"$ref":"#/components/schemas/AutomationStep"}},"from":{"type":"string","nullable":true},"topic_id":{"type":"string","nullable":true},"test":{"type":"boolean","nullable":true,"description":"Sandbox or live, decided by the sender number."},"version":{"type":"integer","description":"Published version; runs are pinned to the one they started on."},"unpublished_changes":{"type":"boolean"},"stats":{"type":"object","properties":{"runs_started":{"type":"integer"},"runs_active":{"type":"integer"},"runs_completed":{"type":"integer"},"runs_failed":{"type":"integer"},"runs_opted_out":{"type":"integer"},"triggers_skipped":{"type":"integer","description":"Triggers ignored because that person already had a run in progress."},"messages_sent":{"type":"integer"},"last_triggered_at":{"type":"string","format":"date-time","nullable":true}}},"issues":{"type":"array","description":"What stops this flow from running. Empty when it can be activated.","items":{"type":"object","properties":{"where":{"type":"string","description":"A step id, \"trigger\" or \"flow\"."},"field":{"type":"string"},"message":{"type":"string"}}}},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"AutomationRun":{"type":"object","properties":{"id":{"type":"string","example":"run_1790000000000a1B2c3D4"},"object":{"type":"string","enum":["automation_run"]},"automation_id":{"type":"string"},"automation_version":{"type":"integer"},"status":{"type":"string","enum":["running","waiting","completed","failed"]},"to":{"type":"string"},"user_id":{"type":"string","nullable":true},"test":{"type":"boolean"},"trigger_event":{"type":"string"},"step":{"type":"integer","description":"Index of the step the run is on."},"waiting_until":{"type":"string","format":"date-time","nullable":true},"waiting_for_event":{"type":"string","nullable":true},"end_reason":{"type":"string","nullable":true,"enum":["finished","end_step","branch_end","opted_out","flow_deleted","version_removed","expired","canceled","error",null]},"messages_sent":{"type":"integer"},"messages_failed":{"type":"integer"},"timeline":{"type":"array","items":{"type":"object","properties":{"at":{"type":"string","format":"date-time"},"type":{"type":"string","enum":["started","sent","send_failed","send_unconfirmed","send_skipped","wait_started","wait_done","waiting_for_event","event_received","timed_out","condition_met","condition_not_met","ended"]},"step_id":{"type":"string"},"detail":{"type":"string"},"message_id":{"type":"string"}}}},"started_at":{"type":"string","format":"date-time"},"ended_at":{"type":"string","format":"date-time","nullable":true}}},"Template":{"type":"object","properties":{"id":{"type":"string","example":"tpl_a1B2c3D4e5F6"},"object":{"type":"string","enum":["template"]},"name":{"type":"string"},"body":{"type":"string","description":"Merge fields: {{name}}, {{first_name}}, {{phone}}, {{field:<custom>}}"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}}}}}