Ir al contenido

Códigos de error

Todas las respuestas de error tienen la misma forma. docs_url apunta a la entrada correspondiente de esta página.

{
"error": {
"code": "invalid_template_params",
"message": "Invalid template parameters. email: Invalid email address.",
"docs_url": "https://docs.frontmail.dev/reference/errors/#invalid-template-params",
"details": {
"issues": [{ "param": "email", "code": "invalid_email", "message": "Invalid email address." }]
}
}
}

Las respuestas 429 incluyen además la cabecera Retry-After en segundos. Los SDK lanzan estos errores como subclases tipadas de FrontmailError: consulta Gestión de errores.

HTTPCódigos
400bad_request, invalid_body, invalid_idempotency_key
401unauthorized, invalid_public_key, invalid_private_key
402insufficient_credits
403forbidden, origin_not_allowed, captcha_required, captcha_failed, captcha_not_configured, headless_blocked, recipient_blocked, private_key_required, private_key_in_browser, email_not_verified, organization_suspended, plan_limit_reached, dynamic_recipient_not_allowed, dynamic_recipient_requires_captcha, dynamic_field_not_allowed, link_not_allowed, attachments_not_allowed
404not_found, service_not_found, template_not_found, message_not_found
409conflict, idempotency_conflict
413payload_too_large, attachment_too_large
422invalid_template_params, translation_not_configured, translation_invalid, translation_provider_auth, template_render_failed, invalid_recipient, service_unavailable_for_template, sender_not_configured
429rate_limited, test_send_limit
451recipient_suppressed
500internal_error
502provider_error, translation_provider_error
503service_unavailable

bad_request HTTP 400

Significado: No se pudo interpretar la solicitud (content type incorrecto, JSON ilegible, método no admitido).

Cómo solucionarlo: Envía Content-Type: application/json (o multipart/form-data para /v1/send-form) y un cuerpo válido. Los SDK lo hacen por ti.

invalid_body HTTP 400

Significado: El cuerpo JSON es válido, pero no coincide con el esquema; por ejemplo, falta template_id o un campo tiene un tipo incorrecto. details enumera los campos con errores.

Cómo solucionarlo: Compara el cuerpo con la referencia de la API REST y corrige los campos que aparecen en details.

invalid_idempotency_key HTTP 400

Significado: La cabecera Idempotency-Key está vacía o supera los 255 caracteres.

Cómo solucionarlo: Usa un UUID u otra cadena única de 255 caracteres como máximo. Los SDK generan una por cada envío lógico.

unauthorized HTTP 401

Significado: No se ha enviado ninguna clave o no se ha podido asociar la clave a ninguna organización.

Cómo solucionarlo: Pasa la clave pública como user_id / X-Frontmail-Public-Key, o la clave privada como Authorization: Bearer sk_….

invalid_public_key HTTP 401

Significado: La clave pública no existe o se ha rotado.

Cómo solucionarlo: Copia la clave pública actual desde Seguridad → Clave pública en el panel. Después de una rotación, vuelve a desplegar tu web con la clave nueva.

invalid_private_key HTTP 401

Significado: La clave privada es desconocida o se ha revocado.

Cómo solucionarlo: Crea una clave privada nueva en Seguridad → Claves privadas y actualiza el secreto en el entorno de tu servidor.

insufficient_credits HTTP 402

Significado: Tu organización no tiene créditos y usa el modo reject. En el modo hold (el predeterminado) recibes 202 con status: "held", salvo que la cola de correos retenidos esté llena (details.reason: "hold_queue_full", con held y limit).

Cómo solucionarlo: Compra un paquete de créditos, activa la recarga automática, mejora tu plan o cambia a Retener los correos en Seguridad → Cuando te quedes sin créditos. Con hold_queue_full, libera o descarta además correos retenidos. Consulta Cola de correos retenidos.

forbidden HTTP 403

Significado: La clave o el usuario no tiene permiso para realizar esta acción.

Cómo solucionarlo: Comprueba el rol del miembro o usa una clave privada para los endpoints del lado del servidor.

