Webhook de email entrante
Convierte los emails de tu propio buzón
en JSON analizado en tu servidor.
Los servicios de parsing quieren tus registros MX o una dirección de reenvío. Conecta en cambio el buzón que ya usas: cada email nuevo llega a tu URL con su asunto, su HTML, sus adjuntos y su hilo, y respondes desde la misma dirección.
¿Con prisa? Resume este artículo con una IA
La respuesta corta
¿Cómo enviar los emails entrantes a un webhook?
Conecta el propio buzón. Unlimited Messaging inicia sesión en tu buzón actual por IMAP (Gmail con una contraseña de aplicación, tu propio dominio, cualquier proveedor IMAP), vigila su bandeja de entrada y envía cada email nuevo a tu URL como un evento JSON firmado, message.received, con asunto, texto, HTML, adjuntos e identificadores de hilo. Tus MX se quedan donde están, a diferencia de SendGrid Inbound Parse. Los webhooks están en los planes de pago Medium y Max.
Los servicios de parsing se colocan delante del correo. SendGrid te pide apuntar el MX de un nombre de host dedicado a mx.sendgrid.net, y luego envía multipart/form-data (o el MIME original) a tu URL. Postmark da a cada servidor una dirección como [email protected], a la que reenvías desde Gmail o enrutas con inbound domain forwarding, y su procesamiento de entrada viene con el plan Pro, desde 16,50 $ al mes. En ambos casos cambia la dirección a la que escriben tus clientes, o su correo pasa por un reenvío.
Unlimited Messaging lee el buzón al que tus clientes ya escriben. Una pasarela mantiene una conexión IMAP IDLE en su bandeja de entrada, en solo lectura, así que un email nuevo se recoge en cuanto el servidor lo anuncia. El email se guarda, sus adjuntos también (hasta 20 por email), y tu webhook recibe un único cuerpo JSON: externalId es el Message-ID, conversation.id la raíz de su hilo y replyTo el mensaje al que responde.
La respuesta cierra el círculo: POST /message con replyToMessageId envía desde el mismo buzón, por su propio servidor SMTP, con Re: y las cabeceras In-Reply-To y References, y guarda una copia en Enviados. El hilo sigue siendo un solo hilo en el correo de tu cliente.
Antes de conectar: configura Gmail por IMAP con una contraseña de aplicación y por qué importa enviar desde tu propio buzón.

