# HTTP API リファレンス

## 言語とエラーコード

既定の言語は英語です。 API、Web コンソール、および通知レンダラーは、 `internal/i18n/locales/` の埋め込みカタログを共有します。 `internal/i18n/languages.json` は、次の正規コードとネイティブ名を定義します。

| コード    | 言語               |
| --------- | ------------------ |
| `en`      | English            |
| `zh-CN`   | 简体中文           |
| `zh-Hant` | 繁體中文           |
| `ja`      | 日本語             |
| `ko`      | 한국어             |
| `de`      | Deutsch            |
| `fr`      | Français           |
| `es`      | Español            |
| `pt-BR`   | Português (Brasil) |

- 最初のアクセスでは、コンソールはブラウザの `Accept-Language` を使用します。そのセレクターは、選択された正規コードをローカルに保存します。コンソールの言語を切り替えると、保存された通知設定は変更されません。
- アプリ管理リクエストを含む API リクエストは、品質の重みによって `Accept-Language` をネゴシエートします。応答には `Content-Language` および `Vary: Accept-Language` が含まれます。サポートされていない設定はスキップされます。欠落している、無効な、または完全にサポートされていない設定は、 `en` にフォールバックします。重みが 0 の言語はネゴシエーションから除外されます。
- 地域バリアントは、サポートされている言語にマップされます: `fr-CA` → `fr` 、 `es-MX` → `es` 、 `pt` / `pt-PT` → `pt-BR` 。中国語の文字が尊重されます: `zh-Hans` / `zh-CN` / `zh-SG` → `zh-CN` ; `zh-Hant` / `zh-TW` / `zh-HK` / `zh-MO` → `zh-Hant` 。明示的なスクリプトはリージョンよりも優先されます。 plain `zh` は簡体字中国語を使用します。
- `GET /locales/default.json` は、ネゴシエートされたカタログを提供します。 `GET /locales/{canonical-code}.json` はそのカタログを明示的に提供します。不明なカタログ パスは `locale_not_supported` を返します。
- 配信設定 `language` は、リクエストやコンソール言語とは関係なく、アウトポスト によって生成される通知を制御します。正規コードとサポートされている地域のバリアントを受け入れ、正規コードを永続化し、省略または空の場合はデフォルトで `en` になります。サポートされていない明示的な値は 400 `config_language_invalid` を返します。
- プッシュ配信サービスは、ペアリングされたデバイスの言語を使用してネイティブ テスト通知テキストを生成します。 アウトポスト 通知言語はその言語をオーバーライドしません。プッシュ配信サービスとアプリはそれぞれ独自の言語セットをサポートしています。
- 標準のフォルダーの役割と一般的なフォルダー名は、コンソールでローカライズされます。未加工の IMAP パス、カスタム名、および `folder_roles` は API では変更されません。アプリは、独自のカタログを使用してこれらの役割をローカライズします。通知のタイトルには、引き続き元のリーフ フォルダー名が使用されます。ユーザーが入力した名前、電子メールの件名、電子メールの内容、認証コードはそのまま残ります。
- ログと CLI 出力では英語が使用されます。ビジネス エラーでは、安定した英語の `snake_case` コードと名前付きパラメータが保持されます。ストレージはコードとパラメータを保持し、表示層はメッセージを変換します。 API 応答内の日付と数値は、既存の機械可読形式を維持します。コンソールは、選択したロケールを使用してタイムスタンプをフォーマットします。

```json
{
  "error": {
    "code": "unauthorized",
    "message": "Sign in with a valid session or API token."
  }
}
```

配信レコードの `last_error` とフォルダー ステータスの `notice` は、同じ `{code, message, params?}` 形状を使用します (たとえば、 `bark_http_error` と `{"status":"503"}` )。 `code` で分岐します。 `message` を表示します。

各メールボックスは、フォルダーの検出と接続テストのために 1 つの接続を予約します。ウォッチャーの需要は `realtime Folder count + (1 if any scheduled Folders exist, otherwise 0)` で、最大 `connection_limit - 1` 接続 (デフォルトでは 9)。そのメールボックスに対するすべての操作は合わせて、`connection_limit` (2 ～ 100) 内に収まります。 1 個の制限で保存された構成は、 `configuration_invalid` を使用してリカバリ モードで開始します。

## HTTP API

