How-to guide

Errors, retries and idempotency

Read an error, decide whether to retry, and retry sends without ever sending twice.

What you'll build

Error handling that tells "fix the request" apart from "try again", and a retry loop for sends that is safe even when a request times out.

Prerequisites

  • A secret API key. See Authentication and API keys.
  • Node.js 18 or later with @voicelayer/api, or Python 3.9 or later with voicelayer, or any HTTP client.

Step 1: Read the error body

A refused request answers an HTTP status and a JSON body:

{
  "error": "window_closed",
  "message": "The WhatsApp reply window for this conversation has closed.",
  "requestId": "req-8f2c1d4b",
  "retryable": false,
  "docs": "https://docs.vlayers.ai/errors/window_closed"
}
FieldUse it to
errorBranch in code. It is a stable code; every code has a page.
messageLog it, or show it to a developer. It may change; don't match on it.
requestIdQuote it to support, or find the request in GET /v1/request-logs.
retryableDecide whether the same request can succeed later.
detailsExtra facts some codes carry, such as the missing scope.
docsThe code's page: what happened, why, and how to fix it.

Authentication errors (401 unauthorized) and the event and webhook routes answer a smaller body, { "error", "message" }.

In the TypeScript SDK, pass throwOnError: true and a refusal throws a VoiceLayerApiError with status, code, requestId, retryable, details and docs. In Python, a refusal raises a voicelayer.ApiException with status and body.

Step 2: Decide whether to retry

  • 4xx with retryable: false: the request is wrong for now. Fix it; retrying the same request fails the same way.
  • 429 (rate limit or test cap) and 5xx: retry after a wait. Honor a Retry-After header when there is one.
  • A network error or timeout: you don't know whether the request arrived. For a send, retry with the same idempotency key (Step 3).

Back off between retries: wait about 1 s, then 2 s, 4 s and so on, with some randomness, up to a cap.

Step 3: Make sends idempotent

Send an Idempotency-Key header (1–255 printable characters; a UUID works) with every POST /v1/messages and POST /v1/conversations/{id}/messages. Make one key per message you mean to send, and reuse it only for retries of that message.

curl -X POST https://api.vlayers.ai/v1/messages \
  -H "Authorization: Bearer $VOICELAYER_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "channel": "sms",
    "to": "+15550123",
    "content": {
      "type": "text",
      "text": "Your table for two is booked for 7pm."
    }
  }'

What happens when you retry with the same key:

SituationAnswer
The first request finishedThe first result again, with the header idempotency-replayed: true. Nothing is sent twice.
The first request is still running409 send_in_progress. Wait a few seconds and retry.
Same key, different body409 idempotency_conflict. Use a new key for a new message.

Without a key, a retried send can go out twice.

Test it

With a test key, send the same request twice with the same Idempotency-Key value (set it yourself instead of $(uuidgen)). The second answer has idempotency-replayed: true and the same message id.

Common errors

ErrorStatusHow to fix
invalid_request400Read message and details.issues (the validation issues, by path), fix the request and send it again.
invalid_idempotency_key400Send 1–255 printable ASCII characters (a UUID works), or leave the header out.
idempotency_conflict409Use a new key for a new message; repeat the original body exactly to replay the original request.
send_in_progress409Retry with the same key after retry-after (30 seconds).
rate_limited429Wait for retry-after, then retry (with the same Idempotency-Key for a send).
provider_retry_later503Retry after retry-after, with the same Idempotency-Key.
messaging_unavailable503Retry shortly, with the same Idempotency-Key.
outcome_unknown409Do not resend under a new Idempotency-Key. Watch the message events (message.sent, message.failed) for the outcome.

Next steps

On this page