MailLandDocs
Envío de emails

REST API

Envíe correos electrónicos utilizando la REST API de Mail.Land con referencia completa de solicitud y respuesta

Envíe correos electrónicos transaccionales mediante POST /v1/transactional/send. Esta es la forma principal de enviar correo electrónico a través de MailLand.

Autenticación

Incluya su clave API en el encabezado X-API-Key:

X-API-Key: ml_pet_Ab3Kx9mN2pQ7rT4wL8yD...

Las credenciales deben tener el canal Api habilitado y ser credenciales de producto Sending.

Enviar un correo electrónico único

POST https://api.mail.land/v1/transactional/send
Send an email
curl -X POST https://api.mail.land/v1/transactional/send \
  -H "X-API-Key: ml_pet_Ab3Kx9mN2pQ7rT4wL8yD..." \
  -H "Content-Type: application/json" \
  -d '{
    "from": {"email": "[email protected]"},
    "to": [{"email": "[email protected]"}],
    "subject": "Hello from MailLand",
    "htmlContent": "<p>Your email content here</p>"
  }'

Cuerpo de la solicitud

Full request example
{
  "from": {
    "email": "[email protected]",
    "name": "Your App"
  },
  "to": [
    {
      "email": "[email protected]",
      "name": "Jane Doe",
      "variables": {
        "firstName": "Jane",
        "orderNumber": "ORD-12345"
      }
    }
  ],
  "subject": "Your order #{{orderNumber}} has shipped",
  "htmlContent": "<h1>Hi {{firstName}},</h1><p>Your order #{{orderNumber}} is on its way.</p>",
  "textContent": "Hi {{firstName}}, Your order #{{orderNumber}} is on its way.",
  "replyTo": "[email protected]",
  "tags": ["order-shipped", "transactional"],
  "customHeaders": {
    "X-Entity-Ref-ID": "order-12345"
  },
  "tracking": {
    "opens": true,
    "clicks": true
  },
  "priority": "High",
  "idempotencyKey": "order-12345-shipped-v1"
}

Referencia de campos

CampoTipoRequeridoDescripción
fromobjectSíDirección del remitente
from.emailstringSíCorreo del remitente (debe estar en un dominio verificado)
from.namestringNoNombre para mostrar
toarraySíLista de destinatarios
to[].emailstringSíDirección de correo del destinatario
to[].namestringNoNombre para mostrar del destinatario
to[].variablesobjectNoVariables de plantilla por destinatario
subjectstringCondicionalRequerido a menos que se use una plantilla
templateIdstringNoUsar una plantilla almacenada en lugar de contenido en línea
htmlContentstringCondicionalCuerpo HTML (se requiere htmlContent o textContent si no hay templateId)
textContentstringNoTexto plano de respaldo
variablesobjectNoVariables de plantilla globales. Se aplican tanto si se define templateId como si no; ante una clave superpuesta, gana el valor propio del destinatario en to[].variables
replyTostringNoDirección de correo para respuesta
tagsarrayNoEtiquetas para filtrado en análisis y webhooks
customHeadersobjectNoEncabezados de correo personalizados (pares clave-valor)
trackingobjectNoAnular configuración de seguimiento
tracking.opensbooleanNoPedir seguimiento de aperturas para este mensaje (por defecto: desactivado en ambos flujos). Solo se usa mientras el dominio de envío no haya elegido; la elección del propio dominio por flujo, hecha en la página Dominios, prevalece. false siempre lo desactiva
tracking.clicksbooleanNoActivar seguimiento de clics (por defecto: false en flujos transaccionales salvo que su cuenta lo active, true en marketing)
ipPoolIdstringNoReservado para pools de IP dedicadas. Aún no disponible: enviarlo devuelve 400 IP_POOL_NOT_FOUND. Omítalo.
scheduledAtdatetimeNoProgramar para entrega futura (UTC), hasta 7 días de antelación
idempotencyKeystringNoPrevenir envíos duplicados para la misma clave. Prefiere la cabecera X-Idempotency-Key
attachmentsarrayNoArchivos adjuntos (máx. 10, máx. 10 MB decodificados en total)
priorityenumNoLow, Normal (por defecto), High o Critical

