Skip to content

Authentication

The public API authenticates with an API key in the X-API-Key header.

bash
curl http://api.localhost/v1/contacts \
  -H "X-API-Key: $NOTIFYZR_API_KEY"

Keys are organization-scoped

A key belongs to exactly one organization. Everything it can read or write belongs to that organization, and there is no parameter that changes which one — so a leaked key exposes one organization's data and no more.

Mint, revoke and delete keys from the dashboard under Settings → API keys. Only the hash is stored, so the key itself is shown once at creation.

Revoking and deleting both stop a key working immediately; they differ in what is left behind. A revoked key stays in the list marked Revoked, so it is still visible that it existed and when it was issued. Deleting removes the row entirely — use it for a key that was created by mistake. Anything the key produced (messages, imports, activity entries) survives either way.

Expiry

A key may be given an expires_at. An expired or deactivated key is rejected exactly like an invalid one — with 401 and no hint about which it was.

Owners and admins are emailed as a key approaches expiry and again once it has passed, so a key does not lapse silently in a running integration.

What is not authenticated this way

SurfaceAuth
POST /v1/forms/{id}/submissionsNone. HTML forms post here directly; it is rate-limited instead.
The hosted unsubscribe pageNone. The signed ?t= token in the link is the authorisation.
The dashboardA JWT session, issued by the control-panel API. The browser never holds an API key.
The MCP serverThe same X-API-Key, relayed to this API. See MCP server.

Errors

A rejected request returns the platform's standard envelope:

json
{
  "success": false,
  "message": "Invalid or inactive API key",
  "status_code": 401,
  "error": { "code": "notifyzr.apikey.error.unauthorized", "message": "…", "issues": [] }
}

Match on error.code, not on the message — codes are stable, messages are not.

Released under the Apache 2.0 License.