`GET /` は現在のページを提供します。静的アセットと `/healthz` は公開されています。セットアップ前は、他のすべての管理エンドポイントが 403 `setup_required` を返します。 `POST /api/v1/setup` は 1 回のみ利用可能です。 `POST /api/v1/session` はセットアップ後に公開されます。残りのすべての API には、セッション Cookie または `Authorization: Bearer mwk_…` が必要です。

Cookie セッションは、HttpOnly、SameSite=Strict、Path=/、TLS 接続または HTTPS オリジンでのセキュア、7 日間のアイドル タイムアウト、および 30 日間の絶対タイムアウトを使用します。すべての Cookie 認証された変更には、`X-CSRF-Token` と HTTP(S) `Origin` が必要です。そのホストとオプションのポートは要求のホストと正確に一致します。ベアラー要求は CSRF チェックをスキップします。有効な Cookie が存在する場合でも、無効な Authorization ヘッダーは失敗します。

ログインおよびセットアップ コードの失敗はソース アドレス バジェットを共有します。15 分あたり 10 件の失敗があり、その後 429 件の `too_many_attempts`、数秒で `Retry-After` が続きます。ソースは、上記の信頼されたプロキシ範囲を使用する Jin ClientIP です。パスワードの変更にもこの予算が使用されます。明示的なクロスサイトオリジンはログイン時とセットアップ時に拒否されます。すべてのプライベート ネットワーク ピアはプロキシとして信頼されているため、ローカル ネットワーク上のクライアントは X-Forwarded-For を設定し、試行を多くの送信元アドレスに分散させることができます。 アウトポスト リスナーを信頼できないローカル ネットワークに公開しないでください。リバース プロキシ経由でのみ公開してください。

すべての JSON 本文は 64 KiB に制限されています。不明なフィールドまたは末尾の JSON は 400 を返します。パスワードは最小 12 文字、最大 1024 UTF-8 バイトです。ユーザー名とトークン名は 1 ～ 128 バイトです。すべての管理 API タイムスタンプは、配信 `created_at` / `accepted_at` 、トークン `created_at` / `last_used_at` 、フォルダー チェック、ログ時間、診断生成時間を含む、UTC の RFC 3339 文字列です。小数秒では、保存された精度が保持されます。未使用のトークン `last_used_at` は null です。受領なしの配達は `accepted_at` を省略します。カウント、期間、リビジョンは数値のままです。

401 は、無効なセッションまたは API トークン、または `POST /api/v1/session` で拒否された認証情報を示します。間違ったセットアップ コードは 400 `setup_code_invalid` を返します。ウィザードはコードステップに戻り、ユーザー名を保存します。 `PUT /api/v1/admin/password` の現在のパスワードが正しくない場合は、400 `current_password_invalid` が返され、失敗制限にカウントされます。現在のセッションは有効なままです。ログインの失敗と現在のパスワードのエラーはフォーム内に表示され、入力は保持されます。