Archivos adjuntos

Cada archivo adjunto requiere tres campos:

Attachment format
{
  "attachments": [
    {
      "fileName": "invoice.pdf",
      "contentType": "application/pdf",
      "content": "JVBERi0xLjQKJ...",
      "inline": false
    },
    {
      "fileName": "logo.png",
      "contentType": "image/png",
      "content": "iVBORw0KGgoAAAA...",
      "contentId": "company-logo",
      "inline": true
    }
  ]
}
CampoTipoRequeridoDescripción
fileNamestringSíNombre del archivo con extensión
contentTypestringSíTipo MIME (p.ej. application/pdf)
contentstringSíContenido del archivo en Base64 estándar: con relleno (padding), sin espacios ni saltos de línea
contentIdstringNoID de contenido para archivos adjuntos en línea (referenciado en HTML como cid:company-logo)
inlinebooleanNoSi es una imagen en línea (por defecto: false)

Como máximo 10 archivos adjuntos por correo y como máximo 10 MB decodificados (tras Base64) en total. Un contentType ausente, un Base64 no canónico, o superar el número o el tamaño permitido devuelve 400 VALIDATION_FAILED. POST /v1/transactional/send-batch no tiene campo attachments.

Respuesta

Estado: 202 Accepted

Success response
{
  "success": true,
  "jobId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
  "messageIds": ["t_a1b2c3d4e5f6..."],
  "acceptedRecipients": ["[email protected]"],
  "rejectedRecipients": [],
  "queuedAt": "2026-02-17T10:30:00Z",
  "isSandboxed": false
}
CampoTipoDescripción
successbooleanSi la solicitud fue aceptada
jobIdstringIdentificador único del trabajo para seguimiento
messageIdsarrayIDs de mensaje por destinatario (prefijo t_ para transaccional, m_ para marketing)
acceptedRecipientsarrayDirecciones de correo aceptadas para entrega
rejectedRecipientsarrayDestinatarios rechazados
queuedAtdatetimeCuándo se puso en cola el correo
isSandboxedbooleantrue si la solicitud usó una credencial sandbox (vea Modo Sandbox)
sandboxReasonstringcredential_sandbox_mode (registrado, no entregado) o all_recipients_not_in_sandbox_allowlist (ningún destinatario estaba en la lista de permitidos); ausente en otros casos

Destinatarios rechazados

Los destinatarios pueden ser rechazados por varias razones:

Response with rejected recipients
{
  "success": true,
  "jobId": "...",
  "acceptedRecipients": ["[email protected]"],
  "rejectedRecipients": [
    {
      "email": "[email protected]",
      "reason": "Suppressed"
    },
    {
      "email": "[email protected]",
      "reason": "domain_typo",
      "suggestion": "[email protected]"
    }
  ]
}

Razones comunes de rechazo:

  • Suppressed: la dirección está en la lista de supresión (rebotes/quejas previas)
  • disposable_domain: proveedor de correo desechable detectado
  • domain_typo: probable error tipográfico en el dominio (incluye campo suggestion)

Verificación previa

Verifique su capacidad de envío antes de realizar la solicitud real:

GET https://api.mail.land/v1/transactional/send/check?recipientCount=1
Pre-flight check
curl https://api.mail.land/v1/transactional/send/check?recipientCount=1 \
  -H "X-API-Key: ml_pet_Ab3Kx9mN2pQ7rT4wL8yD..."
Pre-flight response
{
  "canSend": true,
  "rateLimitStatus": {
    "currentSecond": 2,
    "limitPerSecond": 50,
    "currentHour": 450,
    "limitPerHour": 5000,
    "currentDay": 8200,
    "limitPerDay": 50000
  },
  "monthlyUsed": 125000,
  "monthlyLimit": 500000
}

Si canSend es false, el campo reason explica el motivo (límite de velocidad, cuota, estado de la cuenta, etc.).

Idempotencia

Reintentar un envío tras un tiempo de espera de red no debe entregar el correo dos veces. Con una clave de idempotencia, el reintento reproduce la respuesta original en lugar de volver a enviar.

Envía la clave como cabecera (recomendado):

