For AI agents: a documentation index is available at /llms.txt. A markdown version of this page is available at /guide/api/errors.md.
Errors
Status codes the Developer API returns, what causes each one, and which are worth retrying.
Errors use standard HTTP status codes and the same envelope as successful responses, so you can parse both with one code path.
{
"success": false,
"message": "Invalid signature"
}
#Status codes
#401 Unauthorized
By far the most common error while building an integration, and it covers five
distinct causes. The message field tells you which:
| Message | Cause | Fix |
|---|---|---|
| Missing required header | One of the four auth headers is absent | Send X-API-KEY, X-TIMESTAMP, X-NONCE, X-SIGNATURE |
| Invalid API key | Key unknown, inactive, expired or revoked | Check the key in the admin portal |
| Timestamp outside window | Clock drift beyond ±5 minutes | Sync the machine clock with NTP |
| Nonce already used | Nonce replayed within 10 minutes | Generate fresh randomness per request |
| Invalid signature | Canonical string or body hash mismatch | See below |
#Debugging an invalid signature
Nine times out of ten the body hash is the culprit. Work through these in order:
Hash the bytes you send
Serialise the JSON body once into a string, hash that string, and send that same string. Re-serialising between hashing and sending changes key order or whitespace and invalidates the signature.
Check the separator
The canonical string is joined with
\n(LF). A\r\nfrom a Windows here-doc or a template literal will not match.Check the path
Use the path only —
/api/v1/partner/tasks/list— with a leading slash and no host, no query string, no trailing slash.Check the field order
METHOD,PATH,TIMESTAMP,NONCE,SHA256(BODY). Method uppercase.Check the encoding
The signature is hex, lowercase. Base64 will be rejected.
Tip
Call /api/v1/partner/validate while debugging. It exercises the full signing
path with an empty payload, which removes the body hash from the equation —
if validate succeeds but a real call fails, the problem is in your body
serialisation.
#403 Forbidden
The key authenticated but its scopes do not cover the endpoint. Scopes are set
when the key is issued; a key with * reaches everything. Ask your account
manager to widen the scope, or use a key that already has it.
#422 Validation error
The signature was fine and the payload decrypted, but its contents failed validation — a missing required field, an unknown status value, or a UUID where an integer id was expected.
{
"success": false,
"message": "The project_id field is required."
}
Common causes:
- Passing a user UUID to
assigned_to, which expects a numeric id - A
priorityoutsidelow,medium,high,urgent - A date that is not
YYYY-MM-DD - Referencing a record in a project the key's workspace cannot see
#429 Too Many Requests
You have exceeded 120 requests in a minute or 5,000 in a day. The response carries headers telling you how long to wait:
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 0
Retry-After: 60
See Rate limits for how to stay under them.
#500 Server error
Something failed on our side. These are logged with a correlation id.
#What to retry
Retry rules
Retry 429 after Retry-After, and 500 with exponential backoff and
jitter — but generate a fresh timestamp, nonce and signature for every
attempt. Replaying the original headers will be rejected as a reused nonce.
Never retry 401, 403 or 422 — they are deterministic and will fail
identically until you change the request.
Retries are not idempotent
The create endpoints have no idempotency key. A retried tasks/create that
actually succeeded the first time will create a second task. Confirm with a
search before retrying a write that timed out.
#Audit trail
Every request, successful or not, is recorded with its status code, response time, calling IP and endpoint — the same trail the MCP server writes to. If a call behaves unexpectedly in production, that log is the place to start.