| 方法               | パス                                  | リクエスト/レスポンス                                                                                                                  |
| ------------------ | ------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| POST               | `/api/v1/setup`                       | `{ "code":"XXXX-XXXX-XXXX", "username":"admin", "password":"…" }` ; 201、クッキー + `{ "csrf_token":"…" }` ;リピート設定：409          |
| POST               | `/api/v1/session`                     | `{ "username":"admin", "password":"…" }` ; 200、クッキー + CSRF トークン                                                               |
| GET                | `/api/v1/session`                     | `{ "authenticated":true, "csrf_token":"…" }` ;ベアラーは CSRF の代わりに `method: "token"` を返します                                  |
| DELETE             | `/api/v1/session`                     | 204;現在の Cookie セッションを取り消します。セッションが必要です                                                                       |
| PUT                | `/api/v1/admin/password`              | `{ "current_password":"…", "password":"…" }` ; 204、他のセッションを取り消す。 Bearer はすべてのセッションを取り消します               |
| POST               | `/api/v1/tokens`                      | `{ "name":"automation" }` ; 201, `{ "id":"…", "name":"…", "created_at":"2026-09-29T01:02:03Z", "last_used_at":null, "token":"mwk_…" }` |
| GET                | `/api/v1/tokens`                      | `{ "tokens":[…] }` ;メタデータのみ                                                                                                     |
| DELETE             | `/api/v1/tokens/:id`                  | 204;トークンを取り消します。削除を繰り返すと 204 が返されます                                                                          |
| GET                | `/api/v1/mailboxes`                   | `{ "mailboxes":[…] }` ;編集されたメールボックスビュー                                                                                  |
| POST               | `/api/v1/mailboxes`                   | 以下のメールボックスフォームを使用して作成します。 201、生成された `id` とリビジョン 1 を含む編集されたビュー                          |
| GET / PUT / DELETE | `/api/v1/mailboxes/:id`               | 読み取り / 現在のリビジョンで更新 / 削除 (204); ID が見つからない場合は 404 `mailbox_not_found` が返されます                           |
| POST               | `/api/v1/mailboxes/test`              | 保存されていないフォームをテストします。 `{ "folders":[…] }` を返し、構成は変更されません。                                            |
| POST               | `/api/v1/mailboxes/:id/test`          | このメールボックスから結合された省略された認証情報を使用してフォームをテストします                                                     |
| GET                | `/api/v1/mailboxes/:id/folders`       | このメールボックスの予算を使用して選択可能なフォルダーを検出します                                                                     |
| GET / PUT          | `/api/v1/mailboxes/:id/subscriptions` | 独自のリビジョンを使用してサブスクリプションを読み取り/保存します。保存が成功すると 202 が返されます                                   |
| GET / PUT          | `/api/v1/settings/delivery`           | グローバル配信設定を独自のリビジョンで読み取り/保存                                                                                    |
| DELETE             | `/api/v1/settings/delivery`           | 保存されたチャネルと認証情報を `{revision}` でクリアします。 204、古いリビジョン 409                                                   |
| POST               | `/api/v1/settings/delivery/test`      | 保存された認証情報を結合して配信フォームをテストします。 204、設定は変更されていません                                                 |
| GET                | `/api/v1/status`                      | フォルダーの状態、チャネル、配信数、および範囲指定された通知                                                                           |
| GET                | `/api/v1/diagnostics`                 | 実行時ログなしで編集された JSON レポートをダウンロードする                                                                             |
| GET                | `/api/v1/logs`                        | 認証された操作ログ。 `after` 、 `level` 、 `mailbox_id` 、 `limit` フィルター (下記を参照)                                             |
| GET                | `/api/v1/deliveries`                  | 最新 50 件の配信レコード (件名、メールボックス/フォルダーの概要を含む)                                                                 |
| POST               | `/api/v1/test-push`                   | 保存された設定を使用してテストを送信します。 204 が受け入れられた場合、設定は変更されません                                            |
| POST               | `/api/v1/deliveries/:id/retry`        | `dead` タスクを再度キューに入れます。 202                                                                                              |

配信レコードには、オプションの `message` オブジェクト: `mailbox_id` 、 `mailbox_label` 、 `folder` 、 `subject` (文字列または null) および `test` (ブール値) が含まれます。名前はエンキュー時のスナップショットであり、メールボックスの名前変更または削除後も残ります。 null の件名は、保持されなかったことを意味します。空の文字列は、メールに件名がないことを意味します。テスト通知には `test=true` があり、メールボックス ソースはありません。 API は、この概要から送信者と本文を省略します。

メールボックスの作成例:

```json
{
  "label": "Work mailbox",
  "host": "imap.example.org",
  "port": 993,
  "username": "user@example.org",
  "password": "…",
  "connection_limit": 10
}
```

アウトポスト では、修復を待っている無効な構成を含め、最大 20 個のメールボックスを保存できます。次の作成では 400 `mailbox_limit_exceeded` が返されます。 ID は `mbx_` とランダムな文字列として生成され、安定しています。 `label` のデフォルトはユーザー名 (電子メール アドレス) で、最大 64 個の Unicode 文字が許可され、改行は拒否されます。 64 文字を超えるユーザー名には、明示的に短いラベルが必要です。更新時にラベルを省略すると、保存された値が保持されます。空のラベルでは、送信されたユーザー名が使用されます。