X-Idempotency-Key: order-12345-shipped-v1

O usa el campo idempotencyKey del cuerpo. Ambos se aceptan, y puedes enviar ambos solo si los valores son idénticos.

Reglas

  • Alcance. X-Idempotency-Key solo se acepta en POST /v1/transactional/send y POST /v1/transactional/send-batch. Ningún otro endpoint la lee.
  • Formato. 1–255 caracteres: letras, dígitos, _ y -. Las claves se comparan exactamente, sin recortes ni cambios de mayúsculas.
  • Ventana. Una clave permanece reproducible durante 24 horas. Después, la misma clave se trata como una petición nueva.
  • Reproducción. Un reintento dentro de la ventana devuelve la respuesta original tal cual (mismos ID, mismo estado) y no envía nada.
  • Unicidad. Las claves son únicas por cuenta. Reutilizar una clave en otro endpoint se rechaza con 409; usa una clave distinta por operación.

ID de correlación

Rastree solicitudes entre sistemas incluyendo un ID de correlación:

X-Correlation-ID: my-app-order-12345

o

X-Request-ID: my-app-order-12345

El ID de correlación aparece en los registros y en el flujo de actividad.

Encabezados de límite de velocidad

Las respuestas exitosas incluyen información de límite de velocidad por IP:

X-RateLimit-Limit: 300
X-RateLimit-Remaining: 295

Cuando el motor de políticas de envío aplica el límite (429), un encabezado Retry-After indica los segundos a esperar:

Retry-After: 30

Una solicitud transaccional puede incluir hasta 5 destinatarios, y cada uno cuenta en sus límites de velocidad. Si eso supera su límite por segundo (por ejemplo, 1 por segundo en un plan de 60 por minuto), la solicitud se acepta cuando no se aceptó ninguna otra en el mismo segundo; de lo contrario recibe 429 con Retry-After: 1 y tiene éxito al reintentarla en el segundo siguiente, dentro de su límite por minuto. Consulte Límites de Velocidad.

Un 429 a nivel de IP también incluye X-RateLimit-Reset como hora UTC en formato ISO-8601.

Respuestas de error

Todos los errores devuelven un cuerpo JSON:

Error response format
{
  "code": "ERROR_CODE",
  "message": "Human-readable description",
  "timestamp": "2026-02-17T10:30:00Z",
  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
}

Códigos de error

