# Errors

> Status codes the Developer API returns, what causes each one, and which are worth retrying.

> For the complete documentation index, see [llms.txt](https://helpdesk.orangescrum.com/llms.txt).

Source: https://helpdesk.orangescrum.com/guide/api/errors

---
Errors use standard HTTP status codes and the same envelope as successful
responses, so you can parse both with one code path.

```json
{
  "success": false,
  "message": "Invalid signature"
}
```

## Status codes

- **200** — Success. Check the success field for soft failures.
- **201** — Created. Returned by the create endpoints, with the new record's UUID in data.
- **401** — Authentication failed — key, timestamp, nonce or signature.
- **403** — Authenticated, but the key's scopes do not cover this endpoint.
- **422** — Validation error — the decrypted payload is missing a field or has a bad value.
- **429** — Rate limit exceeded.
- **500** — Server error.

## 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\n` from 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.

```json
{
  "success": false,
  "message": "The project_id field is required."
}
```

Common causes:

- Passing a user UUID to `assigned_to`, which expects a **numeric** id
- A `priority` outside `low`, `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](https://helpdesk.orangescrum.com/guide/api/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.
