Una API REST para cualquier buzón de mailcow: leer, enviar y organizar, con webhooks

Guía práctica de la Mailbox API para mailcow: crea claves con permisos para leer, enviar y organizar el correo de cada buzón, verifica webhooks firmados y envía avisos a Telegram.

Por qué te interesa

mailcow incluye una API de administración. Va muy bien para crear dominios, buzones y alias, pero no dice nada del correo que hay dentro de un buzón. Si quieres que un script, un CRM o un flujo de automatización lea una bandeja compartida, responda con esa dirección o mueva un mensaje a una carpeta, la API de administración te deja solo.

La Mailbox API para mailcow cubre ese hueco. Da a tus propios programas una interfaz REST controlada sobre el correo en sí: listar carpetas y mensajes, buscar, leer un mensaje completo con adjuntos, enviar como el buzón y marcar, mover o borrar. Además, cada buzón monitorizado puede enviar cada mensaje nuevo a un webhook https como JSON firmado, y a un chat de Telegram. Todo se ejecuta dentro de tu red de mailcow, y tú decides qué buzones y qué derechos recibe cada clave.

Este tutorial recorre la puesta en marcha tal como la hice en mi propio servidor: primero las claves, luego la lectura y el envío, después los webhooks hacia una herramienta de automatización como n8n y, por último, los avisos por Telegram.

Requisitos

  • mailcow-dockerized sobre Linux con docker compose y systemd, y el mailcow Toolkit 0.5.0 o superior de MediaLab Works instalado (este producto es uno de sus módulos, con su propia licencia).
  • Acceso root en el servidor para ejecutar el instalador del Toolkit, y salida HTTPS desde el servidor hacia medialab.works para la comprobación de licencia.
  • Una dirección https para la API, usada en los enlaces firmados de adjuntos y, para los webhooks, un endpoint https propio.
  • Opcional: un token de bot de Telegram de @BotFather y el chat id de cada buzón que quieras notificar.
  • El módulo es de código abierto (GPL-3.0), así que recibes el código fuente completo.

Paso 1 — Instala el módulo y activa la licencia

El módulo viene en el paquete del Toolkit. En el servidor de mailcow, como root, ejecuta el instalador como de costumbre:

sudo ./install.sh --mailcow-dir /opt/mailcow-dockerized --name <instance>

Copia data/web/mlw-mailbox-api/, añade el elemento de menú, el trabajo del agente del host y el temporizador de una vez por minuto, y recarga php-fpm. Después abre System → Mailbox API en el menú de administración de mailcow; verás un aviso de licencia y los ajustes debajo.

Para activar, abre System → Updates (Toolkit) y busca la tarjeta Mailbox API para mailcow: pega la clave y actívala, o empieza la prueba. En la shell lo mismo es posible con php /web/mlw-toolkit/cli.php activate mailcow-mailbox-api <KEY> dentro de php-fpm-mailcow. La licencia se comprueba a diario; sin internet el módulo sigue funcionando durante 7 días después de la última comprobación correcta. Sin una licencia válida la API responde 503 not_licensed y el temporizador no envía nada, pero los buzones y los mensajes quedan intactos.

Paso 2 — Activa la API y configura lo básico

En System → Mailbox API:

  • API on activa o desactiva la API. La licencia se comprueba igualmente.
  • Public API address es la dirección que se usa en los enlaces firmados de adjuntos, por ejemplo https://mail.example.com/mlw-mailbox-api/. Déjala vacía para deducirla de la página.
  • Messages per page (1–200) y largest attachment (1–100 MB) fijan tus límites.
  • Dovecot master user es opcional y solo hace falta si defines el tuyo en mailcow.conf; por defecto el módulo lee las credenciales maestras que mailcow crea al arrancar.
  • Telegram bot token es opcional por ahora; volvemos a él en el Paso 6.

Paso 3 — Crea claves con permisos

En la tarjeta API keys, ponle un nombre a la clave, marca los derechos que necesita — read, send, organize —, elige los buzones que puede usar y pulsa Create key. La clave (mbx_…) se muestra una sola vez, al crearla, así que guárdala enseguida. La lista muestra el prefijo, los derechos, los buzones y el último uso; Revoke la desactiva al instante. En la base de datos solo se guarda el SHA-256 de la clave, y cada llamada queda registrada.

