
# Guardar un borrador de plantilla de correo

`PUT /v1/email-templates/{family}/{locale}` · permiso `email-templates:write` · plan Startup y superiores

Guarda el contenido como nueva versión borrador. La versión publicada no cambia hasta que [publique](publish.md) el borrador.

`If-Match` es obligatorio y contiene la versión más reciente que leyó (el `ETag` de [obtener](get.md) o [listar versiones](list-versions.md)). Para un slot que todavía no tiene versiones, envíe `If-Match: "0"`; `*` no coincide con un slot vacío. Si no coincide, se devuelve `412`.

Antes de guardar, el contenido pasa las mismas comprobaciones que un envío real:

- Debe poder renderizarse en el sandbox de Liquid con datos de ejemplo.
- El HTML debe estar equilibrado.
- El código o el enlace de la familia (consulte `requires` en el [listado](list.md)) debe aparecer tanto en el HTML como en el cuerpo de texto plano.
- Los enlaces solo pueden apuntar a hosts de la plataforma.
- Se aplican límites de tamaño.

Un slot conserva como máximo 50 versiones además de la publicada; los borradores más antiguos se eliminan.

## Parámetros de ruta

| Parámetro | Descripción |
|---|---|
| `family` | Familia de correo, por ejemplo `verify_email` o `email_otp` (consulte [Listar plantillas de correo](list.md)). |
| `locale` | Etiqueta de idioma, por ejemplo `en` o `de`. |

## Solicitud

| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
| `subject` | string | sí | Liquid, texto plano, de 1 a 200 caracteres, sin saltos de línea. |
| `html` | string | sí | Fragmento HTML de Liquid, como máximo 256 KiB. La plataforma añade el diseño. |
| `text_body` | string | sí | Cuerpo de texto plano de Liquid, como máximo 64 KiB. |
| `from_name` | string | no | Nombre visible del remitente, como máximo 64 caracteres, sin saltos de línea. Si está vacío o ausente, se usa el nombre de la aplicación o del entorno. |

## Respuesta

`200 OK` con el nuevo borrador y `ETag` definido con su versión.

```json
{
  "family": "verify_email",
  "locale": "en",
  "version": 3,
  "status": "draft",
  "subject": "Confirm your email for {{ application.name }}",
  "html": "<p>Hi,</p><p><a href=\"{{ link }}\">Confirm your email</a></p>",
  "text_body": "Confirm your email: {{ link }}",
  "from_name": null,
  "source_version": null,
  "created_at": "2026-10-19T09:00:00Z",
  "published_at": null,
  "latest_version": 3,
  "published_version": 2
}
```

## Errores

| HTTP | code | Cuándo |
|---|---|---|
| 400 | `unknown_field` | El cuerpo tiene un campo desconocido o no es un objeto JSON. |
| 404 | `not_found` | La familia o el idioma no existe, o la versión no existe. |
| 403 | `plan_feature_unavailable` | El plan del entorno no incluye plantillas de correo personalizadas (Startup y superiores; `required_plan` indica cuál). |
| 412 | `precondition_failed` | `If-Match` no es la versión más reciente (alguien guardó entre tanto), o es `*` en un slot vacío. |
| 413 | `payload_too_large` | El cuerpo supera los 2 MiB. |
| 428 | `precondition_required` | Falta `If-Match`. |
| 422 | `validation_failed` | El contenido no supera la validación; `errors[]` indica cada campo (`/subject`, `/html`, `/text_body`, `/from_name`, `/family`) y su regla. |
| 422 | `template_forbidden_construct` | La plantilla usa una construcción de Liquid fuera del sandbox (por ejemplo, `{% include %}`); `errors[]` indica el campo. |
| 401 | `unauthenticated` | La credencial falta, no es válida o ha sido revocada. |
| 403 | `insufficient_permissions` | La credencial no tiene el permiso requerido. |
| 429 | `rate_limited` | Se superó el límite de frecuencia; reintente después del periodo indicado en `Retry-After`. |
| 503 | `service_unavailable` | Una dependencia no está disponible temporalmente; reintente después del periodo indicado en `Retry-After`. |

Las reglas de validación en `errors[]` incluyen `required`, `too_long`, `invalid`, `unbalanced_html`, `missing_code`, `missing_link`, `missing_code_or_link`, `template_render_failed`, `template_too_large` y `family_not_available`.

## Ejemplo

```bash
curl -X PUT https://api.eu.keenauth.com/v1/email-templates/verify_email/en \
  -H "Authorization: Bearer $KEENAUTH_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -H 'If-Match: "2"' \
  -d '{"subject":"Confirm your email for {{ application.name }}","html":"<p><a href=\"{{ link }}\">Confirm your email</a></p>","text_body":"Confirm your email: {{ link }}"}'
```

CLI: `keenauth email-templates put verify_email en --if-match '"2"' --data @template.json`