origin_not_allowed HTTP 403

Significado: La solicitud procede de un Origin que no está en tu lista de orígenes permitidos, o no lleva ni Origin ni Referer (servidor, curl, app nativa) y Permitir apps móviles está desactivado. En ese caso, details.reason es no_origin.

Cómo solucionarlo: Añade el dominio (p. ej. example.com o *.example.com) en Seguridad → Sitios web permitidos. Para el desarrollo local también necesitas localhost. Para apps nativas, activa Permitir apps móviles; en servidores, usa una clave privada en lugar de la pública.

captcha_required HTTP 403

Significado: La plantilla requiere Turnstile y la solicitud no incluía ningún token.

Cómo solucionarlo: Muestra el widget de Turnstile (el FrontmailForm de React/Vue/Svelte lo hace por ti) o pasa turnstile_token. Las llamadas desde el servidor con una clave privada se saltan esta comprobación.

captcha_failed HTTP 403

Significado: Cloudflare ha rechazado el token de Turnstile: no es válido, ha caducado (los tokens duran unos 5 minutos) o ya se había usado. Con details.reason: "hostname_mismatch", el widget se resolvió en un nombre de host distinto del que envió la solicitud.

Cómo solucionarlo: Restablece el widget y deja que el usuario lo resuelva de nuevo. Nunca reutilices un token entre envíos. Con hostname_mismatch, añade todos los nombres de host de tu web (p. ej. example.com y www.example.com) a tu widget de Turnstile en Cloudflare. Consulta Turnstile.

captcha_not_configured HTTP 403

Significado: La plantilla requiere Turnstile, pero tu organización no ha añadido sus propias claves de Turnstile, así que no se pueden verificar las solicitudes de sitios web. En producción también se rechazan las claves de prueba de Cloudflare (1x…, siempre aprueban) (details.reason: "test_secret").

Cómo solucionarlo: Crea un widget de Turnstile gratuito en tu cuenta de Cloudflare y añade su clave de sitio y su clave secreta en Seguridad → Protección contra bots (Turnstile). O desactiva Exigir CAPTCHA (Turnstile) en la plantilla. Consulta Turnstile.

headless_blocked HTTP 403

Significado: blockHeadless está activado y la solicitud parece venir de un navegador headless.

Cómo solucionarlo: Es lo esperado con los bots. Si afecta a un usuario real, desactiva blockHeadless en el init del SDK o Bloquear navegadores headless en Seguridad → Protección contra abusos.

recipient_blocked HTTP 403

Significado: Un destinatario o la variable vigilada coincide con una entrada de tu lista de bloqueo.

Cómo solucionarlo: Quita la entrada de Seguridad → Lista de bloqueo si se añadió por error.

private_key_required HTTP 403

Significado: El endpoint (batch, historial, plantillas) o el ajuste de la organización requiere una clave privada.

Cómo solucionarlo: Llámalo desde tu servidor con Authorization: Bearer sk_…; nunca envíes una clave privada al navegador.

private_key_in_browser HTTP 403

Significado: Se ha enviado una clave privada (Authorization: Bearer sk_… o accessToken) con una cabecera Origin, es decir, desde una página web, donde cualquier visitante puede leerla. Las claves privadas son credenciales del lado del servidor.

Cómo solucionarlo: Usa la clave pública en el navegador y guarda la clave privada en tu servidor. Si de verdad la necesitas en un navegador (herramienta interna), activa Permitir claves privadas en navegadores en Seguridad, y rota la clave si alguna vez ha sido pública.

email_not_verified HTTP 403

Significado: El propietario de la cuenta aún no ha verificado su dirección de correo. Las cuentas sin verificar pueden navegar por el panel, pero no enviar.

Cómo solucionarlo: Haz clic en el enlace del correo de verificación o vuelve a enviarlo desde el aviso del panel.

organization_suspended HTTP 403

Significado: La organización ha sido suspendida, normalmente por abuso o por un pago en disputa.

Cómo solucionarlo: Escribe a support@frontmail.dev desde la dirección del propietario.

plan_limit_reached HTTP 403

