> ## Documentation Index
> Fetch the complete documentation index at: https://docs.masterid.co.ug/llms.txt
> Use this file to discover all available pages before exploring further.

# Errors

> Full reference of MasterID HTTP status codes, their meanings, retryability, and per-code troubleshooting guidance with Node.js and Python retry examples.

When a MasterID request fails, the response includes an HTTP status code and an JSON error envelope. Use the code to decide whether to retry, correct the request, or contact support.

## Status code reference

| Code | Meaning | Likely cause | Retry? |
| - | - | - | - |
| 200 | Completed | Request succeeded | No |
| 202 | Accepted / pending | Operation is processing asynchronously | Poll or wait for webhook |
| 400 | Invalid input | Missing field, invalid consent text, or malformed idempotency header | No (fix input first) |
| 401 | Unauthorized | Missing, invalid, or revoked API credentials | No (rotate credentials) |
| 403 | Forbidden | Suspended account, or Merchant ID does not match the authenticated workspace | No (check activation) |
| 404 | Not found | Unknown operation or resource outside your workspace | No |
| 409 | Conflict | Idempotency key already used with different input | No (use a new key only if input changed intentionally) |
| 413 | Body too large | Request body exceeds the maximum size | No (reduce payload) |
| 422 | Unprocessable entity | Insufficient wallet funds, merchant not activated, or other operation validation failed | No (resolve the condition) |
| 429 | Too many requests | Rate limit reached | Yes (respect Retry-After) |
| 500 / 503 | Server error / unavailable | Temporary processing failure or unavailability | Yes (with backoff, reuse same idempotency key) |

> [!WARNING]
>
> Never retry 400, 401, 403, 404, 409, 413, or 422 with the same request. These indicate a client-side or account condition that must be fixed first.

## Troubleshooting by code

> **400 Invalid input**
>
> Check that all required fields are present and valid. Confirm that `consentText` matches the exact wording returned by `POST /verifications/consent` and that `consentConfirmed` is `true`. Ensure the `Idempotency-Key` header is 8-100 characters and uses only allowed characters.

> **401 Unauthorized**
>
> Verify that `X-API-Key` and `X-API-Secret` are set and have not been regenerated or deleted. If credentials were rotated, update every integration immediately. Secrets are shown only once and cannot be recovered.

> **403 Forbidden**
>
> Confirm that your merchant account is activated for collections. For payment requests, ensure the `merchantId` in the body belongs to the same workspace that issued the API credentials.

> **404 Not found**
>
> Double-check the session ID or collection reference. Only records created in the issuing workspace are returned. Session IDs are 6-12 digits and are different from API request IDs.

> **409 Conflict**
>
> You reused an idempotency key but changed the request body. Retry with the exact same body, or generate a new key if the change was intentional. See [Idempotency](https://docs.masterid.co.ug/concepts/idempotency.md).

> **413 Body too large**
>
> Reduce the request body size. The maximum JSON body is 20,000 characters.

> **422 Unprocessable entity**
>
> Check your wallet balance with `GET /wallet` before submitting verifications or collections. For collections, confirm merchant activation in the MasterID app.

> **429 Too many requests**
>
> Read the `Retry-After` response header and wait before retrying. For sustained load, implement client-side throttling. See [Rate limits](https://docs.masterid.co.ug/concepts/rate-limits.md).

> **500 / 503 Temporary failure**
>
> These are retryable. Use exponential backoff and reuse the same idempotency key so the retried request is deduplicated safely.

## Retry logic in code

```typescript retry.ts theme={null}
import { setTimeout } from "timers/promises";

async function callWithRetry(
  operation: () => Promise<any>,
  idempotencyKey: string,
  maxAttempts = 3
) {
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
    try {
      return await operation();
    } catch (err: any) {
      const status = err.status as number;

      if (status === 429) {
        const delayMs = (err.retryAfter ?? 60) * 1000;
        console.log(`Rate limited. Waiting ${delayMs}ms…`);
        await setTimeout(delayMs);
        continue;
      }

      if (status >= 500 && status < 600) {
        if (attempt === maxAttempts) throw err;
        const backoff = Math.min(1000 * 2 ** attempt, 8000);
        console.log(`Server error ${status}. Backing off ${backoff}ms…`);
        await setTimeout(backoff);
        continue;
      }

      // Do not retry client errors
      throw err;
    }
  }
  throw new Error("Max retries exceeded");
}
```

```python retry.py theme={null}
import os
import time
import requests

BASE_URL = "https://masterid.me/api/public/v1"


def post_with_retry(path, payload, idempotency_key, max_attempts=3):
    headers = {
        "X-API-Key": os.environ["MASTERID_API_KEY"],
        "X-API-Secret": os.environ["MASTERID_API_SECRET"],
        "Content-Type": "application/json",
        "Idempotency-Key": idempotency_key,
    }

    for attempt in range(1, max_attempts + 1):
        res = requests.post(f"{BASE_URL}{path}", json=payload, headers=headers)

        if res.status_code == 200 or res.status_code == 202:
            return res.json()

        if res.status_code == 429:
            delay = int(res.headers.get("Retry-After", 60))
            print(f"Rate limited. Waiting {delay}s…")
            time.sleep(delay)
            continue

        if 500 <= res.status_code < 600:
            if attempt == max_attempts:
                res.raise_for_status()
            backoff = min(2 ** attempt, 8)
            print(f"Server error {res.status_code}. Backing off {backoff}s…")
            time.sleep(backoff)
            continue

        res.raise_for_status()

    raise RuntimeError("Max retries exceeded")
```

> [!TIP]
>
> For 429 responses, honour the `Retry-After` header. For 500/503 responses, back off exponentially and reuse the same idempotency key so retried submissions are deduplicated automatically.


## Related topics

- [API Overview](https://docs.masterid.co.ug/api-reference/overview.md)
- [Responses and status codes](https://docs.masterid.co.ug/concepts/responses.md)
- [Authentication](https://docs.masterid.co.ug/authentication.md)
- [Idempotency](https://docs.masterid.co.ug/concepts/idempotency.md)
- [Go-live checklist](https://docs.masterid.co.ug/guides/go-live-checklist.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.