Error Handling
All SendSeven API errors follow a consistent JSON structure. This guide covers error codes, handling strategies, and retry logic.
Error response format
{
"detail": "Human-readable error message",
"error_code": "MACHINE_READABLE_CODE"
}
Validation errors include field-level details:
{
"detail": "Validation error",
"error_code": "VALIDATION_ERROR",
"errors": [
{ "field": "email", "message": "Invalid email format" },
{ "field": "phone", "message": "Phone number must include country code" }
]
}
HTTP Status Codes
Success codes
| Code | Name | When Used |
|---|---|---|
| 200 | OK | GET, PUT, PATCH, DELETE |
| 201 | Created | POST that creates a resource |
| 204 | No Content | DELETE with no response body |
Client errors
| Code | Name | Action |
|---|---|---|
| 400 | Bad Request | Check request format |
| 401 | Unauthorized | Verify API token |
| 403 | Forbidden | Check token scopes |
| 404 | Not Found | Verify resource ID and tenant |
| 409 | Conflict | Duplicate resource exists |
| 422 | Unprocessable Entity | Check field validation rules |
| 429 | Too Many Requests | Wait and retry (see Rate Limits) |
Server errors
| Code | Name | Action |
|---|---|---|
| 500 | Internal Server Error | Retry after delay; contact support if persistent |
| 503 | Service Unavailable | Retry with exponential backoff |
Error Codes Reference
INVALID_TOKEN
HTTP 401 - Token is missing, malformed, or expired.
{
"detail": "Invalid or expired authentication token",
"error_code": "INVALID_TOKEN"
}
Fix: Verify the Authorization header format (Bearer s7_api_<32hex>), check expiration, or create a new token.
INSUFFICIENT_SCOPE
HTTP 403 - Token lacks the required permission.
{
"detail": "Missing required scope: campaigns:create",
"error_code": "INSUFFICIENT_SCOPE"
}
Fix: The detail message tells you exactly which scope is missing. Create a token with that scope.
RESOURCE_NOT_FOUND
HTTP 404 - Resource doesn't exist or belongs to a different tenant.
{
"detail": "Conversation not found",
"error_code": "RESOURCE_NOT_FOUND"
}
Fix: Verify the resource ID and that your API token is associated with the correct account.
VALIDATION_ERROR
HTTP 422 - Request body failed validation.
{
"detail": "Validation error",
"error_code": "VALIDATION_ERROR",
"errors": [
{ "field": "email", "message": "Invalid email format" },
{ "field": "phone", "message": "Phone number must include country code (e.g., +1)" }
]
}
Fix: Check the errors array for specific field-level issues.
RATE_LIMITED
HTTP 429 - Too many requests.
{
"detail": "Rate limit exceeded. Retry after 45 seconds.",
"error_code": "RATE_LIMITED"
}
Fix: Read the Retry-After header and wait before retrying. See Rate Limits.
INSUFFICIENT_BALANCE
HTTP 402 - Account has exceeded trial limits or payment failed.
{
"detail": "Insufficient balance to send message.",
"error_code": "INSUFFICIENT_BALANCE"
}
Fix: Check billing status in Settings > Billing, update payment method, or upgrade plan.
DUPLICATE_RESOURCE
HTTP 409 - A resource with the same unique identifier already exists.
{
"detail": "A tag with this name already exists",
"error_code": "DUPLICATE_RESOURCE"
}
Fix: Use a different name or update the existing resource.
Implementing Error Handling
Python
import time
import requests
def api_request_with_retry(method, url, max_retries=5, **kwargs):
headers = {
"Authorization": "Bearer s7_api_your_token",
"Content-Type": "application/json",
}
kwargs.setdefault("headers", headers)
for attempt in range(max_retries):
response = requests.request(method, url, **kwargs)
if response.status_code == 429:
retry_after = int(response.headers.get("Retry-After", 2 ** attempt))
time.sleep(retry_after)
continue
if response.status_code >= 500:
time.sleep(2 ** attempt)
continue
return response
raise Exception(f"Failed after {max_retries} retries")
JavaScript
async function apiRequest(method, path, body = null) {
const url = `https://api.sendseven.com/api/v1${path}`;
const options = {
method,
headers: {
'Authorization': 'Bearer s7_api_your_token',
'Content-Type': 'application/json',
},
};
if (body) options.body = JSON.stringify(body);
const response = await fetch(url, options);
if (response.ok) return response.json();
const error = await response.json();
switch (error.error_code) {
case 'INVALID_TOKEN':
throw new Error('Authentication failed - check your API token');
case 'INSUFFICIENT_SCOPE':
throw new Error(`Missing permission: ${error.detail}`);
case 'RATE_LIMITED':
const retryAfter = response.headers.get('Retry-After') || 60;
await new Promise(r => setTimeout(r, retryAfter * 1000));
return apiRequest(method, path, body); // retry
case 'VALIDATION_ERROR':
throw new Error(`Validation: ${error.errors?.map(e => `${e.field}: ${e.message}`).join(', ')}`);
default:
throw new Error(error.detail || 'Unknown error');
}
}
Retry guidelines
| Status | Retry? | Reason |
|---|---|---|
| 400 | No | Fix the request |
| 401 | No | Fix authentication |
| 403 | No | Fix permissions |
| 404 | No | Resource doesn't exist |
| 409 | No | Resolve conflict |
| 422 | No | Fix validation errors |
| 429 | Yes | Wait for rate limit reset |
| 500 | Yes | Transient server error |
| 503 | Yes | Service temporarily unavailable |
Only retry on 429 and 5xx errors. Client errors (4xx) indicate a problem with the request itself.