Get started

Authentication and API keys

Every request carries an API key. Test keys run the same checks as live keys without sending or charging anything, and each key’s permissions limit what it can reach.

Authentication

All API requests require a Bearer token in the Authorization header. Use test keys (mk_test_...) for development: no real SMS are sent and nothing is charged. Use live keys (mk_live_...) for production.

⚠️ Never expose API keys in client-side code or public repositories.

Test mode

A test key (mk_test_...) works from the moment your account is created, before any top-up. New accounts start PENDING_PAYMENT: a live key gets 402 PAYMENT_REQUIRED for everything except a few onboarding reads (GET /v1/accounts/me, GET /v1/accounts/:id, GET /v1/accounts/:id/topup-allowance and GET /v1/pricing); test keys pass regardless.

A test send runs the same recipient checks a live send does: CASL consent and opt-out, the API key's allow and deny lists, the reserved-exchange and undeliverable-number checks, and the Canadian-destination check. It skips owning the from number, owner phone verification, your balance, the sending limits, and the DNCL check (not enforced on any send yet). No message reaches a carrier: it is priced exactly like a live one, so you see the real cost, then the row is marked delivered.

A test key is a sandbox with no live reach: it cannot buy or release phone numbers, mint, rotate or revoke live keys, send the owner phone verification code, run right-to-erasure, change webhooks, file a volume or phone-number allowance request, or change a live key's lists or default-deny, and it cannot read the dead-letter queue. Message and verification history through a test key show test rows only. Contacts, groups, lists, consents and opt-outs stay writable, except one a live key's allow or deny list references: changing that needs a live key too.

API keys & permissions

Every API key carries a permissions object, one string of letters per resource: r (read, GET), w (write, POST), m (modify, PATCH or PUT), d (delete, DELETE). A missing resource or an empty string means no access to it. You can also send the key as an X-API-Key header instead of an Authorization Bearer token. A few routes need a less obvious letter: POST /v1/compliance/erasure needs compliance:d; POST /v1/accounts/:id/phone-verification and its /confirm need account:m; POST /v1/compliance/dncl/check needs compliance:r; DELETE /v1/contact-groups/:id/members/:contactId needs contact_groups:m; and DELETE /v1/accounts/:id/api-keys/:keyId/lists/:mode needs api_keys:m.

The resources a key's permissions object can name:

ResourceMeaning
messagesSend and read SMS messages
phone_numbersProvision, search and release phone numbers
contactsManage contacts
contact_groupsManage contact groups and broadcasts
listsManage the allow and deny contact lists
complianceCASL consents, opt-outs and DNCL checks
webhooksManage webhook endpoints
verifySend OTPs and check verifications
accountView or update the account profile and usage
api_keysManage API keys
emailsSend and read email
email_domainsManage email domains
email_suppressionsManage email suppression lists
email_templatesManage stored email templates

A new key defaults to the permissions of the key that created it: omit permissions entirely and it gets exactly what the caller has. Asking for more takes a password step-up (an X-Step-Up-Token header with the api_keys:elevate scope), or the request is refused with 403 PERMISSION_ESCALATION. A test key can only create test keys.