Skip to the documentation
API

Errors

One shape, two levels, and a request id on everything.

The envelope

type is a frozen set you can branch on and will never grow. code is specific and additive, so treat one you do not recognise as its type. requestId is on every response, successes included, and is what ties a report to a log line.

422 Unprocessable Entity
{  "error": {    "type": "validation_error",    "code": "invalid_email_address",    "message": "Not a valid email address: ada@",    "param": "to.0",    "docUrl": "https://nextmail.sh/docs/api/errors#invalid_email_address",    "requestId": "req_e103790543af4…"  }}

param is dotted and indexed, so it points at the exact element (to.0, attachments.2.filename) rather than at the field that contains it.

docUrl links to the row for its code in Every error code, further down this page. When this page does not list a code yet, the link opens that table instead.

Status codes

StatustypeCommon codes
400invalid_request_errormalformed_json, invalid_idempotency_key, step_up_code_invalid, step_up_code_expired, step_up_not_applicable
401authentication_errormissing_api_key, invalid_api_key, revoked_api_key, invalid_credential_type, invalid_access_token, expired_access_token, grant_required, grant_expired, grant_workspace_gone, grant_access_lost
403permission_errorinsufficient_scope, from_address_forbidden, owner_only, api_key_only, beyond_caller_authority, grant_no_reach, mailbox_login_forbidden, step_up_required, account_suspended, workspace_suspended
404not_found_errorresource_not_found
409conflict_erroremail_not_cancellable, translation_not_configured, label_name_taken, file_in_use, stream_paused, stream_locked
412conflict_errormissing_chunks
413invalid_request_errorupload_too_large
422validation_errorinvalid_email_address, reserved_header, too_many_recipients, unknown_parameter, idempotency_key_reuse, label_not_directly_settable, label_not_found, label_limit_reached, unknown_language, translation_too_long, workspace_limit_reached, invalid_follow_up, sender_category_limit_reached, ai_label_limit_reached
429rate_limit_errorrate_limited, send_quota_exceeded, ai_quota_exceeded, upload_rate_limited, form_rate_limited, step_up_throttled, step_up_locked
500api_errorinternal_error
502api_errorimage_not_stored, upload_failed
503api_errortranslation_failed, step_up_undeliverable
507api_errorupload_storage_full

A 404 never distinguishes "does not exist" from "belongs to another workspace". That is deliberate: the difference is itself information.

Every 429 carries Retry-After, the seconds to wait before the same call can succeed. send_quota_exceeded is the monthly send allowance of the workspace's plan, counted for that workspace alone, and it resets on the first of the month, which is where its Retry-After points, so show it to a person rather than backing off. ai_quota_exceeded means that same workspace has used all of today's AI actions, and a send or a preview with translate spends one. Nothing was sent, and the allowance resets at midnight UTC, so a retry before then fails the same way.

Every 5xx has the type api_error, and they mean different things to a caller. A 503 is a dependency that did not answer, such as the translator or the mail service that delivers a verification code, and the request is worth retrying unchanged, except not_configured, which stays until disposable inboxes are set up. A 502 is a file or photo that could not be stored, also worth a retry, and a 507 is a workspace out of file storage. A 500 is ours and is worth reporting with its requestId.

Handling them

Branch on type for behaviour and read code for the message you show a human. An unrecognised code is not an error in your client. It means we named a failure more precisely than we used to.

