Errors & Rate Limits
The Platform API reports failures using the standard tRPC error envelope. This page covers its shape, the code-to-HTTP mapping, validation errors, and the platform's rate limits (which are feature-scoped, not global).
The error envelope
Errors are returned in the standard tRPC error shape and — like successful responses — are
superjson-wrapped. The useful fields live under error.data:
code— a machine-readable error code (e.g.UNAUTHORIZED,NOT_FOUND).httpStatus— the corresponding HTTP status.path— the<router>.<procedure>that failed.
{
"error": {
"message": "You must be signed in to do that",
"code": -32001,
"data": {
"code": "UNAUTHORIZED",
"httpStatus": 401,
"path": "orders.list"
}
}
}
Code → HTTP status
code |
httpStatus |
|---|---|
BAD_REQUEST |
400 |
UNAUTHORIZED |
401 |
FORBIDDEN |
403 |
NOT_FOUND |
404 |
INTERNAL_SERVER_ERROR |
500 |
UNAUTHORIZED typically means no valid credential resolved; FORBIDDEN means you are
authenticated but lack the permission or tenant/site scope — see
Sessions & Tokens.
Validation errors (zodError)
When input fails Zod validation, the envelope adds a data.zodError field: a flattened
map of field name → array of messages. The top-level code is BAD_REQUEST (HTTP 400).
{
"error": {
"data": {
"code": "BAD_REQUEST",
"httpStatus": 400,
"path": "clients.create",
"zodError": {
"fieldErrors": {
"email": ["Invalid email"]
},
"formErrors": []
}
}
}
}
Read zodError to surface per-field messages back to the caller.
Rate limits
There is no global tRPC rate limiter. Limits are feature-scoped and apply only to specific subsystems:
| Scope | Limit |
|---|---|
FeedApiKey |
Per-key rateLimit — requests per hour, configured on the key. |
| OIDC token endpoint | 20 requests / minute. |
| Device authorization | 5 requests / minute. |
Do not assume a platform-wide request quota. Outside the features above, the API does not
impose a general rate limit. The FeedApiKey per-hour limit is configured per key — see
Data Model → Feed API keys.