Significado: Se ha alcanzado un límite del plan distinto de los créditos: número de plantillas, de usuarios o una función que tu plan no incluye. Los elementos congelados tras bajar de plan también devuelven este error, tanto en los envíos como al usar Reenviar en el panel (details.reason: "template_frozen").

Cómo solucionarlo: Mejora tu plan (las plantillas y los miembros congelados se descongelan automáticamente) o elimina plantillas / revoca invitaciones pendientes para hacer sitio. Consulta Planes y Cambios de plan.

dynamic_recipient_not_allowed HTTP 403

Significado: Un campo de destinatario (Para/CC/CCO) se construye a partir de un parámetro, pero la plantilla no permite destinatarios dinámicos. Esto evita que te usen como relay abierto.

Cómo solucionarlo: Activa Permitir destinatario dinámico en los ajustes de la plantilla (Ajustes → Protección) solo si de verdad lo necesitas y tienes activados Turnstile y los orígenes permitidos.

dynamic_recipient_requires_captcha HTTP 403

Significado: La plantilla toma el destinatario de los parámetros y la solicitud usa la clave pública, pero la plantilla no exige CAPTCHA. Sin ella, cualquiera que copie tu clave pública podría enviar tu plantilla a cualquier dirección.

Cómo solucionarlo: Añade tus claves de Cloudflare Turnstile en Seguridad → Protección contra bots y activa Exigir CAPTCHA en la plantilla, o envía esta plantilla desde tu servidor con una clave privada. Consulta Lista de enlaces permitidos y bloqueo de contenido.

dynamic_field_not_allowed HTTP 403

Significado: Un campo del remitente se construye a partir de parámetros y la solicitud usa la clave pública: la dirección del remitente nunca puede serlo y, en las plantillas con destinatario dinámico, tampoco el nombre del remitente ni el Reply-To. details.fields los enumera.

Cómo solucionarlo: Usa valores fijos en la plantilla (la dirección del visitante solo va en Reply-To cuando el destinatario es fijo, p. ej. en un formulario de contacto), o envía desde tu servidor con una clave privada.

attachments_not_allowed HTTP 403

Significado: La solicitud contiene archivos adjuntos dinámicos, pero la plantilla no los permite, tu plan no incluye adjuntos (Free) o el tipo de archivo está bloqueado (ejecutables, scripts, imágenes de disco, HTML/SVG, archivos de Office con macros). POST /v1/uploads con la clave pública también requiere al menos una plantilla que permita adjuntos.

Cómo solucionarlo: Activa Permitir archivos adjuntos desde el formulario en los ajustes de la plantilla y comprueba que tu plan incluye adjuntos.

not_found HTTP 404

Significado: La URL no corresponde a ningún endpoint.

Cómo solucionarlo: Revisa la ruta y el prefijo /v1.

service_not_found HTTP 404

Significado: El service_id no existe en la organización de esta clave o no hay ningún servicio predeterminado.

Cómo solucionarlo: Copia el ID desde Servicios de correo o marca un servicio como predeterminado.

template_not_found HTTP 404

Significado: El template_id no existe en la organización de esta clave.

Cómo solucionarlo: Copia el ID de la plantilla desde Plantillas. Las claves de otra organización no ven la plantilla.

message_not_found HTTP 404

Significado: El mensaje no existe, es más antiguo que la retención de tu historial o el token de estado no coincide.

Cómo solucionarlo: Usa el message_id y el status_token de la respuesta del envío.

conflict HTTP 409

Significado: El recurso ha cambiado a la vez por otra vía o está en un estado que no permite la operación (p. ej. liberar un mensaje que ya se envió). Con details.reason: "subscription_not_active" se ha rechazado un cambio de plan porque la suscripción tiene una factura pendiente.

Cómo solucionarlo: Recarga el recurso y vuelve a intentarlo. Con subscription_not_active, paga la factura pendiente (Facturación → Plan → Gestionar tarjetas y facturas) y después cambia de plan.

idempotency_conflict HTTP 409

