
# 이메일 템플릿 초안 저장

`PUT /v1/email-templates/{family}/{locale}` · 권한 `email-templates:write` · Startup 플랜 이상

내용을 새 초안 버전으로 저장합니다. 초안을 [게시](publish.md)하기 전까지 게시된 버전은 바뀌지 않습니다.

`If-Match`는 필수이며, 읽은 최신 버전을 담습니다([조회](get.md) 또는 [버전 목록 조회](list-versions.md)의 `ETag`). 버전이 아직 없는 슬롯에는 `If-Match: "0"`을 보내세요. `*`는 빈 슬롯과 일치하지 않습니다. 일치하지 않으면 `412`가 반환됩니다.

저장하기 전에 내용은 실제 발송과 같은 검사를 통과해야 합니다:

- 샘플 데이터로 Liquid 샌드박스에서 렌더링되어야 합니다.
- HTML의 태그가 균형을 이루어야 합니다.
- 패밀리의 코드 또는 링크(목록의 [`requires`](list.md) 참고)가 HTML과 일반 텍스트 본문 모두에 있어야 합니다.
- 링크는 플랫폼 호스트만 가리킬 수 있습니다.
- 크기 제한이 적용됩니다.

슬롯은 게시된 버전을 제외하고 최대 50개의 버전을 보관하며, 더 오래된 초안은 삭제됩니다.

## 경로 매개변수

| 매개변수 | 설명 |
|---|---|
| `family` | 이메일 패밀리(예: `verify_email` 또는 `email_otp`, [이메일 템플릿 목록 조회](list.md) 참고). |
| `locale` | 로케일 태그(예: `en` 또는 `de`). |

## 요청

| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
| `subject` | string | 예 | Liquid, 일반 텍스트, 1~200자, 줄바꿈 없음. |
| `html` | string | 예 | Liquid HTML 조각, 최대 256 KiB. 플랫폼이 레이아웃을 추가합니다. |
| `text_body` | string | 예 | Liquid 일반 텍스트 본문, 최대 64 KiB. |
| `from_name` | string | 아니요 | 발신자 표시 이름, 최대 64자, 줄바꿈 없음. 비어 있거나 없으면 애플리케이션 또는 환경 이름을 사용합니다. |

## 응답

`200 OK`, 새 초안과 해당 버전으로 설정된 `ETag`.

```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
}
```

## 오류

| HTTP | code | 발생 시점 |
|---|---|---|
| 400 | `unknown_field` | 본문에 알 수 없는 필드가 있거나 JSON 객체가 아닙니다. |
| 404 | `not_found` | 패밀리 또는 로케일을 알 수 없거나 버전이 존재하지 않습니다. |
| 403 | `plan_feature_unavailable` | 환경의 플랜에 사용자 지정 이메일 템플릿이 포함되지 않습니다(Startup 이상; 필요한 플랜은 `required_plan`에 표시됩니다). |
| 412 | `precondition_failed` | `If-Match`가 최신 버전이 아닙니다(그사이 누군가 저장했음), 또는 빈 슬롯에 `*`를 보냈습니다. |
| 413 | `payload_too_large` | 본문이 2 MiB를 초과합니다. |
| 428 | `precondition_required` | `If-Match`가 없습니다. |
| 422 | `validation_failed` | 내용이 검증에 실패했습니다. `errors[]`에 각 필드(`/subject`, `/html`, `/text_body`, `/from_name`, `/family`)와 규칙이 표시됩니다. |
| 422 | `template_forbidden_construct` | 템플릿이 샌드박스 밖의 Liquid 구문을 사용합니다(예: `{% include %}`). `errors[]`에 필드가 표시됩니다. |
| 401 | `unauthenticated` | 자격 증명이 없거나 잘못되었거나 취소되었습니다. |
| 403 | `insufficient_permissions` | 자격 증명에 필요한 권한이 없습니다. |
| 429 | `rate_limited` | 요청 속도 제한을 초과했습니다. `Retry-After` 기간 이후에 다시 시도하세요. |
| 503 | `service_unavailable` | 종속 서비스를 일시적으로 사용할 수 없습니다. `Retry-After` 기간 이후에 다시 시도하세요. |

`errors[]`의 검증 규칙에는 `required`, `too_long`, `invalid`, `unbalanced_html`, `missing_code`, `missing_link`, `missing_code_or_link`, `template_render_failed`, `template_too_large`, `family_not_available`이 포함됩니다.

## 예제

```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`
