Correo (email)

Correo propio para cada negocio: conectás un dominio, creás casillas (ej. ventas@tudominio.com) y alias, y leés, borrás y enviás mensajes por API. Pensado para que un agente de IA opere las casillas de forma autónoma. Sabado corre el servidor de correo — vos nunca ves el token de administración ni las contraseñas de las casillas.

Dos planos. La gestión (dominios, casillas, alias) es una API REST normal. Los mensajes (leer / enviar) corren sobre IMAP/SMTP por detrás, pero vos los usás con los mismos endpoints REST y la misma API key. No tenés que tocar IMAP ni SMTP directamente.

Flujo típico

  1. Conectás el dominio: POST /v1/mail/domains → te devolvemos los registros DNS a publicar.
  2. El dueño del dominio carga esos registros (MX, SPF, DKIM, DMARC) en su proveedor de dominio. ¿El dominio está en Cloudflare? Desde el panel (sección Correo) puede conectar su cuenta de Cloudflare con un click y los registros se aplican solos, con su consentimiento y mostrando antes qué se va a crear o modificar (un SPF existente se combina, nunca se pisa; un DMARC existente se respeta).
  3. Verificás propagación: POST /v1/mail/domains/{domain}/verify hasta que dé dns_verified: true.
  4. Creás casillas: POST /v1/mail/accounts.
  5. Operás mensajes: GET .../messages, POST .../send, etc.

Base de la API: https://waba.galgo.io — todos los paths de esta página cuelgan de ahí (ej. GET https://waba.galgo.io/v1/mail/domains).

Scopes: mail.read para listar/leer, mail.write para crear/borrar/enviar. Si la plataforma todavía no tiene el correo habilitado para tu cuenta, los endpoints responden 503 not_configured.

Conectar un dominio

POST /v1/mail/domains

Da de alta el dominio, genera sus llaves DKIM y devuelve los registros DNS que hay que publicar.

Scope: mail.write.

POST /v1/mail/domains
{
  "tenant_ref": "panaderia-lo-de-juan",
  "domain": "panaderia.com"
}

Respuesta:

{
  "tenant_ref": "panaderia-lo-de-juan",
  "domain": "panaderia.com",
  "dkim_generated": true,
  "dns_verified": false,
  "dns_verified_at": null,
  "dns_records": {
    "dns_mx": "panaderia.com. 600 IN MX 10 mail.sabado.cloud.",
    "dns_spf": "panaderia.com. 600 IN TXT \"v=spf1 mx a:mail.sabado.cloud ~all\"",
    "dns_dkim": "dkim._domainkey.panaderia.com. 600 IN TXT \"v=DKIM1; k=rsa; p=...\"",
    "dns_dmarc": "_dmarc.panaderia.com. 600 IN TXT \"v=DMARC1; p=reject;\""
  },
  "created_at": "2026-06-01T12:00:00+00:00"
}

Mostrale dns_records al dueño del dominio para que los cargue en su proveedor. El correo no funciona hasta que esos registros propaguen.

GET /v1/mail/domains · GET /v1/mail/domains/{domain}

Lista los dominios del negocio, o trae uno con sus dns_records y estado de verificación. Scope: mail.read. Query: tenant_ref (requerido).

GET /v1/mail/domains?tenant_ref=panaderia-lo-de-juan
Authorization: Bearer TU_API_KEY

DELETE /v1/mail/domains/{domain}

Borra el dominio (arrastra sus casillas y alias). Scope: mail.write. Solo borra de nuestro lado si el servidor de correo confirmó el borrado.

Errores:

Configurar el DNS

Los registros que devuelve POST /v1/mail/domains hay que cargarlos en el proveedor del dominio. Son cuatro obligatorios:

TipoPara qué
MXDónde se entrega el correo del dominio (apunta a nuestro servidor).
TXT (SPF)Autoriza a nuestro servidor a enviar en nombre del dominio.
TXT (DKIM)Firma criptográfica de los mensajes salientes.
TXT (DMARC)Política de qué hacer con el correo que no pasa SPF/DKIM.

POST /v1/mail/domains/{domain}/verify

Resuelve el DNS real y te dice qué registros ya propagaron. Marca el dominio como verificado solo si MX + SPF + DKIM resuelven. Scope: mail.write.

POST /v1/mail/domains/panaderia.com/verify
{ "tenant_ref": "panaderia-lo-de-juan" }

Respuesta:

{
  "tenant_ref": "panaderia-lo-de-juan",
  "domain": "panaderia.com",
  "dns_checks": { "mx": true, "spf": true, "dkim": true, "all": true },
  "dns_verified": true
}

El DNS puede tardar hasta 24-48hs en propagar. Reintentá verify cada tanto; mientras all sea false, todavía no está listo.

Crear casillas

POST /v1/mail/accounts

Crea una casilla en un dominio del negocio. La plataforma genera la contraseña y la usa internamente para enviar/recibir — no se devuelve nunca por la API.

Scope: mail.write.

POST /v1/mail/accounts
{
  "tenant_ref": "panaderia-lo-de-juan",
  "domain": "panaderia.com",
  "local_part": "ventas",
  "displayed_name": "Ventas Panadería",
  "quota_bytes": 2147483648
}

Respuesta:

