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:
| Parameter | Type | Description |
|---|---|---|
| code | number | Meta Graph API numeric error code (e.g. 131047 for outside 24h window). 0 for network/client failures. |
| type | string | Error category: "OAuthException", "ClientError", "ConfigError", "NetworkError", "TimeoutError", or Graph API type. |
| message | string | Human-readable description with automatic hint for known WhatsApp error codes. |
| subcode | number | undefined | Meta error subcode, if provided in the Graph response. |
| httpStatus | number | HTTP status code (e.g. 400, 401, 404, 429, 500). 0 for network dropouts. |
| isRetryable | boolean | True if the error was caused by a rate limit (429), temporary 5xx server glitch, or transient network error. |
| fbtraceId | string | undefined | Meta's internal diagnostic trace ID for the request, helpful when submitting support tickets to Meta. |
| raw | unknown | The 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:
| Parameter | Type | Description |
|---|---|---|
| 131047 | Re-engagement required | Outside the 24-hour customer service window. You must use an approved template message to contact this user. |
| 131021 | Invalid recipient | Recipient phone number is invalid or not registered on WhatsApp. |
| 131005 | Invalid access token | Access token has expired, been revoked, or is missing permissions. |
| 131026 | Undeliverable message | Message could not be delivered to the recipient. |
| 131031 | Account restricted | WhatsApp Business Account has been restricted or disabled by Meta. |
| 131042 | Payment issue | Payment or billing issue detected on the WhatsApp Business Account. |
| 132000 | Parameter mismatch | Number of parameters passed does not match the template definition. |
| 132001 | Template not found | Template does not exist in the specified language, or is not approved yet. |
| 132005 | Template paused | Template has been temporarily paused by Meta due to low quality rating from recipient feedback. |
| 133005 | Unregistered number | Phone 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
dataproperties without checkingerrorfirst. - 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/catchboilerplate cluttering your business logic.