メールボックス PUT は、同じ形式に `"revision":1` ( GET から読み取られた値) を加えたものを使用し、ホスト、ポート、およびユーザー名を必要とします。ホスト、ポート、ユーザー名が同じ場合、パスワードを省略すると保存済みの値が維持されます。明示的な空のパスワードは拒否されます。省略された `connection_limit` は現在の値を保持します。新しいメールボックスのデフォルトは 10 で、範囲は 2 ～ 100 です。 POST の作成ではリビジョンが省略されるか、0 が送信されます。テスト ルートはリビジョンを必要とせずに同じフィールドを受け入れ、構成を変更しないままにします。保存されていないテストには認証情報が必要です。保存されたメールボックスのテストでは、指定された ID からのみ省略された認証情報がマージされます。

メールボックス GET ビューには、 `id` 、 `label` 、 `revision` 、 `host` 、 `port` 、 `username` 、 `connection_limit` 、 `connections_in_use` 、および`"password":{"configured":true}` 。回復された無効なメールボックスは `configured:false` を報告し、修復できるようにその ID とリビジョンが保持されます。 GET パスワード オブジェクトはビューであり、有効な書き込み値ではありません。

各メールボックスには独立した接続バジェットがあります。リアルタイム フォルダーはそれぞれ 1 つのスロットを消費します。スケジュールされたすべてのフォルダーが一緒に 1 つを消費します。それらの合計の需要は `connection_limit - 1` 内に収まる必要があり、管理用に 1 つのスロットを予約します。サブスクリプション PUT とメールボックス制限の変更では、この式が適用されます。容量を使い果たした場合は、 `connection_budget_exceeded` を返します。保存されていないメールボックスのテストには、一時的な予算が使用されます。さらに、未保存テスト、保存済みメールボックス テスト、およびフォルダー検出エンドポイントは、グローバル同時実行制限 2 を共有します。過剰なリクエストはすぐに 429 `discovery_busy` を返します。完了またはキャンセルにより入場は解除されます。

最初に構成されていないチャネルの配信 PUT の例:

```json
{
  "revision": 0,
  "channel": "bark",
  "preview": "off",
  "retry_count": 0,
  "language": "en",
  "bark": { "endpoint": "https://api.day.app", "key": "…" }
}
```

各更新の前に `/settings/delivery` を読み取り、そのリビジョンを送信します。最初の保存は 0 から 1 に進みます。Pushoverは `"pushover":{"token":"…","user":"…"}` を使用します。 Webhook は `"webhook":{"url":"https://hooks.example.org/mail","secret":"…"}` を使用します。チャンネル、プレビュー、リビジョンが必要です。省略された言語と再試行回数はデフォルトを使用します。 接続先が同じ場合、Webhook URL を含む認証情報を省略すると保存済みの値が維持されます。明示的に空のアクティブな認証情報は拒否されます。 GET は、すべての認証情報を `{"configured":true|false}` に置き換えます。専用のテスト API ではリビジョン値の指定が不要です。

メールボックスの作成と接続の変更は、保存する前に接続/ログインをテストします。ホスト、ポート、ユーザー名、および connection_limit が保存された値と一致し、パスワードが省略されている場合、PUT はラベルのみを変更します。リビジョン チェックと永続性は引き続き適用され、テストとウォッチャーの置換はスキップされます。キューに入れられたタスクは元のラベルを保持します。今後のタスクでは新しいラベルが使用されます。別のメールボックスで既に使用されている ID を作成または更新すると、409 `mailbox_duplicate` が返されます (ホストとユーザー名は大文字と小文字を区別せずに、ポートは正確に比較されます)。同時作成では、その ID を 1 回だけ保存できます。配信 PUT は、テストを送信したりプロバイダーに連絡したりせずに、フィールド、アドレス、選択したペアリング状態、およびリビジョン検証後のすべてのチャネルを保存します。明示的な配信テストでは、保存された設定は変更されません。失敗しても、その後の保存はブロックされません。メールボックス テストは構成ロックの外側で実行され、既存のウォッチャーをアクティブな状態に保ちます。失敗したメールボックス テストでは、以前の構成が保持されます。古い設定リビジョンは 409 `settings_conflict` を返します。メールボックスのテスト中にサブスクリプションを変更すると、そのテストのスナップショットも無効になります。再試行する前にリロードしてください。各メールボックスとグローバル配信設定には独立したリビジョンがあるため、別のメールボックスに対する編集や、メールボックスと配信に対する編集が競合することはありません。

