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,
"data": null,
"error": {
"code": "RESOURCE_001",
"message": "Contact not found",
"details": null,
"doc_url": "https://docs.sent.dm/errors/RESOURCE_001"
},
"meta": {
"request_id": "req_abc123",
"timestamp": "2024-01-15T10:30:00Z",
"version": "v3"
}
}Error Object Fields
| Field | Type | Description |
|---|---|---|
code | string | Machine-readable error code (e.g., RESOURCE_001) |
message | string | Human-readable error message |
details | object | null | Additional validation error details with field-level errors |
doc_url | string | null | URL to detailed documentation about this error |
Meta Object Fields
| Field | Type | Description |
|---|---|---|
request_id | string | Unique request identifier for support and debugging |
timestamp | string | ISO 8601 timestamp of the error |
version | string | API version (always v3) |
HTTP Status Codes
| Status | Description | Common Causes |
|---|---|---|
200 OK | Request successful | - |
201 Created | Resource created successfully | - |
204 No Content | Request successful, no response body | DELETE operations |
400 Bad Request | Invalid request format or parameters | Missing required fields, invalid JSON |
401 Unauthorized | Authentication failed | Missing or invalid API key |
403 Forbidden | Permission denied | Insufficient role permissions |
404 Not Found | Resource not found | Invalid resource ID |
409 Conflict | Resource conflict | Duplicate entry, concurrent modification |
422 Unprocessable Entity | Validation failed | Invalid field values, business rule violations |
429 Too Many Requests | Rate limit exceeded | Too many requests in time window |
500 Internal Server Error | Unexpected server error | Server-side issue |
502 Bad Gateway | Upstream service error | Provider service unavailable |
503 Service Unavailable | Service temporarily unavailable | Maintenance or overload |
Error Code Reference
Authentication Errors (AUTH_*)
| Code | HTTP Status | Message | Resolution |
|---|---|---|---|
AUTH_001 | 401 | User is not authenticated | Include the x-api-key header with a valid API key |
AUTH_002 | 401 | Invalid or expired API key | Check your API key and generate a new one if needed |
AUTH_004 | 403 | Insufficient permissions for this operation | Verify your user role has permission for this action |
Validation Errors (VALIDATION_*)
| Code | HTTP Status | Message | Resolution |
|---|---|---|---|
VALIDATION_001 | 400 | Request validation failed | Check the details field for specific field errors |
VALIDATION_002 | 400 | Invalid phone number format | Ensure phone number is in valid E.164 or international format |
VALIDATION_003 | 400 | Invalid GUID format | Ensure UUIDs are properly formatted (e.g., xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx) |
VALIDATION_004 | 400 | Required field is missing | Check the request body includes all required fields |
VALIDATION_005 | 400 | Field value out of valid range | Ensure numeric values are within acceptable ranges |
VALIDATION_006 | 400 | Invalid enum value | Use one of the allowed enum values |
VALIDATION_007 | 400 | Invalid Idempotency-Key format | Key must be 1-255 alphanumeric characters, hyphens, or underscores |
Example Validation Error with Details:
{
"success": false,
"error": {
"code": "VALIDATION_001",
"message": "Request validation failed",
"details": {
"phone_number": ["Phone number is required"],
"template_id": ["Invalid UUID format"]
},
"doc_url": "https://docs.sent.dm/errors/VALIDATION_001"
},
"meta": { ... }
}Resource Errors (RESOURCE_*)
| Code | HTTP Status | Message | Resolution |
|---|---|---|---|
RESOURCE_001 | 404 | Contact not found | Verify the contact ID exists and belongs to your account |
RESOURCE_002 | 404 | Template not found | Verify the template ID exists |
RESOURCE_003 | 404 | Message not found | Verify the message ID exists |
RESOURCE_004 | 404 | Customer not found | Contact support if this error occurs |
RESOURCE_005 | 404 | Organization not found | Verify your account is properly configured |
RESOURCE_006 | 404 | User not found | Verify the user ID exists |
RESOURCE_007 | 409 | Resource already exists | Use a different unique value or update the existing resource |
RESOURCE_008 | 404 | Webhook not found | Verify the webhook ID exists |
Business Logic Errors (BUSINESS_*)
| Code | HTTP Status | Message | Resolution |
|---|---|---|---|
BUSINESS_001 | 422 | Cannot modify inherited contact | This contact belongs to another customer |
BUSINESS_002 | 429 | Rate limit exceeded | Reduce request frequency or contact support to increase limits |
BUSINESS_003 | 422 | Insufficient account balance | Add funds to your account |
BUSINESS_004 | 422 | Contact has opted out of messaging | Remove this contact from your messaging list |
BUSINESS_005 | 422 | Template not approved for sending | Wait for template approval or use an approved template |
BUSINESS_006 | 422 | Message cannot be modified in current state | Messages can only be modified in certain states |
BUSINESS_007 | 422 | Channel not available for this contact | This phone number doesn't support the requested channel |
BUSINESS_008 | 422 | Operation would exceed quota | Contact support to increase your quota |
Conflict Errors (CONFLICT_*)
| Code | HTTP Status | Message | Resolution |
|---|---|---|---|
CONFLICT_001 | 409 | Concurrent idempotent request in progress | Wait for the original request to complete before retrying |
Service Errors (SERVICE_*)
| Code | HTTP Status | Message | Resolution |
|---|---|---|---|
SERVICE_001 | 503 | Cache service temporarily unavailable | Retry the request after a short delay |
Internal Errors (INTERNAL_*)
| Code | HTTP Status | Message | Resolution |
|---|---|---|---|
INTERNAL_001 | 500 | Unexpected internal server error | Contact support with the request ID |
INTERNAL_002 | 500 | Database operation failed | Retry the request; contact support if persistent |
INTERNAL_003 | 502 | External service error (SMS/WhatsApp provider) | Provider service issue; retry after delay |
INTERNAL_004 | 504 | Timeout waiting for operation | Retry the request with exponential backoff |
INTERNAL_005 | 503 | Service temporarily unavailable | Service maintenance or overload; retry after delay |
Common Error Scenarios
Authentication Issues
Missing API Key
curl -X GET https://api.sent.dm/v3/me
# Response: 401 AUTH_001Solution: 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
{
"success": false,
"error": {
"code": "VALIDATION_002",
"message": "Invalid phone number format",
"details": {
"phone_number": ["Phone number must be in E.164 format (e.g., +1234567890)"]
},
"doc_url": "https://docs.sent.dm/errors/VALIDATION_002"
}
}Solution: Use E.164 format:
{
"phone_number": "+1234567890"
}Resource Not Found
Contact Not Found
{
"success": false,
"error": {
"code": "RESOURCE_001",
"message": "Contact not found",
"doc_url": "https://docs.sent.dm/errors/RESOURCE_001"
}
}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,
"error": {
"code": "BUSINESS_002",
"message": "Rate limit exceeded",
"doc_url": "https://docs.sent.dm/errors/BUSINESS_002"
}
}Response Headers:
Retry-After: 60
X-RateLimit-Limit: 200
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1705312800Solution: Implement exponential backoff and respect the Retry-After header.
Best Practices for Error Handling
Always Check the Success Flag
const response = await fetch('/v3/messages', { ... });
const data: ApiResponse<Message> = await response.json();
if (!data.success) {
// Handle error
console.error(`Error ${data.error?.code}: ${data.error?.message}`);
return;
}
// Process successful response
console.log(data.data);import requests
response = requests.post('/v3/messages', ...)
data = response.json()
if not data['success']:
# Handle error
print(f"Error {data['error']['code']}: {data['error']['message']}")
return
# Process successful response
print(data['data'])resp, err := http.Post("/v3/messages", "application/json", body)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
var data ApiResponse[Message]
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
log.Fatal(err)
}
if !data.Success {
// Handle error
log.Printf("Error %s: %s\n", data.Error.Code, data.Error.Message)
return
}
// Process successful response
log.Println(data.Data)Handle Specific Error Codes
switch (data.error?.code) {
case 'AUTH_001':
case 'AUTH_002':
// Redirect to login or refresh API key
break;
case 'RESOURCE_001':
// Prompt user to create the resource
break;
case 'BUSINESS_002':
// Implement retry with backoff
const retryAfter = response.headers.get('Retry-After');
await sleep(parseInt(retryAfter || '60') * 1000);
break;
default:
// Log unexpected error
console.error('Unexpected error:', data.error);
}error_code = data['error']['code']
if error_code in ('AUTH_001', 'AUTH_002'):
# Redirect to login or refresh API key
pass
elif error_code == 'RESOURCE_001':
# Prompt user to create the resource
pass
elif error_code == 'BUSINESS_002':
# Implement retry with backoff
retry_after = int(response.headers.get('Retry-After', 60))
time.sleep(retry_after)
else:
# Log unexpected error
print('Unexpected error:', data['error'])switch data.Error.Code {
case "AUTH_001", "AUTH_002":
// Redirect to login or refresh API key
case "RESOURCE_001":
// Prompt user to create the resource
case "BUSINESS_002":
// Implement retry with backoff
retryAfter := resp.Header.Get("Retry-After")
seconds, _ := strconv.Atoi(retryAfter)
if seconds == 0 {
seconds = 60
}
time.Sleep(time.Duration(seconds) * time.Second)
default:
// Log unexpected error
log.Printf("Unexpected error: %+v\n", data.Error)
}Use Request IDs for Support
When contacting support about an error, always include the request ID from the meta object:
console.error(`Request ID: ${data.meta.request_id}`);
// Provide this to support@sent.dm when reporting issuesprint(f"Request ID: {data['meta']['request_id']}")
# Provide this to support@sent.dm when reporting issueslog.Printf("Request ID: %s\n", data.Meta.RequestID)
// Provide this to support@sent.dm when reporting issuesImplement Idempotency for Retries
const idempotencyKey = `send-msg-${messageId}`;
const response = await fetch('/v3/messages', {
method: 'POST',
headers: {
'x-api-key': apiKey,
'Idempotency-Key': idempotencyKey,
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
});idempotency_key = f"send-msg-{message_id}"
response = requests.post(
'/v3/messages',
headers={
'x-api-key': api_key,
'Idempotency-Key': idempotency_key,
'Content-Type': 'application/json'
},
json=payload
)idempotencyKey := fmt.Sprintf("send-msg-%s", messageID)
req, err := http.NewRequest("POST", "/v3/messages", body)
if err != nil {
log.Fatal(err)
}
req.Header.Set("x-api-key", apiKey)
req.Header.Set("Idempotency-Key", idempotencyKey)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()Validate Inputs Client-Side
Prevent validation errors by validating inputs before sending:
// Validate phone number format
function isValidE164(phone: string): boolean {
return /^\+[1-9]\d{1,14}$/.test(phone);
}
if (!isValidE164(phoneNumber)) {
throw new Error('Phone number must be in E.164 format');
}import re
# Validate phone number format
def is_valid_e164(phone: str) -> bool:
return bool(re.match(r'^\+[1-9]\d{1,14}$', phone))
if not is_valid_e164(phone_number):
raise ValueError('Phone number must be in E.164 format')import "regexp"
// Validate phone number format
func isValidE164(phone string) bool {
matched, _ := regexp.MatchString(`^\+[1-9]\d{1,14}$`, phone)
return matched
}
if !isValidE164(phoneNumber) {
log.Fatal("Phone number must be in E.164 format")
}Testing Error Scenarios
Use test_mode to test error handling without side effects:
curl -X POST https://api.sent.dm/v3/messages \
-H "x-api-key: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"test_mode": true,
"phone_number": "invalid",
"template_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}'This will return validation errors without actually attempting to send a message.