Claves API y webhooks
Mail Merge tiene una API REST y webhooks salientes para desarrolladores y constructores sin código. Con una clave API tus scripts pueden hacer lo que hace la aplicación web: leer tu cuenta, listar plantillas y contactos, enviar campañas y seguir a sus destinatarios. Con un webhook Mail Merge llama a tu endpoint en el momento en que una campaña termina o un destinatario abre, hace clic, responde, rebota o se desuscribe, para que un CRM, una hoja de cálculo o un canal de Slack se mantenga sincronizado sin necesidad de consultar.
Ambos se encuentran bajo Configuración → Desarrolladores en la aplicación web y son parte del plan Professional (ver Precios). Si prefieres hablar con Mail Merge desde un asistente de IA en lugar de escribir código, usa el servidor MCP en su lugar: expone las mismas características como herramientas, en todos los planes.
Claves API
Sección titulada «Claves API»Crear una clave
Sección titulada «Crear una clave»- Abre Configuración → Desarrolladores → Claves API y haz clic en Crear clave.
- Nómbrala según dónde se usará (
Zapier,Sincronización CRM), para poder revocar la correcta más tarde. - Copia la clave. Se muestra una sola vez: Mail Merge solo guarda un hash de ella. Si la pierdes, revócala y crea una nueva.
Puedes tener hasta 10 claves por cuenta. Revocar una clave hace que todas las solicitudes realizadas con ella fallen inmediatamente; la fila desaparece de la lista.
Autenticar
Sección titulada «Autenticar»Envía la clave como un token Bearer en cada solicitud, contra la URL base que se muestra en la tarjeta:
curl https://merge.email/api/v1/me \ -H "Authorization: Bearer mm_live_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"Una clave actúa como la cuenta que la creó, exactamente como una sesión iniciada: la misma concesión de Google, la misma cuota diaria, los mismos límites del plan. No hay un “usuario API” separado.
Endpoints
Sección titulada «Endpoints»La API es la misma superficie /v1 que usa la aplicación web, por lo que cualquier cosa que puedas hacer en una pantalla tiene un endpoint detrás. Los que más alcanzan las integraciones:
| Llamada | Qué hace |
|---|---|
GET /v1/me | La cuenta: plan, cuota diaria (máx, usada, restante), zona horaria, metadatos de plantilla. |
GET /v1/templates · GET /v1/templates/{templateId} | Tus plantillas guardadas; el cuerpo se carga por plantilla. |
GET /v1/contacts · POST /v1/contacts | La libreta de contactos (paginada con limit / cursor → nextCursor). |
POST /v1/campaigns | Envía, programa o autopilota una campaña desde una Hoja de Google. El cuerpo es el mismo que publica el constructor de campañas: la hoja, la columna de destinatarios, el asunto y cuerpo con {{etiquetas de fusión}}, y las opciones de envío. |
GET /v1/campaigns · GET /v1/campaigns/{campaignId}/recipients | Tus campañas y el estado de cada destinatario (enviado, leído, hizo clic, respondió, rebotó, desuscrito). |
POST /v1/campaigns/{campaignId}/status | Pausa, reanuda o detiene una campaña en ejecución. |
POST /v1/recipients/verify | El verificador de correo electrónico. |
Los cuerpos de solicitud y respuesta son JSON. La superficie completamente tipada se genera desde el servidor como un documento OpenAPI; contáctanos si necesitas el esquema para un generador de cliente.
Errores y límite de velocidad
Sección titulada «Errores y límite de velocidad»Los fallos son estados HTTP reales con un sobre:
{ "error": { "code": "professional_plan_required", "message": "El acceso a la API es una característica del plan Professional" } }| Estado | code | Significado |
|---|---|---|
| 401 | Clave desconocida o revocada, o el acceso a Google de la cuenta necesita reconectarse. | |
| 403 | professional_plan_required | La cuenta de la clave ya no está en el plan Professional. Las claves no se eliminan al cambiar de plan; funcionan de nuevo cuando el plan lo hace. |
| 429 | rate_limited | Más de 600 solicitudes por minuto en una clave. Espera un minuto e intenta de nuevo. |
| 400 / 404 / 409 | bad_request, not_found, conflict, … | La solicitud en sí, con un mensaje que dice qué corregir. |
El límite de velocidad se cuenta por clave en cada instancia del servidor API, por lo que el techo efectivo es “aproximadamente 600 por minuto”: no diseñes alrededor de alcanzarlo.
Webhooks
Sección titulada «Webhooks»Agregar un endpoint
Sección titulada «Agregar un endpoint»- Abre Configuración → Desarrolladores → Webhooks y haz clic en Agregar webhook.
- Ingresa una URL HTTPS (el
http://simple se rechaza) y marca los eventos que debe recibir. - Haz clic en Enviar prueba en la nueva fila: Mail Merge publica un
pingy la notificación te dice cómo respondió tu endpoint.
Cada endpoint obtiene su propio secreto de firma (whsec_…), que se muestra en la fila con un botón de copiar. Puedes tener hasta 5 endpoints por cuenta.
Eventos
Sección titulada «Eventos»| Evento | Cuándo | data |
|---|---|---|
campaign.completed | El último correo de una campaña fue procesado. | campaignId, name, status: "completed", sentCount, campaignSize |
campaign.paused · campaign.resumed · campaign.stopped | Pausaste, reanudaste o detuviste una campaña. | campaignId, name, status (paused / active / stopped), sentCount |
email.opened | Un destinatario abrió un correo, solo la primera vez por campaña. | campaignId, recipient, at |
email.clicked | Un destinatario hizo clic en un enlace rastreado, cada clic. | campaignId, recipient, at, url |
email.replied | Se detectó una respuesta en un hilo enviado. | campaignId, recipient, at |
email.bounced | Se detectó un rebote. | campaignId, recipient, at |
recipient.unsubscribed | Un destinatario usó el enlace de desuscripción, solo la primera vez por campaña. | campaignId, recipient, at, method |
Las aperturas y clics necesitan rastreo activado para la campaña; las respuestas y rebotes provienen del escaneo de respuestas que sigue a cada campaña enviada.
Entrega
Sección titulada «Entrega»Mail Merge envía un POST por evento por endpoint suscrito, con un cuerpo JSON:
{ "id": "6f1c2d3e-…", "event": "email.opened", "createdAt": "2026-10-15T14:03:21.512Z", "data": { "campaignId": "…", "recipient": "ada@example.com", "at": "2026-10-15T14:03:21.498Z" }}y tres encabezados:
| Encabezado | Valor |
|---|---|
X-MailMerge-Event | El nombre del evento, igual que event en el cuerpo. |
X-MailMerge-Delivery | El id de entrega, igual que id. Úsalo para ignorar un duplicado. |
X-MailMerge-Signature | sha256= seguido del HMAC-SHA256 hexadecimal del cuerpo sin procesar, codificado con el secreto del endpoint. |
Responde con cualquier 2xx dentro de 5 segundos. En un error de red o un 5xx Mail Merge reintenta una vez, después de 2 segundos; un 4xx se toma como tu veredicto y no se reintenta. El mismo id de entrega se envía en el reintento. Después de 20 entregas fallidas seguidas el endpoint se desactiva y la fila lo indica: corrige el endpoint, luego elimínalo y agrégalo de nuevo. Cualquier entrega exitosa reinicia el contador.
Los eventos se entregan al menos una vez y sin orden garantizado. Las aperturas en particular llegan en ráfagas (cada cliente de correo que renderiza el píxel de rastreo), así que haz tu controlador idempotente en X-MailMerge-Delivery o en (event, campaignId, recipient).
Verificar la firma
Sección titulada «Verificar la firma»Siempre verifica antes de confiar en una carga. Calcula el HMAC sobre el cuerpo de solicitud sin procesar (los bytes exactos, antes de cualquier análisis JSON) y compáralo en tiempo constante:
// Node.js (Express con `express.raw({ type: "application/json" })`)import { createHmac, timingSafeEqual } from "node:crypto"
export function verifyMailMergeSignature(rawBody, signatureHeader, secret) { const expected = "sha256=" + createHmac("sha256", secret).update(rawBody).digest("hex") const a = Buffer.from(expected) const b = Buffer.from(signatureHeader ?? "") return a.length === b.length && timingSafeEqual(a, b)}
app.post("/mailmerge", (req, res) => { if (!verifyMailMergeSignature(req.body, req.get("X-MailMerge-Signature"), process.env.MAILMERGE_WEBHOOK_SECRET)) { return res.status(401).end() } const payload = JSON.parse(req.body) // … maneja payload.event / payload.data res.status(204).end()})# Pythonimport hashlib, hmac
def verify_mailmerge_signature(raw_body: bytes, signature_header: str, secret: str) -> bool: expected = "sha256=" + hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest() return hmac.compare_digest(expected, signature_header or "")Zapier, Make y n8n
Sección titulada «Zapier, Make y n8n»Sin código necesario: crea un Catch Hook (Zapier Webhooks by Zapier), un Custom webhook (Make) o un nodo de disparo Webhook (n8n), pega la URL que te da como endpoint, marca los eventos, y haz clic en Enviar prueba para que la herramienta pueda muestrear la carga. Los campos data se convierten en las variables que mapeas en el siguiente paso (agregar una fila a una hoja, actualizar un contacto de CRM, publicar en Slack). Esas herramientas no verifican firmas de forma predeterminada; la URL que te dan es impredecible, que es la protección en la que confían.
Dónde ir a continuación
Sección titulada «Dónde ir a continuación»- Agentes de IA (MCP), si un asistente debe conducir Mail Merge para ti en lugar de un script.
- Campañas, para entender qué significan los estados en el endpoint de destinatarios.
- Precios, para saber qué incluye el plan Professional.