Notideus
API Reference

Errors

The error envelope and every error code the API can return.

The envelope

Every failure — on any endpoint — returns the same JSON envelope with the failure's HTTP status:

{
  "error": {
    "code": "…",
    "message": "…"
  }
}
  • code is a stable machine-readable string (quota_exceeded, contact_exists, …) — switch on this.
  • message is a human-readable explanation in English. It is not contractual and may evolve.

When several things are wrong at once, only the last detected error is reported — fix and re-submit to see the next one.

Error codes

StatusCodeWhen
400bad_requestMalformed JSON, missing field, invalid cursor/limit
400invalid_fromFrom address cannot be parsed
400batch_too_large>100 emails/messages per batch; >1000 contacts per bulk upsert
400invalid_phoneNon E.164 phone in WhatsApp item
400invalid_paramsMissing/unknown template parameter slot
400template_disabled / template_unsupportedWhatsApp template not sendable
401unauthorizedMissing/invalid/revoked key, or session token where a key is required
402quota_exceededSend or contact allowance reached
429rate_limitedSend rate limit exceeded (free: 1 email/s, paid: 10/s, burst 5×) — retry after the Retry-After response header (seconds); batch items carry this code per item without the header
403insufficient_scopeKey lacks the route's scope
403domain_not_allowedKey bound to a different domain than the From address resolves to
404contact_not_foundNo contact with that email
404not_founde.g. unknown WhatsApp template_id
409contact_existsDuplicate create
409contact_suppressedbounced/complained contact cannot be resubscribed
422from_domain_not_verifiedFrom domain matches no verified team domain
500internalUnexpected server error

Per-item errors in batch and bulk responses

Batch and bulk endpoints never fail wholesale for one bad item. Each item in the response is independently either an acceptance or an error, using the same code/message pair:

{
  "index": 2,
  "error": {
    "code": "from_domain_not_verified",
    "message": "no verified domain matches the from address"
  }
}

index is the item's position in the request array (0-based). See Sending batches for the full decode.

Worked examples

401 unauthorized — no key, an invalid key, or a session token where an API key is required:

{
  "error": {
    "code": "unauthorized",
    "message": "missing or invalid api key"
  }
}

403 insufficient_scope — a valid key without the route's scope:

{
  "error": {
    "code": "insufficient_scope",
    "message": "api key lacks the emails:send scope"
  }
}

409 contact_exists — creating a contact whose email already exists:

{
  "error": {
    "code": "contact_exists",
    "message": "contact already exists"
  }
}

400 batch_too_large — more than 100 emails in one batch call:

{
  "error": {
    "code": "batch_too_large",
    "message": "batch is limited to 100 emails"
  }
}

429 rate_limited — sending faster than the plan's rate (1 email/s on the free plan, 10/s on paid plans, with a 5× burst). The response carries a Retry-After header with the seconds to wait. A real response:

{
  "error": {
    "code": "rate_limited",
    "message": "send rate limit exceeded; retry after 1 seconds"
  }
}

In a batch call the limit is applied per item: once the bucket is empty, the remaining items fail with rate_limited in their per-item result (no Retry-After header). See Quotas and limits.

Switch on code, never on message. code values are stable API surface; message strings are diagnostics that can change at any time.
Copyright © 2026