Skip to main content
Every failure is a JSON object with a single message. The HTTP status is the machine-readable part; the string is for a person reading a log.

The order the checks run in

This decides which error you see when a request is wrong in more than one way, and it is the reason a malformed payload can come back as an authentication problem.
1

Authentication

Before the body is parsed at all.
2

Authorisation

IP allow list, then login type.
3

Content negotiation

The Accept header.
4

Parsing

JSON well-formedness, then field names, then field values.
5

Business rules

Sender ID, template, credit, rate limit.
Executed against a live 0.7.3 — an unparseable body with no credentials returns the authentication error, not the parse error:
Fix credentials before you debug a payload. A 401 or 403 tells you nothing about whether the rest of your request is right, because nothing downstream of authentication has run yet.

Authentication and authorisation

401 means “you did not identify yourself”; 403 means “you did, and it was not accepted.” The distinction is worth wiring into your alerting: a sudden 401 is usually a deployment that lost its environment variable, while a sudden 403 is usually a password rotation or an IP that moved.
An unknown path under /secure/ returns 401, not 404. Authentication runs before routing, so the API will not tell an unauthenticated caller which endpoints exist. Do not use a 404 to probe for endpoint availability — you will get 401 whatever you ask for.

Content negotiation

Omitting Accept entirely is fine. Sending Accept: text/plain is not, and this is the one error a browser address bar will reliably produce.

Parsing

412 for bad JSON, not 400. Most APIs use 400 for both, so a client that branches on the status will treat “your JSON is broken” as a network-level surprise. Both are permanent — do not retry either.

Business rules

There is no Retry-After on a 429. Back off on your own schedule — exponential, with jitter. A fixed-interval retry from many workers re-synchronises into the next burst.

Which of these should you retry?

A 500 is safe to retry precisely because the charge is reversed. That is a deliberate property of the ingress, not an accident: credit is taken before queueing and given back when queueing fails, so a retry costs one message, not two. See How billing works.

REST API overview

The envelope, authentication and the field-name rule.

Send one message

Every field, with its default and its refusal.