Skip to main content

Error Codes

All API errors return a consistent JSON format:

{
"error": {
"code": "error_code",
"message": "Human-readable description",
"details": {}
},
"meta": {
"request_id": "req_xyz789"
}
}

Use the request_id when contacting support.

Authentication Errors

CodeHTTP StatusDescriptionResolution
unauthorized401Missing or invalid API keyCheck your X-API-Key header
forbidden403API key lacks required permissionsUse a key with appropriate scopes
ip_not_allowed403Request from non-allowlisted IPAdd the IP to your key's allowlist in Settings
account_locked403Account is lockedContact support@zyphr.dev
account_suspended403Account suspended for policy violationContact support@zyphr.dev

Validation Errors

CodeHTTP StatusDescriptionResolution
validation_error422Request body failed validationCheck details for specific field errors
invalid_email422Malformed email addressEnsure valid email format
invalid_phone_number422Invalid phone number formatUse E.164 format (e.g., +14155551234)
invalid_template422Template syntax errorFix Handlebars syntax in your template
invalid_url422Malformed URL providedEnsure valid URL format
invalid_json400Request body is not valid JSONCheck JSON syntax
missing_required_field422Required field not providedInclude all required fields

Rate Limiting

CodeHTTP StatusDescriptionResolution
rate_limit_exceeded429Too many API requestsBack off and retry after retry_after seconds
otp_send_rate_limit_exceeded429Too many OTP/MFA code requestsWait before requesting another code
login_rate_limit_exceeded429Too many login attemptsWait before retrying

Rate limit headers are included in every response:

X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1705312800

Not Found Errors

CodeHTTP StatusDescriptionResolution
not_found404Requested resource doesn't existCheck the resource ID
template_not_found404Template ID doesn't existVerify the template_id
subscriber_not_found404Subscriber doesn't existCreate the subscriber first
device_not_found404Device ID doesn't existCheck device registration
webhook_not_found404Webhook ID doesn't existVerify the webhook ID
topic_not_found404Topic doesn't existCreate the topic first

Conflict Errors

CodeHTTP StatusDescriptionResolution
conflict409Resource already existsUse update instead of create
duplicate_subscriber409Subscriber with this external_id existsUse the existing subscriber or update it
duplicate_device409Device token already registeredToken already registered for this user
already_verified409Domain is already verifiedNo action needed

Configuration Errors

CodeHTTP StatusDescriptionResolution
sms_not_configured400Twilio credentials not set upConfigure Twilio in Settings > SMS
push_not_configured400Push credentials not set upConfigure APNs/FCM in Settings > Push
domain_not_verified400Sending domain not verifiedVerify domain in Settings > Domains
sender_not_configured400No default sender configuredSet a default sender in Settings

MFA & Security Errors

CodeHTTP StatusDescriptionResolution
mfa_required403MFA verification neededComplete the MFA challenge
invalid_otp422OTP code is incorrectEnter the correct code
otp_expired422OTP code has expiredRequest a new code
invalid_recovery_code422Recovery code is invalidCheck the code and try again
mfa_already_enabled409MFA is already enabledNo action needed

Password Errors

CodeHTTP StatusDescriptionResolution
weak_password422Password doesn't meet requirementsUse at least 8 characters with mixed case, numbers, and symbols
password_recently_used422Password was used recentlyChoose a different password
invalid_current_password422Current password is wrongEnter the correct current password

Payment & Billing Errors

CodeHTTP StatusDescriptionResolution
payment_required402Plan limits exceeded or payment failedUpgrade your plan or update payment method
plan_limit_exceeded403Feature not available on current planUpgrade your plan

Operation Failures

CodeHTTP StatusDescriptionResolution
send_failed500Message failed to sendRetry the request
provider_error502External provider (SES, Twilio, APNs) errorRetry; check provider status if persistent
template_render_failed422Template rendering failedCheck template variables match the data provided
suppressed_recipient422Recipient is on the suppression listRemove from suppression list or skip

Internal Errors

CodeHTTP StatusDescriptionResolution
internal_error500Unexpected server errorRetry; contact support if persistent
service_unavailable503Service temporarily unavailableRetry with exponential backoff
timeout504Request timed outRetry the request

Handling Errors

Retry Strategy

For transient errors (429, 500, 502, 503, 504), implement exponential backoff:

# Example: retry with backoff
for i in 1 2 4 8 16; do
response=$(curl -s -w "\n%{http_code}" -X POST https://api.zyphr.dev/v1/emails \
-H "X-API-Key: zy_live_your_key" \
-H "Content-Type: application/json" \
-d '{"to": "user@example.com", "subject": "Hello", "html": "<p>Hi!</p>"}')

status=$(echo "$response" | tail -1)
if [ "$status" -lt 400 ]; then
echo "Success"
break
fi
echo "Retrying in ${i}s..."
sleep $i
done

Idempotency

Use the Idempotency-Key header to safely retry requests without creating duplicates:

curl -X POST https://api.zyphr.dev/v1/emails \
-H "X-API-Key: zy_live_your_key" \
-H "Idempotency-Key: unique-request-id-123" \
-H "Content-Type: application/json" \
-d '{"to": "user@example.com", "subject": "Hello", "html": "<p>Hi!</p>"}'