Errors

Errors and rate limits

Every error is an RFC 7807 application/problem+json body, with the standard title, status, detail, and instance fields alongside an errorCode extension. Branch on errorCode, not on detail - the message text can change; the code is the contract.

A key that has been revoked - 401

{
  "title": "Unauthorized",
  "status": 401,
  "detail": "API key is invalid or revoked.",
  "instance": "/api/v1/notifications/send",
  "errorCode": "api_keys.invalid",
  "correlationId": "0f2b8a1e-6c9d-4b3a-9e7f-2d4c8a1b6e3f",
  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
}

Validation failures

A send or preview request that fails validation always carries the umbrella code notifications.validation_failed as its errorCode, so the documented promise that every error has one holds without exception. Alongside it, an errorCodes array lists every specific rule that actually fired - a single request can trip more than one at once, for example a missing tags field and an invalid recipient together. Branch on the umbrella code to detect "this request was malformed," then read errorCodes for which of the codes below caused it. The standard errors field is still present too, mapping each request property to its message text - useful for surfacing something to a human, but not for branching on.

Two rules fired at once - 400

{
  "title": "One or more validation errors occurred.",
  "status": 400,
  "errors": {
    "Tags": ["Tags collection is required."],
    "EmailOptions.To[0]": ["To recipient must be a valid email address."]
  },
  "errorCode": "notifications.validation_failed",
  "errorCodes": [
    "notifications.tags_required",
    "notifications.email.to_invalid"
  ],
  "instance": "/api/v1/notifications/send",
  "correlationId": "0f2b8a1e-6c9d-4b3a-9e7f-2d4c8a1b6e3f"
}

Every error code

CodeStatusMeaning
api_keys.invalid401The header was present, but the key doesn't resolve, or it has been revoked or has expired.
api_keys.missing401No X-Api-Key header was sent with the request.
api_keys.tenant_mismatch403The key is valid, but it belongs to a different workspace than the tenantId in the request body.
notifications.monthly_limit_exceeded429The workspace has used its monthly message allowance for the current billing period.
notifications.monthly_limit_not_configured403The workspace's plan has no monthly message limit configured, so the send cannot be authorized. Contact support.
notifications.no_live_version400The template has no version currently live. Publish one, or pin an explicit versionNumber to send or preview a specific version.
notifications.request_missing400The request body was empty or missing entirely.
notifications.tag_key_required400A tag's key was empty or missing.
notifications.tag_key_too_long400A tag's key was longer than 256 characters.
notifications.tags_required400No tags array was supplied on the request. An empty array is fine - the field just can't be missing.
notifications.template_id_required400No templateId was supplied on the request.
notifications.template_not_found404No template matches the given templateId in this workspace.
notifications.template_version_is_draft400The pinned versionNumber exists but is still a draft. Publish it before sending or previewing.
notifications.template_version_not_found400The pinned versionNumber doesn't exist for this template.
notifications.tenant_id_required400No tenantId was supplied on the request.
notifications.unsupported_notification_type400Preview was called on a template that isn't an email template - only email templates can be previewed.
notifications.validation_failed400The umbrella code for a failed send or preview validation. Read the errorCodes array alongside it for the specific rules that fired - see Validation failures below.
notifications.version_number_invalid400versionNumber was supplied but is not greater than zero.
notifications.webhook_delivery_not_available403The workspace's plan does not include webhook delivery.
notifications.webhook_destination_invalid400The webhook destination URL or headers were rejected - for example, the URL targets a disallowed host.
notifications.webhook_destination_required400A webhook template was sent without webhookOptions.urlOverride.

Email field codes

Per-recipient validation on emailOptions. to, cc, bcc, and overrideRecipients are each checked only once you send a non-empty list for that field - omitting the field entirely never triggers these.

CodeStatusMeaning
notifications.email.bcc_empty400An entry in emailOptions.bcc is empty or blank.
notifications.email.bcc_invalid400An entry in emailOptions.bcc is not a valid email address.
notifications.email.cc_empty400An entry in emailOptions.cc is empty or blank.
notifications.email.cc_invalid400An entry in emailOptions.cc is not a valid email address.
notifications.email.from_address_invalid400emailOptions.fromAddress is not a valid email address.
notifications.email.override_recipient_empty400An entry in emailOptions.overrideRecipients is empty or blank.
notifications.email.override_recipient_invalid400An entry in emailOptions.overrideRecipients is not a valid email address.
notifications.email.reply_to_invalid400emailOptions.replyTo is not a valid email address.
notifications.email.to_empty400An entry in emailOptions.to is empty or blank.
notifications.email.to_invalid400An entry in emailOptions.to is not a valid email address.

Attachment field codes

Per-attachment validation on emailOptions.attachments, checked for each entry in the array.

CodeStatusMeaning
notifications.attachment.content_invalid_base64400An attachment's content is not valid base64.
notifications.attachment.content_required400An attachment's content is empty or missing.
notifications.attachment.content_too_large400An attachment's decoded content is larger than 10 MB.
notifications.attachment.content_type_required400An attachment's contentType is empty or missing.
notifications.attachment.content_type_too_long400An attachment's contentType is longer than 256 characters.
notifications.attachment.name_required400An attachment's name is empty or missing.
notifications.attachment.name_too_long400An attachment's name is longer than 255 characters.

Rate limits

Limits are fixed windows, reset every minute, and return 429 once exceeded. The numbers below are the platform defaults CommuniQueue ships with - they're configurable per deployment, so treat them as what you should expect out of the box rather than a number you can rely on exactly.

  • /notifications/send and /notifications/preview share a default budget of 300 requests per minute per API key.
  • /api-keys/validate defaults to 120 requests per minute per source IP, since it is a key-validation check rather than a send.
  • Inbound webhook intake defaults to 600 requests per minute per source IP.

Over the limit - 429

{
  "title": "Too Many Requests",
  "status": 429,
  "detail": "Rate limit exceeded. Try again later.",
  "instance": "/api/v1/notifications/send"
}