How to handle Sent API errors
This guide shows you how to handle Sent API v3 errors in your client code: detect failures, branch on error codes, capture request IDs for support, prevent avoidable errors, and test your handling without side effects. It assumes you can already authenticate and send requests to the API; if not, start with the API reference overview.
Every error uses the same JSON envelope with a machine-readable code. The Error Handling reference documents the envelope and HTTP status codes; the Error Catalog enumerates every code.
Check the success flag on every response
The envelope's success boolean is the reliable failure signal. Check it before reading data:
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)Branch on error codes, not messages
Error messages can change; codes are stable. Branch on error.code and give each class of failure its own recovery path:
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)
}If you retry on BUSINESS_002 (rate limit) or 5xx errors, use exponential backoff. See How to handle Sent API rate limits for a complete implementation.
Log request IDs for support
Every response includes meta.request_id. Log it with each failure, and include it when contacting support@sent.dm. The ID lets support trace the exact request:
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 issuesMake retries safe with idempotency keys
If your error handling retries mutations (POST, PUT, PATCH), send an Idempotency-Key header so a retry after a timeout or 5xx error cannot perform the operation twice:
POST /v3/messages
x-api-key: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Idempotency-Key: send-msg-order-8412
Content-Type: application/jsonDerive the key from a business identifier for the operation (an order ID, a client-generated request ID) and reuse the same key on every retry attempt. How to retry Sent API requests safely covers key derivation and retry loops in full.
Validate inputs before sending
Prevent the most common VALIDATION_* errors client-side. Phone numbers must be in E.164 format:
// 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")
}When the server rejects input, read the error.details object. It carries field-level messages you can surface directly to users.
Test your error handling with sandbox mode
To exercise your error paths without sending real messages, add sandbox: true to any mutation request. Validation still runs, so invalid input returns real validation errors with no 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 '{
"sandbox": true,
"to": ["invalid"],
"template": {"id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"}
}'This returns a VALIDATION_* error envelope without attempting to send a message. Refer to the Sandbox Mode reference for the full contract.
Verify your handling
You have working error handling when:
- A request with an invalid phone number surfaces the field-level message from
error.detailsinstead of a generic failure. - A
429response triggers a delayed retry rather than an immediate one. - Failed requests appear in your logs with
error.codeandmeta.request_id.
Related
- Refer to the Error Handling reference for the envelope format, HTTP status codes, and common codes.
- The Error Catalog lists every error code with causes and remediation.
- How to retry Sent API requests safely
- How to handle Sent API rate limits