The success envelope
A success body always carriessuccess: true, the operation that names the payload shape, the payload itself, and a meta block.
A
search response in full:
items[]splitsitemfromincluded. Theitemis always the same typed shape; relations you request viaincludearrive next to it underincluded, never mixed into it.countscovers the full filter scope, not the page. With 50 rows shown out of 1,000 matching,counts.total_countis 1000, andcounts.groupsbreaks the same scope down by the fields the entity groups on.applied_filtersechoes what the server actually applied, so a client (or an AI agent) can confirm the query it asked for is the query that ran.meta.trace_idis the support handle. It is a UUID v7, identical to theX-Trace-Idresponse header. Send your ownX-Trace-Id(a UUID or a 32-hex trace id) and the server keeps it. Quote it when reporting an issue.
The error envelope
Every failure, from any service, is the same shape:codeis always one of the 16 values below, never an arbitrary string.recoverablesays whether retrying with corrected input can succeed (true) or the state itself is the problem (false).suggestionis a sentence written for a person or an LLM to act on.field_errorsappears only onvalidation_failed, keyed by field path. Each entry’sruleis the machine reason and itsmessagethe sentence for it.- On
conflict,rate_limited,payment_requiredandservice_unavailablethe machine reason iserror.context.reason, and the ids, dates and limits it refers to sit next to it inerror.context(aretry_after, thestatusa state machine found, thesidof the row that already exists). delete_blockederrors additionally carryerror.blockers[]: each blocker names itstype, ahardorsoftseverity, and theresolutionthat clears it.
The 16 error codes
A practical mapping: retry
rate_limited and service_unavailable with backoff, fix and resend validation_failed and relation_not_found, treat the 409 family as “re-read the current state first”, and never auto-retry not_implemented or internal_error.
A refusal over MCP is a tool result
On the MCP server the same envelope arrives inside the tool result: HTTP 200,isError: true, the envelope in structuredContent and the same facts in the text block, ending with the trace id. A client reads isError and error.code, never the text. The HTTP status speaks only before a tool runs: 401 with the OAuth challenge, 403 on a mount the token’s actor may not use, 404 for an unknown mount, 413 for an over-long batch.