Skip to content
couryo

Developer guideReference

Errors

Couryo API error format, types, HTTP statuses and every code with cause and fix.

View as Markdown

Every error has the same shape. The code is stable: you can rely on it in your code. Each code always has the same type and the same HTTP status.

Error
{
  "error": {
    "type": "invalid_request",
    "code": "domain_not_verified",
    "message": "The domain example.com is not verified in this project yet.",
    "param": "from",
    "doc_url": "https://couryo.com/docs/errors#domain_not_verified",
    "request_id": "req_6f2a9c1e7b0k3m4n"
  }
}
Field What it is
type the error category (table below)
code the exact reason, stable
message a human explanation, in English or Portuguese following Accept-Language (Portuguese by default; send Accept-Language: en)
param the offending field, when there is one (for example to[1] or emails[2].from)
doc_url a link to the explanation of this code
request_id the request ID, same as the Request-Id header; send it to support if you need help

Types#

type HTTP When
invalid_request 400 or 422 the request has a problem you can fix
authentication 401 missing or invalid key
permission 403 the key or the plan does not allow this
not_found 404 the resource or route does not exist
conflict 409 a conflict, such as a reused idempotency key
rate_limited 429 too many calls in a short time
tier_limit 429 a limit of your level, your plan or your spending limit
api_error 500 or 503 a problem on our side; you can retry

Codes#

code type HTTP Summary
missing_field invalid_request 400 a required field is missing
invalid_field invalid_request 400 a field has an invalid value
invalid_json invalid_request 400 the body is not JSON
batch_too_large invalid_request 400 more than 100 emails in a batch
invalid_cloudflare_token invalid_request 400 Cloudflare token refused
cloudflare_zone_not_found invalid_request 400 the token cannot reach the domain's zone
domain_not_verified invalid_request 422 the from domain is not verified
recipient_suppressed invalid_request 422 every recipient is suppressed
missing_api_key authentication 401 no key
invalid_api_key authentication 401 invalid key
not_authenticated authentication 401 dashboard call without a session
insufficient_scope permission 403 the key's scope is not enough
ip_not_allowed permission 403 IP outside the key's list
sandbox_recipient permission 403 level 0 account sending outside the account
domain_limit_reached permission 403 the account already has the plan's domains
forbidden_origin permission 403 dashboard write from another site
resource_not_found not_found 404 unknown ID
route_not_found not_found 404 unknown route
idempotency_conflict conflict 409 same idempotency key, different body
idempotency_in_progress conflict 409 the same request is still running
domain_exists conflict 409 domain already registered
already_suppressed conflict 409 address already on the list
last_project conflict 409 trying to delete the only project
rate_limited rate_limited 429 too many calls per second
daily_limit_reached tier_limit 429 daily limit reached
monthly_limit_reached tier_limit 429 monthly quota reached
spend_limit_reached tier_limit 429 overage reached your spending limit
sending_paused tier_limit 429 sending paused, with the reason
internal_error api_error 500 an error on our side
billing_unavailable api_error 503 billing unavailable right now

missing_field#

A required field is missing, named in param. To send, from, to, subject and one of html or text are required (with neither, param is html).

invalid_field#

The field named in param has a value we do not accept: a malformed address, more than 50 recipients across to, cc and bcc, a scheduled_at in the past or more than 30 days ahead, a webhook URL without https://. The message says what is wrong. In a batch, param includes the index: emails[2].to[0].

invalid_json#

The body is not valid JSON. Check Content-Type: application/json and your quotes.

batch_too_large#

The batch has more than 100 emails. Split it into smaller calls.

invalid_cloudflare_token#

Cloudflare did not accept the token. It needs the Zone > DNS > Edit permission for the domain. See Domains and DNS.

cloudflare_zone_not_found#

The token is valid but does not give access to the domain's zone on Cloudflare. Create the token including that zone.

domain_not_verified#

The from domain is not verified in the key's project. Verify it in Domains and DNS and try again. ck_test_ keys do not need a verified domain.

recipient_suppressed#

Every recipient is on the suppression list, because of a hard bounce, a complaint, an unsubscribe or a manual entry. param points to the first one (to[n]). If only some recipients are suppressed, the email goes to the others and the timeline gets a suppressed event for each skipped address.

missing_api_key#

The Authorization: Bearer ck_... header is missing. See Authentication.

invalid_api_key#

The key does not exist, was deleted or was copied only in part. Create a new one in the dashboard if needed.

not_authenticated#

A dashboard call (/api) without a session. Sign in again at app.couryo.com. It never happens on the public API (/v1).

insufficient_scope#

The key is not allowed to do this. For example, a send key trying to read emails, which needs read. The message says which scope is missing.

ip_not_allowed#

The key has an allowed IP list and the call came from another address. Adjust the list in the dashboard.

sandbox_recipient#

The account is on level 0 (sandbox), or the sender is teste@sandbox.couryo.com, and that path only sends to the account's own addresses. Verify a domain to move to level 1. See Limits and levels.

domain_limit_reached#

The account already has the number of domains its plan allows: Free 1, Pro 10, Scale 50, Enterprise unlimited (across all projects). Delete a domain you do not use or change plans. The message gives the current limit and the next plan's.

forbidden_origin#

A dashboard write came from another site (CSRF protection). It never happens on the public API.

resource_not_found#

The ID does not exist in this project, or belongs to another project. The message says which resource was not found.

route_not_found#

The path does not exist. Check the method and the URL, for example POST /v1/emails.

idempotency_conflict#

The same Idempotency-Key was used in the last 24 hours with a different body. Use a new key for a new request. See Idempotency.

idempotency_in_progress#

A request with the same Idempotency-Key is still being processed. Wait for Retry-After (1 second) and repeat: you get the first request's response.

domain_exists#

This domain is already registered on Couryo, in this or another account. If it is yours and lives in another account, delete it there first.

already_suppressed#

The address is already on the suppression list for that stream.

last_project#

The account needs at least one project. Create another one before deleting this one.

rate_limited#

Too many calls in a short time (the default is 10 per second per key). Wait the seconds in the Retry-After header and retry with the same Idempotency-Key. The RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset headers show your headroom.

daily_limit_reached#

You reached your trust level's daily limit, or the Free plan's (100 per day). The message gives the limit, how much was sent and what is missing to move up a level. The counter resets at midnight in your account's time zone.

monthly_limit_reached#

You reached the monthly quota: 3,000 on the Free plan and also on level 1 of any plan. Sending resumes next month (on a paid plan, next period). On level 1, the quota goes up when the account reaches level 2; on Free, when you subscribe to Pro or Scale.

spend_limit_reached#

This month's overage reached the spending limit you set under Billing. All sending stops (transactional and marketing) until you raise the limit or the period renews. Nothing is charged above the cap.

sending_paused#

Sending is paused. On a regular pause, marketing stops and transactional email keeps going with up to 30 per day; on a full pause, everything stops. The message brings the reason, the numbers and how to fix it, and the dashboard has a Request review button. See Limits and levels.

internal_error#

A problem on our side. The request was not processed. Retry with the same Idempotency-Key: there is no risk of a duplicate.

billing_unavailable#

Billing is unavailable right now (checkout or payment portal). Try again in a few minutes; sending email is not affected.