{
  "tenant_ref": "panaderia-lo-de-juan",
  "email": "ventas@panaderia.com",
  "displayed_name": "Ventas Panadería",
  "quota_bytes": 2147483648,
  "enabled": true,
  "created_at": "2026-06-01T12:00:00+00:00"
}

GET /v1/mail/accounts · GET /v1/mail/accounts/{email}

Lista las casillas del negocio (filtrable con ?domain=) o trae una. Scope: mail.read. Query: tenant_ref (requerido).

PATCH /v1/mail/accounts/{email}

Actualiza quota_bytes, displayed_name, enabled, o rota la contraseña con "rotate_password": true. Scope: mail.write.

DELETE /v1/mail/accounts/{email}

Borra la casilla. Scope: mail.write.

Errores:

Alias

Un alias reenvía una dirección a otra(s). Sirve para tener info@, hola@, etc. cayendo en una casilla real, sin crear buzones nuevos.

GET /v1/mail/aliases · POST /v1/mail/aliases · DELETE /v1/mail/aliases/{alias}

Scopes: mail.read (listar) / mail.write (crear, borrar). El alias tiene que caer en un dominio del negocio.

POST /v1/mail/aliases
{
  "tenant_ref": "panaderia-lo-de-juan",
  "alias": "info@panaderia.com",
  "destination": "ventas@panaderia.com",
  "wildcard": false
}

destination puede ser varias direcciones separadas por coma. wildcard: true hace catch-all del dominio.

Leer y enviar mensajes

Estos endpoints operan el buzón de una casilla. Es lo que le da a un agente la capacidad de leer y responder correo de forma autónoma.

GET /v1/mail/accounts/{email}/messages

Lista mensajes (resúmenes, sin cuerpo). Scope: mail.read. Query: tenant_ref (requerido), folder (default INBOX), limit (1–100, default 25). Listar no marca como leído.

GET /v1/mail/accounts/ventas@panaderia.com/messages?tenant_ref=panaderia-lo-de-juan
Authorization: Bearer TU_API_KEY

Respuesta:

{
  "tenant_ref": "panaderia-lo-de-juan",
  "email": "ventas@panaderia.com",
  "folder": "INBOX",
  "messages": [
    {
      "uid": 12,
      "subject": "Pedido del sábado",
      "from": ["Ana <ana@gmail.com>"],
      "date": "2026-06-01T10:00:00+00:00",
      "seen": false
    }
  ]
}

GET /v1/mail/accounts/{email}/unseen

Resumen liviano de no leídos (sin cuerpos), pensado para consultar el inbox seguido con costo mínimo — el caso típico es un agente que revisa si hay algo nuevo cada un rato. Scope: mail.read. Query: tenant_ref (requerido), since_uid (opcional: solo mensajes con UID mayor), folder (default INBOX), limit (1–100, default 25).

GET /v1/mail/accounts/ventas@panaderia.com/unseen?tenant_ref=panaderia-lo-de-juan&since_uid=123
Authorization: Bearer TU_API_KEY

Respuesta:

{
  "tenant_ref": "panaderia-lo-de-juan",
  "email": "ventas@panaderia.com",
  "folder": "INBOX",
  "count": 2,
  "latest_uid": 125,
  "uidvalidity": 17,
  "messages": [
    { "uid": 125, "subject": "Urgente", "from": ["Ana <ana@gmail.com>"], "date": "2026-07-31T10:00:00+00:00", "seen": false },
    { "uid": 124, "subject": "Pedido", "from": ["Leo <leo@gmail.com>"], "date": "2026-07-31T09:00:00+00:00", "seen": false }
  ]
}

GET /v1/mail/accounts/{email}/messages/{uid}

Trae un mensaje completo (cuerpo en texto y HTML, destinatarios, si tiene adjuntos). Scope: mail.read.

PATCH /v1/mail/accounts/{email}/messages/{uid}

Marca un mensaje como leído o no leído (es el flag \Seen de IMAP). Leer por API no marca leído: este endpoint es el único que toca el flag, así varios procesos pueden coordinar qué está atendido sin llevar estado propio. Scope: mail.write.

PATCH /v1/mail/accounts/ventas@panaderia.com/messages/124
{
  "tenant_ref": "panaderia-lo-de-juan",
  "seen": true
}

Respuesta: { "ok": true, "email": "ventas@panaderia.com", "uid": 124, "seen": true }

DELETE /v1/mail/accounts/{email}/messages/{uid}

Borra un mensaje del buzón. Scope: mail.write.

POST /v1/mail/accounts/{email}/send

Envía un correo desde la casilla. Requiere text o html (o ambos). Scope: mail.write.

POST /v1/mail/accounts/ventas@panaderia.com/send
{
  "tenant_ref": "panaderia-lo-de-juan",
  "to": ["cliente@gmail.com"],
  "cc": [],
  "reply_to": "ventas@panaderia.com",
  "subject": "Tu pedido está listo",
  "text": "Hola! Tu pedido te espera.",
  "html": "

Hola! Tu pedido te espera.

" }

Respuesta:

{
  "ok": true,
  "tenant_ref": "panaderia-lo-de-juan",
  "from": "ventas@panaderia.com",
  "sent": true
}

Errores (mensajes):

Privacidad. No guardamos ni mostramos el contenido de los correos en ningún panel nuestro — el cuerpo de los mensajes vive en el buzón, no en nuestra base. Las contraseñas de las casillas se guardan cifradas y nunca se devuelven por la API.