メール API
メールテンプレートの管理と、候補者へのメール送信・プレビュー・送信ログ。共通規約は API 共通仕様 を参照。
エンドポイント一覧
ベースパスが 3 つに分かれている。
/api/v1/email-templates(システム共通テンプレート)
| メソッド | パス | 概要 | 必要ロール |
|---|---|---|---|
| GET | / |
システムテンプレート一覧取得 | viewer |
/api/v1/projects/{projectId}/email-templates(プロジェクト個別テンプレート)
| メソッド | パス | 概要 | 必要ロール |
|---|---|---|---|
| GET | / |
プロジェクトテンプレート一覧取得 | viewer |
| POST | / |
テンプレート作成 | member |
| PATCH | /{templateId} |
テンプレート更新 | member |
| DELETE | /{templateId} |
テンプレート削除 | member |
| POST | /preview |
テンプレートプレビュー | member |
| POST | /send |
テンプレートでメール送信 | member |
/api/v1/projects/{projectId}/emails(送信とログ)
| メソッド | パス | 概要 | 必要ロール |
|---|---|---|---|
| GET | /logs |
メール送信ログ一覧取得 | viewer |
| POST | /send |
メール一括送信 | member |
| POST | /preview |
メールプレビュー | member |
全エンドポイントで Authorization: Bearer <access_token> が必要。更新系は member 以上。
/email-templates/send と /emails/send は同一の入出力
プレビューと送信は 2 系統に同じものが用意されている。どちらを使っても動作は同じ。
EmailTemplate オブジェクト
| フィールド | 型 | 説明 |
|---|---|---|
id |
integer | テンプレート ID |
projectId |
integer | null | NULL ならシステム共通 |
type |
enum | invitation / reminder / confirmation / cancellation |
name |
string | テンプレート名 |
subject |
string | 件名(プレースホルダ可) |
body |
string | 本文(プレースホルダ可) |
createdAt / updatedAt |
string(date-time) | 作成/更新日時 |
プレースホルダ
件名・本文には差し込み用のプレースホルダを埋め込める。
| プレースホルダ | 差し込まれる値 |
|---|---|
{{candidate_name}} |
候補者の氏名 |
{{project_name}} |
プロジェクト名 |
実際に利用可能なプレースホルダは services/email.ts の実装に従う。差し込み結果は「プレビュー」で確認できる。
システムテンプレート一覧取得
project_id が NULL のテンプレート(全プロジェクト共通)を返す。
URI
レスポンス(200 OK)
data が EmailTemplate の配列(ページネーションなし)。
例外処理
| 説明 | ステータスコード | ステータス名 |
|---|---|---|
| トークンが欠落または無効 | 401 | Unauthorized |
| サーバ内部エラー | 500 | Internal Server Error |
プロジェクトテンプレート一覧取得
URI
レスポンス(200 OK)
data が EmailTemplate の配列。
{
"success": true,
"status": "success",
"statusCode": 200,
"data": [
{
"id": 1,
"projectId": 1,
"type": "invitation",
"name": "初回案内",
"subject": "【{{project_name}}】インタビューのご案内",
"body": "{{candidate_name}} 様\n\nこの度は...",
"createdAt": "2026-01-15T09:00:00Z",
"updatedAt": "2026-01-15T09:00:00Z"
}
],
"path": "/api/v1/projects/1/email-templates",
"method": "GET"
}
例外処理
| 説明 | ステータスコード | ステータス名 |
|---|---|---|
| トークンが欠落または無効 | 401 | Unauthorized |
| プロジェクトが存在しない | 404 | Not Found |
テンプレート作成
URI
リクエストボディ
{
"type": "invitation",
"name": "初回案内",
"subject": "【{{project_name}}】インタビューのご案内",
"body": "{{candidate_name}} 様\n\nこの度はご協力ありがとうございます。"
}
バリデーションルール
| フィールド | ルール |
|---|---|
type |
必須。invitation / reminder / confirmation / cancellation |
name |
必須。1〜255 文字 |
subject |
必須。1〜500 文字 |
body |
必須。1 文字以上 |
レスポンス(201 Created)
data に作成された EmailTemplate。
例外処理
| 説明 | ステータスコード | ステータス名 |
|---|---|---|
| バリデーションエラー | 400 | Bad Request |
| トークンが欠落または無効 | 401 | Unauthorized |
| ロール不足 | 403 | Forbidden |
| プロジェクトが存在しない | 404 | Not Found |
テンプレート更新
URI
パスパラメータ
| フィールド | ルール |
|---|---|
templateId |
必須。正の整数 |
リクエストボディ
作成時の全フィールドが任意になる。
レスポンス(200 OK)
data に更新後の EmailTemplate。
例外処理
| 説明 | ステータスコード | ステータス名 |
|---|---|---|
| バリデーションエラー | 400 | Bad Request |
| トークンが欠落または無効 | 401 | Unauthorized |
| ロール不足 | 403 | Forbidden |
| テンプレートが存在しない | 404 | Not Found |
テンプレート削除
URI
レスポンス(204 No Content)
ボディなし。
例外処理
| 説明 | ステータスコード | ステータス名 |
|---|---|---|
| トークンが欠落または無効 | 401 | Unauthorized |
| ロール不足 | 403 | Forbidden |
| テンプレートが存在しない | 404 | Not Found |
Note
email_logs.template_id には外部キー制約がないため、テンプレートを削除しても送信ログは残る。
メールプレビュー
1 候補者に対する差し込み結果を返す。送信は行わない。
URI
POST /api/v1/projects/{projectId}/email-templates/preview
POST /api/v1/projects/{projectId}/emails/preview
リクエストボディ
バリデーションルール
| フィールド | ルール |
|---|---|
templateId |
必須。正の整数 |
candidateId |
必須。正の整数(project_candidates.id) |
レスポンス(200 OK)
{
"success": true,
"status": "success",
"statusCode": 200,
"data": {
"subject": "【2026年春季ユーザーインタビュー】インタビューのご案内",
"body": "山田太郎 様\n\nこの度はご協力ありがとうございます。",
"to": "yamada@example.com"
},
"path": "/api/v1/projects/1/emails/preview",
"method": "POST"
}
例外処理
| 説明 | ステータスコード | ステータス名 |
|---|---|---|
| バリデーションエラー | 400 | Bad Request |
| トークンが欠落または無効 | 401 | Unauthorized |
| ロール不足 | 403 | Forbidden |
| テンプレートまたは候補者が存在しない | 404 | Not Found |
メール一括送信
URI
POST /api/v1/projects/{projectId}/email-templates/send
POST /api/v1/projects/{projectId}/emails/send
リクエストボディ
バリデーションルール
| フィールド | ルール |
|---|---|
templateId |
必須。正の整数 |
candidateIds |
必須。正の整数の配列。1 件以上 |
レスポンス(200 OK)
送信結果は成功/失敗の件数と、失敗した候補者の明細で返る。
{
"success": true,
"status": "success",
"statusCode": 200,
"data": {
"sent_count": 5,
"failed_count": 1,
"errors": [
{ "candidateId": 56, "email": "invalid@example", "message": "メールアドレスが不正です" }
]
},
"path": "/api/v1/projects/1/emails/send",
"method": "POST"
}
このレスポンスは snake_case
sent_count / failed_count は camelCase ではない。
例外処理
| 説明 | ステータスコード | ステータス名 |
|---|---|---|
バリデーションエラー(candidateIds が空など) |
400 | Bad Request |
| トークンが欠落または無効 | 401 | Unauthorized |
| ロール不足 | 403 | Forbidden |
| テンプレートまたはプロジェクトが存在しない | 404 | Not Found |
送信失敗(EMAIL_SEND_ERROR) |
500 | Internal Server Error |
副作用
- 送信ごとに
email_logsに差し込み後の件名・本文が記録される - 案内メール(
invitation)の送信では調整トークン(scheduling_tokens)が発行され、URL が本文に差し込まれる - 候補者のステータスが遷移する場合がある
PROTOTYPE_MODE
PROTOTYPE_MODE=true(既定)の間は 実際のメール送信が行われない。判定は isEmailEnabled() による。
メール送信ログ一覧取得
URI
クエリパラメータ
| フィールド | 型 | 必須 | 既定値 | ルール |
|---|---|---|---|---|
page |
integer | - | 1 |
正の整数 |
limit |
integer | - | 20 |
1〜100 |
sort |
string | - | - | - |
レスポンス(200 OK)
data が EmailLog の配列、pagination 付き。
{
"success": true,
"status": "success",
"statusCode": 200,
"data": [
{
"id": 1,
"projectCandidateId": 12,
"templateId": 1,
"subject": "インタビューのご案内",
"body": "山田太郎 様...",
"sentAt": "2026-01-15T09:00:00Z",
"status": "sent",
"sentBy": 1,
"candidateName": "山田太郎",
"candidateEmail": "yamada@example.com",
"senderName": "管理者"
}
],
"pagination": { "page": 1, "limit": 20, "totalCount": 42, "totalPages": 3, "hasNext": true, "hasPrevious": false },
"path": "/api/v1/projects/1/emails/logs",
"method": "GET"
}
| フィールド | 型 | 説明 |
|---|---|---|
id |
integer | ログ ID |
projectCandidateId |
integer | 宛先の候補者 |
templateId |
integer | null | 使用テンプレート |
subject / body |
string | 差し込み後の件名・本文 |
sentAt |
string(date-time) | 送信日時 |
status |
enum | sent / delivered / bounced / failed |
sentBy |
integer | 送信操作を行ったユーザー ID |
candidateName / candidateEmail |
string | null | 候補者情報(結合結果) |
senderName |
string | null | 送信者名(結合結果) |
例外処理
| 説明 | ステータスコード | ステータス名 |
|---|---|---|
| トークンが欠落または無効 | 401 | Unauthorized |
| プロジェクトが存在しない | 404 | Not Found |
候補者 1 人分の履歴は 候補者 API の GET /{candidateId}/email-logs を使う。