Enviar Email

Envía un correo electrónico de forma inmediata o programada, con plantillas Handlebars, adjuntos y múltiples destinatarios.

POST /send-email

Encola un correo para envío inmediato o lo programa para una fecha futura.

Nota: Esta es la API avanzada de ReallyQuickEmails (campos recipient/sender/html): es la única que soporta envío programado (scheduled_at), plantillas (templateId) y dry_run. Para integraciones simples se recomienda la API v1 (POST /v1/send-email), con nombres de campo en convención REST (recipient_email/sender_email/html_body) y contrato estable.

Autenticacion

Autentica con Bearer token usando una API key del proyecto:

text
Authorization: Bearer sk_proj_xxxxxxxxxxxx

Se aceptan claves sk_proj_*, sk_live_* (modo Live) y sk_test_* (modo Test). El project_id se infiere automáticamente desde la key — no es necesario pasarlo como header.

Ver más en Autenticación.

Request Body

CampoTipoRequeridoDescripción
htmlstringSí*Contenido HTML del correo. Requerido si no se usa templateId.
subjectstringAsunto del correo.
recipientstring | string[]Dirección de correo del destinatario, o un array de direcciones.
senderstringDirección de correo del remitente.
senderNamestringNoNombre visible del remitente.
templateIdstringNoID de una plantilla almacenada. Si se proporciona, se usa en lugar de html.
dataobjectNoObjeto con variables para sustitución Handlebars en la plantilla o HTML.
email_typestringNoTipo de correo (ej. transactional, marketing, automation).
campaign_idstringNoUUID de la campaña asociada.
automation_run_idstringNoUUID de la ejecución de automatización asociada.
scheduled_atstringNoFecha/hora de envío programado. Ver Programacion de Envio.
timezonestringNoZona horaria para interpretar scheduled_at. Ver Programacion de Envio.
ccstring | string[]NoDirección(es) de correo en copia.
bccstring | string[]NoDirección(es) de correo en copia oculta.
attachmentsarrayNoLista de objetos de adjuntos. Máximo 10 adjuntos. Ver Adjuntos.
textstringNoVersión texto plano del correo (alternativa MIME al HTML).
custom_headersobjectNoHeaders adicionales para el correo.
in_reply_tostringNoMessage-ID al que responde este correo (threading).
referencesstring | string[]NoMessage-IDs de la cadena de threading (header References).
thread_idstringNoID de hilo para agrupar correos relacionados.
dry_runbooleanNoSi es true, renderiza el correo (template + variables) sin enviarlo ni registrar actividad. Ver Dry Run.
environmentstringNoNombre de un webhook environment configurado en el proyecto. Rutea los webhooks de este envío a esa URL. Ver Webhook Environments.

Adjuntos

Cada objeto en el array attachments tiene la siguiente estructura:

CampoTipoRequeridoDescripción
filenamestringNombre del archivo (ej. factura.pdf).
urlstringNo*URL pública del archivo. Requerido si no se proporciona content.
contentstringNo*Contenido del archivo codificado en Base64. Requerido si no se proporciona url.
contentTypestringNoTipo MIME del archivo (ej. application/pdf). Se infiere del nombre si no se proporciona.

Límites:

  • Máximo 10 adjuntos por correo.
  • Máximo 10 MB por adjunto individual.
  • Máximo 10 MB en total por correo (límite de la infraestructura de envío).

Los adjuntos se procesan en segundo plano (no en la respuesta HTTP): si un adjunto excede los límites o su URL no es accesible, el envío falla de forma asíncrona y queda registrado en el activity record.

Ver más en Adjuntos.

Programacion de Envio

El campo scheduled_at soporta múltiples formatos:

Timestamp ISO 8601

text
"scheduled_at": "2025-03-15T14:30:00Z"

Lenguaje natural (ingles)

text
"scheduled_at": "tomorrow at 3pm"
"scheduled_at": "in 2 hours"
"scheduled_at": "next monday at 9am"

Zona horaria (timezone)

El campo timezone se usa para interpretar correctamente scheduled_at. Soporta:

  • Nombre IANA: "America/Santiago", "US/Eastern", "Europe/Madrid"
  • Offset: "+09:00", "-05:00", "UTC-3", "GMT+9"
  • Numérico entero: -3, 9 (sin fracciones — para zonas de media hora como India usa el formato "+05:30")

Si no se proporciona timezone, se usa la zona horaria por defecto del proyecto (default_timezone). Si el proyecto no tiene zona horaria configurada, se asume UTC.

Ver más en Programar Envíos.

Dry Run

