Docs menu
On this page

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.

Two statuses are the exception, because neither reaches the API itself. A request body over 10 MB is refused with a bare 413 before it is parsed, and 429 comes from the rate limiter. Both answer without an errorCode, so branch on the status for those two.

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.permission_denied403The key is valid, but it was not given the permission this route needs. The requiredPermission field names it: Send for sending and previewing, ManageLists for distribution lists. Permissions are chosen when a key is created, so create a key that has it and revoke this one.
api_keys.tenant_mismatch403The key is valid, but it belongs to a different workspace than the tenantId in the request body.
distribution_lists.batch_empty422A member write names nobody. Only a replace (PUT) accepts an empty members array, which empties the list.
distribution_lists.batch_too_large422A member write carries more than 1,000 members or addresses. Split it into several requests.
distribution_lists.description_too_long422The list description is longer than 500 characters.
distribution_lists.duplicate_email_in_batch422The same address appears twice in one member write. Addresses are compared ignoring case and surrounding spaces.
distribution_lists.field_key_invalid422A custom field key must start with a letter and use only letters, digits and underscores, 64 characters at most.
distribution_lists.field_key_reserved422A custom field is named email or name, in any case. Set those on the member directly.
distribution_lists.field_value_invalid422A custom field value is not a string or null, or is longer than 1,000 characters. Send null to remove a field.
distribution_lists.in_use409The list is saved as a recipient on a project, folder or template, so it cannot be deleted. The usages array says where; remove it there first.
distribution_lists.limit_exceeded403The workspace already has as many distribution lists as its plan allows. Delete a list or upgrade the plan.
distribution_lists.limit_not_configured403The plan has no distribution list limit configured, so a list cannot be created. Contact support.
distribution_lists.member_email_invalid422A member email is missing or is not one valid address of at most 254 characters. itemErrors gives the position of each bad member.
distribution_lists.member_limit_reached422The write would take the list past 1,000 members, so nothing was written. currentCount, attempted and limit carry the numbers.
distribution_lists.member_name_too_long422A member name is longer than 200 characters.
distribution_lists.member_not_found404That email address is not a member of the list.
distribution_lists.name_conflict409Another list in the workspace already has that name. Names are compared ignoring case and surrounding spaces.
distribution_lists.name_invalid422The list name is missing, blank, or longer than 100 characters after trimming.
distribution_lists.not_found404No distribution list with that id exists in the workspace the API key belongs to.
distribution_lists.paging_invalid400limit is outside 1 to 200, or cursor is not one this API returned. Start again without a cursor.
distribution_lists.too_many_fields422A member would have more than 50 custom fields, counting the fields already stored for that address, or one member carries more than 100 field keys, counting null removals.
notifications.monthly_limit_exceeded429The account has used its monthly message allowance for the current billing period. The allowance is shared across every workspace on the account, not per workspace.
notifications.monthly_limit_not_configured403The account'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.preview.recipient_invalid400The sample recipient on a preview was rejected: a missing or invalid email, a name over 200 characters, more than 50 fields, or a field with an invalid or reserved key (email, name), a missing value, or a value over 1,000 characters. It arrives in errorCodes under notifications.validation_failed.
notifications.recipient_variables_require_marketing422The template version is Transactional, one email shared by every recipient, but its content references recipient variables. Make the version Marketing, or remove the recipient references.
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.tag_value_too_long400A tag value is longer than 8,192 characters. Tag values are substituted into the template once per reference, so a large one is multiplied by however many times the template mentions it.
notifications.tags.recipient_reserved422A tag key is recipient or starts with recipient. (ignoring case). That name is reserved for the data of the recipient a Marketing email is rendered for.
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.too_many_tags400The request carries more than 100 tags.
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.
request.invalid400The request could not be read. Either the body is not JSON matching the endpoint's schema (it is incomplete, or a value has the wrong type, such as a number where an email goes or an object where an array goes), or a route or query value does not parse, such as limit=abc. Sending the same request again will not succeed.
sender.not_verified422The from address (from the template or the fromAddress override) is not a verified sender for this workspace. Verify the domain or the address under Settings, Senders, then retry. Workspaces sending through their own provider are not subject to this check.
sending.no_route422The workspace has no way to send email: it has no email provider of its own connected, and the account does not have managed sending. Nothing was queued. Connect your own email provider in the workspace settings, or add managed sending on the billing page, then retry. The problem body carries providerSettingsUrl and managedSendingUrl for those two places; managedSendingUrl is null for a complimentary account, which cannot buy managed sending, and its detail says to contact support instead.

