Browse documentation

Plans and reliability

Errors and retries

Handle Yanola error envelopes consistently and retry only transient or rate-limited failures.

DevelopersAdvancedAvailable

What this guide covers

  • Branch on stable error codes for validation, auth, permission, billing, and Sender failures.
  • Do not retry deterministic client errors without changing the request.
  • Use SDK error classes and safe retry behavior in server-side applications.

One error envelope

Every documented API failure uses the same JSON envelope. Branch on error.type or the more specific stable error.code; do not parse message text.

Public error response
json
{
  "error": {
    "type": "business_state_error",
    "code": "service_window_required",
    "message": "A text, media, location, or contact-card message requires an active Customer Service Window. Send an approved template instead.",
    "requestId": "req_...",
    "retryable": false,
    "details": {
      "from": "+15557654321",
      "to": "+15551234567"
    }
  }
}
Error object fields
FieldHow to use it
typeBroad error family used by SDK subclasses.
codeStable machine-readable reason for application branching.
messageHuman-readable diagnostic; wording can evolve.
requestIdCorrelation ID to log and include in support requests.
retryableWhether the same operation may succeed later; still apply a bounded retry policy.
detailsStructured, code-specific context. Handle additive fields.

HTTP status and error types

Public error families
HTTP statusError typeTypical causeRetry guidance
400, 404, 413, 415invalid_request_errorInvalid fields, identifiers, media size, or content type.Change the request; do not retry unchanged.
401authentication_errorMissing, invalid, expired, or revoked API key.Replace the credential.
402billing_errorPlan Message Cap reached (plan_message_limit_reached; not retryable).wait for the next period or upgrade the Plan; replies inside the 24-hour window, free-form or template, still go out.
403permission_errorMissing scope, Sender restriction, or entitlement.Change key access or account configuration.
409idempotency_error / business_state_errorConflicting reuse or transient resource state.Retry only when retryable is true; preserve the same logical operation identity.
422business_state_errorSender, template, service-window, or other valid-but-unavailable state.Follow code-specific recovery.
429rate_limit_errorA rate-limit bucket is exhausted.Wait for Retry-After.
500api_errorYanola server failure.Use bounded backoff; the SDK retries automatically.
503provider_error / api_errorProvider or Yanola dependency is temporarily unavailable.Retry only when retryable is true.

Use typed SDK errors

The SDK converts a valid error envelope into a YanolaAPIError subclass and exposes statusCode, type, code, requestId, retryable, and details. Connection and timeout failures use separate classes because no API error envelope was received.

Handle errors by class and code
ts
import {
  WhatsAppClient,
  YanolaAPIError,
  YanolaBillingError,
  YanolaConnectionError,
  YanolaRateLimitError,
} from "@yanola/whatsapp"

const apiKey = process.env.YANOLA_API_KEY
if (!apiKey) throw new Error("Missing YANOLA_API_KEY")

const yanola = new WhatsAppClient({ apiKey })

try {
  await yanola.messages.sendText({
    from: "+15557654321",
    to: "+15551234567",
    body: "Your order is ready",
  })
} catch (error) {
  if (error instanceof YanolaBillingError) {
    await alertBillingOwner(error.requestId)
    return
  }

  if (error instanceof YanolaRateLimitError) {
    console.error("SDK retry budget exhausted", error.requestId)
    return
  }

  if (error instanceof YanolaAPIError) {
    console.error(error.code, error.retryable, error.requestId)
    return
  }

  if (error instanceof YanolaConnectionError) {
    console.error(error.requestId, error.attempts)
    return
  }

  throw error
}

What the SDK retries

WhatsAppClient defaults to a 30-second timeout and two retries after the first attempt. It automatically retries connection failures, HTTP 408, HTTP 429, HTTP 500 and higher, and HTTP 409 only when the error envelope has retryable: true.

The SDK honors Retry-After when present. Otherwise it uses capped exponential backoff with jitter. A caller AbortSignal cancels both requests and pending retry waits.

Default SDK retry decisions
FailureAutomatic retryReason
Connection failure or timeoutYes, within maxRetriesThe server may not have produced a usable response.
408 Request TimeoutYesThe request timed out at the HTTP layer.
409 with retryable: trueYesThe conflict represents transient business state.
429 Too Many RequestsYesThe SDK waits according to Retry-After when provided.
500 or 503YesServer or provider failure may be transient.
400, 401, 402, 403, 404, 413, 415, 422NoThe request, credential, funds, permissions, or business state must change.

Configure timeout, retries, and cancellation

A supplied requestId must start with req_, contain only letters, numbers, underscores, or hyphens, and be at most 64 characters. Yanola returns a request ID in responses and error envelopes for correlation.

Bound a request
ts
import { WhatsAppClient } from "@yanola/whatsapp"

const apiKey = process.env.YANOLA_API_KEY
if (!apiKey) throw new Error("Missing YANOLA_API_KEY")

const yanola = new WhatsAppClient({
  apiKey,
  timeoutMs: 10_000,
  maxRetries: 1,
})

const controller = new AbortController()

const page = await yanola.messages.list(
  { limit: 20 },
  { signal: controller.signal, requestId: "req_reconcile_1042" }
)

Retry sends without duplicates

Every SDK send generates an idempotency key and preserves it across the client's automatic retries. When a job runner or workflow can call the SDK again later, supply and persist your own stable idempotencyKey for that logical send.

Reusing the same key with the same body returns the original accepted result. Reusing it with a different body returns 409 idempotency_key_reused.

  1. Create one idempotency key for one recipient, Sender, message type, and content.
  2. Persist it before the first network attempt.
  3. Reuse it for retries of that exact logical send.
  4. Generate a new key when the intended message changes.
  5. After an ambiguous failure, retrieve or reconcile by the stored operation identity before creating a replacement send.

Production error policy

  1. Log error.type, error.code, error.requestId, statusCode, and safe structured details.
  2. Never log the Authorization header, API key, webhook secret, signed media URL, or unredacted customer content.
  3. Alert immediately on authentication, permission, billing, Sender reconnect, and reconciliation-needed conditions.
  4. Bound retries by attempts and elapsed time; move exhausted work to a visible failure or dead-letter state.
  5. Do not retry deterministic 4xx responses until the request or account state changes.