application/problem+json:
code, never on type or the wording of detail. code is the
contract; title and detail are for people and we reserve the right to rewrite them.
instance identifies this one occurrence — it is an identifier, not a URL — and quoting
it is the fastest way for your Maat contact to find the request in our logs.
The codes
Two codes carry a typed extension member:
validation_failed adds errors[]
({ field, code } per rejected field — never the value you sent), and conflict adds
conflicts[] (every colliding date, untruncated). Parse those, not the prose.
One refusal, several causes — on purpose
Some errors deliberately do not say which of several checks failed. An unknown key, a revoked key and a wrong secret all answer the same401; a sandbox/production mismatch
and a workspace that does not resolve answer the same 403; a listing that does not
exist and one that belongs to somebody else answer the same 404. Distinguishing them
would let anyone enumerate credentials, tenants or another customer’s inventory. If you
are stuck on one of these, the answer is a conversation with your Maat contact, not more
requests.
The one exception
The token exchange (POST /v1/token) answers its refusals with a plain
{ "message": … } body rather than a problem document — you meet it once, at the start
of a session. An unexpected fault there is a problem document like everywhere else.
The
type URIs are stable identifiers, and code is what you should branch on. Do not fetch a
type URI at runtime or assume it serves content.