Skip to main content

Authentication

Every request carries one header:

Authorization: Bearer tix_live_...

There is no other authentication mechanism on the API. Keys are created in the dashboard under API Keys & SMTP, or through the API itself with a key holding api-keys:manage.

Reading a key

The prefix tells you which environment a key belongs to.

PrefixMeaning
tix_live_Sends real mail and consumes quota
tix_test_Simulated only, see Test mode

The prefix is checked against the key, so a live secret presented with a test prefix is rejected with authentication_failed. You cannot make a key behave differently by editing its prefix.

Scopes

A key grants only what it was created with. A request to a route the key does not cover returns insufficient_scope naming the scope it needed.

ScopeGrants
sendingSubmit, cancel, resend and pre-flight messages, and upload attachments
emails:readRead messages and their events
domains:manageAdd, verify, configure and remove sending domains
contacts:manageRead and write contacts and tags
webhooks:manageCreate and edit webhook endpoints, read and replay deliveries
suppressions:readRead the suppression list
suppressions:manageAdd and remove suppressions, and read them
analytics:readRead aggregate reporting
*Everything, including scopes added later

*:manage includes the matching *:read, so a key with suppressions:manage can also list suppressions.

warning

api-keys:manage is the one to think twice about. A key holding it can mint further keys, so it is not a limited key at all. Give it only to something that genuinely provisions credentials.

Domain scoping

Three scopes can be narrowed to particular domains: sending, emails:read and domains:manage. A key scoped that way can send from those domains and no others, which is how you give a subsystem the ability to send transactional mail without also letting it send as your marketing domain.

A request outside the allowed set returns domain_scope_restricted.

IP allow-lists

A key can be limited to a set of CIDR ranges. Requests from anywhere else are rejected with api_key_ip_restricted, which turns a stolen token into a much smaller problem.

Leave the list empty and the key works from anywhere. Only set it when your sender's egress address is genuinely stable, because a cloud provider rotating your NAT address will take your sending down.

Expiry

Every key has an expiry date and there is no way to create one without. This is deliberate: a credential nobody ever rotates is one nobody ever notices has leaked.

An expired key returns api_key_expired rather than a generic authentication failure, so it is obvious from the response what happened.

Rotate in this order: create the replacement, deploy it, confirm it is being used from its request count, then delete the old one. Deleting first means an outage that lasts as long as your deploy.

If a key leaks

Delete it. There is no rotation and no grace period, because a leaked key is valid until it stops existing. A key that has ever been in a repository, a build log or a support ticket should be treated as public.