---
title: Errors & rate limits
description: "Reference: HTTP status codes (4xx vs 5xx), error envelope shape, free-tier daily call caps, and 429 retry guidance with exponential back-off."
canonical: "https://www.tracepass.eu/docs/errors"
locale: en
source: "https://www.tracepass.eu/docs/errors"
---

# Errors & rate limits

> Reference: HTTP status codes (4xx vs 5xx), error envelope shape, free-tier daily call caps, and 429 retry guidance with exponential back-off.

Every error response carries the same JSON envelope, the HTTP status code matches the semantics, and the error string is stable enough to switch on. The list below is exhaustive — if you hit something not on it, please report it to [support@tracepass.eu](mailto:support@tracepass.eu).

## Envelope

```json
{
  "error": "Validation error",
  "details": {
    "fieldErrors": {
      "model": ["Required"]
    }
  }
}
```

`error` is always present and is a stable string — switch on it in your client. `details` is optional; when present its shape depends on the specific error (a flattened Zod result for validation, plan/usage numbers for an overage, role / permission name for a 403).

## Status codes

| Status | When                                                                                                                                                                                        | Retry?                                       |
| ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------- |
| 400    | Validation error — body or query failed schema check; or unknown field key on a passport PATCH.                                                                                             | No (fix the request)                         |
| 401    | Missing or revoked API key, or session expired.                                                                                                                                             | No (mint a new key / re-auth)                |
| 402    | DPP quota exhausted on a passport-create call. Retry with \`confirmOverage: true\` if the plan supports overage. Also fired when an active subscription is required but past\_due/canceled. | Yes, with \`confirmOverage: true\`           |
| 403    | Plan-gate (e.g. v1 API on a Free plan) or workspace permission denied.                                                                                                                      | No                                           |
| 404    | Resource doesn't exist or is in a different workspace.                                                                                                                                      | No                                           |
| 409    | Conflict — duplicate model on a product, GTIN registered to a different tenant, serial collision within a GTIN.                                                                             | Sometimes (resolve the conflict, then retry) |
| 410    | Resource was permanently removed (rare; mostly applies to public passport URLs after archive).                                                                                              | No                                           |
| 413    | Payload too large — applies to multipart image uploads (5 MB cap).                                                                                                                          | No                                           |
| 415    | Unsupported media type — image upload that isn't PNG / JPG / WebP.                                                                                                                          | No                                           |
| 422    | Idempotency-Key reused with a different request body.                                                                                                                                       | No (generate a fresh key)                    |
| 423    | Resource is suspended (public passport viewer only).                                                                                                                                        | No                                           |
| 429    | Rate limit exceeded — daily writes or daily passport reads.                                                                                                                                 | Yes (after the next UTC midnight)            |
| 500    | Unhandled server error. We're paged.                                                                                                                                                        | Yes (with backoff)                           |
| 503    | Maintenance window or overload. Returns Retry-After.                                                                                                                                        | Yes (after Retry-After)                      |

## Retry guidance

For the “Yes” rows above: exponential backoff with jitter, capped at six attempts. We don't accept retries more aggressive than once-per-second on a single endpoint — anything faster won't hit the database (in-memory rate limiter rejects), but you'll burn your daily-write budget. The`Retry-After` header is set on every `503`; the rate-limit reset for `429` is always the next 00:00 UTC.

**Tip —** Pair every retry with the same `Idempotency-Key` as the original — that's how you avoid double-creating a passport when the original write succeeded but the response was lost.

## Common error strings

| Error string                                                                                                                                  | Status | Meaning                                                                                                                                              |
| --------------------------------------------------------------------------------------------------------------------------------------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| Validation error                                                                                                                              | 400    | Schema check failed. details.fieldErrors carries the per-field reasons (Zod flatten()).                                                              |
| Invalid field key: <key>                                                                                                                      | 400    | PATCH .../fields/<key> referenced a key that's not on the passport's template.                                                                       |
| No template found for category: <slug>                                                                                                        | 400    | The category you sent isn't seeded — check the spelling against the public Buyer's Guide.                                                            |
| A product with this model already exists for your company                                                                                     | 409    | Model strings are unique within a workspace. Pick a different model or update the existing product.                                                  |
| This GTIN is already registered by another company. Contact support if this is an error.                                                      | 409    | GTINs are globally unique across tenants — two companies can't both own the same GTIN.                                                               |
| Serial number already exists for this GTIN                                                                                                    | 409    | Pick a different serial. Within a single GTIN, serials must be unique.                                                                               |
| Idempotency-Key has already been used with a different request body. Use a new key for the new request, or reuse the original body.           | 422    | You sent the same key twice with different bodies. Generate a fresh key.                                                                             |
| overage\_required                                                                                                                             | 402    | DPP quota exhausted. Body carries planLimit + currentUsage + extraPriceCents. Retry with \`confirmOverage: true\` to accept the per-passport charge. |
| API rate limit: N writes/day via /api/v1\. Currently X today; requested Y. Retry tomorrow (UTC) or upgrade your plan.                         | 429    | Daily v1 write budget exhausted. Wait or upgrade.                                                                                                    |
| API rate limit: N passports/day read via /api/v1\. Currently X today; requested Y. Retry tomorrow (UTC) or contact support for a bulk export. | 429    | Daily v1 passport-read budget exhausted. Wait, upgrade, or use the dashboard JSON-LD bulk export.                                                    |
| Passport not found                                                                                                                            | 404    | Wrong ID or wrong workspace. Verify the API key matches the workspace that owns the passport.                                                        |

We don't change the wording of an error string in a minor version. If we need a clearer string we add a sibling `code` field in the envelope (numeric, namespaced) and announce it in the regulatory changelog. Stable strings let you switch with confidence.