For the distribution-list and recipient-variable codes above, see Distribution lists for the request shapes that trigger them.

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. The one exception is notifications.email.too_many_recipients, which counts all four lists together rather than checking any one of them, so splitting a large send across the fields does not avoid it.

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.custom_headers.name_duplicated400Two customHeaders names differ only in case. Header names are case-insensitive, so that is one header with two values; send it once.
notifications.email.custom_headers.name_invalid400A customHeaders name is empty or uses a character outside the RFC 5322 field-name set. Letters, digits, hyphens and underscores are safe; spaces and colons are not.
notifications.email.custom_headers.name_not_permitted400A customHeaders name is one the platform sets. Address and envelope headers (To, Cc, Bcc, From, Sender, Reply-To, Return-Path), the headers that describe the message itself (Message-Id, Subject, Date, Content-Type, MIME-Version, Received), and anything starting X-CQ-. Recipients go in emailOptions and the subject comes from the template, not from a header.
notifications.email.custom_headers.too_large400The customHeaders names and values come to more than 4,096 bytes in total.
notifications.email.custom_headers.too_many400The request carries more than 10 entries in customHeaders.
notifications.email.custom_headers.value_invalid400A customHeaders value contains a carriage return or line feed. Those would end the header and let the rest of the value be read as further headers.
notifications.email.distribution_list_not_found422A name in emailOptions.toLists, ccLists, bccLists or overrideLists matches no distribution list in this workspace (names match ignoring case). The unknownLists extension names every one that did not match.
notifications.email.from_address_invalid400emailOptions.fromAddress is not a valid email address.
notifications.email.list_name_too_long400A name in toLists, ccLists, bccLists or overrideLists is longer than 100 characters, which no list name can be. It arrives in errorCodes under notifications.validation_failed.
notifications.email.marketing_list_with_copies422A Marketing send that involves a distribution list also has Cc or Bcc recipients (cc, bcc, ccLists, bccLists, or saved Cc and Bcc recipients). Marketing sends one email per To recipient with every copy recipient on each, so each copy would carry one member's address and personal content. Remove the copies, or send the list as Transactional.
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.
notifications.email.too_many_lists400The send names more than 100 distribution lists across toLists, ccLists, bccLists and overrideLists. It arrives in errorCodes under notifications.validation_failed.
notifications.email.too_many_recipients400The send reaches more than 1,000 recipients, counting to, cc, bcc and overrideRecipients as named, or once saved recipients and distribution lists are expanded. Split the send into batches within that ceiling.

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_not_permitted400An attachment contentType names an executable or script format. Renaming the file does not help; both the name and the declared type are checked.
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_not_permitted400An attachment name ends in an executable or script extension: .bat, .cmd, .com, .exe, .hta, .jar, .js, .msi, .ps1, .scr or .vbs. Mail from the platform is authenticated by a shared sending domain, so these are refused whoever sends them.
notifications.attachment.name_required400An attachment's name is empty or missing.
notifications.attachment.name_too_long400An attachment's name is longer than 255 characters.
notifications.attachment.too_many400The request carries more than 10 attachments.
notifications.attachment.total_content_too_large400The attachments' combined decoded content is larger than 25 MB. In practice the request body limit is reached first: a body over 10 MB is rejected with a bare 413 before this rule can run.

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.
  • Those two routes also share a default of 300 requests per minute per source IP, counted across every key. One key from one address never meets it; several keys behind one office network or one CI runner can, and the fix is to spread the traffic rather than to add keys.
  • /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.
  • One-click unsubscribe defaults to 120 requests per minute per source IP.
  • The /distribution-lists routes default to 120 requests per minute per API key and, separately, 120 per minute per source IP, a budget of their own that list management and sends never share.

Over the limit - 429

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