SMTP y API HTTP
Cree claves de API y envíe correo por SMTP o mediante la API HTTP, con campos de solicitud, respuestas, códigos de error y ejemplos.
Puede enviar a través de un flujo de dos maneras: por SMTP desde cualquier aplicación o biblioteca de correo, o con la API HTTP. Ambas usan las mismas claves de API, cuotas, lista de supresión y eventos. Antes de empezar, verifique un dominio de envío y cree una clave de API.
Claves de API
- Abra el flujo y vaya a la pestaña Clave de API.
- Haga clic en Crear clave, introduzca un Nombre como
productiony confirme. - Copie la clave que se muestra y haga clic en Ya lo guardé. La clave solo se muestra esta vez. Después, la lista solo muestra su Prefijo, como
ytm_aB3dE6gH….
Cada clave empieza por ytm_. Un flujo puede tener hasta 20 claves activas. La lista muestra cuándo se usó cada clave por última vez. Para retirar una clave, haga clic en Revocar: los programas que la usan dejan de funcionar de inmediato. Una clave solo funciona para el flujo al que pertenece.
Guarde las claves de API en su servidor. No las incluya en código de navegador, aplicaciones móviles ni repositorios públicos. Cualquiera que tenga la clave puede enviar correo en nombre de su dominio.
SMTP
La pestaña Resumen del flujo muestra la configuración SMTP:
| Configuración | Valor |
|---|---|
| Host | El host SMTP que se muestra para el flujo, normalmente smtp.tm.yeapcloud.net |
| Puerto | 587 con STARTTLS, o 465 con TLS |
| Nombre de usuario | apikey (literalmente esta palabra) |
| Contraseña | Una clave de API de este flujo |
| Encabezado de categoría | X-Yeap-Category |
Use el host que se muestra en la consola. Algunos flujos envían a través de otro grupo de servidores con su propio nombre de host.
Cómo funciona el envío por SMTP:
- El encabezado From debe usar una dirección de un dominio verificado para el flujo.
- Cada destinatario (To, Cc y Bcc) recibe su propia copia con su propio ID de mensaje, y cuenta como un correo.
- Para establecer una categoría, añada un encabezado como
X-Yeap-Category: password-reset. Se elimina antes de la entrega. Los caracteres permitidos son letras, dígitos,.,_y-, con un máximo de 64 caracteres. - Un mensaje puede ocupar hasta 25 MB, con un máximo de 1,000 destinatarios por transacción SMTP.
- Cada mensaje recibe un encabezado
X-TM-ID(el ID de mensaje que aparece en el registro de envíos y en los eventos) y un encabezadoFeedback-IDpara los bucles de retroalimentación de los proveedores de correo. - En los flujos de marketing, cualquier encabezado
List-Unsubscribeque usted establezca se sustituye por los encabezados de baja con un clic de Yeap Cloud. SMTP no modifica el cuerpo del mensaje, así que añada usted mismo su dirección postal y un enlace de baja al contenido. Consulte Supresiones y bajas. - El seguimiento de aperturas y clics solo está disponible a través de la API HTTP.
Ejemplo con la biblioteca estándar de Python:
import smtplib
from email.message import EmailMessage
msg = EmailMessage()
msg["From"] = "Example <noreply@example.com>"
msg["To"] = "user@example.org"
msg["Subject"] = "Reset your password"
msg["X-Yeap-Category"] = "password-reset"
msg.set_content("Click the link to reset your password: https://example.com/reset/abc")
with smtplib.SMTP("smtp.tm.yeapcloud.net", 587) as s:
s.starttls()
s.login("apikey", "ytm_your_api_key")
s.send_message(msg)Códigos de respuesta SMTP
Cuando el servidor rechaza un mensaje, el código de respuesta indica el motivo. Los códigos 4xx son temporales: vuelva a intentarlo más tarde. Los códigos 5xx son permanentes: corrija la causa antes de volver a enviar.
| Código | Mensaje | Causa y qué hacer |
|---|---|---|
535 5.7.8 | Authentication failed / API key revoked | Nombre de usuario o clave incorrectos, o la clave fue revocada. El nombre de usuario debe ser apikey. |
454 4.7.0 | Temporary authentication failure / Too many authentication failures | El servidor aún no está listo, o su IP ha fallado demasiadas veces al iniciar sesión. Espere y vuelva a intentarlo. |
451 4.7.1 | Stream is paused | El flujo se pausó automáticamente. Consulte el motivo en el flujo y póngase en contacto con soporte. |
550 5.7.1 | Stream is banned / suspended / terminated | El flujo fue bloqueado, su suscripción está suspendida o fue liberado. |
550 5.7.1 | From domain … is not verified for this stream | Verifique el dominio o compruebe que está compartido con este flujo o vinculado a él. |
550 5.7.1 | Sandbox mode: recipient is not a verified address | El flujo está en el sandbox. Verifique el destinatario o envíe a su propio dominio. |
550 5.7.1 | Recipient is on a suppression list / All recipients are suppressed | La dirección está suprimida (rebote, queja, baja o manual). |
452 4.7.1 | Hourly sending limit reached, try later | Se ha agotado el límite por hora (o el límite diario del sandbox). Vuelva a intentarlo en la hora siguiente. |
550 5.7.1 | Monthly sending quota exhausted | Se ha agotado el volumen mensual y no se permite excedente: en el plan gratuito, o cuando se alcanza el límite de gasto de excedente. |
452 4.5.3 | Too many recipients | Más de 1,000 destinatarios en una transacción. |
553 5.1.3 | Invalid recipient address | La dirección del destinatario tiene un formato incorrecto. |
554 5.7.1 | Message rejected: virus detected | El mensaje contenía un virus. El flujo se bloquea de inmediato y se revocan todas sus claves de API. |
554 5.7.1 | Message rejected as spam | El contenido se calificó como spam. Los rechazos repetidos pausan el flujo. |
451 4.3.0 | Content scanner unavailable, try again later | El correo de marketing solo se acepta después del análisis. Vuelva a intentarlo más tarde. Las demás respuestas 451 4.3.0 son errores temporales del servidor: vuelva a intentarlo. |
554 5.6.0 | Malformed message headers | Corrija los encabezados del mensaje. |
API HTTP
| Elemento | Valor |
|---|---|
| URL base | https://api.tm.yeapcloud.net/v1 (también disponible como https://api.yeap.cloud/api/tm/v1) |
| Autenticación | Authorization: Bearer ytm_your_api_key |
| Formato | Cuerpos de solicitud y respuesta en JSON, Content-Type: application/json |
| Límite de frecuencia | 600 solicitudes por minuto por clave de API |
Los errores devuelven un código de estado HTTP y un cuerpo como este:
{"error": {"code": "invalid_request", "message": "subject is required (single line)"}}| Estado HTTP | error.code | Significado |
|---|---|---|
| 400 | invalid_json, invalid_request | El cuerpo no es un objeto JSON, o un lote está vacío o es demasiado grande |
| 401 | unauthorized | Clave de API ausente, con formato incorrecto, revocada o desconocida |
| 403 | stream_paused, stream_banned, stream_suspended | El flujo no puede enviar |
| 403 | from_domain_unverified, invalid_from | El dominio From no está verificado para este flujo, o la dirección From no es válida |
| 403 | postal_address_required | Flujo de marketing sin dirección postal. Rellénela en la pestaña Ajustes de marketing. |
| 403 | virus | Un adjunto contenía un virus. El flujo queda bloqueado. |
| 404 | not_found | El mensaje no existe en este flujo |
| 422 | invalid_request | Falta un campo o no es válido. message indica cuál. |
| 429 | rate_limited | Más de 600 solicitudes en un minuto. Reduzca el ritmo y vuelva a intentarlo. |
| 503 | no_node | No hay ningún servidor de envío disponible en este momento. Vuelva a intentarlo más tarde. |
Enviar un mensaje: POST /v1/send
| Campo | Tipo | Descripción |
|---|---|---|
from | string | Obligatorio. Una dirección, como noreply@example.com o Example <noreply@example.com>. El dominio debe estar verificado para el flujo. |
to | string o array | Obligatorio. Una dirección, una cadena separada por comas o un array. Los elementos del array también pueden ser objetos {"email": "...", "name": "..."}. |
cc, bcc | string o array | Opcional. Mismo formato que to. Las direcciones Bcc no aparecen en los encabezados. |
reply_to | string o array | Opcional. Dirección Reply-To. |
subject | string | Obligatorio. Una línea, de hasta 998 caracteres. |
html, text | string | Se requiere al menos uno. Envíe ambos para obtener los mejores resultados. |
headers | object | Encabezados adicionales opcionales, como {"X-Campaign": "spring"}. Los encabezados estándar (From, To, Subject, Reply-To, Message-ID, List-Unsubscribe, etc.) no se pueden establecer aquí. |
attachments | array | Opcional. Elementos {"filename": "invoice.pdf", "content_base64": "...", "type": "application/pdf"}. Como máximo 10 archivos y 10 MB en total. |
category | string | Opcional. Letras, dígitos, ., _, -, con un máximo de 64 caracteres. |
tags | array de strings | Opcional. Hasta 10 etiquetas para su propio filtrado, cada una de hasta 64 caracteres. |
track_opens, track_clicks | boolean | Opcional. Activa o desactiva para este mensaje el seguimiento de aperturas (un píxel de seguimiento) y el seguimiento de clics (enlaces reescritos a través de t.tm.yeapcloud.net). Solo HTML. Si se omiten, se aplica el valor predeterminado del flujo. Ambos están desactivados de forma predeterminada. |
variables | object | Opcional. Sustituye los marcadores {{name}} en subject, html y text. Los valores se escapan como HTML en html. Los marcadores desconocidos se dejan tal cual. |
Una sola llamada puede tener hasta 50 destinatarios en total entre to, cc y bcc. Cada destinatario recibe una copia independiente con su propio ID de mensaje. Para enviar contenido diferente a muchas personas, use el endpoint de lotes.
Ejemplo:
curl -X POST https://api.tm.yeapcloud.net/v1/send \
-H "Authorization: Bearer ytm_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"from": "Example <noreply@example.com>",
"to": ["alice@example.org"],
"subject": "Your order {{order_id}} has shipped",
"html": "<p>Hi {{name}}, your order is on its way.</p>",
"text": "Hi {{name}}, your order is on its way.",
"category": "shipping",
"tags": ["order"],
"variables": {"name": "Alice", "order_id": "A-1024"}
}'Respuesta:
{
"message_ids": ["01JAQ3Z9V8K4M2N6P7R5S1T0XW"],
"rejected": []
}message_ids enumera las copias aceptadas. rejected enumera los destinatarios a los que no se envió, cada uno con un rcpt y un reason. Una llamada puede tener éxito aunque se rechacen algunos destinatarios o incluso todos, así que compruebe siempre rejected:
reason | Significado |
|---|---|
invalid_recipient | La dirección no es válida |
suppressed:<scope>:<reason> | La dirección está en una lista de supresión, por ejemplo suppressed:global:hard_bounce |
sandbox_recipient_not_verified | El flujo está en el sandbox y el destinatario no está permitido |
rate_limited_hourly, rate_limited_sandbox_or_hourly | Se ha agotado el límite por hora (o el límite diario del sandbox) |
monthly_quota_exceeded | Se ha agotado el volumen mensual y no se permite excedente |
enqueue_failed | No se pudo poner el mensaje en cola. No se descuenta de su cuota. Vuelva a intentarlo. |
Ejemplo en Python con requests:
import requests
resp = requests.post(
"https://api.tm.yeapcloud.net/v1/send",
headers={"Authorization": "Bearer ytm_your_api_key"},
json={
"from": "Example <noreply@example.com>",
"to": "alice@example.org",
"subject": "Welcome",
"html": "<p>Welcome aboard!</p>",
"text": "Welcome aboard!",
"category": "welcome",
},
timeout=30,
)
resp.raise_for_status()
result = resp.json()
print(result["message_ids"], result["rejected"])Envío masivo: POST /v1/batch
Envíe hasta 500 mensajes en una sola solicitud. Cada elemento tiene los mismos campos que /v1/send.
{
"messages": [
{"from": "noreply@example.com", "to": "a@example.org", "subject": "Hi A", "text": "Hello A"},
{"from": "noreply@example.com", "to": "b@example.org", "subject": "Hi B", "text": "Hello B"}
]
}La respuesta contiene un resultado por mensaje, en el mismo orden:
{
"results": [
{"index": 0, "message_ids": ["01JAQ..."], "rejected": []},
{"index": 1, "error": {"code": "invalid_request", "message": "subject is required (single line)"}, "rejected": []}
]
}Consultar mensajes: GET /v1/messages
| Parámetro de consulta | Descripción |
|---|---|
status | Filtrar por estado: queued, delivered, deferred, bounced, complained, rejected o failed |
to | Filtrar por dirección de destinatario |
since | Solo mensajes creados en esta fecha y hora o después, como 2026-10-01T00:00:00Z |
limit | Resultados por página, 1–100 (50 de forma predeterminada) |
before | Para la página siguiente, pase el valor next_before de la respuesta anterior |
La respuesta es {"data": [...], "next_before": "..."}. next_before es null en la última página. Los mensajes se conservan durante 30 días.
GET /v1/messages/{id} devuelve un mensaje y sus eventos:
{
"id": "01JAQ3Z9V8K4M2N6P7R5S1T0XW",
"stream_id": 12,
"source": "api",
"to": "alice@example.org",
"from": "Example <noreply@example.com>",
"subject": "Welcome",
"category": "welcome",
"tags": [],
"status": "delivered",
"smtp_code": 250,
"smtp_response": "250 2.0.0 OK",
"opened_at": null,
"clicked_at": null,
"unsubscribed_at": null,
"created_at": "2026-10-20 10:15:02",
"updated_at": "2026-10-20 10:15:04",
"events": [
{"type": "delivered", "at": "2026-10-20 10:15:04", "rcpt": "alice@example.org", "smtp_code": 250, "mx": "mx.example.org"}
]
}Supresiones: /v1/suppressions
Estos endpoints gestionan la lista de supresión del flujo de la clave de API.
| Solicitud | Cuerpo o consulta | Respuesta |
|---|---|---|
GET /v1/suppressions | Consulta email (opcional), limit (1–500, 100 de forma predeterminada), offset | {"data": [{"id", "scope", "email", "reason", "category", "note", "created_at"}]} |
POST /v1/suppressions | {"email": "..."} o {"emails": [...]} (hasta 1,000), category opcional | {"added": 2, "invalid": []} |
DELETE /v1/suppressions | {"email": "..."}, category opcional (en el cuerpo o en la consulta) | {"removed": 1} |
Las direcciones añadidas a través de la API tienen el motivo manual. Las supresiones de toda la cuenta y las globales no se pueden modificar con la API. Consulte Supresiones y bajas.