Significado: Se ha usado la misma Idempotency-Key en las últimas 24 horas con un cuerpo de solicitud distinto.

Cómo solucionarlo: Genera una clave nueva para cada envío lógico nuevo. Reutiliza una clave solo al reintentar exactamente la misma solicitud.

payload_too_large HTTP 413

Significado: El cuerpo de la solicitud supera el límite de tamaño.

Cómo solucionarlo: Pasa los archivos grandes a subidas y mantén los parámetros pequeños.

attachment_too_large HTTP 413

Significado: El tamaño total de los adjuntos de una solicitud supera el límite de tu plan.

Cómo solucionarlo: Comprime los archivos, envía un enlace en su lugar o mejora tu plan. Consulta Planes.

invalid_template_params HTTP 422

Significado: Los parámetros no coinciden con el esquema de la plantilla: falta un parámetro obligatorio, un valor tiene un tipo incorrecto o incumple una regla (longitud, rango, patrón, enum). En modo estricto, los parámetros desconocidos también fallan. Los envíos con clave pública a plantillas con destinatario dinámico rechazan además los parámetros HTML y los valores de más de 500 caracteres (details.reason: "content_locked"). details tiene una entrada por campo.

Cómo solucionarlo: Corrige los campos que aparecen en details. Genera tipos con npx frontmail types para detectarlo en tiempo de compilación.

translation_not_configured HTTP 422

Significado: Se pidió una traducción automática, pero la organización no tiene clave de API de ningún proveedor de traducción (o no del solicitado).

Cómo solucionarlo: Añade tu propia clave de OpenAI, Anthropic, Gemini o DeepL en Ajustes → Traducción automática. Consulta Traducciones de plantillas.

translation_invalid HTTP 422

Significado: El proveedor devolvió una traducción que cambia la estructura de la plantilla: falta o cambia una expresión Handlebars, un enlace o la dirección de una imagen. No se guardó nada.

Cómo solucionarlo: Vuelve a intentarlo, prueba otro proveedor o modelo, o traduce la variante a mano.

translation_provider_auth HTTP 422

Significado: El proveedor de traducción rechazó la clave de API de la organización (no válida, caducada o sin acceso al modelo).

Cómo solucionarlo: Cambia la clave en Ajustes → Traducción automática o elige un modelo al que tenga acceso tu cuenta.

template_render_failed HTTP 422