PUT が成功すると、編集されたビューが返され、すぐに適用されます。メールボックス接続を変更すると、保存とオプションのカーソル リセットの前に、独自のウォッチャーが停止します。保存に失敗すると復元されます。他のメールボックスは引き続き実行されます。配信の変更により、実行中の試行のスナップショットが保存されます。以降の試行では新しいものが使用されます。保留中のタスクは、配信が構成されていない間待機します。

`GET /api/v1/mailboxes/:id/subscriptions` は `{"revision":1,"folders":[{"name":"INBOX","check":"realtime"},{"name":"Clients","check":"5m"}]}` を返します。 PUT は、読み取ったリビジョンと完全な `folders` 配列を送信します。空の配列はすべてのサブスクライブを解除します。名前は各メールボックス内で一意であり、INBOX は正規化されており、各名前は最大 4096 UTF-8 バイトです。すべてのフォルダーには `name` および `check` が必要です。チェックは `realtime` 、 `5m` または `15m` です。欠落/無効なチェックがあり、以前の文字列配列形式では 400 が返されます。上記の接続要求の式は、フォルダー数の制限を置き換えます。チェックのみを変更すると、進行状況が保持され、新しいメールが追いつき、既存のイベント ID が保持されます。キャンセルして後で再度サブスクライブすると、新しいベースラインが構築されます。すべてのフィールドは必須です。保存すると、次のリビジョンでは 202 が返されます。古いリビジョンは 409 `subscriptions_conflict` を返します。フォルダーの存在はウォッチャーによって確認されるため、メールボックスがオフラインでもサブスクリプションを削除できます。ストレージ障害が発生すると 503 が返され、現在のサブスクリプションが保持されます。

すべての `/status.folders[]` 項目には、 `mailbox_id` 、 `mailbox_label` 、および `check` が含まれます。 `mode` は `idle` 、 `poll` または `scheduled` です。スケジュールされたフォルダーには、`next_check` (RFC 3339 時間、再試行時間を含む) も含まれます。リアルタイム フォルダーでは `next_check` が省略されます。 JSON またはモデル検証に失敗した保存された構成は構成解除され、 `mailbox:<id>` または `delivery` の下の `/status.notices` に `configuration_invalid` が表示されます。 PUT はそのアイテムを修理します。過剰なサブスクリプションはそのメールボックスのみを一時停止し、 `subscriptions:<id>` の下で `connection_budget_exceeded` を報告します。サブスクリプションを減らすか制限を増やすと、監視が再開されます。復号化に失敗すると起動が停止します。

診断ホワイトリストには、生成時間、ビルド/Go/プラットフォーム情報、キュー数、匿名メールボックスとウォッチャーの状態 ( `check` および `mode` を含む) が含まれます。 `mailboxes[].index` および `watches[].mailbox_index` は、そのレポート内で「メールボックス 1」、「メールボックス 2」などを識別します。各メールボックスのウォッチャーには独自の `index` があります。メールボックスのエントリには、構成/サブスクリプション通知コードが含まれます。 `imap_folder_rejected` のみが `watches[].response_code` を含めることができます。このレポートでは、メールボックス ID、表示名、アドレス、サーバー名、フォルダー名、メッセージ データ、タスク ID、認証情報、およびその他すべてのエラー パラメーターが省略されます。

タスクの状態: `pending` はリクエストを待機しています。`accepted` はチャネルがリクエストを受け入れたことを意味します。`dead` は記録された失敗で試行が終了したことを意味します。

## 実行時ログ

JSON stderr 出力には `docker compose logs -f core` (または `docker logs <container>` ) を使用します。認証されたクライアントは、 `GET /api/v1/logs?after=0&level=info&mailbox_id=mbx_example&limit=200` を通じて同じ操作イベントをクエリできます。セッション Cookie または API トークンが必要です。

メモリ内リングには最新の **2000** 情報/警告/エラー エントリが保持され、再起動時にクリアされます。 `after` は排他的な非負のシーケンスです。 `level` はそのレベル以上を選択します (`info` デフォルト)。省略 `mailbox_id` には、すべてのメールボックスとグローバル操作が含まれます。 `limit` のデフォルトは 200、範囲は 1 ～ 500 です。無効なパラメータは 400 `logs_request_invalid` を返します。

```json
{
  "entries": [
    {
      "seq": 42,
      "time": "2026-09-29T00:00:00Z",
      "level": "info",
      "message": "Folder connection established",
      "attrs": {
        "mailbox_id": "mbx_example",
        "folder": "INBOX",
        "mode": "idle",
        "check": "realtime"
      }
    }
  ],
  "next": 42,
  "cleared_through": 0
}
```

