Error Handling

All errors in the Sent API v3 follow a consistent JSON envelope format with structured error codes, making it easy to programmatically handle errors and troubleshoot issues.


Error Response Format

All errors follow a consistent JSON envelope:

{
  "success": false,
  "status": 404,
  "error": {
    "code": "RESOURCE_001",
    "message": "Contact not found",
    "doc_url": "https://docs.sent.dm/reference/api/error-catalog"
  },
  "meta": {
    "request_id": "req_a1b2c3d4e5f60718",
    "timestamp": "2026-01-15T10:30:00Z",
    "version": "v3"
  }
}

Top-level Fields

FieldTypeDescription
successbooleanAlways false on an error response
statusintegerHTTP status code, repeated in the body
errorobjectThe error itself, described below
metaobjectRequest metadata, described below

An error response carries no data field.

Error Object Fields

FieldTypeDescription
codestringMachine-readable error code (for example, RESOURCE_001)
messagestringHuman-readable error message
detailsobjectField-level validation errors, as a map of field name to an array of messages. Omitted when there are none
doc_urlstringLink to the documentation page for this class of error. Omitted when unset

details and doc_url are omitted rather than sent as null, so read them defensively.

Meta Object Fields

FieldTypeDescription
request_idstringUnique request identifier for support and debugging
timestampstringISO 8601 timestamp of the error
versionstringAPI version (always v3)

HTTP Status Codes

StatusDescriptionCommon Causes
200 OKRequest successful-
201 CreatedResource created successfully-
204 No ContentRequest successful, no response bodyDELETE operations
400 Bad RequestInvalid request format or parametersMissing required fields, invalid JSON
401 UnauthorizedAuthentication failedMissing or invalid API key
403 ForbiddenPermission deniedInsufficient role permissions
404 Not FoundResource not foundInvalid resource ID
409 ConflictResource conflictDuplicate entry, concurrent modification
422 Unprocessable EntityValidation failedInvalid field values, business rule violations
429 Too Many RequestsRate limit exceededToo many requests in time window
500 Internal Server ErrorUnexpected server errorServer-side issue
502 Bad GatewayUpstream service errorProvider service unavailable
503 Service UnavailableService temporarily unavailableMaintenance or overload

Error Code Reference

The Error Catalog is the canonical enumeration of every error code, with causes and step-by-step remediation. The codes below are the ones client code most commonly branches on:

CodeHTTP StatusMeaningHandling
AUTH_001401User is not authenticatedInclude the x-api-key header with a valid API key
AUTH_002401Invalid or missing API keyVerify the key; regenerate it if revoked
VALIDATION_001400Request validation failedCheck the details field for field-level errors
RESOURCE_001404Contact not foundVerify the contact ID exists and belongs to your account
BUSINESS_002429Rate limit exceededBack off and respect the Retry-After header
CONFLICT_001409Concurrent idempotent request in progressWait for the original request to complete before retrying
SERVICE_001503Cache service temporarily unavailableRetry the request after a short delay
INTERNAL_001500Unexpected internal server errorRetry, then contact support with the request ID

For the full AUTH_*, VALIDATION_*, RESOURCE_*, BUSINESS_*, CONFLICT_*, SERVICE_*, and INTERNAL_* listings, refer to the Error Code Quick Reference in the Error Catalog.

Example Validation Error with Details (POST /v3/messages with an empty to array and a template missing both id and name):

{
  "success": false,
  "status": 400,
  "error": {
    "code": "VALIDATION_001",
    "message": "Request validation failed",
    "details": {
      "to": ["'to' must contain at least one recipient"],
      "template": ["'template' must have either 'id' (non-empty GUID) or 'name' (non-empty string)"]
    },
    "doc_url": "https://docs.sent.dm/reference/api/error-catalog"
  },
  "meta": { ... }
}

Send-time Error Codes (ERR_*)

These codes are produced by the per-message send pipeline. They are not returned in the HTTP response. POST /v3/messages responds 202 for the batch, and each message is finalized asynchronously with a terminal status:

