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? →

1

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:

client.ts
// 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.

2

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).

enviar.ts
// 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.

3

Listá y paginá

Cada listado ofrece .list() (una página) y .iterate() (AsyncIterable que recorre todas las páginas por vos).

paginar.ts
// 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);
}
4

Manejá errores tipados

Los errores son una jerarquía: MosendApiError y subclases por código HTTP (MosendAuthError, MosendRateLimitError, …), MosendNetworkError y MosendValidationError.

errores.ts
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;
  }
}
5

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.

sesion.ts
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.

6

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.

webhook-server.ts (Express)
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.