API keys & environments

Every request carries a key in the x-api-key header. The key decides who you are, what you may do, and whether anything real happens.

Every request
x-api-key: msg_live_a6bd5aea...

Test and live

The environment is in the prefix, so it is obvious in a log or a config file. A msg_test_ key runs the same code as a live one but reaches nobody, so you can build the whole integration before anything is approved or anyone is messaged.

What each operation does under a test key — and the one that behaves the same either way — is on Test keys and the sandbox.

An environment cannot be changed after a key is created. Create a new key instead.

Scopes

A key gets one or more. Give each key the least it needs.

ScopeLets the key
otp:sendUse Verify — send and check codes
messages:sendSend messages and read their status
templates:manageCreate, validate and update templates
readRead-only access

A call outside a key's scopes fails with insufficient_scope.

Per-key limits

Each key can carry its own ceilings, set when you create it — see Set up your API access.

  • Requests per minute — burst control. Breaching it returns rate_limited with a Retry-After, and the SDK retries for you.
  • Monthly messages — messages per billing cycle. Breaching it returns key_limit_reached, which does not clear by waiting — raise the key's limit.
  • Spend cap — a ceiling on what this key can run up on your Meta bill. Breaching it returns spend_cap_reached.

These sit inside your plan's allowance, not on top of it: ten keys at 5,000 messages each on a plan allowing 15,000 means the plan runs out first. See Rate limits & quotas.

One key per customer

If you build software that many businesses use — clinic software, a booking platform, a CRM — issue each of them their own key rather than sharing one.

One shared keyA key per customer
One usage number for everyoneUsage per customer, for billing or support
One rate limit, sharedA noisy customer cannot starve the rest
A leak means rotating for everyoneRevoke one, nobody else notices
"Which customer sent this?" is unanswerableAnswered by the key on the request

Give each key that customer's name and only the scopes they need, and store the secret against your customer record. Start them on a test key, then swap in a live one — nothing in their code changes.

Two things to watch. Per-key limits come out of the same plan allowance, so the console warns you when they sum past it; over-allocating on purpose is fine, as long as it is on purpose. And everyone shares your one sending number, so recipients see your business, not your customer's.

A webhook fires before a key reaches its monthly limit, so you can warn the customer or raise the limit before anything breaks.

Losing a key

The secret is shown once, at creation, and cannot be retrieved afterwards. If it is lost or leaked, rotate it — you get a new secret and the old one keeps working for a grace period so nothing breaks mid-deploy.

See Rotate a key safely.