EstadoCódigoDescripción
400INVALID_REQUESTCuerpo de solicitud malformado, o scheduledAt con más de 7 días de antelación ("Cannot schedule email more than 7 days in advance")
400VALIDATION_FAILEDSin htmlContent, textContent ni templateId; o un archivo adjunto no pasa la validación (ver los límites de adjuntos arriba). La respuesta incluye un array errors[] con el campo y la regla que falló. (Anteriormente: MISSING_CONTENT: obsoleto desde 2026-04.)
413CONTENT_TOO_LARGE"The message is too large to send: it would exceed the platform's message-size limit. Reduce the HTML, text, attachments or recipients.": El mensaje supera el límite de tamaño de la plataforma. POST /v1/transactional/send y send-batch lo devuelven como 413; no se pone nada en cola. Reduzca el mensaje. En la importación CSV es 400
400transactional_recipient_limit_exceededMás de 5 destinatarios en una credencial transaccional (1.000 en marketing)
400TEMPLATE_NOT_FOUNDtemplateId no nombra ninguna plantilla activa de esta cuenta (también se devuelve para una plantilla eliminada)
400IP_POOL_NOT_FOUNDipPoolId no nombra ningún pool asignado a esta cuenta (actualmente siempre, ya que aún no se puede asignar ningún pool: ver la referencia de campos arriba)
422TEMPLATE_VARIABLE_MISSINGEl contenido tiene un {{placeholder}} sin valor; details.missingVariables lista los nombres. No se encola nada
400IDEMPOTENCY_KEY_INVALIDLa clave está vacía, es inválida, supera 255 caracteres, o la cabecera se envió más de una vez
400IDEMPOTENCY_KEY_CONFLICTSe enviaron a la vez la cabecera y el campo idempotencyKey y difieren
400SEND_FAILEDTodos los destinatarios están suprimidos (mensaje: "All recipients are suppressed")
409IDEMPOTENCY_KEY_REUSEDLa clave ya está en uso para otro endpoint de esta cuenta
401UNAUTHORIZEDX-API-Key ausente, inválida, vencida o revocada
403ACCOUNT_SUSPENDEDLa cuenta ha sido suspendida
403ACCOUNT_DISABLEDLa cuenta ha sido deshabilitada
403ACCOUNT_NOT_ACTIVELa cuenta no está activa
403NO_ACTIVE_SUBSCRIPTIONLa cuenta no tiene una suscripción activa
403SUBSCRIPTION_EXPIREDLa suscripción de la cuenta ha vencido
403IP_NOT_ALLOWEDIP de la solicitud no está en la lista permitida de credenciales
403SENDING_BLOCKEDEl envío está bloqueado para esta cuenta
403ACTION_REQUIREDSe requiere una acción en la cuenta antes de enviar
403TENANT_APPROVAL_REQUIREDLa cuenta está pendiente de revisión antes de poder enviar. Reintentar no ayuda; contacte con soporte
422DOMAIN_NOT_VERIFIEDEl dominio del remitente no está verificado
422DOMAIN_NOT_ALLOWED_FOR_CREDENTIALDominio fuera del alcance de las credenciales
422DOMAIN_NOT_FOUNDDominio del remitente no encontrado
429RATE_LIMIT_EXCEEDEDSe superó el límite de velocidad
429QUOTA_EXCEEDEDCuota mensual agotada
429WARMUP_LIMIT_EXCEEDEDSe alcanzó el límite diario de arranque de envíos de la cuenta; se restablece a las 00:00 UTC
429DAILY_LIMIT_EXCEEDEDSe alcanzó el límite diario de envío
429WARMUP_POOL_BUDGET_EXCEEDED"Sending is temporarily paused for today. It resumes automatically at 00:00 UTC; no action is needed." / "Sending is briefly delayed. Please retry in a few minutes."
429WARMUP_POOL_EMPTY"Sending is temporarily paused. It resumes automatically; no action is needed."
429PLATFORM_DAILY_CAPACITY_REACHED"Platform sending capacity for today is fully booked. This is a temporary platform-wide limit, not your account quota, and it resets at midnight UTC."
429PLATFORM_CAPACITY_PROTECTION"New sending is briefly paused to protect platform capacity. Mail already accepted is not affected. This is transient and clears on its own; retry shortly."
429PROBATION_CAP_REACHED"New accounts can send to N recipients per rolling 24 hours until our team has reviewed the account. Capacity returns as earlier sends age out of the 24-hour window; contact support to request a review."
429PAYMENT_DAILY_LIMIT_EXCEEDEDLímite diario alcanzado en una cuenta con pago vencido
429MARKETING_PAUSEDEnvío de marketing en pausa para esta cuenta
429SENDING_BLOCKEDEl nivel de verificación de la cuenta no permite este envío (rechazos de sandbox/nivel de verificación; un bloqueo de envío estricto devuelve 403 en su lugar, ver arriba)
503SERVICE_UNAVAILABLEServicio de correo temporalmente no disponible

Límite de destinatarios transaccionales

Las credenciales transaccionales imponen un máximo de 5 destinatarios por solicitud de envío. Esto previene el uso indebido de flujos transaccionales para envíos masivos. Si necesita más destinatarios, use una credencial de Marketing con envío por lotes.

Uso de plantillas

En lugar de HTML en línea, haga referencia a una plantilla almacenada:

Send with a template
{
  "from": { "email": "[email protected]" },
  "to": [
    {
      "email": "[email protected]",
      "variables": {
        "firstName": "Jane",
        "resetLink": "https://app.example.com/reset?token=abc123"
      }
    }
  ],
  "templateId": "t8x2m4q9zk7wpr",
  "variables": {
    "companyName": "Acme Inc"
  }
}

El subject proviene de la plantilla. Ante una clave superpuesta, el valor propio del destinatario en to[].variables anula las variables globales: la misma regla que con el contenido en línea. Consulte Plantillas para crear y administrar plantillas.

Próximos pasos

En esta página