El cliente TypeScript
Un método tipado por endpoint, generado con las mismas definiciones que responden tus peticiones. Lo que dice tu editor es lo que hace la API. Cero dependencias, ESM, Node 20+.
Instálalo
npm i @whatsapi.sh/sdkEl cliente recibe tu clave una vez y la lleva a todas partes. Después de eso son llamadas a métodos. Cada método, argumento y campo llega con la documentación que estás leyendo ahora adjunta, así que pasar el mouse en tu editor rinde más que abrir otra pestaña. TypeScript es opcional: el paquete es ESM puro y funciona desde JavaScript, incluido require() en Node 22.12+.
Envía algo
import { Whatsapi } from "@whatsapi.sh/sdk";
const wa = new Whatsapi({ apiKey: process.env.WHATSAPI_KEY });
const message = await wa.sendMessage({
to: "+5511988887777",
text: "Your table is ready.",
});
console.log(message.id, message.quotaRemaining);Cada método recibe un objeto y devuelve exactamente lo que responde el endpoint, incluido quotaRemaining, así que un medidor de uso no necesita una segunda llamada.
Manejar los errores
import { WhatsapiError } from "@whatsapi.sh/sdk";
try {
await wa.sendMessage({ to, text });
} catch (error) {
if (!(error instanceof WhatsapiError)) throw error;
if (error.code === "PAYMENT_REQUIRED") return offerAnUpgrade();
if (error.code === "NUMBER_NOT_CONNECTED") return askForARepair();
if (error.code === "GATEWAY_TIMEOUT") return watchFor(error.messageId);
throw error;
}Las fallas llegan como WhatsapiError, con el código de la propia API, el status HTTP, un retryAfter siempre que se pueda saber la espera, y el id de la petición para citar en soporte.
Cuando el envío queda sin confirmar
GATEWAY_TIMEOUT significa que WhatsApp no confirmó a tiempo. No sabemos si el mensaje llegó. Si error.messageId aparece en la respuesta, sigue ese id en el webhook message.status para saber si llegó. Revisa el chat antes de volver a enviarlo. Otro envío con una clave nueva, o sin clave, puede duplicar el mensaje. Con el mismo idempotencyKey, recibes la misma respuesta, sin un segundo envío.
Qué reintenta, y qué nunca lo hace
const message = await wa.sendMessage(
{ to: "+5511988887777", text: "Your table is ready." },
{ idempotencyKey: order.id },
);Dale a un envío un idempotencyKey, un id que eliges tú, como el id de un pedido. Si el envío falla sin respuesta o con un 5xx, el cliente lo repite con la misma clave de idempotencia, y la API responde con el primer resultado en lugar de enviar el mensaje dos veces. Si el primer envío todavía está en curso, la API responde 409 con un Retry-After, y el cliente espera ese tiempo y vuelve a intentarlo. Guarda esa clave junto con lo que envías, para que un reintento después de una caída use la misma. Las lecturas también se reintentan tras un 5xx o una respuesta perdida. Un envío sin clave de idempotencia nunca se reintenta tras un 5xx o una respuesta perdida: ese reintento sería un segundo mensaje real a una persona real. Un 429 que se libera en 10 segundos o menos se reintenta en cualquier llamada, porque no se ejecutó nada.
Verifica lo que llega
import { verifyWebhook } from "@whatsapi.sh/sdk";
const event = await verifyWebhook({
payload: rawBody, // the raw bytes, not the parsed body
signature: req.header("x-whatsapi-signature"),
secret: process.env.WHATSAPI_WEBHOOK_SECRET,
});
if (event.type === "message.created") {
await reply(event.data.number.id, event.data.message.text);
}verifyWebhook revisa la firma, rechaza una entrega repetida, y devuelve el evento ya estrechado por su type, así que los campos de la carga útil se conocen antes de tocarlos.
¿Estás construyendo un agente?
Entonces quieres MCP, no este paquete: las mismas operaciones como herramientas, con la misma clave. El cliente es para las aplicaciones que tú mismo escribes.