Esta es la parte que más me gusta para automatizar: un flujo que solo archiva el correo entrante en carpetas recibe una clave con organize sobre un buzón y nada más. Un script de notificaciones recibe solo read. Nunca entregas una clave que pueda llegar a todos los buzones del servidor.

Envía la clave en cada llamada:

Authorization: Bearer mbx_…

Paso 4 — Lee y organiza el correo

Todas las rutas se llaman sobre https://<mail host>/mlw-mailbox-api/?r=<route>: nginx de mailcow no tiene PATH_INFO, así que la ruta va en la cadena de consulta. Las respuestas son JSON. Todas las rutas excepto el enlace firmado de adjuntos necesitan la clave, y la clave debe permitir el buzón y el derecho.

Empieza comprobando qué puede hacer una clave:

curl -H "Authorization: Bearer $KEY" \
  "https://mail.example.com/mlw-mailbox-api/?r=/v1/me"

GET /v1/me devuelve el nombre de la clave, los derechos, los buzones y los límites; GET /v1/mailboxes lista los buzones permitidos con su número de carpetas y el UIDNEXT de INBOX. Para ver las carpetas y una página de los mensajes más recientes:

curl -H "Authorization: Bearer $KEY" \
  "https://mail.example.com/mlw-mailbox-api/?r=/v1/folders&mailbox=office@example.com"

curl -H "Authorization: Bearer $KEY" \
  "https://mail.example.com/mlw-mailbox-api/?r=/v1/messages&mailbox=office@example.com&folder=INBOX&limit=20&offset=0&unseen=1"

La lista de mensajes admite q= para buscar y unseen=1 para solo no leídos, y llega de más reciente a más antiguo con uid, asunto, remitente, destinatarios, fecha, tamaño y visto. Leer un mensaje devuelve el texto, el HTML, las cabeceras y los adjuntos (nombre, tipo, tamaño, url firmada):

curl -H "Authorization: Bearer $KEY" \
  "https://mail.example.com/mlw-mailbox-api/?r=/v1/messages/1234&mailbox=office@example.com&folder=INBOX"

Los adjuntos llegan como enlaces firmados que caducan a la hora y están ligados a la clave API que puede leer el buzón, así que puedes descargarlos sin ir pasando la clave. Regenerar un enlace significa volver a leer el mensaje a través de la API.

Con el derecho organize puedes marcar como leído o no leído, mover y borrar:

curl -X POST -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' \
  -d '{"read":true}' \
  "https://mail.example.com/mlw-mailbox-api/?r=/v1/messages/1234/read&mailbox=office@example.com&folder=INBOX"

curl -X POST -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' \
  -d '{"folder":"Archive"}' \
  "https://mail.example.com/mlw-mailbox-api/?r=/v1/messages/1234/move&mailbox=office@example.com&folder=INBOX"

Un DELETE sobre /v1/messages/{uid} elimina el mensaje. Los errores son JSON con un estado HTTP: 401 clave ausente o incorrecta, 403 la clave no tiene derecho sobre ese buzón, 404 ruta o mensaje desconocido, 422 entrada no válida, 429/502 límites o un error del servidor.

Paso 5 — Envía como el buzón

Enviar necesita el derecho send. El cuerpo lleva to, subject y text, y también admite cc, bcc, html y reply_to. Se pone una copia en Sent, lo que importa si luego alguien abre ese buzón y se pregunta qué se respondió:

curl -X POST -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' \
  -d '{"to":"customer@example.org","subject":"Your invoice","text":"Attached."}' \
  "https://mail.example.com/mlw-mailbox-api/?r=/v1/send&mailbox=office@example.com"

No se envía nada a medialab.works; el mensaje sale por tu propio mailcow.

Paso 6 — Webhooks para correo nuevo, verificados

En la tarjeta Monitored mailboxes añade un buzón, la carpeta que quieres vigilar (INBOX por defecto), una dirección de webhook https y, opcionalmente, un chat id de Telegram. El módulo comprueba cada minuto si hay mensajes nuevos usando UIDNEXT por carpeta. Ten en cuenta el diseño: la primera ejecución solo fija el punto de partida, así que no se reenvía correo antiguo; el correo nuevo a partir de ahí sí se entrega.