Con "dry_run": true el endpoint renderiza el correo (resuelve la plantilla y sustituye variables {{var}} y bloques {{#each arr}}) sin enviarlo: no envía, no encola, no crea registro de actividad ni consume cuota. Útil para validar el payload y las variables antes de un envío real.

json
{
  "dry_run": true,
  "test_mode": false,
  "sample_cart_items_injected": false,
  "would_send": {
    "to": ["cliente@ejemplo.com"],
    "cc": [],
    "bcc": [],
    "from": "Mi Tienda <ventas@mitienda.com>",
    "subject": "Tu pedido está en camino",
    "html_preview": "<h1>Hola Juan</h1><p>Tu pedido #12345 está en camino.</p>",
    "template_id": null,
    "variables_used": ["nombre", "pedido"]
  }
}

html_preview se trunca a 8000 caracteres. Las variables sin valor en data se dejan como {{variable}} en el preview.

Headers

HeaderTipoRequeridoDescripción
AuthorizationstringBearer sk_proj_... / sk_live_... / sk_test_....
Content-TypestringDebe ser application/json.
Idempotency-KeystringNoKey arbitraria 1-256 chars. Mismo (project_id, key) dentro de 24h retorna respuesta cacheada.
x-sourcestringNoIdentificador del origen de la solicitud (api, platform, test).

Ver más sobre Idempotency-Key en API Keys.

Ejemplo de Solicitud

Envio inmediato

bash
curl -X POST https://api.reallyquickemails.com/send-email \
  -H "Authorization: Bearer sk_proj_xxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "html": "<h1>Hola {{nombre}}</h1><p>Tu pedido #{{pedido}} está en camino.</p>",
    "subject": "Tu pedido está en camino",
    "recipient": "cliente@ejemplo.com",
    "sender": "ventas@mitienda.com",
    "senderName": "Mi Tienda",
    "data": {
      "nombre": "Juan",
      "pedido": "12345"
    },
    "email_type": "transactional"
  }'

Envio programado con adjuntos

bash
curl -X POST https://api.reallyquickemails.com/send-email \
  -H "Authorization: Bearer sk_proj_xxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "subject": "Reporte mensual - Marzo 2025",
    "recipient": ["gerente@empresa.com", "director@empresa.com"],
    "sender": "reportes@empresa.com",
    "senderName": "Sistema de Reportes",
    "templateId": "reporte-mensual",
    "data": {
      "mes": "Marzo",
      "anio": "2025"
    },
    "scheduled_at": "next monday at 9am",
    "timezone": "America/Santiago",
    "attachments": [
      {
        "filename": "reporte-marzo-2025.pdf",
        "url": "https://storage.ejemplo.com/reportes/marzo-2025.pdf",
        "contentType": "application/pdf"
      }
    ]
  }'

Ver más en SDK de Node.js.

Respuestas

Envio inmediato exitoso

El envío es asíncrono: el endpoint encola el correo y responde de inmediato con 200. El envío real ocurre en segundo plano.

json
{
  "success": true,
  "queued": true,
  "jobId": "email-1780676307345-6da66195",
  "email_id": "d116a543-9b13-41b4-93cf-647539018275",
  "activityId": "d116a543-9b13-41b4-93cf-647539018275",
  "message": "Email queued for sending"
}
CampoTipoDescripción
queuedbooleanSiempre true en envío inmediato. El correo quedó encolado.
jobIdstringID del job en la cola de envío.
email_idstring | nullUUID del registro de actividad. Mismo valor que activityId.
activityIdstring | nullUUID del registro de actividad, pre-creado con estado queued.

El resultado final del envío (delivered, bounced, failed) no viene en esta respuesta: se consulta vía la Activity API usando email_id, o se recibe vía webhooks (email.send, email.delivery, email.bounce, etc.).

Envio programado exitoso

json
{
  "success": true,
  "scheduled": true,
  "scheduled_send_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "scheduled_for": "2025-03-17T12:00:00.000Z",
  "scheduled_for_local": "2025-03-17 09:00 (America/Santiago)",
  "timezone": "America/Santiago",
  "message": "Correo programado para 2025-03-17 09:00 (America/Santiago)"
}
CampoTipoDescripción
scheduled_send_idstringUUID del correo programado.
scheduled_forstringFecha y hora de envío en UTC (ISO 8601).
scheduled_for_localstringFecha y hora de envío en la zona horaria especificada (formato legible).
timezonestringZona horaria utilizada para la programación.

Reply-To Automatico (Inbound Email)

Todos los envíos vía API incluyen automáticamente un header Reply-To con el nombre del remitente:

text
Reply-To: "Mi Tienda" <r-x7K9mP2q@rqe.inbound.reallyquickemails.com>

Los clientes de correo (Gmail, Outlook, Apple Mail) muestran el nombre del remitente en lugar de la dirección técnica. Cuando el destinatario responde, la respuesta se enruta automáticamente a RQE y dispara un webhook email.inbound con el reply + adjuntos. Ver más en Webhooks → Inbound.

Verificacion de Remitente

El sender debe estar verificado antes de poder enviar. Hay dos formas:

  1. Verificar el email individual vía magic link (POST /domains/verify-email).
  2. Verificar el dominio completo vía DNS (POST /domains/register) — recomendado, mejora deliverability y permite enviar desde cualquier dirección del dominio.

La verificación no se valida sincrónicamente en este endpoint: como el envío es encolado, un sender no verificado igual recibe 200 con queued: true, y el rechazo ocurre después, durante el procesamiento en segundo plano, cuando la infraestructura de envío rechaza la identidad. El fallo queda registrado en el activity record (current_status: "failed", con el detalle en error_message), consultable vía la Activity API con el email_id de la respuesta.

Ver más en Domains API.

Codigos de Error

Como el envío es asíncrono, los únicos errores síncronos (en la respuesta HTTP) son de validación:

CódigoDescripción
400Solicitud inválida. Faltan campos requeridos (recipient/sender, o html+subject sin templateId), scheduled_at no parseable, o environment no configurado.
401API key inválida o ausente.
404Plantilla no encontrada (solo en modo dry_run; con envío real, una plantilla inexistente falla de forma asíncrona en segundo plano).
500Error interno del servidor (incluye fallo al insertar el envío programado).

Los errores de envío (sender no verificado, adjunto inválido, rechazo de la infraestructura de envío, rate limit) ocurren después, durante el procesamiento en segundo plano: el activity record pasa a current_status: "failed" con el detalle en error_message.

Ejemplo de error

json
{
  "error": "Either templateId (with projectId) or both html and subject are required"
}