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

# Notifizz Error Codes

> All public Notifizz error codes — auth, HTTP, admin, enricher. With HTTP status mappings, when each fires, and how to fix it.

# Error catalogue reference

Every error Notifizz returns has a stable string code so you can match on it programmatically. This page lists the public catalogue: backend HTTP errors, enricher SDK errors, and the HTTP status each maps to.

## TL;DR

* Backend errors return `{ code: <stable-string>, ... }` in the body.
* Enricher errors (Node SDK) are typed exception classes — `EnricherInputValidationError`, `EnricherHmacVerificationError`, etc.
* HTTP status follows the family (`401` for auth, `400` for input, `404` for not-found, `500` for handler failure).

## Backend HTTP errors

### Authorization (`auth/*`, `authorization/*`)

| Code                                                      | Status | When                                                                                                             |
| --------------------------------------------------------- | ------ | ---------------------------------------------------------------------------------------------------------------- |
| `auth/tracking-secret-key`                                | `401`  | The `sdkSecretKey` header is missing, malformed, or doesn't resolve to an environment. (NoTrackAuthorization)    |
| `auth/invalid-authorization`                              | `403`  | The `sdkSecretKey` did not match any environment. Most common on `POST /v1/events/track`. (InvalidAuthorization) |
| `auth/invalid-token-signature`                            | `401`  | A widget JWT failed signature verification (Firebase, PubliclySignedJwt).                                        |
| `auth/invalid-api-key`                                    | `401`  | The Front API Key in the widget request is unknown.                                                              |
| `auth/no-public-key-registered`                           | `401`  | `PubliclySignedJwt` strategy was selected but the environment has no public key configured in the dashboard.     |
| `auth/invalid-inputs`                                     | `400`  | A widget auth call (`/v1/users/auth`) is missing required JWT paths or fields.                                   |
| `auth/onboarding-not-completed`                           | `403`  | The organisation hasn't finished its onboarding — the surface in question is gated until onboarding completes.   |
| `authorization/organisation-not-authorized-for-this-user` | `403`  | The dashboard user doesn't have a role on the target organisation.                                               |
| `authorization/unauthorizedConfigIdForOrganisation`       | `404`  | A request referenced a `configId` that belongs to a different organisation.                                      |

### HTTP / functional (`other/*`, `notification/*`)

| Code                            | Status | When                                                                             |
| ------------------------------- | ------ | -------------------------------------------------------------------------------- |
| `other/unknown`                 | `500`  | Catch-all for unexpected backend errors. The body's `message` field has details. |
| `notification/config-not-found` | `404`  | A request referenced a `notificationConfigId` that doesn't exist.                |

### Admin / event (`event/*`, `region/*`, `organization-settings/*`)

| Code                                   | Status | When                                                                                                                         |
| -------------------------------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------- |
| `event/not-found`                      | `404`  | A dashboard request referenced an event id that doesn't exist.                                                               |
| `event/invalid-name`                   | `400`  | An event name failed the grammar (see [Event Tracking](/docs/sdks/event-tracking/overview)). Body includes a `reason` field. |
| `region/already-set`                   | `409`  | The organisation's region was already chosen and cannot be changed.                                                          |
| `organization-settings/already-exists` | `409`  | Tried to create org settings for an org that already has them.                                                               |

## Enricher SDK errors (Node)

The Node SDK (`@notifizz/nodejs`) ships typed exception classes for the enricher subsystem. `client.dispatch(body)` encodes them in the response body (`{ ok: false, error: { code, message } }`) and the Notifizz backend gateway translates `error.code` back to typed domain errors. The SDK does NOT throw across the dispatch boundary — `Promise<DispatchResponse>` is the contract. The classes below are the registration-time and internal exceptions you may still want to catch in unit tests.

| Class                           | Body `error.code` (when it surfaces from dispatch) | When                                                                                              |                                                                                   |
| ------------------------------- | -------------------------------------------------- | ------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- |
| `EnricherRegistrationError`     | n/a (thrown synchronously at registration time)    | `client.enricher()` called with a duplicate name, empty name, invalid schema, or missing handler. |                                                                                   |
| `EnricherInputValidationError`  | `input-invalid` (with `details`)                   | Incoming `params` failed the registered input schema.                                             |                                                                                   |
| `EnricherHmacVerificationError` | `401`                                              | `missing-header` / `bad-signature` / `stale-timestamp`                                            | HMAC verification failed. The `reason` discriminant tells you which check failed. |
| `EnricherNotFoundError`         | `404`                                              | `enricher-not-found`                                                                              | `POST ?name=foo` for a name that wasn't registered on this client.                |
| `EnricherHandlerError`          | `500`                                              | `handler-error`                                                                                   | The user-supplied handler threw. The `cause` field carries the original error.    |
| (any other thrown error)        | `500`                                              | `internal`                                                                                        | Catch-all.                                                                        |

