Convierte el correo entrante en un webhook
- 01Crear un origen de correo y encontrar su dirección
- 02El sobre que recibe tu destino
- 03Enrutar según remitente o asunto
- 04Deduplicación, presupuesto de tamaño y adjuntos
- 05La única regla: un origen recibe correo o HTTP, nunca ambos
Cómo encaja en el pipeline
El correo es un proveedor de un origen, que se elige igual que GitHub o Stripe, no un producto aparte. Una vez analizado, el correo es un evento como cualquier otro, así que las partes de SolidHooks que ya conoces siguen funcionando: las conexiones lo emparejan, las entregas lo reintentan, la retención lo hace caducar y tu consumidor recibe JSON por HTTP como siempre.
Configúralo
- 1
Crea un origen con el proveedor email
Pasa
provider: "email"al crearlo, o cambia un origen existente con un PATCH. La respuesta incluyeemailAddress, que esnullen cualquier otro proveedor.curlcurl -s "$API/v1/sources" \ -H "authorization: Bearer $SDHK_KEY" \ -H 'content-type: application/json' \ -d '{ "name": "Supplier notices", "provider": "email", "verifierConfig": {} }' → { "id": "src_...", "maskId": "Kb3xk29ZtQm7Rv1A", "emailAddress": "Kb3xk29ZtQm7Rv1A@sdhk.events", ... }
Convertir un origen que ya tiene un verificador necesita
verifierType: "noop"en el mismo PATCH. Nada firma un correo entrante, así que la API se niega a trasladar la credencial guardada en lugar de conservar un secreto que nunca leería. El mismo PATCH también borra eleventTypeFielddel origen y cualquieridempotencyKeys, ya que ambos describían una forma de payload que ya no llega. - 2
Dale la dirección al remitente
Pégala donde el otro sistema espere un destinatario. No hay paso de verificación ni DNS que configurar por tu parte.
- 3
Conecta el origen a un destino
Igual que con cualquier otro origen. Filtras por el remitente o por el asunto, según lo que indique el
eventTypeFielddel origen, o poremail.receivedsi no lo configuraste. El enrutado por remitente o asunto se explica más abajo.
Qué recibe tu destino
El payload guardado es un sobre normalizado en lugar de un bloque MIME sin procesar, así que tu handler no necesita un analizador de correo. Las direcciones van separadas, los dos cuerpos van por separado y los adjuntos llegan en base64 con sus metadatos.
{ "from": "billing@supplier.example", "to": ["Kb3xk29ZtQm7Rv1A@sdhk.events"], "cc": [], "subject": "Invoice #42", "messageId": "<abc123@mail.supplier.example>", "date": "Tue, 14 Jul 2026 09:00:00 +0000", "text": "plain-text body", "html": "<p>html body</p>", "spamStatus": "NotSpam", "bounce": false, "attachments": [ { "name": "invoice.pdf", "contentType": "application/pdf", "size": 12345, "data": "<base64>" } ]}
from es la cabecera From: del mensaje reducida a una dirección simple, así que Acme Billing <billing@supplier.example> llega como billing@supplier.example. Eso es lo que permite usarla como clave de filtro. El nombre visible cambia, y el Return-Path subyacente es una dirección de rebote por mensaje en todo lo que se envía a través de un servicio de correo. Ese remitente del sobre solo se usa como último recurso, cuando un mensaje no lleva ninguna cabecera From:.
El cuerpo sin procesar del evento conserva el payload original completo del proveedor de correo, incluidos los campos que no aparecen arriba, con attachments[].data reducido a null. Los bytes de los adjuntos solo están en el sobre, así que un PDF grande no se guarda dos veces.
Un origen hace una cosa o la otra
En un origen de correo, un POST HTTP a la URL de ingesta de ese origen devuelve 400 BAD_REQUEST con el mensaje de que el origen ingiere correo. Es intencionado. El correo y los webhooks llegan por caminos distintos y solo uno de ellos produce un sobre, así que un origen que aceptara ambos le daría a tu consumidor dos formas de payload bajo un mismo tipo de evento.
idempotencyKeys se rechaza con 400 en un origen de correo. Ahí la deduplicación es el Message-ID, y esa ruta no la eliges tú.Enrutar según remitente o asunto
El tipo de evento de un correo sale del eventTypeField del origen, el mismo ajuste que usa un origen HTTP. Apúntalo a body.from y cada mensaje recibe como tipo la dirección del remitente, que un filtro de conexión compara como cualquier otro tipo de evento. La comparación no distingue mayúsculas y minúsculas, porque los sistemas de correo no conservan las mayúsculas de una cabecera From:, así que una lista de permitidos escrita una vez sigue coincidiendo con un remitente que más adelante llega como Billing@Supplier.example. Al repetir un evento se enruta según el tipo guardado en la ingesta, así que los filtros se comportan igual la segunda vez.
Hay dos rutas disponibles y ninguna más: body.from y body.subject. Son los campos que de verdad distinguen un mensaje de otro. El panel ofrece las mismas dos, así que un origen configurado por la API sigue siendo editable en el SolidHooks Dashboard.
# Type each email with its sender instead of email.receivedcurl -s -X PATCH "$API/v1/sources/src_..." \ -H "authorization: Bearer $SDHK_KEY" \ -H 'content-type: application/json' \ -d '{ "data": { "eventTypeField": "body.from" } }' # Then a connection takes only that supplier:# "filterRules": { "eventTypes": ["billing@supplier.example"] }
eventTypeField, cada correo aceptado tiene el tipo email.received. Se puede seguir filtrando, pero no separar por remitente. La misma alternativa se aplica a cada mensaje: un correo sin asunto en un origen con body.subject cae en email.received en lugar de quedar fuera de todos los filtros.Deduplicación y presupuesto de tamaño
El correo es más grande y más desordenado que el JSON de un webhook, así que conviene conocer las reglas sobre lo que se conserva antes de que llegue un adjunto de 20 MB a las 3 de la madrugada. No se rechaza nada por ser demasiado grande. Se recorta para que quepa, y el presupuesto en el que tiene que caber tiene un máximo de 4096 KB.
Message-ID. Una repetición devuelve duplicate: true y no guarda nada.maxPayloadSizeKb del origen, compartido por cuerpos y adjuntos. Entre 1 y 4096 KB, con 256 por defecto.null si el texto más el html siguieran superándolo, nunca se corta en parte. En ambos casos se establece bodyTruncated: true.data pasa a null y se establece truncated: true. Los bytes nunca se cortan en parte. Lo que se descuenta es el base64, así que calcula aproximadamente un tercio más que el tamaño del archivo.rotate-mask cambia la dirección, porque se deriva del maskId.Prácticas que conviene adoptar
- Configura
eventTypeFieldcomobody.fromantes de necesitarlo. Los filtros escritos contraemail.receivedhay que reescribirlos en cuanto aparece un segundo remitente en la misma dirección. - Sube
maxPayloadSizeKbantes de necesitarlo si el remitente adjunta PDF. El máximo es 4096 KB, y el base64 ocupa aproximadamente un tercio más que el propio archivo. Descubrir el presupuesto por un indicadortruncatedes una forma peor de conocerlo. - Lee
spamStatusybounceen tu handler. Están en el sobre porque el correo llega de remitentes que no controlas. - Trata la dirección como una credencial. Cualquiera que la tenga puede crear eventos en ese origen, así que rótala si acaba en algún sitio público.
Preguntas
Dale un webhook a los sistemas que solo te envían correos
Elige email como proveedor, comparte la dirección y trata el resultado como cualquier otro evento.