Errors

Error response format, HTTP status codes, and typed error codes in the senderZ API.

Every senderZ API response follows a consistent envelope format. Understanding this format makes error handling predictable across all endpoints.

Response Envelope

All API responses use this JSON structure:

{
  "data": {},
  "error": "Human-readable error message",
  "code": "MACHINE_READABLE_ERROR_CODE"
}
FieldPresent WhenDescription
dataSuccess responsesThe response payload. Shape varies by endpoint.
errorError responsesA plain-English explanation of what went wrong. Safe to display to end users.
codeError responsesA stable, machine-readable error code. Use this for programmatic error handling.

On success, error and code are absent. On error, data is absent.

Success Example

{
  "data": {
    "message_id": "01JMSG123ABC",
    "status": "queued"
  }
}

Error Example

{
  "error": "The recipient has opted out of messages from your account.",
  "code": "RECIPIENT_OPTED_OUT"
}

HTTP Status Codes

senderZ uses standard HTTP status codes. Here is what each one means in context:

Success Codes

StatusMeaningWhen You See It
200 OKRequest succeededGET requests, updates, deletions
201 CreatedResource createdPOST requests that create new records (contacts, API keys, templates)
202 AcceptedQueued for processingMessage sends and campaign launches — the message is enqueued but not yet delivered

Client Error Codes

StatusMeaningWhen You See It
400 Bad RequestInvalid inputMissing required fields, malformed JSON, invalid phone number format
401 UnauthorizedAuth failedMissing or invalid API key, expired Clerk JWT
403 ForbiddenAccess deniedAPI key is deactivated, tenant is suspended, or accessing another tenant’s data
404 Not FoundResource missingThe ID does not exist or belongs to a different tenant
409 ConflictDuplicateCreating a contact with a phone number that already exists
429 Too Many RequestsRate limitedYou exceeded your plan’s rate limit. Check the Retry-After header.

Server Error Codes

StatusMeaningWhen You See It
500 Internal Server ErrorServer failureUnexpected error. These are logged and investigated. If persistent, contact support.
503 Service UnavailableTemporary outageThe iMessage engine or a downstream service is temporarily unreachable. Retry after a short delay.

Typed Error Codes

Every error response includes a code field with a stable string identifier. These codes never change between API versions, making them safe to match against in your code.

Authentication Errors

CodeHTTP StatusDescription
INVALID_API_KEY401API key is missing, malformed, or has been revoked
KEY_INACTIVE403API key exists but has been deactivated
TRIAL_EXPIRED403Your 14-day trial has ended and no subscription is active. Subscribe to restore access.
TENANT_SUSPENDED403Your account has been suspended by the operator

Validation Errors

CodeHTTP StatusDescription
VALIDATION_ERROR400One or more request fields are invalid. The error message describes which field and why.
INVALID_PHONE_NUMBER400Phone number is not in valid E.164 format (must start with + and contain 10-15 digits)
INVALID_TEMPLATE400The referenced template name does not exist in your tenant
MISSING_TEMPLATE_VAR400A required {{variable}} in the template has no matching value in the data object

Messaging Errors

CodeHTTP StatusDescription
RECIPIENT_OPTED_OUT400The recipient has sent STOP and opted out. You cannot message them until they send START.
NO_PHONE_AVAILABLE503No phone is available to send this message. All assigned phones may be at their daily limit or offline.
DELIVERY_FAILED500The message was sent but delivery to the recipient failed. Check the message status endpoint for details.
QUIET_HOURS200The message was accepted but will be delivered after quiet hours end (8 AM recipient local time). This is not an error — the message is queued.

Quota Errors

CodeHTTP StatusDescription
RATE_LIMIT_EXCEEDED429You are sending requests faster than your plan allows. Wait for the Retry-After period.
QUOTA_EXCEEDED429You have reached your monthly API call limit or daily new-contact limit.
NEW_CONTACT_LIMIT429You have reached your daily new-contact limit. Messages to existing contacts are still allowed.

Resource Errors

CodeHTTP StatusDescription
NOT_FOUND404The requested resource does not exist
CONTACT_NOT_FOUND404The contact ID does not exist in your tenant
CAMPAIGN_NOT_FOUND404The campaign ID does not exist in your tenant
DUPLICATE_CONTACT409A contact with this phone number already exists. The response includes the existing contact’s ID.

Detection Errors

CodeHTTP StatusDescription
DETECTION_UNAVAILABLE503The iMessage detection service is temporarily unreachable. Retry after a short delay.

Handling Errors in Code

Use the code field for reliable error handling — never match against the human-readable error string, which may change.

const res = await fetch('https://api.senderz.com/v1/messages', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer sz_live_YOUR_KEY',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    to: '+15551234567',
    channel: 'auto',
    body: 'Hello!',
  }),
})

const result = await res.json()

if (!res.ok) {
  switch (result.code) {
    case 'RECIPIENT_OPTED_OUT':
      // Remove this number from your send list
      break
    case 'TRIAL_EXPIRED':
      // Redirect user to subscribe
      break
    case 'RATE_LIMIT_EXCEEDED':
      // Wait and retry
      const retryAfter = res.headers.get('Retry-After')
      break
    case 'NEW_CONTACT_LIMIT':
      // Queue for tomorrow or upgrade plan
      break
    default:
      console.error(`API error: ${result.code} — ${result.error}`)
  }
}

Retry Strategy

Not all errors should be retried. Here is a quick guide:

Error TypeRetry?Strategy
400 validation errorsNoFix the request body
401 / 403 auth errorsNoCheck your API key or subscription status
404 not foundNoVerify the resource ID
409 conflictNoUse the existing resource
429 rate limitYesWait for Retry-After seconds, then retry
500 server errorYesRetry with exponential backoff (1s, 2s, 4s), max 3 attempts
503 service unavailableYesRetry with exponential backoff (2s, 4s, 8s), max 5 attempts

Frequently Asked Questions

What does TRIAL_EXPIRED mean? Your 14-day free trial has ended and you have not subscribed to a plan. All API requests will return this error until you choose a plan (Starter, Growth, or Scale) from the developer portal.

Why do I get RECIPIENT_OPTED_OUT when I know the number is valid? The recipient has previously sent a STOP keyword to your tenant’s phone number. senderZ automatically blocks all future messages to opted-out numbers. The recipient must send START to re-subscribe.

How do I know which field caused a VALIDATION_ERROR? The error field in the response describes exactly which field is invalid and why. For example: “The ‘to’ field must be a valid E.164 phone number starting with +”.

What is the difference between QUOTA_EXCEEDED and NEW_CONTACT_LIMIT? QUOTA_EXCEEDED means you hit your monthly API call ceiling. NEW_CONTACT_LIMIT means you reached your daily limit for messaging phone numbers you have never contacted before. Messages to existing contacts are always unlimited.

Can I get more details about a DELIVERY_FAILED error? Yes. Use GET /v1/messages/:id to retrieve the full message record, which includes a failure_reason field with carrier-level error details when available.