The error classes themselves are exported from `@notifizz/nodejs` so you can `instanceof`-check them in your own logging or error reporting.

```ts theme={null}
import { EnricherInputValidationError, EnricherHmacVerificationError } from "@notifizz/nodejs";

try {
  // ...
} catch (e) {
  if (e instanceof EnricherInputValidationError) {
    logger.warn({ details: e.details }, "enricher input mismatch");
  } else if (e instanceof EnricherHmacVerificationError) {
    logger.warn({ reason: e.reason }, "enricher HMAC failed");
  }
  throw e;
}
```

## Connector webhook errors

Connector webhooks return HTTP-level errors when signature verification fails — there's no per-provider error code, just the status. See [connector webhooks reference](/docs/sdks/how-to/connector-webhooks) for the per-provider details.

| Status         | When                                                                                                |
| -------------- | --------------------------------------------------------------------------------------------------- |
| `401`          | Signature verification failed (header missing, secret mismatch, or — for Stripe — timestamp drift). |
| `404`          | The `:sourceId` in the URL doesn't exist.                                                           |
| `200` (paused) | Source is paused in the dashboard — Notifizz acks the provider but drops the payload.               |

## SDK retry behaviour

All three event-tracking SDKs retry transient failures twice (`1s`, then `2s`) before bubbling. Errors that bubble:

* **Node** — original `axios` error after 3 attempts.
* **Java** — `IOException` (the last one) after 3 attempts.
* **PHP** — original `GuzzleHttp\Exception\GuzzleException` after 3 attempts.

Permanent errors (e.g. `403 auth/invalid-authorization`) still go through the retry loop today — improvements to short-circuit on permanent failures are tracked separately.

## FAQ

<AccordionGroup>
  <Accordion title="`InvalidAuthorization` — which secret?">
    `auth/invalid-authorization` on `POST /v1/events/track` means the `sdkSecretKey` (in the body and the `Bearer` header) didn't resolve to an environment. Three common causes: (1) wrong environment (dev key in prod or vice versa); (2) typo / extra whitespace; (3) the key was rotated in the dashboard and your service hasn't reloaded.
  </Accordion>

  <Accordion title="`input-validation-failed` from my enricher — what does the body look like?">
    The 400 response carries `{ error: { code: "input-validation-failed", message, details } }`. The `details` field is an array of validation issues — for Zod-derived schemas, it's the Zod `issues` array; for raw JSON Schemas, it's Ajv's `errors` array. Read the first entry to see which property failed.
  </Accordion>

  <Accordion title="`enricher-not-found` but it *is* registered — why?">
    The registry is per-client-instance. If you have two `NotifizzClient` instances and only register the enricher on one, dispatch on the other returns `{ ok: false, error: { code: "enricher-not-found" } }`. Make sure the same instance that called `enricher()` is the one whose `dispatch()` is wired in the controller.
  </Accordion>

  <Accordion title="Difference between 400 (`event/invalid-name`) and 422?">
    There is no 422 path on the public endpoints. `400` covers both grammar violations (event name) and DTO validation (`class-validator` rejecting malformed bodies). Status follows the family — read the `code` field to disambiguate.
  </Accordion>

  <Accordion title="What does `other/unknown` actually mean?">
    A backend exception that didn't get caught by a more specific handler. Treat it as "infrastructure issue, retry, escalate if persistent". The `message` field has the specific error message; the correlation id (in response headers) lets us trace it.
  </Accordion>

  <Accordion title="My `EnricherHmacVerificationError` keeps firing despite correct setup.">
    Read the `reason` field. `missing-header` means the HMAC headers weren't sent (the backend usually sends them — if missing, the request didn't come from Notifizz). `bad-signature` means the secret mismatches or the body was modified (often a JSON parse + re-serialise breaking the byte-for-byte match). `stale-timestamp` means clock drift outside ±5 minutes — NTP your enricher host.
  </Accordion>
</AccordionGroup>

## See also

<CardGroup cols={2}>
  <Card title="Event Tracking" icon="code" href="/docs/sdks/event-tracking/overview">
    Detailed error shapes for `POST /v1/events/track`.
  </Card>

  <Card title="Enrichers protocol" icon="puzzle-piece" href="/docs/sdks/event-tracking/enrichers-protocol">
    HMAC verification, input/output schemas.
  </Card>

  <Card title="Connector webhooks" icon="plug" href="/docs/sdks/how-to/connector-webhooks">
    Per-provider signature error semantics.
  </Card>

  <Card title="Troubleshooting" icon="wrench" href="/docs/operations/troubleshooting">
    Cross-cutting failure modes — symptom → cause → fix.
  </Card>
</CardGroup>
