{"openapi":"3.0.3","info":{"title":"ポストメッシュ API","description":"ポストメッシュは、複数のSNSプラットフォームに同時投稿できるサービスです。\nこの API を使って、SNS アカウントの連携管理、メディアのアップロード、投稿の作成・管理を行えます。\n\n## 対応プラットフォーム\n\nYouTube / TikTok / Instagram / Threads / X (Twitter) / Facebook\n\n## 認証\n\nすべての API エンドポイントには、`Authorization` ヘッダーに Bearer トークン（API キー）が必要です。\n\n```\nAuthorization: Bearer YOUR_API_KEY\n```\n\nAPI キーはポストメッシュダッシュボードから発行できます。\n\n## 共通仕様\n\n- リクエスト・レスポンスのフィールド名はすべて **snake_case** です。\n- タイムスタンプはすべて **ISO 8601** 形式（例: `2026-02-17T10:00:00.000Z`）です。\n- 投稿一覧エンドポイントはページネーション付きで結果を返します。\n","version":"1.0.0"},"servers":[{"url":"https://post-mesh.com/api/v1","description":"本番環境"}],"security":[{"apiKey":[]}],"tags":[{"name":"Connections","description":"SNS アカウント連携の管理"},{"name":"Media","description":"メディアファイル（動画・画像）のアップロード"},{"name":"Posts","description":"投稿の作成・管理"}],"paths":{"/connections":{"get":{"tags":["Connections"],"summary":"連携一覧を取得","description":"チームに紐づくすべての有効な SNS アカウント連携を返します。","operationId":"listConnections","parameters":[{"name":"platform","in":"query","description":"プラットフォームでフィルタ","required":false,"schema":{"$ref":"#/components/schemas/Platform"}}],"responses":{"200":{"description":"連携一覧","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Connection"}}}},"example":{"data":[{"id":"conn_abc123","platform":"youtube","account_name":"My Channel","icon_url":"https://storage.googleapis.com/...","connected_at":"2026-01-15T08:30:00.000Z"},{"id":"conn_def456","platform":"tiktok","account_name":"@myaccount","icon_url":null,"connected_at":"2026-02-01T12:00:00.000Z"}]}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/media/upload-url":{"post":{"tags":["Media"],"summary":"メディアアップロード URL を生成","description":"メディアファイル（動画・画像）アップロード用の署名付き URL を生成します。\n\n**対応ファイル形式:**\n- 動画: `.mp4`, `.mov`\n- 画像: `.jpg`, `.jpeg`, `.png`, `.webp`\n\n**サイズ上限:**\n- 動画: 最大 1 GB\n- 画像: 最大 20 MB\n\n**アップロード手順:**\n\n1. このエンドポイントで `upload_url` と `media_id` を取得\n2. `upload_url` に `PUT` リクエストでファイルをアップロード\n3. 投稿作成時に `media_id` を指定\n\n`PUT` リクエストには `Content-Type` ヘッダーが**必須**です。\n署名付き URL はリクエストで指定した `mime_type` で生成されるため、\n一致しない `Content-Type` を指定すると `403` エラーになります。\n\n```bash\ncurl -X PUT \\\n  -H \"Content-Type: video/mp4\" \\\n  --upload-file ./my-video.mp4 \\\n  \"UPLOAD_URL\"\n```\n\n```javascript\nawait fetch(uploadUrl, {\n  method: 'PUT',\n  headers: { 'Content-Type': 'video/mp4' },\n  body: file,\n});\n```\n\nアップロード URL の有効期限は **15 分**です。期限切れの場合は再度このエンドポイントを呼び出してください。\n","operationId":"createMediaUploadUrl","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateMediaUploadUrlRequest"},"example":{"mime_type":"video/mp4","size_bytes":52428800,"name":"my-video.mp4"}}}},"responses":{"201":{"description":"アップロード URL を生成しました","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/MediaUpload"}}},"example":{"data":{"media_id":"media_abc123","upload_url":"https://storage.googleapis.com/...","name":"my-video.mp4"}}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/SubscriptionRequired"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/posts":{"get":{"tags":["Posts"],"summary":"投稿一覧を取得","description":"ページネーション付きの投稿一覧を返します。ステータスやプラットフォームでフィルタできます。","operationId":"listPosts","parameters":[{"name":"page","in":"query","description":"ページ番号（1 始まり）。1 未満の場合は 1 にクランプされます。","required":false,"schema":{"type":"integer","default":1}},{"name":"limit","in":"query","description":"1 ページあたりの件数。1〜100 の範囲にクランプされます。","required":false,"schema":{"type":"integer","default":20}},{"name":"status","in":"query","description":"ステータスでフィルタ","required":false,"schema":{"$ref":"#/components/schemas/PostListStatus"}},{"name":"platform","in":"query","description":"プラットフォームでフィルタ","required":false,"schema":{"$ref":"#/components/schemas/Platform"}}],"responses":{"200":{"description":"投稿一覧","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PostSummary"}},"pagination":{"$ref":"#/components/schemas/Pagination"}}},"example":{"data":[{"id":"pub_abc123","title":"初めての動画","status":"posted","category":"video","platforms":[{"platform":"youtube","status":"posted"},{"platform":"tiktok","status":"posted"}],"display_at":"2026-02-17T10:00:00.000Z"}],"pagination":{"total":42,"page":1,"limit":20,"has_next":true}}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"tags":["Posts"],"summary":"投稿を作成","description":"新しい投稿を作成し、指定したプラットフォームに公開します。\n\n**投稿カテゴリ:**\n- `video` — 事前にアップロードした動画の `media_id` が必要です。\n- `image` — 事前にアップロードした画像の `media_ids` が必要です。\n- `text` — メディア不要です。\n- `tree` — ツリー（連続投稿）です。`tree_cards` が必要です。\n\n**ツリー投稿（`category: tree`）:**\n- 投稿先はXとThreadsだけです。他のプラットフォームを含めると400になります（下書きでも同様）。\n- 同じSNSアカウントを`targets`に重複して指定すると400になります。\n- 本文とメディアはカード（`tree_cards`）に持たせます。`targets[]`では`connection_id`だけを指定します。`caption` / `youtube_title` / `is_ai_generated` / `tiktok_draft` / `tiktok_auto_add_music`を指定すると400になり、それ以外の未知フィールドは無視されます。\n- 全投稿先に同じカード内容を配信します。投稿先ごとにカードを変えることはできません。\n- 公開・予約投稿ではカードが2件以上必要です。`draft: true`の下書きは1件でも保存できます。\n- `scheduled_at` / `draft` は他カテゴリと同じように使えます。`media_id` / `media_ids` / `thumbnail_time` は使いません。\n\n**予約投稿:**\n- `scheduled_at` を省略すると即時投稿になります。\n- `scheduled_at` に未来の ISO 8601 日時を指定すると予約投稿になります。\n\n**下書き:**\n- `draft: true` を指定すると下書きとして保存し、SNS へは配信されません。\n- `scheduled_at` とは同時に指定できません（400 になります）。\n- 下書きでは `targets[].caption` と `youtube_title` を省略できます。\n- 下書きから公開への昇格は、Web アプリの編集画面から行います（API に更新エンドポイントはありません）。\n\n**SNS連携の状態による 409:**\n- 投稿先の連携が切れている・トークンが失効している場合は `POSTING_READINESS_RECONNECT_REQUIRED` で 409 を返します。連携画面で再連携してから再送してください。\n- SNS 側の一時的なエラーで投稿準備を確認できなかった場合は `POSTING_READINESS_UNAVAILABLE` で 409 を返します。時間をおいて再送してください。\n- どちらの場合も投稿は作成されません。`draft: true` の下書きでは配信しないため、この確認は行いません。\n\n**ターゲット:**\n各ターゲットは連携済みの SNS アカウントとプラットフォーム別のキャプションを指定します。\nYouTube の場合は `youtube_title` でタイトルを指定できます。\nTikTok の場合は `is_ai_generated` で AI 生成コンテンツラベルを付与できます。\nTikTok への画像投稿の場合は `tiktok_auto_add_music` でおすすめ音楽の自動追加を指定できます。\nTikTok の場合は `tiktok_draft` で、公開せず TikTok アプリの受信箱へ下書きとして送れます。\nTikTok の場合は `tiktok_privacy_level` で公開範囲を、`tiktok_allow_comment` / `tiktok_allow_duet` / `tiktok_allow_stitch` で\nコメント・デュエット・ステッチの可否を指定できます。公開範囲は選べる値がアカウントによって変わるため、\n選べない値を指定すると投稿は作成されず 400 になります。\n","operationId":"createPost","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePostRequest"},"examples":{"video_post":{"summary":"YouTube と TikTok に動画投稿","value":{"category":"video","media_id":"media_abc123","thumbnail_time":5.5,"targets":[{"connection_id":"conn_yt123","caption":"新しい動画をアップしました！ #youtube","youtube_title":"素敵な動画"},{"connection_id":"conn_tt456","caption":"新しい動画をアップしました！ #tiktok","is_ai_generated":true}]}},"text_post":{"summary":"X と Threads にテキスト投稿","value":{"category":"text","targets":[{"connection_id":"conn_x123","caption":"こんにちは！ #X"},{"connection_id":"conn_th456","caption":"こんにちは！ #threads"}]}},"image_post":{"summary":"X と Instagram に画像投稿","value":{"category":"image","media_ids":["media_img001","media_img002"],"targets":[{"connection_id":"conn_x123","caption":"美しい夕焼け #photography"},{"connection_id":"conn_ig789","caption":"美しい夕焼け #photography #sunset"}]}},"scheduled_post":{"summary":"動画の予約投稿","value":{"category":"video","media_id":"media_abc123","scheduled_at":"2030-01-01T10:00:00Z","targets":[{"connection_id":"conn_yt123","caption":"もうすぐ公開！ #youtube","youtube_title":"近日公開"}]}},"tree_post":{"summary":"X と Threads にツリー投稿","value":{"category":"tree","tree_cards":[{"caption":"個人開発の話をスレッドで書きます"},{"caption":"まず作ったきっかけから。","media_ids":["media_img001"]},{"caption":"続きはプロフィールのリンクから。"}],"targets":[{"connection_id":"conn_x123"},{"connection_id":"conn_th456"}]}},"draft_post":{"summary":"キャプション未指定の下書き","value":{"category":"text","draft":true,"targets":[{"connection_id":"conn_x123"}]}}}}}},"responses":{"201":{"description":"投稿を作成しました","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PostDetail"}}}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/SubscriptionRequired"},"409":{"description":"投稿先のSNS連携の状態により投稿を受け付けられません。投稿は作成されていません。","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"reconnect_required":{"summary":"再連携が必要","value":{"error":{"code":"POSTING_READINESS_RECONNECT_REQUIRED","message":"Xアカウントは再連携が必要です。連携画面で再連携してから投稿してください。"}}},"posting_readiness_unavailable":{"summary":"SNS側の一時的なエラーで投稿準備を確認できなかった","value":{"error":{"code":"POSTING_READINESS_UNAVAILABLE","message":"X側の一時的なエラーで投稿準備を確認できませんでした。時間をおいて再度お試しください。"}}}}}}},"500":{"$ref":"#/components/responses/InternalError"}}}},"/posts/{id}":{"get":{"tags":["Posts"],"summary":"投稿の詳細を取得","description":"指定した投稿の詳細情報を返します。各プラットフォームごとのステータスを含みます。","operationId":"getPost","parameters":[{"name":"id","in":"path","required":true,"description":"投稿 ID","schema":{"type":"string"}}],"responses":{"200":{"description":"投稿の詳細","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PostDetail"}}},"example":{"data":{"id":"pub_abc123","status":"posted","category":"video","media_ids":["media_abc123"],"tree_cards":[],"scheduled_at":null,"can_cancel":false,"platforms":[{"platform":"youtube","connection_id":"conn_yt123","account_name":"My Channel","status":"posted","caption":"新しい動画をアップしました！ #youtube","youtube_title":"素敵な動画","external_url":"https://www.youtube.com/shorts/dQw4w9WgXcQ","error_message":null},{"platform":"tiktok","connection_id":"conn_tt456","account_name":"@myaccount","status":"posted","caption":"新しい動画をアップしました！ #tiktok","youtube_title":null,"external_url":"https://www.tiktok.com/@myaccount","error_message":null}],"display_at":"2026-02-17T10:00:00.000Z"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"tags":["Posts"],"summary":"予約投稿・下書きをキャンセル","description":"予約投稿または下書きをキャンセルします。\nステータスが `scheduled`（かつ処理開始前）または `draft` の投稿をキャンセルできます。\n","operationId":"cancelPost","parameters":[{"name":"id","in":"path","required":true,"description":"投稿 ID","schema":{"type":"string"}}],"responses":{"200":{"description":"投稿をキャンセルしました","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","description":"投稿 ID"},"status":{"type":"string","enum":["cancelled"],"description":"常に `cancelled`"}},"required":["id","status"]}}},"example":{"data":{"id":"pub_abc123","status":"cancelled"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"キャンセルできません（処理中、キャンセル済み、即時投稿のいずれか）","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"CONFLICT","message":"Cannot cancel: publication is already being processed"}}}}},"500":{"$ref":"#/components/responses/InternalError"}}}}},"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"API キーを Bearer トークンとして送ります。API キーはポストメッシュダッシュボードの「API キー」画面から発行できます。\n\n```\nAuthorization: Bearer YOUR_API_KEY\n```\n"}},"schemas":{"Connection":{"type":"object","description":"SNS アカウント連携","properties":{"id":{"type":"string","description":"連携 ID"},"platform":{"$ref":"#/components/schemas/Platform"},"account_name":{"type":"string","nullable":true,"description":"連携アカウントの表示名"},"icon_url":{"type":"string","nullable":true,"description":"連携アカウントのプロフィールアイコン URL"},"connected_at":{"type":"string","format":"date-time","description":"連携日時"}},"required":["id","platform","account_name","icon_url","connected_at"]},"CreateMediaUploadUrlRequest":{"type":"object","description":"メディアアップロード URL 生成リクエスト","properties":{"mime_type":{"type":"string","description":"アップロードするファイルの MIME タイプ。\n対応: `video/mp4`, `video/quicktime`, `image/jpeg`, `image/png`, `image/webp`\n","enum":["video/mp4","video/quicktime","image/jpeg","image/png","image/webp"]},"size_bytes":{"type":"integer","minimum":1,"description":"ファイルサイズ（バイト）。動画は最大 1 GB、画像は最大 20 MB。\n"},"name":{"type":"string","minLength":1,"description":"ファイル名"}},"required":["mime_type","size_bytes","name"]},"MediaUpload":{"type":"object","description":"メディアアップロード URL","properties":{"media_id":{"type":"string","description":"投稿作成時に参照するメディア ID"},"upload_url":{"type":"string","format":"uri","description":"ファイルアップロード用の署名付き URL（PUT リクエストで使用）"},"name":{"type":"string","description":"ファイル名"}},"required":["media_id","upload_url","name"]},"CreatePostRequest":{"description":"投稿作成リクエスト。`category`の値ごとに必須フィールドが異なるため、カテゴリ別の4つのスキーマをoneOfで定義しています。\n公開・予約と下書き（`draft: true`）で変わる条件（`targets[].caption`の必須など）はOpenAPI 3.0のスキーマでは表現できないため、各フィールドのdescriptionを参照してください。\n","oneOf":[{"$ref":"#/components/schemas/CreateVideoPostRequest"},{"$ref":"#/components/schemas/CreateImagePostRequest"},{"$ref":"#/components/schemas/CreateTextPostRequest"},{"$ref":"#/components/schemas/CreateTreePostRequest"}],"discriminator":{"propertyName":"category","mapping":{"video":"#/components/schemas/CreateVideoPostRequest","image":"#/components/schemas/CreateImagePostRequest","text":"#/components/schemas/CreateTextPostRequest","tree":"#/components/schemas/CreateTreePostRequest"}}},"CreatePostRequestBase":{"type":"object","properties":{"scheduled_at":{"type":"string","format":"date-time","nullable":true,"description":"予約投稿日時（ISO 8601）。省略または null で即時投稿。未来の日時を指定してください。"},"draft":{"type":"boolean","default":false,"description":"true で下書きとして保存し、SNS へは配信されません。\n- `scheduled_at` とは同時に指定できません（400 になります）。\n- 下書きでは `targets[].caption` と `youtube_title` を省略できます。\n- 下書きから公開への昇格は、Web アプリの編集画面から行います（API に更新エンドポイントはありません）。\n"}}},"CreateVideoPostRequest":{"description":"動画投稿の作成リクエスト","allOf":[{"$ref":"#/components/schemas/CreatePostRequestBase"},{"type":"object","properties":{"category":{"type":"string","enum":["video"],"description":"投稿カテゴリ"},"targets":{"type":"array","minItems":1,"description":"投稿先のプラットフォームリスト","items":{"$ref":"#/components/schemas/PostTarget"}},"media_id":{"type":"string","description":"アップロード済み動画のメディア ID。下書き（`draft: true`）でも必須です。"},"thumbnail_time":{"type":"number","description":"サムネイルの時間位置（秒）。任意。"}},"required":["category","targets","media_id"]}]},"CreateImagePostRequest":{"description":"画像投稿の作成リクエスト","allOf":[{"$ref":"#/components/schemas/CreatePostRequestBase"},{"type":"object","properties":{"category":{"type":"string","enum":["image"],"description":"投稿カテゴリ"},"targets":{"type":"array","minItems":1,"description":"投稿先のプラットフォームリスト","items":{"$ref":"#/components/schemas/PostTarget"}},"media_ids":{"type":"array","items":{"type":"string"},"description":"アップロード済み画像のメディア ID 配列。下書き（`draft: true`）でも必須です。\n1 件以上。上限は投稿先のうち最も厳しいプラットフォームの枚数上限（X 4 枚 / Instagram・Facebook 10 枚 / Threads 20 枚 / TikTok 35 枚）。\n","minItems":1,"maxItems":35}},"required":["category","targets","media_ids"]}]},"CreateTextPostRequest":{"description":"テキスト投稿の作成リクエスト","allOf":[{"$ref":"#/components/schemas/CreatePostRequestBase"},{"type":"object","properties":{"category":{"type":"string","enum":["text"],"description":"投稿カテゴリ"},"targets":{"type":"array","minItems":1,"description":"投稿先のプラットフォームリスト","items":{"$ref":"#/components/schemas/PostTarget"}}},"required":["category","targets"]}]},"CreateTreePostRequest":{"description":"ツリー投稿（連続投稿）の作成リクエスト。投稿先はXとThreadsのみです。","allOf":[{"$ref":"#/components/schemas/CreatePostRequestBase"},{"type":"object","properties":{"category":{"type":"string","enum":["tree"],"description":"投稿カテゴリ"},"targets":{"type":"array","minItems":1,"description":"投稿先のプラットフォームリスト。投稿先はXとThreadsのみで、他のプラットフォームを含めると400になります。","items":{"$ref":"#/components/schemas/TreePostTarget"}},"tree_cards":{"type":"array","minItems":1,"description":"ツリー（連続投稿）を構成するカードの配列で、先頭が起点の投稿、以降がぶら下がる返信になります。\n\n- 全投稿先に同じカード内容を配信します。投稿先ごとにカードを変えることはできません。\n- 公開・予約投稿では2件以上必要です。1件だけだと400になります。\n- `draft: true`の下書きでは1件でも保存できます。\n- 下書きでも、カード・`caption`・`media_ids`の型、メディアIDの実在とチームへの所属、メディア種別（GIFは不可）は作成時に検証され、違反は400になります。下書きで後回しにできるのは、カード数（2件以上）、本文とメディアが両方空のカードの禁止、文字数制限、メディアの枚数・サイズ制限だけです。\n- 空配列や配列以外を指定すると400になります。\n","items":{"$ref":"#/components/schemas/CreateTreeCard"}}},"required":["category","targets","tree_cards"]}]},"TreePostTarget":{"type":"object","description":"ツリー投稿の投稿先。`connection_id`だけを指定します。\n`caption` / `youtube_title` / `is_ai_generated` / `tiktok_draft` / `tiktok_auto_add_music`を指定すると400になります。\nそれ以外のフィールド（`tiktok_privacy_level`など）は指定しても無視され、設定は反映されません。\n","properties":{"connection_id":{"type":"string","minLength":1,"description":"投稿先の連携 ID"}},"required":["connection_id"]},"PostTarget":{"type":"object","description":"投稿先ターゲット（video / image / textカテゴリ用。treeカテゴリはTreePostTargetを使います）","properties":{"connection_id":{"type":"string","minLength":1,"description":"投稿先の連携 ID"},"caption":{"type":"string","minLength":1,"description":"プラットフォーム別のキャプション。公開・予約投稿では必須です。\n`draft: true`の下書きでは省略できます（省略時は空のキャプションとして保存され、昇格時に検証されます）。\n\n各プラットフォームの文字数制限:\n- X: 280文字（加重カウント） https://developer.x.com/en/docs/counting-characters\n- Threads: 500文字 https://developers.facebook.com/docs/threads/posts\n- TikTok: 2,200文字 https://developers.tiktok.com/doc/content-posting-api-reference-direct-post\n- Instagram: 2,200文字 https://developers.facebook.com/docs/instagram-platform/instagram-api-with-instagram-login/content-publishing\n- YouTube（説明文）: 5,000バイト https://developers.google.com/youtube/v3/docs/videos#snippet.description\n- Facebook: 制限なし\n"},"youtube_title":{"type":"string","minLength":1,"description":"YouTube 投稿のタイトル。投稿先が YouTube の場合は**必須**です。"},"is_ai_generated":{"type":"boolean","default":false,"description":"AI で生成されたコンテンツかどうか。TikTok 投稿の場合のみ有効です。true にすると TikTok 上で「AI generated」ラベルが表示されます。"},"tiktok_auto_add_music":{"type":"boolean","default":false,"description":"TikTok がおすすめの音楽を自動で付けるかどうか。TikTok への画像投稿（`category: image`）の場合のみ有効です。`image` 以外のカテゴリで指定すると 400 になります。`tiktok_draft: true` とは同時に指定できません。"},"tiktok_draft":{"type":"boolean","default":false,"description":"公開せず、TikTok アプリの受信箱に下書きとして送るかどうか。TikTok 投稿（動画・画像）の場合のみ有効です。\n- 動画の下書きにはキャプションが送られません（TikTok アプリ側で入力します）。\n- 保留中の下書きは 24 時間あたり 5 件までです。\n- `tiktok_auto_add_music: true` とは同時に指定できません（400 になります）。\n- `tiktok_privacy_level` とは同時に指定できません（400 になります）。\n"},"tiktok_privacy_level":{"type":"string","enum":["PUBLIC_TO_EVERYONE","SELF_ONLY"],"description":"この投稿を見せる相手。TikTok 投稿の場合のみ有効です。省略すると `PUBLIC_TO_EVERYONE`（全員に公開）になります。\n非公開アカウントでは `PUBLIC_TO_EVERYONE` を選べず、`SELF_ONLY` のみになります。\nそのアカウントで選べない値を指定した場合、投稿は作成されず `TIKTOK_PRIVACY_LEVEL_UNAVAILABLE` で 400 になります。\nエラーメッセージにそのアカウントで選べる値が含まれるので、選び直して再送してください。\n`tiktok_draft: true` とは同時に指定できません（400 になります）。\n"},"tiktok_allow_comment":{"type":"boolean","default":true,"description":"この投稿へのコメントを許可するかどうか。TikTok 投稿の場合のみ有効です。TikTok アカウント側でコメントが無効になっている場合は、`true` を指定しても無効のまま投稿されます。"},"tiktok_allow_duet":{"type":"boolean","default":true,"description":"デュエットを許可するかどうか。TikTok への動画投稿（`category: video`）の場合のみ有効です。`video` 以外のカテゴリで指定すると 400 になります。TikTok アカウント側でデュエットが無効になっている場合は、`true` を指定しても無効のまま投稿されます。"},"tiktok_allow_stitch":{"type":"boolean","default":true,"description":"ステッチを許可するかどうか。TikTok への動画投稿（`category: video`）の場合のみ有効です。`video` 以外のカテゴリで指定すると 400 になります。TikTok アカウント側でステッチが無効になっている場合は、`true` を指定しても無効のまま投稿されます。"}},"required":["connection_id"]},"CreateTreeCard":{"type":"object","description":"ツリー投稿のカード（リクエスト）。本文・メディアのどちらか一方だけでも構いませんが、\n公開・予約投稿では本文とメディアの両方が空のカードは400になります。\n","properties":{"caption":{"type":"string","nullable":true,"description":"カードの本文。省略・null・空文字はいずれも本文なしとして扱われます。\n文字数制限は投稿先ごとに異なり、Xは280文字（加重カウント）、Threadsは500文字です。\n指定した投稿先のいずれかの制限を超えると400になります。\n"},"media_ids":{"type":"array","items":{"type":"string"},"description":"カードに添えるメディア ID の配列。省略時は空配列として扱われます。\n`POST /media/upload-url`でアップロード済みのメディアだけを指定できます。\n\nメディアIDの実在とチームへの所属、メディア種別（GIFは不可）は下書きでも作成時に検証されます。\n以下の枚数・サイズ制限は公開・予約投稿のみ検証されます（いずれかの投稿先の制限を超えると400）:\n- X: 1カードあたり合計4件まで。画像は4枚（`image/jpeg` / `image/png` / `image/webp`、1枚5MBまで）、動画は1本（`video/mp4` / `video/quicktime`、512MBまで）。画像と動画は混在できません。\n- Threads: 1カードあたり合計20件まで。画像は20枚（1枚8MBまで）、動画は20本（`video/mp4` / `video/quicktime`、1GBまで）。画像と動画を混在できます。\n\nGIF（`image/gif`）は対応していません。\n"}}},"PostSummary":{"type":"object","description":"投稿の概要（一覧用）","properties":{"id":{"type":"string","description":"投稿 ID"},"title":{"type":"string","description":"投稿タイトル（YouTube タイトルまたはキャプション先頭 30 文字）"},"status":{"$ref":"#/components/schemas/PostListStatus"},"category":{"$ref":"#/components/schemas/PostCategory"},"platforms":{"type":"array","description":"プラットフォームごとのステータス","items":{"type":"object","properties":{"platform":{"$ref":"#/components/schemas/Platform"},"status":{"$ref":"#/components/schemas/PostListStatus"}},"required":["platform","status"]}},"display_at":{"type":"string","format":"date-time","description":"表示日時"}},"required":["id","title","status","category","platforms","display_at"]},"PostDetail":{"type":"object","description":"投稿の詳細","properties":{"id":{"type":"string","description":"投稿 ID"},"status":{"$ref":"#/components/schemas/PostDetailStatus"},"category":{"$ref":"#/components/schemas/PostCategory"},"media_ids":{"type":"array","items":{"type":"string"},"description":"関連するメディア ID の配列（API 経由で作成した投稿の場合。動画は最大 1 件、画像は複数件。Web UI から作成した投稿は空配列）"},"tree_cards":{"type":"array","description":"ツリー投稿のカードの配列（`category: tree`の場合のみ。それ以外のカテゴリでは常に空配列）。\nリクエストの`tree_cards`と同じ順序で、先頭が起点の投稿、以降がぶら下がる返信です。\n","items":{"$ref":"#/components/schemas/TreeCard"}},"scheduled_at":{"type":"string","format":"date-time","nullable":true,"description":"予約投稿日時。即時投稿の場合は null。"},"can_cancel":{"type":"boolean","description":"キャンセル可能かどうか。配信処理が始まっておらず、下書き、または予約日時が未来の予約投稿の場合に true。"},"platforms":{"type":"array","description":"プラットフォームごとの詳細ステータス","items":{"$ref":"#/components/schemas/PlatformPostDetail"}},"display_at":{"type":"string","format":"date-time","description":"表示日時"}},"required":["id","status","category","media_ids","tree_cards","scheduled_at","can_cancel","platforms","display_at"]},"TreeCard":{"type":"object","description":"ツリー投稿のカード（レスポンス）","properties":{"caption":{"type":"string","nullable":true,"description":"カードの本文。本文なしのカードでは null。"},"media_ids":{"type":"array","items":{"type":"string"},"description":"カードに添えたメディア ID の配列（メディアなしの場合は空配列）"}},"required":["caption","media_ids"]},"PlatformPostDetail":{"type":"object","description":"プラットフォームごとの投稿ステータス","properties":{"platform":{"$ref":"#/components/schemas/Platform"},"connection_id":{"type":"string","description":"連携 ID（連携が存在しない場合は空文字列）"},"account_name":{"type":"string","description":"アカウント名（取得できない場合は空文字列）"},"status":{"$ref":"#/components/schemas/PlatformPostDetailStatus"},"caption":{"type":"string","description":"プラットフォームごとのキャプション"},"youtube_title":{"type":"string","nullable":true,"description":"YouTube 投稿のタイトル。YouTube 以外のプラットフォームでは null。"},"external_url":{"type":"string","nullable":true,"description":"プラットフォーム上の投稿 URL"},"error_message":{"type":"string","nullable":true,"description":"投稿失敗時のエラーメッセージ"}},"required":["platform","connection_id","account_name","status","caption","youtube_title","external_url","error_message"]},"Platform":{"type":"string","enum":["youtube","tiktok","instagram","threads","x","facebook"],"description":"SNS プラットフォーム"},"PostCategory":{"type":"string","enum":["video","image","text","tree"],"description":"投稿のコンテンツ種別: video（動画）/ image（画像）/ text（テキスト）/ tree（ツリー投稿）"},"PostListStatus":{"type":"string","enum":["posted","scheduled","processing","failed","draft"],"description":"投稿ステータス: posted（投稿済み）/ scheduled（予約済み）/ processing（処理中）/ failed（失敗）/ draft（下書き）"},"PostDetailStatus":{"type":"string","enum":["posted","scheduled","processing","failed","draft","cancelled"],"description":"投稿ステータス（詳細）: draft（下書き）/ cancelled（キャンセル済み）を含む"},"PlatformPostDetailStatus":{"type":"string","enum":["posted","scheduled","processing","failed","draft","cancelled"],"description":"プラットフォームごとの投稿ステータス: draft（下書き）/ cancelled（キャンセル済み）を含む"},"Pagination":{"type":"object","description":"ページネーション情報","properties":{"total":{"type":"integer","description":"全件数"},"page":{"type":"integer","description":"現在のページ番号"},"limit":{"type":"integer","description":"1 ページあたりの件数"},"has_next":{"type":"boolean","description":"次のページがあるか"}},"required":["total","page","limit","has_next"]},"Error":{"type":"object","description":"エラーレスポンス","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"エラーコード。\n\n- `VALIDATION_ERROR`（400）— リクエストのバリデーションに失敗\n- `TIKTOK_PRIVACY_LEVEL_UNAVAILABLE`（400）— `POST /posts`で、そのTikTokアカウントでは選べない`tiktok_privacy_level`を指定した\n- `UNAUTHORIZED`（401）— API キーが未指定・無効・失効済み\n- `SUBSCRIPTION_REQUIRED`（403）— 有効なサブスクリプションが必要\n- `NOT_FOUND`（404）— リソースが見つからない\n- `POSTING_READINESS_RECONNECT_REQUIRED`（409）— `POST /posts`で、投稿先のSNS連携が切れており再連携が必要\n- `POSTING_READINESS_UNAVAILABLE`（409）— `POST /posts`で、SNS側の一時的なエラーにより投稿準備を確認できなかった\n- `CONFLICT`（409）— `DELETE /posts/{id}`で、処理中・キャンセル済みなどでキャンセルできない\n- `INTERNAL_ERROR`（500）— 内部サーバーエラー\n","enum":["VALIDATION_ERROR","TIKTOK_PRIVACY_LEVEL_UNAVAILABLE","UNAUTHORIZED","SUBSCRIPTION_REQUIRED","NOT_FOUND","POSTING_READINESS_RECONNECT_REQUIRED","POSTING_READINESS_UNAVAILABLE","CONFLICT","INTERNAL_ERROR"]},"message":{"type":"string","description":"エラーの詳細メッセージ"}},"required":["code","message"]}},"required":["error"]}},"responses":{"Unauthorized":{"description":"API キーが未指定または無効です","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header"}}}}},"ValidationError":{"description":"リクエストのバリデーションに失敗しました","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"VALIDATION_ERROR","message":"targets must be a non-empty array"}}}}},"NotFound":{"description":"リソースが見つかりません","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"NOT_FOUND","message":"Post not found"}}}}},"SubscriptionRequired":{"description":"有効なサブスクリプションが必要です","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"SUBSCRIPTION_REQUIRED","message":"Active subscription is required"}}}}},"InternalError":{"description":"内部サーバーエラー","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"INTERNAL_ERROR","message":"An unexpected error occurred"}}}}}}}}