The shape

Every non-2xx answer carries one body:

JSON
{
  "error": {
    "code": "concurrent_idempotent_requests",
    "message": "A request with this idempotency key is still being processed. Retry shortly.",
    "retryable": true,
    "request_id": "req_019a2b3c-4d5e-7f60-8a9b-0c1d2e3f4a5b"
  }
}
Field Meaning
code A stable identifier from the table below. Branch on this, not on the message.
message What happened and what to do next. A validation message names the first invalid field by its full path (Missing `definition.steps.0.template_id` field.).
retryable True when repeating the same request with the same idempotency key can succeed.
request_id The request's id, see Request ids and logs.
issues Validation only: [{ "path": "attachments.0.filename", "message": "…" }], one per invalid field, with dotted paths into the request body.

A failed item of a batch carries { "code", "message", "retryable" } in its own result.

Retrying

  • When retryable is true, repeat the request with the same Idempotency-Key: the key makes sure a retry never sends a second email. A retry-after header says how many seconds to wait first; it comes with 429 rate_limit_exceeded and 409 concurrent_idempotent_requests.
  • 500 errors and 502 upstream_unavailable are retryable. 502 outcome_unknown is not: the provider's answer was lost, so the email may have gone out, and it is failed rather than sent again (Never sent twice).
  • 429 daily_quota_exceeded lasts until the next UTC day, or until the cap is raised in the project's settings.
  • Validation errors (400, 422) need a changed request; issues names the fields.
  • A body that is not valid JSON is 422 validation_error, and a body sent without a JSON content type is 415 unsupported_media_type. Send Content-Type: application/json, which fetch leaves out for a string body. A request with no body at all reads as {}.
  • 501 not_implemented means the route is not served yet. It is not retryable.

Codes

CodeStatusRetryableMessage
invalid_idempotency_key400noThe idempotency key must be between 1 and 256 characters.
invalid_sign_in_code400noThat sign-in code is not valid. Check it and try again, or ask for a new code.
missing_api_key401noMissing API key in the authorization header.
restricted_api_key401noThis API key is restricted to only send emails.
unauthorized401noSign in to continue.
account_blocked403noThis account is blocked. Contact Minket if you think this is a mistake.
domain_authentication_failed403noSPF, DKIM or DMARC is failing for this domain. Sending resumes when DNS is fixed.
domain_not_verified403noThis domain is not verified yet. Finish its DNS records in the console.
forbidden403noYour role does not allow this. Ask an owner or admin of the organization.
invalid_access403noAccess to this resource is not allowed.
invalid_api_key403noAPI key is invalid.
invalid_from_address403noThe from address does not belong to a verified domain of this project.
organization_suspended403noThis organization is suspended. Contact Minket to restore sending.
signup_closed403noSign-up is closed. Ask an owner of an organization for an invitation.
test_mode_only403noTest keys cannot do this. Use a live key.
not_found404noThe requested item does not exist.
method_not_allowed405noThis method is not allowed for this endpoint.
concurrent_idempotent_requests409yesA request with this idempotency key is still being processed. Retry shortly.
conflict409noThis already exists.
invalid_state409noThis action is not possible in the item's current state. Check its status first.
mailbox_unavailable409noThis mailbox needs to be reconnected before it can send.
payload_too_large413noThe request body is too large.
unsupported_media_type415noSend JSON with a content-type of application/json.
content_blocked422noThe content failed a deliverability check. See issues for details.
invalid_attachment422noAn attachment is invalid.
invalid_idempotent_request422noThis idempotency key was already used with a different request.
invalid_parameter422noA parameter has an invalid value.
invalid_region422noThis region is not supported.
missing_required_field422noA required field is missing.
stream_rule_violation422noMarketing email needs an unsubscribe link and List-Unsubscribe headers.
validation_error422noThe request is not valid.
daily_quota_exceeded429noThis organization has used its daily sending quota. It resets at midnight UTC; ask Minket to raise it.
monthly_quota_exceeded429noThis organization has used its monthly sending quota. It resets on the first of the month (UTC); ask Minket to raise it.
rate_limit_exceeded429yesToo many requests. Slow down and retry after the time in retry-after.
security_error451noThis request was blocked for security reasons.
application_error500yesSomething went wrong on our side. Retry with the same idempotency key.
internal_server_error500yesSomething went wrong on our side. Retry with the same idempotency key.
not_implemented501noThis endpoint is not available yet. Check the deployment's version.
outcome_unknown502noThe provider's answer was lost, so it is unknown whether this email went out. It was not sent again, to avoid a duplicate.
upstream_unavailable502yesA provider did not respond. Retry with the same idempotency key.
sending_paused503yesSending is paused for this project. Check alerts in the console.

Request ids and logs

Every response carries an x-request-id header. Send your own (8 to 128 characters from A-Z, a-z, 0-9, _ and -) and it comes back unchanged; otherwise the API makes one, such as req_019a2b3c-4d5e-7f60-8a9b-0c1d2e3f4a5b.

Every request made with a key is logged with its method, route pattern (/v1/emails/:id, never the concrete path), status, latency, key id, environment, mode, error code, request id, the caller's address and user agent, and the SHA-256 of its Idempotency-Key. Bodies and recipients are never logged. GET /v1/logs lists a key's own environment and mode, newest first, filtered by status, path, api_key_id, error_code, from_date and to_date; GET /v1/logs/{id} returns one. Logs are kept 30 days, and the console shows them under Logs.

Shell
curl "https://api.minket.com/v1/logs?error_code=rate_limit_exceeded" \
  -H "Authorization: Bearer $MINKET_API_KEY"

Updated