Skip to main content

Errors

The Arsel API uses standard HTTP status codes and returns errors in a consistent JSON format.

Error Response Format​

All errors follow this structure:

{
"status_code": 422,
"name": "validation_error",
"message": "to must be an array, each value in to must be an email"
}
FieldTypeDescription
status_codenumberThe HTTP status code
namestringA machine-readable error identifier in snake_case
messagestringA human-readable description of what went wrong

Status Codes​

Successful responses are 200 OK (reads and updates), 201 Created (creates), and 202 Accepted (a send that has been queued). Everything below carries the error envelope above.

CodeNameWhen It Happens
400bad_requestThe request body is malformed, the sender email is not on a verified domain, or an account-level rule rejects the send. See Common Send Errors below.
401unauthorizedThe API key is missing, invalid, expired, or revoked
403forbiddenThe API key does not have permission for this action
404not_foundThe requested resource does not exist
409conflictThe request conflicts with the current state of the resource
422validation_errorThe request body failed validation (e.g., invalid email format)
413payload_too_largeThe request body exceeds the size limit (30 MB on /email/send)
429rate_limit_exceededYou have exceeded the request limit for the current 60-second window — or, on /events/send, the event limit. See Rate Limiting
429monthly_quota_exceededYour organization has used its monthly quota for the channel. See Common Send Errors.
500internal_server_errorAn unexpected error occurred on our end
502upstream_errorA downstream provider rejected the request in a way Arsel could not map
503service_unavailableTemporarily unavailable — retry shortly
504upstream_timeoutA downstream provider did not answer in time

These are the generic names, one per status. Some errors carry a more specific name under the same status code — see Specific error names. Treat name as an open set: branch on the ones you handle and fall back to status_code for the rest.

Common Send Errors​

A successful authentication and well-formed request can still be rejected at send time — for example, because the sender domain isn't verified or the organization's quota is exhausted. These are returned with 400 / name: bad_request unless stated otherwise.

ScenarioStatusnameExample message
Sender domain not verified400bad_requestSender email domain is not verified for this organization. Please verify the domain first.
Sender email format invalid or not allowed400bad_requestSender email is required / Invalid sender email
Recipient list empty or address invalid400bad_requestAt least one recipient is required.
Total recipients (to + cc + bcc) exceeds 50400bad_requestToo many recipients. Maximum 50 allowed, but N provided.
An attachment has a blocked type or extension400attachment_rejected1 of 2 attachment(s) rejected: setup.exe. …
Email sending suspended (high bounce/complaint rate)403sending_suspendedEmail sending is suspended for this organization. Reason: high bounce/complaint rate. Review on the dashboard or contact support to unsuspend.
Monthly email quota exhausted429monthly_quota_exceededMonthly email quota exceeded
note

monthly_quota_exceeded is distinct from rate_limit_exceeded. The first means you've used your plan's allotment for the month; the second means you're sending faster than the 60-second window allows (counted in events on /events/send). Retrying the quota error won't help until the next billing period or a plan upgrade.

Specific error names​

nameStatusWhereMeaning
sending_suspended403Email sendsEmail sending is suspended for the organization. See Sender reputation.
attachment_rejected400POST /email/sendAn attachment has a blocked type; nothing was sent.
invalid_idempotency_key400Idempotent endpointsThe Idempotency-Key is empty or longer than 256 characters.
invalid_idempotent_request409Idempotent endpointsThe key was already used with a different request.
concurrent_idempotent_requests503Idempotent endpointsAn earlier request with the same key is still processing.
contact_required400POST /events/sendNo contact identifier was provided.
contact_ambiguous400POST /events/sendcontact_id was combined with another identifier.
contact_not_found404POST /events/sendThe contact_id does not exist.
event_not_found404POST /events/sendNo event with that name is defined.
schema_mismatch422POST /events/sendThe payload does not match the event's schema.
event_in_use409DELETE /events/:idAn active automation is triggered by the event.
event_limit_reached422POST /eventsThe organization has reached its event-definition limit.
conversion_value_field_invalid400POST/PATCH /eventsThe conversion value_field is not a numeric field. Other conversion misconfigurations return a conversion_* name.
segment_limit_reached422POST /segmentsThe organization has reached its segment limit.
segment_in_use409DELETE /segments/:idA scheduled or sending campaign targets the segment.
segment_not_ready409GET /contacts?segment_id=The segment is still being computed.
domain_unavailable400POST /email/domainsThe domain, or a related one, is already registered by another organization.
domain_limit_reached400POST /email/domainsThe plan's sending-domain limit is reached.

See Idempotency for the three idempotency errors.

Validation Errors​

When a request fails validation, the message field contains a comma-separated list of all validation issues:

{
"status_code": 422,
"name": "validation_error",
"message": "from must be an email, to should not be empty, subject must be shorter than or equal to 998 characters"
}

Handling Errors​

Check status_code first to determine the category of error. Use name for programmatic error handling, and message for logging or displaying to users.

const response = await fetch("https://api.arsel.sa/v1/email/send", options);
const data = await response.json();

if (response.status >= 400) {
switch (data.name) {
case "unauthorized":
// Refresh or rotate your API key
break;
case "validation_error":
// Fix the request payload
break;
case "rate_limit_exceeded":
// Back off and retry
break;
default:
// Log and alert
console.error(`API error: ${data.name} — ${data.message}`);
}
}