Cada mensaje nuevo genera un POST JSON con event, mailbox, folder, uid, received_at y un objeto message con message_id, date, from, to, cc, subject, size, text, html, headers y adjuntos (nombre, tipo, tamaño, sha256 y una url firmada).

Antes de usar el cuerpo, verifica la cabecera de firma:

X-MLW-Signature: t=1792300000,v1=<hex HMAC-SHA256(secret, "t.body")>
X-MLW-Event: message.received

Cada monitor tiene su propio secreto, generado en el primer uso; la página lo muestra al crear el monitor y con el botón Secret. Compara el HMAC con tu secreto y rechaza las marcas de tiempo de más de cinco minutos. En n8n, por ejemplo, pon esa comprobación en un pequeño nodo Code o Function justo después del nodo Webhook, antes de que nada más toque el payload.

Las entregas fallidas se reintentan a los 30 s, 1 min, 2 min… hasta cinco intentos. Las entregas se encolan en la base de datos y las envía el temporizador de una vez por minuto, y la tarjeta Recent deliveries muestra el estado y la respuesta: tu primera parada cuando un flujo parece callado.

Paso 7 — Notificaciones de Telegram por buzón

Pega el token del bot en Settings (se guarda cifrado, solo se muestran los cuatro últimos caracteres; un bot sirve para todos los buzones). Después pon el chat id —un usuario, un grupo o un canal— en cada buzón monitorizado. El bot debe ser miembro y, en canales, administrador. Telegram junto a un buzón envía un mensaje de prueba. La notificación muestra el remitente, el asunto y los primeros 400 caracteres del texto.

Encaja muy bien en una bandeja compartida: un chat por buzón monitorizado, y ves el correo nuevo en el móvil sin abrir un cliente de correo.

Consejos y solución de problemas

  • 503 not_licensed: activa la licencia en System → Updates (Toolkit); sin internet el módulo funciona 7 días después de la última comprobación.
  • 502 server_error con "no dovecot master credentials": mailcow no creó /etc/sogo/sieve.creds; comprueba que dovecot-mailcow y sogo-mailcow están en marcha, o define el usuario y la contraseña maestros a mano.
  • 403 not_allowed: la clave no cubre ese buzón o le falta el derecho; crea una nueva clave con el ámbito correcto en la página de administración.
  • No llega ningún webhook: comprueba que el monitor está activo, que la dirección es https y responde 2xx, y mira Recent deliveries; el temporizador se ejecuta cada minuto (systemctl status mlcow-mailapi-<name>.timer).
  • No hay correo antiguo tras añadir un monitor: por diseño, la primera ejecución solo registra la posición actual.
  • Mantén Public API address con tu dirección https real; de lo contrario los enlaces firmados de adjuntos apuntan a un sitio equivocado.
  • La API no crea, borra ni cambia buzones; solo trabaja dentro de los buzones que permites. Lee el correo mediante el login maestro de dovecot dentro de la red de mailcow, el mismo mecanismo que usa mailcow para imapsync.

Conclusión

La API de administración gestiona el servidor; este módulo gestiona el correo, con una clave con permisos cada vez. Con una clave de lectura/escritura/organización por buzón, webhooks firmados hacia n8n y un chat de Telegram para las bandejas que te importan, consigues automatizar tu propio mailcow sin ceder nada. Empieza con un buzón y una clave de solo lectura, confirma que la verificación de firma del webhook funciona y luego amplía el ámbito. La guía de usuario tiene la tabla completa de rutas, y el changelog recoge las novedades; si algo no cuadra, la base de conocimiento y las FAQ son buenos siguientes pasos, o contáctanos.

Esta publicación se redactó con ayuda de IA a partir de la documentación de nuestros productos o de las fuentes indicadas y se revisó automáticamente antes de publicarse.

Sigue leyendo

¿Usas Emby, Jellyfin o Nextcloud?

Nuestros plugins y apps añaden TV en directo, subtítulos, registro de usuarios y seguridad a tu propio servidor, con prueba gratuita.