Significado: Handlebars no ha podido renderizar la plantilla con estos parámetros (p. ej. #each sobre algo que no es una lista, un helper desconocido).

Cómo solucionarlo: Abre el editor de la plantilla: el panel de comprobaciones muestra el problema. Prueba con los mismos parámetros en la vista previa.

invalid_recipient HTTP 422

Significado: Después de renderizar, un valor de Para/CC/CCO/Reply-To no es una dirección de correo válida o Para está vacío.

Cómo solucionarlo: Revisa los campos de destinatario de la plantilla y los parámetros que usan.

service_unavailable_for_template HTTP 422

Significado: La plantilla no tiene ningún servicio utilizable: no hay ninguno vinculado, el vinculado se ha eliminado o no puede enviar desde la dirección de remitente configurada.

Cómo solucionarlo: Vincula a la plantilla un servicio que funcione (y, a ser posible, uno de respaldo).

sender_not_configured HTTP 422

Significado: El correo no tiene dirección de remitente: la plantilla no tiene ninguna y el servicio de correo no tiene remitente predeterminado (ni cuenta conectada o nombre de usuario SMTP con forma de correo que usar en su lugar). Lo devuelven los envíos de prueba desde el panel.

Cómo solucionarlo: Define el correo del remitente predeterminado en los ajustes del servicio (Servicios de correo → servicio) o un correo de remitente en los ajustes de la plantilla. Con Amazon SES y la mayoría de las API, la dirección debe ser una identidad verificada.

rate_limited HTTP 429

Significado: Demasiadas solicitudes para tu plan, IP, clave o ajuste limitRate, o la detección de anomalías ha frenado a tu organización. details.scope: "recipient" significa que el destinatario ya ha recibido el máximo diario de correos con destinatario dinámico (Seguridad → Correos por destinatario y día); details.scope: "uploadbytes" significa que se ha agotado el presupuesto diario de subidas con clave pública.

Cómo solucionarlo: Espera los segundos que indique Retry-After (los SDK lo hacen automáticamente). Para envíos masivos usa /v1/send-batch. Consulta Rate limits.

test_send_limit HTTP 429

Significado: Has usado todos los envíos de prueba gratuitos del editor de plantillas de hoy (20 al día).

Cómo solucionarlo: Espera hasta mañana (UTC) o envía un mensaje real a través de la API.

recipient_suppressed HTTP 451

Significado: El destinatario está en tu lista de supresión tras un rebote permanente, una queja o una entrada manual. No se consume ningún crédito.

Cómo solucionarlo: Quita la dirección de la Lista de supresión solo si estás seguro de que es válida y de que la persona quiere recibir tus correos.

internal_error HTTP 500

Significado: Algo ha fallado por nuestra parte.

Cómo solucionarlo: Reintenta con la misma Idempotency-Key (es seguro, no hay duplicados). Si persiste, consulta status.frontmail.dev y contacta con soporte indicando el ID de la solicitud.

provider_error HTTP 502

Significado: El proveedor de correo devolvió un error inesperado durante una operación síncrona, como la prueba de un servicio.

Cómo solucionarlo: Abre el servicio en el panel para ver el mensaje del proveedor. Consulta las guías de proveedores.

translation_provider_error HTTP 502

Significado: El proveedor de traducción falló o no respondió a tiempo. details.status indica su código HTTP.

Cómo solucionarlo: Inténtalo más tarde. La causa más habitual son los límites o la cuota de tu cuenta en el proveedor.

service_unavailable HTTP 503

Significado: Frontmail no está disponible temporalmente o está en mantenimiento.

Cómo solucionarlo: Reintenta con backoff; los SDK lo hacen automáticamente.

Errores del lado del cliente (SDK)

Estos códigos los generan los SDK sin que haya respuesta de la API. El error tiene el estado 0.

network_error SDK

Significado: La solicitud nunca obtuvo una respuesta HTTP: sin conexión, fallo de DNS, bloqueada por una extensión o por una regla de CORS/CSP. Ya se ha reintentado con backoff.

Cómo solucionarlo: Comprueba la conexión y tu Content-Security-Policy (connect-src https://api.frontmail.dev). Pide al usuario que lo intente de nuevo.

timeout SDK

Significado: Un intento tardó más de timeoutMs (15 s por defecto) y todos los reintentos también agotaron el tiempo.

Cómo solucionarlo: Reintentar es seguro: el SDK reutiliza la clave de idempotencia, así que no se envía ningún duplicado.

aborted SDK

Significado: Se ha abortado el AbortSignal pasado en options.signal.

Cómo solucionarlo: No hay nada que corregir: normalmente el componente se desmontó o el usuario salió de la página.

invalid_response SDK

Significado: El servidor respondió con algo que no es una respuesta JSON de Frontmail; normalmente un proxy, un portal cautivo o un apiUrl incorrecto.

Cómo solucionarlo: Revisa la opción apiUrl y cualquier proxy entre el cliente y api.frontmail.dev.

not_initialized SDK

Significado: Se ha llamado a send() antes de init() y sin clave en las opciones de la llamada.

Cómo solucionarlo: Llama a init({ publicKey }) una vez al arrancar o pasa la clave como último argumento.

private_key_in_browser SDK

Significado: Se ha configurado una clave privada (privateKey, o accessToken en @frontmail/emailjs-compat) en código que se ejecuta en un navegador web, o se ha empaquetado @frontmail/node en una página. Cualquier visitante podría copiar la clave y leer tu historial de mensajes.

Cómo solucionarlo: Usa la clave pública (pk_…) en el navegador y guarda la clave privada en tu servidor. Rota la clave privada si alguna vez se desplegó en una página. Solo para una herramienta interna en equipos de confianza, pasa dangerouslyAllowPrivateKeyInBrowser: true.