Core concepts

The { data, error } result

Every wapi-cloud method resolves to a consistent response object — it never throws for expected API, network, or validation failures.

Instead of throwing exceptions on failed requests, every method in wapi-cloud returns a promise that resolves to a strongly-typed WhatsappResponse<T> object. Exactly one of data or error will be non-null:

interface WhatsappResponse<T> {
  data: T | null;
  error: WhatsappApiError | null;
  status: number;      // HTTP status code (0 for network/client errors)
  statusText: string;  // HTTP status text
  raw: unknown;        // Untouched raw JSON payload from Meta Graph API
}

Checking a result with TypeScript

Because data and error are mutually exclusive, checking error first lets TypeScript narrow data to its non-null type automatically:

const { data, error, status } = await whatsapp.templates.list();

if (error) {
  // data is narrowed to null
  console.error("[" + error.type + "] Code " + error.code + ": " + error.message);
  if (error.fbtraceId) console.error("Trace ID for Meta support:", error.fbtraceId);
  return;
}

// error is narrowed to null — data.items is 100% type-safe
console.log("Fetched " + data.items.length + " templates (HTTP " + status + ")");

The WhatsappApiError class

Every failure is normalized into a WhatsappApiError instance attached to response.error:

ParameterTypeDescription
codenumberMeta Graph API numeric error code (e.g. 131047 for outside 24h window). 0 for network/client failures.
typestringError category: "OAuthException", "ClientError", "ConfigError", "NetworkError", "TimeoutError", or Graph API type.
messagestringHuman-readable description with automatic hint for known WhatsApp error codes.
subcodenumber | undefinedMeta error subcode, if provided in the Graph response.
httpStatusnumberHTTP status code (e.g. 400, 401, 404, 429, 500). 0 for network dropouts.
isRetryablebooleanTrue if the error was caused by a rate limit (429), temporary 5xx server glitch, or transient network error.
fbtraceIdstring | undefinedMeta's internal diagnostic trace ID for the request, helpful when submitting support tickets to Meta.
rawunknownThe unmodified raw JSON error payload from the Graph API.

Automatic retries & backoff

wapi-cloud automatically retries failed requests that return HTTP 429 (rate limits) or 5xx (server errors) up to maxRetries times (default: 3). Retries use exponential backoff with full jitter and respect Retry-After headers.

Common WhatsApp error codes

wapi-cloud includes built-in descriptions for common Graph API error codes:

ParameterTypeDescription
131047Re-engagement requiredOutside the 24-hour customer service window. You must use an approved template message to contact this user.
131021Invalid recipientRecipient phone number is invalid or not registered on WhatsApp.
131005Invalid access tokenAccess token has expired, been revoked, or is missing permissions.
131026Undeliverable messageMessage could not be delivered to the recipient.
131031Account restrictedWhatsApp Business Account has been restricted or disabled by Meta.
131042Payment issuePayment or billing issue detected on the WhatsApp Business Account.
132000Parameter mismatchNumber of parameters passed does not match the template definition.
132001Template not foundTemplate does not exist in the specified language, or is not approved yet.
132005Template pausedTemplate has been temporarily paused by Meta due to low quality rating from recipient feedback.
133005Unregistered numberPhone number is not registered on WhatsApp Cloud API. Call phoneNumbers.register() first.

Why no try/catch?

In messaging applications, expected operational states — rate limits, unapproved templates, or customers outside the 24-hour window — are normal program outcomes, not fatal runtime crashes.

  • Compile-time safety: You cannot accidentally forget to handle an error — TypeScript prevents accessing data properties without checking error first.
  • Batch resilience: Using Promise.all() across hundreds of messages won't abort early if a single recipient is invalid.
  • Clean async code: No nested try/catch boilerplate cluttering your business logic.