Los eventos
¿Qué contiene el webhook de email entrante?
El email comparte el sobre de todos los canales y añade tres campos: subject, html y attachments.
message.received
Llegó un email nuevo a la bandeja de entrada. content es el cuerpo de texto, o el HTML convertido a texto cuando el email no tiene parte de texto; html es el cuerpo tal como llegó, no fiable, que hay que sanear antes de mostrarlo.
attachments
Cada archivo en orden, hasta 20 por email: url (la copia guardada), filename, mimeType, size y contentId para una imagen incrustada que el HTML referencia como cid:. El primero también se copia en mediaUrl.
Hilos
externalId es el Message-ID RFC 5322 sin corchetes angulares. conversation.id es la raíz del hilo (primer elemento de References, si no In-Reply-To, si no el propio email), así que todos los emails de un hilo lo comparten.
message.sent y message.status
Tus respuestas se anuncian como message.sent, y un rebote definitivo pasa el mensaje a UNDELIVERABLE (message.status). La dirección rebotada queda en pausa 30 días en ese buzón para proteger su reputación.
account.status_changed
El buzón se desconectó, por ejemplo porque el servidor rechazó las credenciales tras un cambio de contraseña. Suscríbete para que un buzón mudo no pase desapercibido.
{
"type": "message.received",
"eventId": "whd_9a4f2e61-7c3b-4d85-b0e9-5f1a8c2d7e34",
"occurredAt": "2026-11-09T14:05:12.000Z",
"data": {
"message": {
"id": "msg_3f8a1c2e-9d4b-4e7a-8b6c-2d1e0f9a8b7c",
"externalId": "[email protected]",
"direction": "IN",
"fromMe": false,
"content": "Hi, please find the signed quote attached. Claire",
"sentAt": "2026-11-09T14:05:12.000Z",
"receivedAt": "2026-11-09T14:05:12.000Z",
"sender": "[email protected]",
"senderId": "[email protected]",
"senderName": null,
"participant": null,
"replyTo": { "externalId": "[email protected]", "participant": null },
"mediaUrl": "https://api.unlimitedmessaging.app/message/media/stored/4c1d...",
"mediaType": "document",
"mimeType": "application/pdf",
"mediaFilename": "quote-1042-signed.pdf",
"transcript": null,
"subject": "Re: Quote #1042",
"html": "<p>Hi, please find the signed quote attached.</p><p>Claire</p>",
"attachments": [
{
"url": "https://api.unlimitedmessaging.app/message/media/stored/4c1d...",
"filename": "quote-1042-signed.pdf",
"mimeType": "application/pdf",
"size": 184320,
"contentId": null
}
]
},
"channel": {
"type": "EMAIL",
"account": { "id": "acc_8b2e4f6a-1c3d-4e5f-a7b9-0c1d2e3f4a5b", "name": "[email protected]" },
"conversation": {
"id": "[email protected]",
"isGroup": false,
"address": "[email protected]"
}
}
}
}Puesta en marcha
¿Cómo analizar emails entrantes en Next.js?
Conecta el buzón, registra un endpoint, verifica cada POST. Ni parser MIME ni librería IMAP en tu código.
- 01
Conecta tu buzón
En la aplicación web, añade una cuenta de email con la dirección y su contraseña (una contraseña de aplicación para Gmail). Los ajustes IMAP y SMTP se detectan a partir de la dirección.
- 02
Registra el endpoint
POST /webhooks con tu URL y tus eventTypes, o añádelo en la página Webhooks del panel. El secreto (whsec_...) se muestra una sola vez.
- 03
Verifica, guarda, responde
Comprueba la cabecera webhook-signature, haz un upsert por eventId, descarga los adjuntos desde su url cuando los necesites y responde con replyToMessageId.
curl https://api.unlimitedmessaging.app/webhooks \
-H "Authorization: Bearer $UM_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/webhooks/um",
"eventTypes": ["message.received", "account.status_changed"],
"description": "Inbound email"
}'
# The response carries "secret": "whsec_..." once. Store it as UM_WEBHOOK_SECRET.// app/api/webhooks/um/route.ts (Next.js App Router)
import crypto from 'node:crypto';
import { UnlimitedMessagingApiClient } from '@unlimited-messaging/sdk';
const um = new UnlimitedMessagingApiClient({ token: process.env.UM_API_KEY! });
const key = Buffer.from(process.env.UM_WEBHOOK_SECRET!.replace(/^whsec_/, ''), 'base64');
// Standard Webhooks: HMAC-SHA256 of "id.timestamp.rawBody", base64.
function verify(raw: string, id: string, ts: string, header: string) {
if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false;
const expected = crypto.createHmac('sha256', key).update(`${id}.${ts}.${raw}`).digest('base64');
return header.split(' ').some((entry) => {
const sig = entry.startsWith('v1,') ? entry.slice(3) : '';
return sig.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected));
});
}
export async function POST(req: Request) {
const raw = await req.text(); // the raw body, before any JSON parsing
const h = req.headers;
if (!verify(raw, h.get('webhook-id') ?? '', h.get('webhook-timestamp') ?? '',
h.get('webhook-signature') ?? '')) {
return new Response('bad signature', { status: 401 });
}
const event = JSON.parse(raw);
if (event.type === 'message.received' && event.data.channel.type === 'EMAIL') {
const m = event.data.message;
const thread = event.data.channel.conversation.id; // same id for the whole thread
const files = m.attachments ?? []; // [{ url, filename, mimeType, size, contentId }]
await saveTicket({ eventId: event.eventId, thread, from: m.sender, subject: m.subject,
text: m.content, files }); // upsert on eventId: delivery is at least once
// Answer in the same thread, from the same mailbox.
await um.messages.messageSend({ replyToMessageId: m.id, text: 'Thanks, received. We reply within a day.' });
}
return new Response(null, { status: 200 }); // within 10 s; queue slow work
}Vibe coding
¿Programas con Cursor, Claude Code, Lovable o Bolt? Pega esto
Tu editor con IA recibe la ruta, la verificación de firma y los campos de email que necesita, sin inventar un parser MIME.
Add an Unlimited Messaging webhook to this app so incoming emails from my own mailbox become support tickets.
1. Create a POST route (Next.js: app/api/webhooks/um/route.ts) that reads the RAW body with req.text().
2. Verify the Standard Webhooks signature: key = base64-decode(UM_WEBHOOK_SECRET without "whsec_"); expected = base64(HMAC-SHA256(key, `${webhook-id}.${webhook-timestamp}.${rawBody}`)); accept if any space-separated entry of the webhook-signature header equals `v1,${expected}` (constant-time compare); reject timestamps more than 5 minutes off; return 401 otherwise.
3. On type "message.received" with data.channel.type "EMAIL": upsert a ticket keyed on body.eventId with data.channel.conversation.id (thread id), data.message.sender, data.message.subject, data.message.content (text) and data.message.attachments ([{ url, filename, mimeType, size, contentId }]). Treat data.message.html as untrusted: sanitise before rendering.
4. To answer, install @unlimited-messaging/sdk, read UM_API_KEY from env, and call `client.messages.messageSend({ replyToMessageId: data.message.id, text })`: it replies in the same thread from the same mailbox.
5. Return 200 within 10 seconds; queue slow work.
6. Put UM_API_KEY and UM_WEBHOOK_SECRET in .env and .env.example.
Docs: https://docs.unlimitedmessaging.app (Webhooks). Register with POST https://api.unlimitedmessaging.app/webhooks and eventTypes ["message.received"].Lo que se construye
¿Qué puedes construir con el email entrante?
Todo lo que hoy consiste en que alguien copie un email en otra herramienta.
Un helpdesk en soporte@
Un ticket por conversation.id, cada respuesta añadida, contestaciones enviadas desde soporte@ en el mismo hilo. Ninguna dirección de ticketing que publicar.
Clasificación y respuesta automática con IA
Pasa subject y content a un LLM, etiqueta y enruta el email, o envía una primera respuesta con replyToMessageId y deja que una persona tome el relevo.
Facturas y documentos entrantes
Descarga el PDF desde attachments[].url y envíalo a tu contabilidad o a tu OCR.
Captación de leads
Convierte las notificaciones de formularios o los emails directos en leads del CRM, por dirección del remitente.
Una sola bandeja con WhatsApp y SMS
El mismo handler recibe los eventos de WhatsApp y SMS: solo cambia data.channel.type.
Flujos sin código
Apunta el webhook a n8n, Make o Zapier y ramifica según el asunto o el remitente, sin servidor.
La tabla
Email entrante a webhook: SendGrid, Postmark y Unlimited Messaging
Los servicios de parsing reciben el correo de una dirección o un nombre de host nuevos. Unlimited Messaging lee el buzón que ya tienes.
| SendGrid Inbound ParseMX en un nombre de host dedicado | Postmark InboundDirección o dominio de entrada | Unlimited MessagingTu buzón por IMAP, planes de pago | |
|---|---|---|---|
| Dirección que recibe | Cualquier dirección de un nombre de host dedicado al parsing | [email protected], o un reenvío hacia ella | Tu dirección de siempre |
| Cambio de DNS | MX hacia mx.sendgrid.net | Ninguno con reenvío; DNS para inbound domain forwarding | Ninguno |
| Formato del cuerpo | multipart/form-data, o el mensaje MIME original | JSON; autenticación HTTP basic en la URL como protección | JSON, firmado según Standard Webhooks |
| Identificadores de hilo | En las cabeceras que analizas | En las cabeceras analizadas | conversation.id (raíz del hilo) y replyTo, listos para usar |
| Responder desde la misma dirección | Con el envío de SendGrid, configurado aparte | Con el envío de Postmark, configurado aparte | replyToMessageId, por el SMTP de tu buzón, copia en Enviados |
| Precio | Incluido en un plan de SendGrid | Entrada en el plan Pro, desde 16,50 $ al mes | Webhooks con Medium o Max; sin coste por email |
| WhatsApp y SMS en el mismo webhook | No | No | Sí, el mismo sobre |
Comprobado el 9 de noviembre de 2026 en la guía de configuración del Inbound Parse de SendGrid, la guía de servidores de entrada y la página de precios de Postmark (enlaces en las fuentes).
La letra pequeña
¿Qué límites tiene una pasarela de buzón a webhook?
Leer tu propio buzón elimina el trabajo de DNS. Trae consigo las reglas de ese buzón.
Los webhooks son de pago
En el plan Free, los emails recibidos aparecen en la bandeja de la aplicación web, agrupados por hilo. Enviarlos a tu servidor requiere Medium (19 € al mes, o 17 € al mes con pago anual) o Max.
Solo la bandeja de entrada
La pasarela vigila INBOX. Un email que tu proveedor marca como spam, o que un filtro mueve a otra carpeta, nunca se ve.
Todavía sin Outlook ni Microsoft 365
Outlook, Hotmail y Microsoft 365 exigen OAuth, previsto pero aún no disponible. Gmail funciona con una contraseña de aplicación, que requiere la verificación en dos pasos en la cuenta de Google.
Hasta 20 adjuntos por email
Los archivos a partir del 21 no se guardan. El HTML se pasa tal como llegó: sanéalo antes de mostrarlo en cualquier sitio.
Al menos una vez, sin orden garantizado
El mismo evento puede llegar dos veces, y los eventos no llegan en el orden en que ocurrieron: haz un upsert por eventId y ordena por sentAt.
FAQ
Preguntas sobre los webhooks de email entrante
Fuentes
Fuentes y referencias
Para verificar por ti mismo la información de este artículo:
- Setting up the Inbound Parse webhook (MX hacia mx.sendgrid.net, payload)Twilio SendGrid Docs
- Configure an inbound server (dirección de entrada, JSON, reenvío)Postmark
- Precios (procesamiento de entrada en Pro, desde 16,50 $ al mes)Postmark
- RFC 5322, Internet Message Format (Message-ID, In-Reply-To, References)IETF
- Iniciar sesión con contraseñas de aplicaciónAyuda de Cuenta de Google
- Especificación Standard Webhooksstandardwebhooks.com
- Referencia de la API de Unlimited Messaging - WebhooksUnlimited Messaging

Escrito por
Ilian · Ingeniero de software senior
Ingeniero de software senior, funda y dirige varias empresas tecnológicas. Diseña productos en torno a las API de mensajería, la automatización y la integración de la IA, y comparte aquí lo que aprende construyendo.
Tu bandeja de entrada, en JSON limpio.
Crea una cuenta, conecta tu buzón y activa los webhooks con Medium o Max. Enviar emails sigue siendo gratis en todos los planes.