Errors
The API uses conventional HTTP status codes and returns a JSON error body. There are three error shapes.
Error shapes
Section titled “Error shapes”Validation errors (400) return an error string plus a details array, one entry per invalid field:
{ "error": "Validation failed", "details": [ { "field": "clientId", "message": "Invalid client ID format" }, { "field": "firstName", "message": "First name is required" } ]}Most other errors return an error label and a human-readable message:
{ "error": "Unauthorized", "message": "Invalid organization authentication token"}Some errors return just an error label, with no message. An unmatched
route and an unexpected server error are both this shape, so treat message as
optional on every error:
{ "error": "Not Found"}{ "error": "Internal Server Error"}Status codes
Section titled “Status codes”| Status | error label |
When it happens |
|---|---|---|
400 |
Validation failed / Bad Request / Invalid request |
Request body, params or query failed schema validation; a path parameter is not a valid resource reference; malformed or expired token on revoke; missing X-Idempotency-Key on a request that requires one. |
401 |
Unauthorized |
Missing/invalid/expired/revoked token, missing x-client-id, x-client-id ≠ token, invalid credentials, no signing key registered, or failed signature / Content-Digest verification. |
403 |
Forbidden |
Client IP is not in the credential’s allowlist (trustedIps). |
404 |
Not found / Transaction not found / Not Found |
The resource does not exist or does not belong to your organization. An unmatched route returns { "error": "Not Found" } with no message. |
409 |
Conflict |
A concurrent request with the same X-Idempotency-Key is still in flight. Retry once it settles. |
422 |
Unprocessable Entity |
An X-Idempotency-Key was reused with a different request payload. |
429 |
Too Many Requests |
A rate limit was exceeded — the global 300/min budget, or the stricter 10 per 15 min on POST /v1/oauth/token. Both are keyed per IP. Honour Retry-After. |
500 |
Internal Server Error |
Unexpected server error, and any request body the parser rejects — see Request body limits. The body is the error label alone — Pipevest never returns an exception message, stack frame or database detail to a partner. Safe to retry idempotent requests with backoff. |
503 |
Service Unavailable |
A dependency the endpoint needs is not configured or not reachable. Retry with backoff. |
Request body limits
Section titled “Request body limits”JSON request bodies are capped at 100 kB (102,400 bytes). A body over that limit, and a body that is not well-formed JSON, are both rejected before the request reaches any route.
Both cases answer 500 with the bare Internal Server Error label — not
400, and not 413:
{ "error": "Internal Server Error"}Send Content-Type: application/json exactly. A +json suffix type such as
application/vnd.api+json is not parsed at all, and the request then fails
validation at the root with an empty field:
{ "error": "Validation failed", "details": [ { "field": "", "message": "Invalid input: expected object, received undefined" } ]}An empty field always means the failure is about the body as a whole rather
than about one property, so key your error handling on field being empty rather
than assuming every entry names a property.
Common examples
Section titled “Common examples”Missing x-client-id header (401)
{ "error": "Unauthorized", "message": "x-client-id header is required"}x-client-id does not match the token (401)
{ "error": "Unauthorized", "message": "Client ID mismatch"}Client IP not allowlisted (403)
{ "error": "Forbidden", "message": "Client IP not allowed"}Rate limit exceeded (429)
{ "error": "Too Many Requests", "message": "Too many requests. Please slow down and try again shortly."}The token endpoint has its own limiter and its own message — see Rate Limits.
Request not signed, or signature does not verify (401)
{ "error": "Unauthorized", "message": "Signature verification failed"}Returned when Signature / Signature-Input are missing, malformed, expired, cover a component the request does not carry, or do not verify against your registered key.
Body does not match the digest (401)
{ "error": "Unauthorized", "message": "Content-Digest verification failed"}No public key registered for your credential (401)
{ "error": "Unauthorized", "message": "No signing key registered for this client"}You cannot fix this one by re-signing — your credential has no public key on file. Contact engineering.support@pipevest.com to register or rotate it.
Missing idempotency key on a mutation (400)
{ "error": "Bad Request", "message": "X-Idempotency-Key header is required for this request"}