TypeScript
const res = await fetch(`${BASE}/emails`, { method: 'POST', headers, body }); if (!res.ok) {  const { error } = await res.json();   switch (error.type) {    case 'rate_limit_error':      throw new RateLimited(error.code);    case 'validation_error':      // error.param points at the offending field      throw new BadRequest(`${error.param}: ${error.message}`);    case 'authentication_error':      // revoked_api_key and expired_api_key are worth telling an operator apart      throw new AuthFailed(error.code);    default:      // quote requestId when you report it      throw new Unexpected(error.message, error.requestId);  }}

Workspace limits

No plan caps how many webhooks, keys, addresses, templates, rules, roles or audiences a workspace has. Each one does carry a soft limit, a guard against a runaway script or a leaked key filling a workspace, and reaching it is a 422 workspace_limit_reached whose message names the limit. Nothing was created, so retrying the same call fails the same way until the workspace is below the limit again. Members are not limited at all.

ResourceSoft limit
Webhook endpoints10
Active API keys20
Addresses1,000
Pending invitations100
Audiences100
Templates200
Rules100
Custom roles24

These are soft limits, set per workspace rather than per plan. If you need more, write to support@openemail.uk with the workspace ID and what you need, and we raise it within one working day.

Verification codes

Only an OAuth access token meets these. Before a sensitive change, such as deleting a domain or changing a webhook, the API asks the app for a verification code from the person it acts for, the same code the web app would ask them for. Authentication lists the operations and the two calls that verify a code.

CodeStatusWhat to do
step_up_required403Ask for a code with POST /security/step-up, verify it with POST /security/step-up/verify, then repeat the call once. Or ask the person to choose Allow changes for 60 minutes on the app in Account → Connected apps. Nothing was changed.
step_up_code_invalid400The code did not match. The message says how many tries are left, the fewest of those left on this code, the wrong codes left before the app is paused and the wrong codes left before all of the person’s apps are paused, so ask the person again.
step_up_code_expired400The code ran out after 10 minutes, or none was waiting. Ask for a new one with POST /security/step-up.
step_up_locked429Five wrong tries at the same code: a plain POST /security/step-up starts a fresh one. Or the app has entered 10 wrong codes in 24 hours, or all of the person’s apps together 20: the message says verification is paused until a given time, and no code can be asked for or checked before then. Removing an app does not lift the second pause, and codes entered on the website still work.
step_up_throttled429The app has asked for 5 codes in the last hour, or 20 in the last 24 hours. The message says when to try again.
step_up_undeliverable503The email carrying the code could not be sent, and no code is waiting. Try again in a moment.
step_up_not_applicable400An API key called a verification endpoint. Keys are never asked for a code, so there is nothing to verify.

Every error code

Every code the API sends, in alphabetical order, with the statuses and types it comes with. The docUrl of an error links to the row for its code.

CodeStatustypeMeaning
account_exists409conflict_errorAn account on Nextmail already signs in as that address, so the address cannot get a password of its own. Invite that account to the workspace instead.
account_suspended403permission_errorThe account behind the key or app is suspended, so nothing it signs in to answers until the suspension is lifted. Write to support@openemail.uk to ask why or to appeal.
add_on_already_active409conflict_errorThe workspace already has this add-on, so there is nothing to buy. GET /billing/add-ons shows its state.
add_on_not_active409conflict_errorThe workspace does not have this add-on, or it is already ending, so there is nothing to cancel.
add_on_not_eligible409conflict_errorThe workspace does not qualify for this add-on yet. A dedicated IP needs the Business or Enterprise plan and at least 100,000 emails sent in a month, because below that volume it delivers worse than the shared IPs. eligibility on GET /billing/add-ons says which of the two is missing.
add_on_sold_out409conflict_errorEvery dedicated IP is taken at the moment. Contact support and one is leased for the workspace.
add_on_unavailable503api_errorAdd-ons are not on sale right now, or billing did not answer, so nothing was bought or changed.
address_disabled409conflict_errorThe address is switched off, so nothing would reach a forwarding destination. Turn the address on first.
address_has_no_mailbox422validation_errorThe address is switched off or forwards all its mail, so a mail app has no mailbox to open.
address_not_allowed403permission_errorThe caller may not use that address here, such as importing mail into it or publishing an encryption key for it, so choose an address it holds.
address_not_found404not_found_errorThe address to import into is not in this workspace.
address_required422validation_errorThese settings are kept per address, so pass ?address= with the address, or *@domain for a catch-all.
address_reserved409conflict_errorThe address is reserved for an account, so it cannot be added to the domain.
address_unavailable409conflict_errorThe address cannot have a password: it has to be an enabled address on a domain of this workspace.
ai_label_limit_reached422validation_errorThe workspace already has as many labels that AI applies as it may, so turn one off to turn another on.
ai_not_configured409conflict_errorAI writing is not available on this server, so nothing was written.
ai_quota_exceeded429rate_limit_errorThe workspace has used all of the AI actions its plan includes today. A send or preview with translate spends one, and so does writing with AI, so nothing was sent or written and a retry works after midnight UTC.
already_enrolled409conflict_errorThe contact is in the automation now, or finished it too recently to come back in under its re-entry setting.
already_running409conflict_errorAn import into that address, an import from another provider into this workspace or an export of this workspace is already under way, so wait for it to finish, or cancel the import.
api_key_only403permission_errorThis call needs an API key and was made with an OAuth access token, so make it with a key.
app_host_in_use409conflict_errorAnother workspace already uses that web app address, or a mail domain, a tracking host or a files host has that name, so choose another subdomain.
app_host_unavailable503api_errorThe web app address could not be set up or removed just now, so try again in a minute.
app_password_limit_reached422validation_errorThe person already holds as many app passwords for this address as it allows. Revoke one first.
attachment_unreachable422validation_errorA file named by url could not be fetched: the address is not https, points at a private network, did not answer within 15 seconds, answered with an error, or is larger than 25 MB.
audience_immutable409, 422conflict_error, validation_errorThe default audience cannot be renamed, deleted or have its members changed by hand.
audience_is_everyone422validation_errorOne of the audiences is All contacts, which holds everyone this workspace has ever written to, so a broadcast cannot go to it. Send to an audience people joined.
audience_member_not_found404, 422not_found_error, validation_errorThe contact is not in that audience.
audience_not_found404not_found_errorNo audience in this workspace has that id.
automation_archived409conflict_errorThe automation is archived, so it can no longer be changed or turned on. Duplicate it to start again from its steps.
automation_enrollment_finished409conflict_errorThe contact already completed or left the automation, so there is nothing to take them out of.
automation_enrollment_not_found404not_found_errorNo enrollment in that automation has that id.
automation_limit_reached403permission_errorThe plan of the workspace covers no more live automations, as the message says, so pause one or upgrade before turning this one on.
automation_not_found404not_found_errorNo automation you can reach has that id. Restoring a version answers this, with param set to version, when the automation has no such version.
automation_not_live409conflict_errorThe automation is a draft, paused or archived, so nobody can be enrolled in it until it is live.
automation_not_published409conflict_errorThe automation was never published, so publish it before you pause or resume it.
automation_sender_refused422validation_errorThe caller may not send mail for this automation, so publish, resume or test it with a credential that holds emails:send.
bad_chunk400invalid_request_errorAn uploaded part does not match the import file it belongs to, so send that part again.
beyond_caller_authority403permission_errorA key can only get scopes, a role, a mode, an expiry and send addresses within what the caller holds, and this request asks for more, as the message says.
billing_address_rejected422validation_errorThe billing provider refused the address, so check it. In the US and Canada the state is its two-letter code.
billing_details_required409conflict_errorThe charge has no billing name and address yet, so add them with PUT /billing/invoices/{orderId}/details before asking for its invoice.
billing_not_configured503api_errorBilling, paid plans or pay as you go are unavailable right now, as the message says, so there is nothing to buy or change.
billing_unreachable502api_errorThe billing provider did not answer, so nothing changed. Try again in a moment.
blocklist_entry_too_broad422validation_errorA blocklist entry needs at least two letters or numbers, or it would match far too much mail.
booking_ended409conflict_errorThe booking was already declined or cancelled, or the meeting has started, so it can no longer be changed.
booking_not_found404not_found_errorNo booking of the person the key or app acts for has that id.
booking_not_pending409conflict_errorThe booking is not waiting for approval: it was already approved, declined or cancelled, or its time has passed.
booking_page_limit_reached403permission_errorThe person already has as many booking pages as one person may hold. Delete one first.
booking_page_not_found404not_found_errorNo booking page of the person the key or app acts for has that id.
booking_time_unavailable409conflict_errorThe new time is not open on the booking page: it is taken, outside the hours, too soon or too far ahead. List the open times with bookingId and pick one.
bot_check_failed403permission_errorThe sign-up carried a bot check token, in cf-turnstile-response, oe_bot_check or the X-Bot-Check header, and the check refused it. Nothing was stored. Complete the check again and send the new token.
branding_unavailable409conflict_errorThe workspace is a personal space, which carries no brand, so its brand images, fonts and sign-in background cannot be set. Use a key from a business workspace.
broadcast_not_cancellable409conflict_errorEvery message in the broadcast has gone out, or it is past the point where it can be cancelled.
broadcast_not_draft409conflict_errorThe broadcast is not a draft, so it cannot be sent this way: it has already been sent or scheduled.
broadcast_not_editable409conflict_errorOnly a draft, or a scheduled broadcast that has not started going out, can be changed.
broadcast_not_found404not_found_errorNo broadcast has that id, or it was sent from an address or domain the key does not hold.
calendar_feed_limit409conflict_errorYou already have as many calendar links as one person may keep. Delete one before you create another.
calendar_feed_not_found404not_found_errorNo calendar link of yours has that id.
calendar_subscription_exists409conflict_errorYou already subscribe to a calendar at that address.
calendar_subscription_limit409conflict_errorYou already subscribe to as many calendars as one person may. Remove one before you add another.
calendar_subscription_not_found404not_found_errorNo calendar subscription of yours has that id.
calendar_subscription_refresh_too_soon429rate_limit_errorThe calendar was refreshed moments ago. Wait a minute before you refresh it again.
calendar_subscription_unreadable422validation_errorThe address did not answer with a calendar: it was unreachable, private, too large, or not an iCalendar (.ics) file.
calendar_subscription_url_invalid422validation_errorThe calendar address must be a public https, http or webcal link without a user name or password.
capability_unsupported422validation_errorThe key or app is limited to some addresses or domains, and this call reaches mail or settings beyond them, so use a key without that limit.
chunk_too_large400invalid_request_errorAn import upload part is larger than the part size, so send the file in parts of the size the message gives.
connected_mailbox_already_connected409conflict_errorThat mailbox is already connected to this workspace, so there is nothing to connect again. Send a new password with PATCH if it stopped working.
connected_mailbox_connected_elsewhere409conflict_errorAnother workspace has that mailbox connected, and a mailbox syncs into one workspace at a time. Disconnect it there first.
connected_mailbox_domain_hosted400invalid_request_errorMail for that address already comes to Nextmail, because its domain is verified here or belongs to Nextmail, so it needs no connecting.
connected_mailbox_no_password400invalid_request_errorThe mailbox signs in with Microsoft, so it has no password to change. Sign in to Microsoft again from the app when it needs signing in.
connected_mailbox_not_allowed403permission_errorConnecting, changing and disconnecting mailboxes needs the domains:write permission, which the caller lacks.
connected_mailbox_not_found404not_found_errorNo connected mailbox has that id in this workspace, or the caller may not see its address.
connected_mailbox_send_server_unknown400invalid_request_errorNo outgoing mail server answered for the address, so mail could not be sent through it. Pass smtpHost, and smtpPort if it is not 465 or 587.
connected_mailbox_send_sign_in_failed400invalid_request_errorThe outgoing mail server did not accept the password, even though the incoming one did. Check that the app password allows sending.
connected_mailbox_wrong_account400invalid_request_errorThe Microsoft sign-in was for a different account than the mailbox being signed in again. Sign in as the address of that mailbox.
connection_not_found422validation_errorThe mailbox the message would be sent from no longer exists.
connector_action_failed422validation_errorThe connected app refused the action, most often because the input does not match what the action takes. The message carries what the app said.
connector_action_not_found404not_found_errorThe app behind the connector has no action with that name. List the ones it has with the actions endpoint.
connector_app_not_found404not_found_errorThe connector catalogue has no app with that slug.
connector_app_unavailable422validation_errorThe app is in the catalogue but cannot be connected yet. Its connectable field says so before you try.
connector_limit_reached403permission_errorThe plan holds no more connectors for this account or workspace. Disconnect one, or move to a larger plan.
connector_not_active409conflict_errorThe connector is paused, still waiting for its sign-in, or needs reconnecting, so nothing can be run through it.
connector_not_found404not_found_errorNo connector the key or app can reach has that id.
connector_provider_failed503api_errorThe connection service did not answer in time. Connecting, reading and listing changed nothing and can be sent again. A run may or may not have reached the app, so look before running it again.
connector_read_only403permission_errorThe connector is set to read only, so an action that changes something in the app is refused. Set its access to full first.
connectors_unavailable503api_errorConnectors are not switched on for this service yet, so no app can be connected or used.
contact_card_email_taken409conflict_errorThat email address already belongs to another contact card, so change that card instead.
contact_card_limit409conflict_errorThe workspace already keeps as many contact cards as it may. Delete some before you add more.
contact_card_not_found404not_found_errorNo contact card you can see has that id.
contact_card_uid_taken409conflict_errorAnother contact card already has that vCard UID.
contact_exists409conflict_errorThat email address is already a contact, so change the existing contact instead.
contact_not_found404, 422not_found_error, validation_errorNo contact has that email address, so add the contact to the book first.
contact_unreachable409conflict_errorThe contact cannot be enrolled because the address is on the suppression list or has unsubscribed from the audience the automation sends through.
deep_check_limit_reached409conflict_errorThe deep check would pass the monthly pay as you go limit of the workspace, so no address was checked and nothing was billed. Raise the limit, or run the check without deep.
deep_check_unavailable409, 503conflict_error, api_errorA deep check is billed per address through pay as you go, which is off, paused or not part of the plan, as the message says. With 503 the mailbox check itself did not answer. Nothing was billed. Run the check without deep, which is free.
delivery_not_replayable409conflict_errorThe webhook delivery has no stored event to send again.
dns_busy409conflict_errorA DNS sync is running on the domain right now, so nothing was changed. Try again in a moment.
dns_connection_in_use409conflict_errorDomains are still attached to the DNS connection, or records written through it are still published, so it cannot be removed yet.
dns_connection_unusable409conflict_errorThe DNS connection was disconnected, or the provider no longer accepts it, so connect the account again in the app.
dns_provider_error502api_errorThe DNS provider refused or failed the request, so try again in a moment, and connect the account again if it keeps failing.
dns_unavailable503api_errorSetting up DNS through a provider is not available on this server right now.
dns_zone_conflict409conflict_errorThe domain is already set up through another zone, or still has records written in one, so disconnect the connection that holds that zone first.
dns_zone_unusable409conflict_errorThe zone cannot take records through this connection: it is not active at the DNS provider, the connection may not write DNS, or a test record could not be written.
domain_allowance_reached403permission_errorThe plan of the workspace includes no more domains, so upgrade it or remove a domain first.
domain_already_added409conflict_errorThe workspace already has this domain.
domain_claimed409conflict_errorAnother workspace has already claimed this domain.
domain_holds_reserved_addresses409conflict_errorThe domain holds reserved account addresses that removing it would delete, so it cannot be removed here.
domain_moving409conflict_errorThe domain is being moved to another workspace, so try again once the move has finished.
domain_not_sendable409conflict_errorThe domain cannot send yet because its signing records are not in place, so add the records shown for the domain and try again. A DMARC policy stricter than none waits for them too.
domain_not_verified409conflict_errorThe domain has to be verified before a tracking host, a files host, a web app address, a logo or a mark certificate can be set on it, or an encryption key published for one of its addresses.
draft_body_unavailable422validation_errorThe draft has no body to send, so check that the message is there and send again.
email_not_cancellable409conflict_errorThe message is no longer queued, so it can no longer be cancelled, rescheduled or edited.
empty_file400invalid_request_errorOne of the files chosen for the import is empty.
encryption_locked422validation_errorThe message was encrypted before it was scheduled, so its wording and recipients cannot be edited, and it has to be cancelled and sent again.
event_cancelled409conflict_errorThe meeting has been cancelled, so it can no longer be answered or changed.
event_not_found404not_found_errorNo calendar event has that id.
event_not_subscribed409conflict_errorThe webhook no longer listens for this event, so the delivery is not sent again.
event_out_of_scope409conflict_errorThe webhook no longer covers the address this event is about, so the delivery is not sent again.
event_rate_limited429rate_limit_errorThe key or app sent more contact events in a minute than it may, and a batch counts each event it holds, so send the rest a minute later.
event_read_only403permission_errorThe event comes from a calendar you subscribe to, so it can only be changed where that calendar is published.
expired409conflict_errorThe API key has expired, so it cannot be rotated and a new one has to be created, or the export is more than a year old and was deleted, so start a new one.
expired_access_token401authentication_errorThe OAuth access token has expired, so renew it with its refresh token or connect the app again.
expired_api_key401authentication_errorThe API key has passed its expiry date, so create a new one in the console.
export_limit_reached429rate_limit_errorThe workspace was exported in the last day, and it can be exported once a day, so try again when the last export is a day old.
export_not_permitted403permission_errorThe caller may not export this workspace: only its owner, or a member whose role holds every permission, may, and a workspace that requires two-factor sign-in needs it on that account. The message says which.
export_not_ready409conflict_errorThe export is still being made, or failed, so there is no archive to download yet.
extension_limit422validation_errorAll 23 extensions of the disposable inbox are spent, so it cannot be extended again.
file_in_use409conflict_errorThe file is kept because a message received it, sent it, links to it or will send it, as the message says, so it cannot be deleted.
file_too_large400invalid_request_errorOne of the files chosen for the import is larger than 100 GB.
file_unshareable422validation_errorThat type of file can run code on the computer that opens it, so it cannot get a public link.
first_workspace409conflict_errorThe first workspace an account made cannot be deleted, so delete the whole account in the app instead.
form_closed409conflict_errorThe form is paused, so it takes no sign-ups until it is resumed.
form_not_found404not_found_errorNo form you can reach has that id. The public sign-up endpoint also answers this for a form that was never published.
form_not_published409conflict_errorThe form was never published, so publish it before you pause or resume it.
form_rate_limited429rate_limit_errorToo many sign-ups came from the same network in the last few minutes, so try again later.
form_sender_refused422validation_errorConfirmation emails cannot go out from that address, so choose a sending address of this workspace that you may send as.
form_site_not_allowed403permission_errorThe form lists the sites that may use it, and the sign-up came from another site or did not say where it came from. Post from a listed site, or from a server send the page address as oe_source.
form_starter_not_found404not_found_errorNo form starter has that slug. List them with GET /forms/starters.
form_submission_not_found404not_found_errorThe form has no submission with that id.
forward_required409conflict_errorThe address has no forwarding destination that is on, so it cannot stop keeping a copy in the mailbox. Add one first.
from_address_forbidden403permission_errorThe role, key or app may not send as the From address, so send from an address it holds.
grant_access_lost401authentication_errorThe person who approved the app no longer has access to its workspace, so connect the app again and choose another. The same code comes back while the workspace requires two-factor authentication and that person has not turned it on, and then the message says so.
grant_expired401authentication_errorThe access given to the app has run out, so connect it again to renew it.
grant_no_reach403permission_errorThe person who connected the app can no longer reach any address it was approved for, so connect it again to choose what it may reach.
grant_required401authentication_errorThe app has not been approved under the current access rules, so connect it again to choose what it may reach.
grant_workspace_gone401authentication_errorThe workspace the app was approved for no longer exists, so connect the app again and choose another.
idempotency_key_reuse422validation_errorThe Idempotency-Key was already used for a different request, so send a new key with a new request.
image_busy503api_errorThe image could not be processed right now, so upload it again in a moment.
image_not_stored502api_errorThe photo was processed but could not be saved, so upload it again.
inactive_api_key401authentication_errorThe API key is switched off, so turn it back on in the console or use another key.
inbox_expired401authentication_errorThe disposable inbox has expired, so create a new one.
incomplete_order422validation_errorA rule reorder has to name every rule on the mailbox exactly once.
insufficient_authority403permission_errorThe caller lacks the authority for this, and the message says why: a role or invitation it hands out or sends again holds a permission the caller lacks or reaches addresses or domains the caller does not, or the member an access token acts for lacks a console-only permission the call needs, such as workspace:manage. Leave out what the message names, or ask the owner.
insufficient_scope403permission_errorThe key or token lacks the scope this call needs, and the message names it. For an access token that is a permission the app was not given, or one the role of the member it acts for no longer holds, so connect the app again once the role holds it.
internal_error500api_errorSomething failed on our side, so report it with the requestId.
invalid_access_token401authentication_errorThe access token belongs to an app that is no longer registered, or to an account that no longer exists, so connect the app again.
invalid_address422validation_errorThat name cannot be used in a disposable email address.
invalid_api_key401authentication_errorThe Authorization header does not carry a valid API key, so check the key and send it as a Bearer token.
invalid_app_host422validation_errorThe web app address cannot be used: it is not a hostname, it is a bare domain, or it is on a domain Nextmail runs. The message suggests one that works.
invalid_audience422validation_errorThe audience has no name or could not be created, or the call names more audiences than one call may.
invalid_automation422validation_errorThe automation breaks a rule, and the message names the first one. When the trigger, the steps or the settings are at fault, problems in the error lists every problem that blocks it, each with its code, path, stepKey and message. A test send answers this when the step is not an email or its template cannot be sent.
invalid_booking_page422validation_errorThe booking page is not valid, as the message says: the link has characters it may not have or is reserved, the sender is not an address the person may send from, a video meeting has no link, or a question is malformed.
invalid_contact422validation_errorThe contact has no email address or could not be saved, it names more audiences than one call may, or a custom field breaks a rule: a key that is not a letter followed by up to 63 letters, digits or underscores, a value over 500 characters, or a 51st field. For a custom field param is attributes.<key>, or attributes when the contact would hold too many.
invalid_contact_card422validation_errorThe contact card has nothing to keep, a field is not valid (a birthday is YYYY-MM-DD or --MM-DD), or it tries to change the address a saved contact is known by.
invalid_credential_type401authentication_errorThe request sent a session cookie, or a token that is neither an API key nor an access token Nextmail issued, so send an API key or access token as a Bearer token.
invalid_cursor400, 422invalid_request_error, validation_errorThe cursor does not come from this list, so start without one or pass the nextCursor of the previous page.
invalid_email_address422validation_errorA recipient or address field is not a valid email address, and param points at it.
invalid_event422validation_errorThe contact event breaks a rule, and param points at the field: a name that is not 1 to 100 letters, digits, dots, colons, dashes and underscores, no contact named, properties over 50 keys or 4 KB, or an occurredAt in the future or more than 90 days ago.
invalid_follow_up422validation_errorThe reminder time has to be in the future, after the message goes out and at most 365 days out.
invalid_form422validation_errorThe form document or settings break a rule, and the message names the field and the rule. From a design with AI it means the designer could not make a valid form, so nothing was created or changed.
invalid_form_submission422validation_errorAn answer is missing or not valid, and fields lists each field key with the reason.
invalid_idempotency_key400invalid_request_errorThe Idempotency-Key header has to be 1 to 255 letters, digits, underscores, dots, colons or hyphens.
invalid_image422validation_errorThe upload is not an image that can be used as a contact or address photo or a brand image, and the message says why.
invalid_key400invalid_request_errorThe provider did not accept the API key given for the import.
invalid_knowledge_body422validation_errorA note needs text of at most 20,000 characters, so send a body within that length.
invalid_knowledge_connector422validation_errorThe connector cannot be read: its url is not a public https address, or the page there is not a site, sitemap, feed or Zendesk help center of the kind given, so check both.
invalid_knowledge_refresh422validation_errorA link or a connector is read again every 1, 7 or 30 days, so send one of those in refreshDays, or null to never read it again.
invalid_knowledge_scope422validation_errorThe scope must be empty for the whole workspace, @ and one of the workspace's domains, or one of its addresses, so pick one of those.
invalid_knowledge_title422validation_errorA knowledge item needs a title of at most 200 characters, so send a shorter or non-empty title.
invalid_knowledge_url422validation_errorThe link is not a public http or https address on the default port, or it points at a private host or at the Nextmail API, so send a public page.
invalid_parameter400, 422invalid_request_error, validation_errorA query or body value is not valid, and param names it while the message says why.
invalid_rule422validation_errorThe conditions or actions of the rule are not valid, and the message says which.
invalid_schedule422validation_errorscheduledAt has to be in the future and at most 365 days out.
invalid_storage_host422validation_errorThe files host is not a host this domain can use, and the message says why.
invalid_task422validation_errorThe task is not valid, as the message says: a schedule trigger needs a schedule, a time zone has to be one the platform knows, and a toolkit has to be one a task may use.
invalid_template422validation_errorThe template is not valid, for example a placeholder is not declared or the html is missing or too long.
invalid_template_prop422validation_errorA template prop has to be a string, a number or a boolean.
invalid_time400invalid_request_errorThe start or end of the event is not a valid time, it ends before it starts, or it lasts more than two years.
invalid_tracking_host422validation_errorThe tracking host is not a host this domain can use, and the message says why.
invalid_webhook_url422validation_errorThe webhook URL cannot receive deliveries, and the message says why.
invitation_accepted409conflict_errorThe invitation has already been accepted, so remove the member instead.
invitation_expired422validation_errorThe invitation has expired, so ask whoever sent it for a new one.
invitation_limit_reached409conflict_errorThe workspace, or the person inviting, has sent as many invitations as a day allows, so try again tomorrow.
invitation_not_found404not_found_errorThe invitation does not exist or is no longer outstanding.
invitation_revoked422validation_errorThe invitation was withdrawn or answered before it could be accepted.
invitation_too_soon409conflict_errorThat person was invited in the last ten minutes, so wait before inviting them again.
invitation_wrong_account422validation_errorThe invitation was sent to another email address than the account accepting it.
invoice_not_ready504api_errorThe invoice is still being written, so ask for it again in a moment.
invoice_unavailable409conflict_errorA draft or void charge has no invoice.
ip_not_allowed403permission_errorThe API key has an IP allowlist and the request came from an address outside it, so send it from one of those addresses or change the list.
item_import_empty400invalid_request_errorThe file sent for the calendar or contacts import is empty.
item_import_not_cancellable409conflict_errorThe calendar or contacts import has already finished, so there is nothing to cancel.
item_import_not_found404not_found_errorThe calendar or contacts import does not exist.
item_import_not_undoable409conflict_errorThe calendar or contacts import is still running, or what it added was already removed.
item_import_not_uploading409conflict_errorThe calendar or contacts import already has its file.
item_import_too_large413invalid_request_errorThe file is too large, since a calendar file may be 10 MB and a contacts file 20 MB.
key_already_published409conflict_errorThe address already has a published key, or that key was published for it before, so rotate it with replaces or generate a new key.
knowledge_ai_unavailable503api_errorThe AI did not answer while it drafted the note, so try again in a moment.
knowledge_allowance_reached409conflict_errorThe workspace already holds as many knowledge items, or as much text, as its plan allows, so remove items or move to a larger plan.
knowledge_answer_required422validation_errorAn unanswered question becomes a note only with its answer, so send the answer in body.
knowledge_connector_exists409conflict_errorA connector for the same address already exists at that level, so change that connector instead of adding it again.
knowledge_connector_not_found404not_found_errorNo connector with that id is in this workspace, or the caller cannot reach the level it belongs to.
knowledge_file_empty422validation_errorThe uploaded file has no bytes, so send the file itself as the request body.
knowledge_file_too_large413invalid_request_errorThe file is larger than a knowledge item can be: 20 MB for a document, 10 MB for an image.
knowledge_file_unsupported422validation_errorThat kind of file cannot be read into the knowledge base, so send a PDF, Word, Excel, OpenDocument, CSV, HTML, XML, Markdown, text or image file.
knowledge_flag_not_found404not_found_errorNo flag with that id is in this workspace, or the caller cannot reach both of the items it names.
knowledge_not_a_note422validation_errorOnly a note has a body you can edit or pin, so leave body and pinned out for files and links.
knowledge_not_found404not_found_errorNo knowledge item with that id is in this workspace, or the caller cannot reach the level it belongs to.
knowledge_nothing_to_save422validation_errorThe conversation has no messages yet, or holds no facts worth keeping, so there is no note to draft from it.
knowledge_scope_not_reached403permission_errorThe caller cannot change knowledge at that level: the whole workspace needs every address, a domain needs the whole domain, and an address needs that address.
knowledge_store_unavailable503api_errorThe knowledge index did not answer in time. Nothing was lost, so try again in a moment.
knowledge_suggestion_decided409conflict_errorThe suggestion or question was already accepted or dismissed, so list the pending ones again.
knowledge_suggestion_not_found404not_found_errorNo suggestion or question with that id is in this workspace, or the caller cannot reach the level it belongs to.
knowledge_thread_not_found404not_found_errorNo conversation with that id is in this workspace, or the caller cannot read the address it belongs to.
knowledge_upload_unusable422validation_errorThe upload reservation is missing, expired or belongs to someone else, so start the upload again.
label_limit_reached422validation_errorThe mailbox has as many labels as it may hold, so delete one to create another.
label_name_taken409conflict_errorA label with that name already exists.
label_not_directly_settable422validation_errorThat label is set by an action such as trash or snooze, so call the endpoint the message names instead.
label_not_found422validation_errorNo label has that id, so list them with GET /labels or create one with POST /labels.
last_domain409conflict_errorThis is the last domain in the workspace and removing it deletes the whole mailbox, so remove it in the app, which asks for confirmation first.
mailbox_address_invalid400invalid_request_errorThe address given for the mailbox at another provider is not an email address.
mailbox_checks_limited429rate_limit_errorToo many sign-in attempts or lookups for a mailbox at another provider were made in the last hour, so try again later.
mailbox_host_not_allowed400invalid_request_errorThe mail server given cannot be used, because it has to be a public host on port 993, or on port 143 with STARTTLS.
mailbox_login403permission_errorA password sign-in for a single address cannot be invited to or join a workspace, so invite the person's own account instead.
mailbox_login_failed400invalid_request_errorThe mail server of the mailbox being imported or connected did not accept the address and password, whatever its reason.
mailbox_login_forbidden403permission_errorThe token belongs to a password sign-in for a single address, which cannot use apps, so connect the app from a full Nextmail account.
mailbox_settings_unknown400invalid_request_errorNo mail server was found for the address of the mailbox being imported or connected, so send its host and port.
mailbox_unreachable502api_errorThe mail server of the mailbox being imported or connected did not answer, so check the host and port and try again.
mailbox_uses_microsoft400invalid_request_errorThe mailbox signs in with Microsoft and takes no password here, so connect it with the Microsoft sign-in in the app.
malformed_json400invalid_request_errorThe request body is not valid JSON.
member_is_owner422validation_errorThe workspace owner already holds every permission and cannot be changed or removed this way.
member_not_found422validation_errorThat person or domain is not in this workspace.
message_too_large422validation_errorThe attachments together are larger than one message may carry.
microsoft_not_configured503api_errorThe Microsoft sign-in for imports is not set up on this server.
microsoft_sign_in_failed400invalid_request_errorMicrosoft did not approve the sign-in for the import, so start it again.
missing_api_key401authentication_errorNo credential was sent, so send an API key or an OAuth access token in the Authorization header as a Bearer token.
missing_chunks412conflict_errorPart of the import upload never arrived, so upload the same files again to finish it.
missing_inbox_token401authentication_errorA disposable inbox call was made without the inbox token the address was created with, so send it as a Bearer token.
missing_template_prop422validation_errorThe template requires a prop that was not supplied.
no_recipients422validation_errorNobody in the chosen audiences can be sent to, because they are empty or every contact has unsubscribed or is suppressed.
no_workspace422validation_errorThe workspace does not exist.
not_a_calendar_file400invalid_request_errorThe body is not an iCalendar file, so send an .ics file.
not_a_contacts_file400invalid_request_errorThe body holds no vCard, so send a .vcf file.
not_cancellable409conflict_errorThe import has already finished, so there is nothing to cancel.
not_configured503api_errorDisposable inboxes are unavailable right now: no disposable domain is on offer, or the mailbox behind the pool cannot be read.
not_found404not_found_errorThe import does not exist.
not_invited403permission_errorThe event did not invite the address named in respondingAs, or none of the addresses the caller may send as, so there is nobody to answer as.
not_organizer403permission_errorOnly the organiser can change or cancel this event, so change your own response instead.
not_per_address422validation_errorThe scope ?address= names does not keep that setting. An address or a catch-all keeps its own signature, footer, tracking, external images, trusted senders and blocklist, and @domain only the last three, so send the setting to a scope that keeps it, or without ?address=.
not_resumable409conflict_errorThe import is not waiting, or it came from a Microsoft sign-in, so there is nothing this call can resume.
not_revoked409conflict_errorThe API key has to be revoked before it can be deleted.
not_uploading409conflict_errorThe import is no longer taking uploads.
nothing_selected400invalid_request_errorThe import from another provider chose nothing it can bring across, so choose at least one kind of thing the provider holds and the key can read.
operator_domain409conflict_errorThe domain belongs to Nextmail itself, so it cannot be added as a workspace domain.
owner_only403permission_errorOnly the workspace owner can do this, or for webhooks a member whose role reaches every address. The token acts for a member who may not, so ask the owner, or use a key the owner created.
password_required400invalid_request_errorThe import stopped because the old mailbox refused its password, so send the new password to resume it.
pay_as_you_go_not_enrolled409conflict_errorPay as you go has never been set up on this workspace, so it has no limit to change yet. Turn it on with PATCH /billing/pay-as-you-go first.
plan_conflict409conflict_errorThe workspace already pays for a plan, or a checkout for it could not be started, as the message says. Change the plan on the billing portal, POST /billing/portal, rather than buying a second one.
plan_not_cancelled502api_errorThe plan on the workspace could not be cancelled, so the workspace was kept. Try again in a moment.
plan_required403permission_errorThe plan of the workspace does not include team members, a web app address on its own domain or labels that AI applies, as the message says, so upgrade it first.
plan_unavailable422validation_errorThat plan is not on sale, or not at that interval, so choose one GET /billing/plans lists, with an interval it offers.
proposal_not_found404not_found_errorThat guest has not proposed a new time for the event.
rate_limited429rate_limit_errorThe key or app sent more requests in a minute than it may. Retry-After says how many seconds to wait, and the RateLimit-Remaining and RateLimit-Reset headers on every answer show how close a key is to the limit.
recipient_not_found404not_found_errorThe broadcast has no copy with that emailId.
reject_needs_envelope422validation_errorA rule that refuses mail at SMTP has to test envelope_from, because a From header match bounces the mailing list rather than the author.
replay_in_progress409conflict_errorA replay of this event is being sent right now, so read its result before replaying again.
reply_not_sent409conflict_errorYour response could not be sent to the organiser, and the message says why.
rerun_not_available409conflict_errorThe import named in rerunOf is not a finished import of the same mailbox into the same address from the last 30 days.
research_limit_reached429rate_limit_errorThe workspace has looked up as many senders as it may today, so try again tomorrow.
resend_unavailable409conflict_errorThe broadcast cannot go again to the people who did not open it: it has not finished going out, or it did not track opens.
reserved_address422validation_errorThat name has to stay reachable on the domain, so it cannot be issued as a disposable address.
reserved_header422validation_errorThe header is set by Nextmail or is not one a message may set, so leave it out.
resource_not_found404not_found_errorNothing with that id exists in this workspace, and a 404 never says whether it belongs to another one.
restricted_key400invalid_request_errorThe provider key can only send, so create a key with full access there, import, then revoke it.
retry_in_progress409conflict_errorAn automatic retry of this event is being sent right now, so wait a few seconds and replay only if it fails.
revoked409conflict_errorThe API key was revoked, so it cannot be rotated, switched back on or changed, and a new one has to be created.
revoked_api_key401authentication_errorThe API key was revoked, or the workspace it belongs to no longer exists, so create a new key.
role_immutable409conflict_errorThe owner role cannot be changed or handed out, so transfer the workspace instead.
role_in_use409conflict_errorMembers, keys or invitations still hold the role, so name a role to move them to.
role_name_taken409conflict_errorA role with that name already exists in the workspace.
role_not_found404, 422not_found_error, validation_errorNo role in this workspace has that id.
role_undeletable409conflict_errorThe owner role cannot be deleted.
rule_name_taken409conflict_errorA rule with that name already exists on the mailbox.
segment_limit_reached409conflict_errorThe workspace already has 100 segments, the most it may keep. Delete one before you save another.
segment_not_found404not_found_errorNo segment with that id in this workspace, or it was deleted.
segment_too_large422validation_errorA segment with a custom field condition reads each contact it checks, and stops at 20000 of them. Narrow the audiences it applies to, or match on another field.
send_quota_exceeded429rate_limit_errorThe workspace has used the monthly send allowance its plan includes, which resets on the first of the month, so show it to a person rather than retrying.
sender_category_limit_reached422validation_errorThe workspace holds as many sender choices as it may, so remove one to add another.
setup_email_not_sent503api_errorThe setup email could not be sent. Nothing was created, so try again.
setup_email_rate_limited429rate_limit_errorA workspace can email 50 mail app setup links a day, and it has. Wait until tomorrow, or create an app password and hand it over yourself.
signature_limit_reached422validation_errorThe address holds 50 signatures, as many as one may, so delete one to add another.
signature_name_taken409conflict_errorThe address already has a signature with that name, compared without case.
signature_too_long422validation_errorThe signature is longer than a signature may be, before or after its HTML is cleaned up.
smtp_rate_limited429rate_limit_errorA key can hand over 300 messages a minute over SMTP, and it has. The message was refused with a temporary 451, so mail software tries again by itself. It shows in the request log and is never an HTTP response.
step_up_code_expired400invalid_request_errorThe verification code ran out after 10 minutes, or none was waiting, so ask for a new one with POST /security/step-up.
step_up_code_invalid400invalid_request_errorThe verification code did not match, and the message says how many tries are left.
step_up_locked429rate_limit_errorToo many wrong verification codes, so start a fresh code, or wait until the time the message gives if verification is paused.
step_up_not_applicable400invalid_request_errorAn API key called a verification endpoint, and keys are never asked for a code.
step_up_required403permission_errorAn OAuth app has to verify a code from the person it acts for before this change, then repeat the call once. The Verification codes section of this page shows how.
step_up_throttled429rate_limit_errorThe app has asked for too many verification codes lately, and the message says when to try again.
step_up_undeliverable503api_errorThe email carrying the verification code could not be sent and no code is waiting, so try again in a moment.
still_running409conflict_errorThe import is still running, so cancel it before deleting its upload.
storage_host_in_use409conflict_errorThe files host is already used by another domain or server, and the message says where.
stream_locked409conflict_errorThe lane paused itself 3 times in 30 days, so only Nextmail support can resume it. Write to support@openemail.uk once the cause is fixed.
stream_not_paused409conflict_errorThe lane is not paused, so there is nothing to resume.
stream_paused409conflict_errorThe lane the message travels on is paused for this workspace because too much of its mail bounced or was reported as spam over the last 7 days. Each lane pauses on its own, so the other one still goes out. GET /sending/streams shows the numbers, and POST /sending/streams/{stream}/resume resumes the lane once the list is clean.
support_rate_limited429rate_limit_errorOne address can send 3 messages to support in 10 minutes, and they all arrived, so wait a few minutes before sending another.
suppression_not_removable409conflict_errorMail to the address could not be delivered, so it cannot be allowed again from here, and the corrected address should be used instead.
task_active_limit_reached403permission_errorThe plan of the workspace covers no more active tasks, so pause another task or upgrade before turning this one on.
task_approval_decided409conflict_errorThe approval request was already answered, has expired, or belongs to a run that has ended.
task_approval_not_found404not_found_errorNo approval request in this workspace has that id.
task_limit_reached403permission_errorThe workspace holds as many tasks as it may, so delete one before adding another.
task_not_found404not_found_errorNo task in this workspace has that id.
task_run_finished409conflict_errorThe run has already ended, so there is nothing left to cancel.
task_run_limit_reached409conflict_errorThe task has run as many times as it may in a day, or the workspace has started as many one-off runs as it may in a day.
task_run_not_found404not_found_errorNo task run in this workspace has that id.
temp_inbox_rate_limited429rate_limit_errorOne network created more disposable inboxes than it may in a few minutes. Wait for the number of seconds in Retry-After, or reuse an inbox you already hold.
template_archived422validation_errorThe template is archived, so restore it before sending with it.
template_in_use409conflict_errorA broadcast that has not started yet uses the template, so cancel the broadcast or wait until it starts sending.
template_name_taken409conflict_errorA template with that name already exists.
template_not_found422validation_errorNo template has that id or slug.
template_not_published422validation_errorThe template or the chosen version is a draft, so publish it before sending with it.
template_slug_taken409conflict_errorA template with that slug already exists.
template_version_not_deletable422validation_errorThe version is the only one, the live one or the one being edited, so it cannot be deleted.
template_version_not_found422validation_errorThe template has no such version.
thread_delete_failed500api_errorThe thread could not be deleted and nothing was removed, so try again.
thread_update_failed500api_errorThe label changes could not be written and nothing on the thread changed, so try again.
too_many_events400invalid_request_errorThe calendar file holds more than 25,000 events, so split it and import each part.
too_many_files400invalid_request_errorAn import takes at most 50 files.
too_many_items422validation_errorA batch holds at most 100 messages, and fewer when they use translate.
too_many_recipients422validation_errorThe message has more recipients than one send allows.
tracking_host_in_use409conflict_errorThe tracking host is already used by another domain or server, and the message says where.
translation_failed503api_errorThe message could not be translated and nothing was sent, so try again or send without translate.
translation_locked409conflict_errorThe message was translated when it was accepted, so its wording can no longer be edited, and it has to be cancelled and sent again.
translation_not_configured409conflict_errorTranslation is not available here, so nothing was sent, and it can go out as written without translate.
translation_too_long422validation_errorThe message is too long to translate.
two_factor_required403permission_errorThe workspace asks everyone in it to sign in with two-factor authentication, so turn it on for the account in the app before joining.
unknown_domain422validation_errorThe domain is not offered for disposable inboxes.
unknown_language422validation_errortranslate.to is not a language messages can be translated into.
unknown_parameter422validation_errorThe request names a field this endpoint does not accept.
unknown_template_prop422validation_errorThe template declares no prop with that name.
unknown_template_slot422validation_errorThe template declares no slot with that name.
unreachable503api_errorSomething the call had to reach did not answer, such as the provider during an import or public DNS during a domain check, so try again in a moment.
unsubscribe_not_removable409conflict_errorThe person unsubscribed themselves, or complained, or left at another provider, so only they can subscribe again, through a sign-up form they confirm from their inbox or the Subscribe again button on the unsubscribe page.
unsubscribe_refused502api_errorThe sender did not accept the one-click unsubscribe and gives no address to write to, so nothing changed. Try again later, or open its unsubscribe link.
unsubscribe_unsupported422validation_errorThe sender gives no way to unsubscribe, so nothing was sent and the subscription is unchanged.
unsupported_file400invalid_request_errorOne of the import files is not a .zip, .tgz, .mbox or .eml export, so there is nothing in it to import.
upload_dangerous400invalid_request_errorThat type of file cannot be stored here.
upload_empty400invalid_request_errorThe uploaded file is empty.
upload_failed502api_errorThe file could not be saved, or its upload reservation is missing or has expired, so start the upload again.
upload_forbidden403permission_errorThe upload was reserved by someone else or in another workspace.
upload_no_address403permission_errorUploading files needs access to at least one address in the workspace.
upload_no_name400invalid_request_errorThe upload gave no file name, so send it in the X-Filename header or the filename query parameter.
upload_rate_limited429rate_limit_errorToo many files were uploaded in the last hour, so try again later.
upload_storage_full507api_errorUploaded files in the workspace have reached their storage limit, so delete some before uploading more.
upload_too_large413invalid_request_errorThe file is larger than the upload limit the message gives.
user_not_found404not_found_errorNo account on Nextmail has that userId.
username_locked409conflict_errorA username is permanent once it is chosen, so it cannot be changed. Contact support if you need a different one.
username_taken409conflict_errorAnother account already has that username, so choose a different one.
version_conflict409conflict_errorSomeone saved the template, form or automation after it was read, so reload it and make the change again.
webhook_disabled409conflict_errorThe webhook is switched off, so switch it back on to replay its deliveries.
workspace_allowance_reached403permission_errorThe account already owns a workspace on the Free plan, and an account can own one, so upgrade that workspace before creating another.
workspace_limit_reached422validation_errorThe workspace has reached a soft limit on that kind of resource, as Workspace limits lists, and the message names it.
workspace_suspended403permission_errorThe workspace is suspended, or its owner is, so it cannot send mail or use the API until the suspension is lifted. Write to support@openemail.uk to ask why or to appeal.

Rate limits

Each API key, and each access token an app holds, may make 600 requests a minute, counted across every endpoint. The minute is a fixed window that starts on the minute of the clock, and every answer to a key or an app carries the count, so a client can slow down before it is refused.

HeaderWhat it says
RateLimit-PolicyThe limit as requests and a window in seconds: 600;w=60.
RateLimit-LimitHow many requests the key or app may make in one window.
RateLimit-RemainingHow many are left in the current window, this request counted.
RateLimit-ResetSeconds until the window ends and the count starts again.
Retry-AfterOnly on a 429: the seconds to wait before the same request can succeed.

The request after the last one a window allows is a 429 rate_limited, and its Retry-After is the rest of the window. Nothing it asked for was done, so send it again once the wait is over. Every other 429 carries Retry-After as well: the length of its own window for a throttle such as upload_rate_limited, the seconds to midnight UTC for a daily allowance such as ai_quota_exceeded, and the seconds to the first of the month for send_quota_exceeded.

Some endpoints keep a tighter limit of their own on top. POST /events takes 600 events a minute from a key or an app, and POST /temp-mail/inboxes, which needs no key, lets one network create 10 inboxes every 10 minutes. Their rows in Every error code say what each one answers.