`next` を次のリクエストの `after` として使用します。ページ制限に達すると、それが最後に返されたシーケンスになります。スナップショットをスキャンした後、フィルターが何も一致しない場合も含めて、スナップショットの末尾に進みます。排除されたエントリはスキップされます。 restart が `next < after` を返した場合は、カーソルを 0 にリセットします。エントリはシーケンス順に並べられます。

ログには、メールボックスとフォルダーのライフサイクル、ベースライン/エンキュー数、配信試行、構成変更、管理者のアクションが含まれます。グローバル イベントでは、空の `mailbox_id` を使用します。次のスカラー属性のみが公開されます: `mailbox_id` 、 `folder` 、 `mode` 、 `check` 、 `code` 、 `event_id` 、 `channel` 、 `attempt` 、 `duration_ms` 、`http_status`、`next_attempt`、`backoff_seconds`、`count`、`revision`、`token_id`、`version`、`listen`。 `http_status=0` は、HTTP 応答が受信されなかったことを意味します。 `token_id` はパブリック レコード識別子であり、トークン値ではありません。パスワード、トークン/セッション値、セットアップ コード、送信者、件名、本文、認証情報 URL は省略されています。 setup-code 行はコンソールにのみ送信されます。ログにはフォルダー名が含まれます。匿名診断エクスポートでは、ログと名前は除外されます。

フォルダー検出および管理者メールボックスのテストは、元のパス配列として `folders` とオプションの `folder_roles` 、パスから `inbox` 、 `drafts` 、 `sent` 、 `trash` 、 `junk` 、 `archive` 、 `all` または `flagged` 。ロールは IMAP SPECIAL-USE 属性から取得されます。 INBOX は、プロトコルで定義された名前によって認識されます。ロールが存在しない場合、Web UI は一般的な英語のフォルダー名の大文字と小文字を区別しない完全一致に戻ります。翻訳された名前は、括弧内に元のパスを示します。他の名前は変更されません。表示翻訳によってサブスクリプション パスが変更されることはありません。通知のタイトルには、スラッシュで区切られたフォルダー パスの最後のセグメントが使用されます。構造化フォルダーフィールドには完全なパスが保持されます。アプリのメールボックス テストは引き続き接続ステータスのみを返します。

`GET /api/v1/session` には、認証された Cookie セッションの `username` が含まれます。トークン認証は既存の応答を保持します。

### アプリ受信デバイス

