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 and the sending limits. 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. It also cannot change or delete a live sending domain, change the profile of a verified business, or publish, roll back or delete an email template. Message and verification history through a test key show test rows only. Contacts, groups, lists, consents, opt-outs and email suppressions are kept per key mode: a test key lists and changes its own test ones and never sees the live ones.

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; 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 erasure
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 message templates and publish versions
pushManage push apps, send and read pushes, list devices
push_devicesRegister and unregister the devices your users receive pushes on
recipientsRead, create, change and delete recipient profiles
notificationsSend notifications and read the notification log
auth_appsManage sign-in apps
auth_usersRead and manage sign-in users, their sessions, sign-in history and exports
auth_organizationsManage sign-in organizations, memberships and invitations

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.