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"
}
| Field | Type | Description |
|---|---|---|
status_code | number | The HTTP status code |
name | string | A machine-readable error identifier in snake_case |
message | string | A 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.
| Code | Name | When It Happens |
|---|---|---|
| 400 | bad_request | The 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. |
| 401 | unauthorized | The API key is missing, invalid, expired, or revoked |
| 403 | forbidden | The API key does not have permission for this action |
| 404 | not_found | The requested resource does not exist |
| 409 | conflict | The request conflicts with the current state of the resource |
| 422 | validation_error | The request body failed validation (e.g., invalid email format) |
| 413 | payload_too_large | The request body exceeds the size limit (30 MB on /email/send) |
| 429 | rate_limit_exceeded | You have exceeded the request limit for the current 60-second window — or, on /events/send, the event limit. See Rate Limiting |
| 429 | monthly_quota_exceeded | Your organization has used its monthly quota for the channel. See Common Send Errors. |
| 500 | internal_server_error | An unexpected error occurred on our end |
| 502 | upstream_error | A downstream provider rejected the request in a way Arsel could not map |
| 503 | service_unavailable | Temporarily unavailable — retry shortly |
| 504 | upstream_timeout | A 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.
| Scenario | Status | name | Example message |
|---|---|---|---|
| Sender domain not verified | 400 | bad_request | Sender email domain is not verified for this organization. Please verify the domain first. |
| Sender email format invalid or not allowed | 400 | bad_request | Sender email is required / Invalid sender email |
| Recipient list empty or address invalid | 400 | bad_request | At least one recipient is required. |
Total recipients (to + cc + bcc) exceeds 50 | 400 | bad_request | Too many recipients. Maximum 50 allowed, but N provided. |
| An attachment has a blocked type or extension | 400 | attachment_rejected | 1 of 2 attachment(s) rejected: setup.exe. … |
| Email sending suspended (high bounce/complaint rate) | 403 | sending_suspended | Email sending is suspended for this organization. Reason: high bounce/complaint rate. Review on the dashboard or contact support to unsuspend. |
| Monthly email quota exhausted | 429 | monthly_quota_exceeded | Monthly email quota exceeded |
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
name | Status | Where | Meaning |
|---|---|---|---|
sending_suspended | 403 | Email sends | Email sending is suspended for the organization. See Sender reputation. |
attachment_rejected | 400 | POST /email/send | An attachment has a blocked type; nothing was sent. |
invalid_idempotency_key | 400 | Idempotent endpoints | The Idempotency-Key is empty or longer than 256 characters. |
invalid_idempotent_request | 409 | Idempotent endpoints | The key was already used with a different request. |
concurrent_idempotent_requests | 503 | Idempotent endpoints | An earlier request with the same key is still processing. |
contact_required | 400 | POST /events/send | No contact identifier was provided. |
contact_ambiguous | 400 | POST /events/send | contact_id was combined with another identifier. |
contact_not_found | 404 | POST /events/send | The contact_id does not exist. |
event_not_found | 404 | POST /events/send | No event with that name is defined. |
schema_mismatch | 422 | POST /events/send | The payload does not match the event's schema. |
event_in_use | 409 | DELETE /events/:id | An active automation is triggered by the event. |
event_limit_reached | 422 | POST /events | The organization has reached its event-definition limit. |
conversion_value_field_invalid | 400 | POST/PATCH /events | The conversion value_field is not a numeric field. Other conversion misconfigurations return a conversion_* name. |
segment_limit_reached | 422 | POST /segments | The organization has reached its segment limit. |
segment_in_use | 409 | DELETE /segments/:id | A scheduled or sending campaign targets the segment. |
segment_not_ready | 409 | GET /contacts?segment_id= | The segment is still being computed. |
domain_unavailable | 400 | POST /email/domains | The domain, or a related one, is already registered by another organization. |
domain_limit_reached | 400 | POST /email/domains | The 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}`);
}
}