Developers
REST errors
Every REST error has the same JSON shape, so a client can handle them all in one place.
The error shape
{
"error": {
"type": "permission",
"code": "insufficient_scope",
"message": "This route needs the deliverables:read scope, which this token does not hold.",
"param": "deliverables:read"
},
"request_id": "req_4f9aXc2b7d3e8f0a"
}typeis the broad class to branch on:invalid_request,authentication,permission,not_found,conflict,rate_limit,plan,server.codeis the specific reason, listed below.paramappears when one query parameter, field or scope is at fault, and names it.request_idis also sent as thex-request-idheader on every response, errors or not. Quote it when you contact support.
Error codes
| Code | HTTP | Type | Meaning |
|---|---|---|---|
invalid_parameter | 422 | invalid_request | A query parameter or body field is unknown or has a bad value. param names it. |
invalid_body | 422 | invalid_request | The request body is not a JSON object, or a PATCH changes nothing. |
invalid_idempotency_key | 422 | invalid_request | The Idempotency-Key header is empty, longer than 255 characters, or has spaces or non-ASCII characters. |
missing_token | 401 | authentication | No Authorization: Bearer header was sent. |
invalid_token | 401 | authentication | The token is unknown, revoked or expired, or the admin who made it is no longer an active owner or admin. |
plan_inactive | 402 | plan | The workspace's plan is not active. Reads and writes both stop until an owner chooses a plan. |
insufficient_scope | 403 | permission | The token does not hold a scope this route needs. param names the scope. |
plan_limit | 403 | plan | The workspace's plan does not allow another active project. An owner can change the plan in Settings. |
not_link_owner | 403 | permission | Only the token that created an external link can change or delete it. Links made in the app, or by another token, are read-only to you. |
not_found | 404 | not_found | No such record in this workspace. Another workspace's ids, deleted records and unknown ids all answer this way. |
project_closed | 409 | conflict | The project was closed through Wrap. Nothing on it can change until someone reopens it in Production Engine. |
link_exists | 409 | conflict | The project already has a link with this provider and external_id. The message names the existing link's id. |
idempotency_mismatch | 409 | conflict | This Idempotency-Key was already used for a different request. Use a new key for a new request. |
idempotency_in_progress | 409 | conflict | A request with this Idempotency-Key is still running. Retry after it finishes. |
rate_limited | 429 | rate_limit | Too many requests for this token. Wait the number of seconds in Retry-After. |
internal_error | 500 | server | Something failed on our side. Quote the request_id when you contact support. |
Retries and idempotency
A network error can leave you unsure whether a POST landed. Send an Idempotency-Key header, any string of up to 255 visible ASCII characters that you make up per request (a UUID works), and retry with the same key and the same body as often as you need:
curl -X POST https://production-engine.com/api/v1/projects/cmexampleproject01/links \
-H "Authorization: Bearer $PE_TOKEN" \
-H "Idempotency-Key: 4d1c9a2e-link-drive_8f2k" \
-H "Content-Type: application/json" \
-d '{"provider":"shade","external_id":"drive_8f2k","url":"https://app.shade.inc/example/drive_8f2k"}'- The first request runs. A retry within 24 hours gets the first response back, with the same status and body and an
Idempotent-Replayed: trueheader, and writes nothing. - The same key with a different path or body answers 409
idempotency_mismatch. Use a new key for a new request. - A retry that arrives while the first request is still running answers 409
idempotency_in_progress. Wait a moment and retry. A request that never finished frees its key after 60 seconds. - A request that fails (any 4xx or 5xx) does not keep its key, so you can fix the body and retry with the same key.
- Keys belong to one token. Another token's key with the same value is a separate request. After 24 hours a key can be used again as new.
- PATCH and DELETE take no key: repeating a PATCH sets the same values again, and repeating a DELETE answers 404.