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

FieldTypeDescription
codestringMachine-readable error code (e.g., RESOURCE_001)
messagestringHuman-readable error message
detailsobject | nullAdditional validation error details with field-level errors
doc_urlstring | nullURL to detailed documentation about this error

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

Authentication Errors (AUTH_*)

CodeHTTP StatusMessageResolution
AUTH_001401User is not authenticatedInclude the x-api-key header with a valid API key
AUTH_002401Invalid or expired API keyCheck your API key and generate a new one if needed
AUTH_004403Insufficient permissions for this operationVerify your user role has permission for this action

Validation Errors (VALIDATION_*)

CodeHTTP StatusMessageResolution
VALIDATION_001400Request validation failedCheck the details field for specific field errors
VALIDATION_002400Invalid phone number formatEnsure phone number is in valid E.164 or international format
VALIDATION_003400Invalid GUID formatEnsure UUIDs are properly formatted (e.g., xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)
VALIDATION_004400Required field is missingCheck the request body includes all required fields
VALIDATION_005400Field value out of valid rangeEnsure numeric values are within acceptable ranges
VALIDATION_006400Invalid enum valueUse one of the allowed enum values
VALIDATION_007400Invalid Idempotency-Key formatKey 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_*)

CodeHTTP StatusMessageResolution
RESOURCE_001404Contact not foundVerify the contact ID exists and belongs to your account
RESOURCE_002404Template not foundVerify the template ID exists
RESOURCE_003404Message not foundVerify the message ID exists
RESOURCE_004404Customer not foundContact support if this error occurs
RESOURCE_005404Organization not foundVerify your account is properly configured
RESOURCE_006404User not foundVerify the user ID exists
RESOURCE_007409Resource already existsUse a different unique value or update the existing resource
RESOURCE_008404Webhook not foundVerify the webhook ID exists

Business Logic Errors (BUSINESS_*)

CodeHTTP StatusMessageResolution
BUSINESS_001422Cannot modify inherited contactThis contact belongs to another customer
BUSINESS_002429Rate limit exceededReduce request frequency or contact support to increase limits
BUSINESS_003422Insufficient account balanceAdd funds to your account
BUSINESS_004422Contact has opted out of messagingRemove this contact from your messaging list
BUSINESS_005422Template not approved for sendingWait for template approval or use an approved template
BUSINESS_006422Message cannot be modified in current stateMessages can only be modified in certain states
BUSINESS_007422Channel not available for this contactThis phone number doesn't support the requested channel
BUSINESS_008422Operation would exceed quotaContact support to increase your quota

Conflict Errors (CONFLICT_*)

CodeHTTP StatusMessageResolution
CONFLICT_001409Concurrent idempotent request in progressWait for the original request to complete before retrying

Service Errors (SERVICE_*)

CodeHTTP StatusMessageResolution
SERVICE_001503Cache service temporarily unavailableRetry the request after a short delay

Internal Errors (INTERNAL_*)

CodeHTTP StatusMessageResolution
INTERNAL_001500Unexpected internal server errorContact support with the request ID
INTERNAL_002500Database operation failedRetry the request; contact support if persistent
INTERNAL_003502External service error (SMS/WhatsApp provider)Provider service issue; retry after delay
INTERNAL_004504Timeout waiting for operationRetry the request with exponential backoff
INTERNAL_005503Service temporarily unavailableService 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_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

{
  "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: 1705312800

Solution: 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 issues
print(f"Request ID: {data['meta']['request_id']}")
# Provide this to support@sent.dm when reporting issues
log.Printf("Request ID: %s\n", data.Meta.RequestID)
// Provide this to support@sent.dm when reporting issues

Implement 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.


On this page