CodeTerminal statusTriggerWhat it means
ERR_CONSENT_BLOCKEDFILTEREDPer-message consent gateThe recipient's contact has opt_out = true, or their phone is on your phone-channel suppression list. The send is suppressed pre-routing: no provider call is made and no charge applies.
ERR_ROUTE_DENIEDFILTEREDPer-message routing gateRouting rules denied every candidate route for this send, and no fallback was allowed.
ERR_TEMPLATE_PARAMS_INVALIDFAILEDPer-message template validationRequired template variables were missing or failed validation.

Track each message's outcome through:

  • GET /v3/messages/{id}: the status field carries the terminal status (FILTERED or FAILED)
  • GET /v3/messages/{id}/activities: a FILTERED or FAILED activity; activity description values are generic (Message updated to FILTERED) and do not carry the ERR_* code or reason
  • The message.filtered and message.failed webhook events

The ERR_* code and detailed reason are recorded internally and are not currently included in API responses or webhook payloads. Contact support@sent.dm with the message ID if you need the exact reason.

Conversation-window codes

Two send-time codes are not ERR_* prefixed, because they are part of the two-way messaging contract and carry an actionable reason. Like the ERR_* codes, they are not returned on the HTTP response: POST /v3/messages responds 202 and the outcome lands on the message as its terminal status. Both call for an approved template as the first step, but they answer to different rules and a template does not clear both: on SMS, RCS, and auto-detect it opens a 7-day window for a contact who has never replied, while only a recent inbound WhatsApp message opens Meta's 24-hour one.

CodeTerminal statusTriggerWhose rule
CONVERSATION_TEMPLATE_REQUIREDBLOCKEDFree-form send on SMS, RCS, or auto-detect to a contact who has never replied, and either no template was ever sent or the last one is more than 7 days old.Sent's
WHATSAPP_TEMPLATE_REQUIREDFAILEDFree-form send pinned to channel: "whatsapp" outside Meta's 24-hour customer-service window, or a Meta 131047 re-engagement rejection.Meta's

Like the ERR_* codes, neither is currently exposed in API responses, webhook payloads, or the dashboard. The client-visible signal is the terminal status, which differs between them: the conversation gate records BLOCKED (excluded from your deliverability rate, fires message.blocked), while the WhatsApp gate records FAILED. Infer the cause from the send conditions, or contact support@sent.dm with the message ID. Full causes and remediation: CONVERSATION_TEMPLATE_REQUIRED and WHATSAPP_TEMPLATE_REQUIRED. Window mechanics: Conversation Windows.


Common Error Scenarios

Authentication Issues

Missing API Key

curl -X GET https://api.sent.dm/v3/me
# Response: 401 AUTH_001

Solution: Include the x-api-key header:

curl -X GET https://api.sent.dm/v3/me \
  -H "x-api-key: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"

Validation Issues

Invalid Phone Number

A POST /v3/messages request with a recipient that is not in E.164 format returns:

{
  "success": false,
  "status": 400,
  "error": {
    "code": "VALIDATION_001",
    "message": "Request validation failed",
    "details": {
      "to": ["Each entry in 'to' must be a valid phone number in E.164 format (e.g. +14155551234)"]
    },
    "doc_url": "https://docs.sent.dm/reference/api/error-catalog"
  }
}

Solution: Use E.164 format for every entry in to:

{
  "to": ["+14155551234"]
}

Resource Not Found

Contact Not Found

{
  "success": false,
  "status": 404,
  "error": {
    "code": "RESOURCE_001",
    "message": "Contact not found",
    "doc_url": "https://docs.sent.dm/reference/api/error-catalog"
  }
}

Solution: Verify the contact ID exists:

curl -X GET https://api.sent.dm/v3/contacts \
  -H "x-api-key: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"

Rate Limiting

Too Many Requests

{
  "success": false,
  "status": 429,
  "error": {
    "code": "BUSINESS_002",
    "message": "Rate limit exceeded",
    "doc_url": "https://docs.sent.dm/reference/api/rate-limits"
  }
}

Response Headers:

Retry-After: 60
X-RateLimit-Limit: 200
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1705312800

Solution: Implement exponential backoff and respect the Retry-After header. See Rate Limits for per-endpoint limit values and the full set of rate limit headers.


On this page