SDK · Quickstart
De cero a producción con @moshipp/mosend-sdk: instalar, enviar, paginar, manejar errores y verificar webhooks. Todos los ejemplos son TypeScript/Node. ¿Qué es el SDK? →
Instalá e instanciá el cliente
El cliente acepta API key (server-to-server) o una sesión JWT. Para integraciones de backend usá API key:
// npm i @moshipp/mosend-sdk (Node ≥ 18.17)
import { MosendClient } from '@moshipp/mosend-sdk';
export const mosend = new MosendClient({
apiKey: process.env.MOSEND_API_KEY!, // mk_live_<prefix>.<secret>
orgId: process.env.MOSEND_ORG_ID!, // default para rutas /organizations/{orgId}/...
// opcionales:
// retries: { max: 3, on: [429, 502, 503] }, // reintentos con backoff (opt-in)
// timeout: 30_000,
});Con orgId en el constructor no tenés que repetirlo en cada llamada; igual podés pasarlo por método para multi-tenant.
Enviá un mensaje o una plantilla
Texto dentro de la ventana de 24h, o una plantilla aprobada para iniciar conversación. El SDK arma el payload de Meta por vos (modo simple).
// Texto (ventana de 24h abierta)
await mosend.messages.send({
phoneNumberId: '<phone-uuid>',
to: '573001234567', // E.164 sin '+'
type: 'text',
payload: { body: 'Hola 👋' },
});
// Plantilla — body posicional
const msg = await mosend.messages.send({
phoneNumberId: '<phone-uuid>',
to: '573001234567',
type: 'template',
templateId: '<uuid-de-la-plantilla>',
variables: ['Juan', 'FAC-2026-0042'],
});
console.log(msg.id, msg.metaMessageId);
// Plantilla con header media + botón URL dinámico
await mosend.messages.send({
phoneNumberId: '<phone-uuid>',
to: '573001234567',
type: 'template',
templateId: '<uuid>',
variables: {
body: ['Juan', 'FAC-2026-0042'],
header: { type: 'image', link: 'https://cdn.tu-empresa.com/factura.png' },
buttons: [{ index: 0, value: '456789' }],
},
});Operaciones críticas aceptan idempotencia: mosend.messages.send(input, { idempotencyKey: 'order-42' }) — reenviar el mismo key no duplica el efecto.
Listá y paginá
Cada listado ofrece .list() (una página) y .iterate() (AsyncIterable que recorre todas las páginas por vos).
// Página a página
const { data, pageInfo } = await mosend.conversations.list({ status: 'open', take: 50 });
// Recorrer TODO con for-await (maneja el cursor internamente)
for await (const conv of mosend.conversations.iterate({ status: 'open' })) {
console.log(conv.id, conv.contact?.waId);
}
// Contactos (paginación por página)
for await (const contact of mosend.contacts.iterate({ q: 'juan' })) {
console.log(contact.waId, contact.name);
}Manejá errores tipados
Los errores son una jerarquía: MosendApiError y subclases por código HTTP (MosendAuthError, MosendRateLimitError, …), MosendNetworkError y MosendValidationError.
import {
MosendRateLimitError,
MosendApiError,
MosendNetworkError,
} from '@moshipp/mosend-sdk';
try {
await mosend.messages.send({ /* ... */ });
} catch (err) {
if (err instanceof MosendRateLimitError) {
console.warn('rate limit; reintentar en', err.retryAfterSec, 's');
} else if (err instanceof MosendApiError) {
// 4xx/5xx con body del backend; errores de Meta traen metaCode/metaSubcode
console.error(err.status, err.code, err.message, err.metaCode);
} else if (err instanceof MosendNetworkError) {
console.error('falló la red/timeout', err.cause);
} else {
throw err;
}
}Sesión interactiva con JWT (auto-refresh)
Para apps con login de usuario, pasá los tokens obtenidos de auth.login(). El SDK refresca el access token solo (proactivo y ante 401) y te avisa para persistir el par rotado.
const { tokens } = await new MosendClient().auth.login({
email: 'agente@empresa.com',
password: '••••••••',
});
const mosend = new MosendClient({
tokens, // { accessToken, refreshToken, expiresIn }
orgId: '<org-uuid>',
onTokenRefresh: async (next) => {
// persistí el par rotado (DB, cookie segura, secure storage)
await saveTokens(next);
},
onAuthFailure: async () => {
// el refresh token fue rechazado → limpiar sesión y redirigir a login
redirectToLogin();
},
});
// usá el cliente normalmente; el refresh es transparente
const me = await mosend.users.me();10 requests concurrentes que necesiten refresh disparan una sola llamada a /auth/refresh. Ver Autenticación.
Verificá webhooks entrantes
Mosend firma cada webhook saliente con HMAC SHA-256 sobre el body crudo. Validalo con verifyWebhookSignature (o parseWebhookEvent, que valida y parsea) antes de procesar.
import express from 'express';
import { parseWebhookEvent, MosendWebhookSignatureError } from '@moshipp/mosend-sdk';
const app = express();
// IMPORTANTE: necesitás el body CRUDO para validar la firma
app.post('/webhooks/mosend', express.raw({ type: 'application/json' }), (req, res) => {
try {
const event = parseWebhookEvent(
req.body, // Buffer crudo
req.header('X-Mosend-Signature'),
process.env.MOSEND_WEBHOOK_SECRET!,
);
// deduplicá por event.deliveryId (puede reintentarse)
switch (event.event) {
case 'message.new': /* ... */ break;
case 'message.status': /* ... */ break;
case 'conversation.opened': /* ... */ break;
}
res.sendStatus(200);
} catch (err) {
if (err instanceof MosendWebhookSignatureError) return res.sendStatus(401);
throw err;
}
});Detalles del payload y los eventos en webhooks salientes.
El SDK cubre los 57 módulos de la API. Cualquier endpoint que veas en la referencia (ej. /api/broadcasts) tiene su método tipado equivalente — mosend.broadcasts.create(...), .send(id), etc.