配信設定には `native_pairing_id` が含まれます。 `native` チャネルの場合は、 `/api/v1/native/devices` からアクティブなペアを 1 つ選択します。テスト通知と新しいイベントは両方とも、そのペアのみを対象としています。更新時にフィールドを省略すると、保存された選択内容が保持されます。空の選択は `native_target_required` を返します。使用できない選択は `native_target_unavailable` を返します。選択したネイティブ ペアリングを取り消すと、channel とnative_pairing_id がアトミックにクリアされ、リビジョンが 1 回増分されます。他のターゲットとチャネルはそのまま残ります。既存の配信は、ターゲットと取り消し結果を保持します。タイミング、同時実行性、および再起動の動作については、[ペアリング同期](https://github.com/mingzaily/mailwake/blob/main/docs/native-push.md#pairing-revocation-synchronization) を参照してください。

アプリの承認は、管理とプッシュ権限を個別に管理します。どちらかの権限を取り消すと、もう一方の権限は変更されないままになります。

### ネイティブテストの通知

アウトポスト は、署名された `POST /v1/pairing-tests` と `{pairing_id,test_id}` (明示的なテストによる新しい UUID) を使用します。プッシュ配信サービスは、固定コンテンツを生成し、Native Push の利用権を必要とせずに、それを APNs に同期的に送信します。フォーム テスト、アプリの保存設定テスト、および `/api/v1/test-push` は、これと同じパスを使用します。テストはメールの送信トレイとは別のものであり、自動配信の再試行はありません。

アウトポスト は、プッシュ配信サービスが `accepted` を報告する場合にのみ HTTP 204 を返します。 APNs 拒否、不明な結果、および `sending` はローカライズされたエラーを返します。プッシュ配信サービスのレート制限により、アウトポストは `Retry-After` で HTTP 429 を返します。安全なログには、応答本文、トークン、通知内容を除く、プッシュ配信サービスの HTTP ステータスと安定したエラー コードが含まれます。 アウトポスト とアプリは、プッシュ配信サービスの既存のデバイスごとの分単位およびサブジェクトごとの日次割り当てと `(device_id,test_id)` 冪等性を共有します。ユーザーが別のテストを開始すると、新しいテスト ID が作成されます。

正式メールは `/v1/push` を使用し、イベント ID やクライアント テスト フラグに関係なく、資格ゲートされたままになります。この アウトポスト バージョンの前に、プッシュ配信サービスのペアリング テスト ルートをデプロイします。 アウトポスト は有料メール配信には戻りません。

## 配信履歴と実行時ログをクリアする

- `DELETE /api/v1/deliveries` は `200 {"deleted":N}` を返します。関連するネイティブ レコードを含む、完了したレコード ( `accepted` / `dead` ) を履歴全体にわたって永久に削除します。保留中の配信/再試行、まだ保留中/キューに入っている/送信中またはステータス チェックのスケジュールが設定されているネイティブ レコード、および保留中のネイティブ アクティビティの更新は保持されます。失敗をクリアすると、手動再試行オプションが削除されます。メールボックスのチェックポイントと構成は保持されます。
- `DELETE /api/v1/logs` は `200 {"cleared_through":N}` を返し、すべてのレベルとメールボックスの共有メモリ内リングをクリアします。シーケンス番号は増加し続けます。 stderr/container ログとエクスポートされたファイルは残ります。 `GET /api/v1/logs` は `cleared_through` も返します。クライアントは、返されたエントリを追加する前に、`seq <= cleared_through` を持つキャッシュされたエントリを破棄します。
- どちらのエンドポイントにも、管理者セッション + Origin/CSRF、または管理者 API トークンが必要です。アプリデバイスの認証情報では、これらの操作は許可されません。繰り返しクリアしても安全です。操作後に新しい作業とログ エントリが表示される場合があります。

## アプリのメッセージ内容

`POST /api/v1/app/content` は端末に紐づく制御用認証情報と所有者が許可した `content` スコープを使用します。`{ "reference": <message_ref> }` を送信すると、`text`、`truncated`、任意のサニタイズ済み `html`、`has_remote_images` が返ります。本文の閲覧は無料です。

本文が読める状態になった後、同じ認証情報、スコープ、参照で `POST /api/v1/app/content/read` を呼び出すと、メールボックス内のメールが既読になります。成功時は `204 No Content` と `Cache-Control: no-store` が返ります。本文の取得だけでは既読状態は変わりません。両操作で現在の権限、購読フォルダ、メールボックスの識別情報、メール参照を検証します。

## 認証情報と接続先

テストと保存で `host`、`port`、`username` を変更する場合、保存済みのメールボックスパスワードを再入力します。省略すると 400 `mailbox_password_required_for_change` が返ります。保存済みの Bark アドレスを変更する場合、別のチャネルが選択されていてもデバイスキーの再入力が必要です。省略すると 400 `bark_key_required_for_change` が返ります。ポート、パス、末尾のスラッシュもアドレスに含みます。拒否されたリクエストは接続を開始せず、設定と revision を維持します。

## App リクエストの診断

診断エクスポートは `schema_version: 2` を使用し、共有の 2000 件のメモリログから最近の完了済み App リクエストを最大 300 件、`app_requests` に含めます。時刻、サーバー生成の `request_id`（`X-Request-ID` にも返却）、ルートテンプレート、メソッド、HTTP ステータス、所要時間、段階、結果、安全なエラーコードを記録します。リソース ID、クエリ文字列、認証情報、署名付き参照、本文、完全な実行ログは含みません。再起動やログ消去で記録が消え、他のログによって古い記録が押し出される場合もあります。ダウンロード前にレポートを更新します。サーバーに届かないリクエストには App の診断が必要です。

`POST /api/v1/app/content/read` は UID とフラグを確認し、既読なら成功、未読なら既読化して再確認します。STORE OK だけでは存在を証明できません。UID の不在、UIDVALIDITY の変更、明示的なフォルダー不在は 404、プロバイダーや権限の失敗は 503、タイムアウトは 504 です。リモート参照が失効してもキャッシュ本文は読めます。
