{
  "openapi": "3.1.0",
  "info": {
    "title": "Parky API (Marketing Portal)",
    "version": "0.1.0",
    "description": "Parky API — マーケティングポータル (dev-marketing.parky.co.jp) が叩くエンドポイント。マーケティング権限で保護。"
  },
  "servers": [
    {
      "url": "https://api.parky.co.jp",
      "description": "Production"
    },
    {
      "url": "https://stg-api.parky.co.jp",
      "description": "Staging"
    },
    {
      "url": "https://dev-api.parky.co.jp",
      "description": "Development"
    },
    {
      "url": "http://localhost:8787",
      "description": "Local"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "description": "Supabase Auth が発行した JWT（HS256 / SUPABASE_JWT_SECRET 署名）"
      }
    },
    "schemas": {
      "AppleAppSiteAssociation": {
        "type": "object",
        "properties": {
          "applinks": {
            "type": "object",
            "properties": {
              "details": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "appIDs": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "components": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "/": {
                            "type": "string"
                          },
                          "comment": {
                            "type": "string"
                          }
                        },
                        "required": [
                          "/"
                        ]
                      }
                    }
                  },
                  "required": [
                    "appIDs",
                    "components"
                  ]
                }
              }
            },
            "required": [
              "details"
            ]
          },
          "webcredentials": {
            "type": "object",
            "properties": {
              "apps": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            },
            "required": [
              "apps"
            ]
          }
        },
        "required": [
          "applinks",
          "webcredentials"
        ]
      },
      "AssetLinksEntry": {
        "type": "object",
        "properties": {
          "relation": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "target": {
            "type": "object",
            "properties": {
              "namespace": {
                "type": "string"
              },
              "package_name": {
                "type": "string"
              },
              "sha256_cert_fingerprints": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            },
            "required": [
              "namespace",
              "package_name",
              "sha256_cert_fingerprints"
            ]
          }
        },
        "required": [
          "relation",
          "target"
        ]
      },
      "AssetLinksResponse": {
        "type": "array",
        "items": {
          "$ref": "#/components/schemas/AssetLinksEntry"
        }
      },
      "AdminArticle": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "slug": {
            "type": [
              "string",
              "null"
            ]
          },
          "title": {
            "type": "string"
          },
          "body": {
            "type": [
              "string",
              "null"
            ]
          },
          "excerpt": {
            "type": [
              "string",
              "null"
            ]
          },
          "category": {
            "type": "string"
          },
          "author_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string"
          },
          "published_at": {
            "type": [
              "string",
              "null"
            ]
          },
          "view_count": {
            "type": "integer"
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "thumbnail_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string"
          },
          "updated_at": {
            "type": "string"
          },
          "publish_to_web": {
            "type": "boolean"
          },
          "publish_to_app": {
            "type": "boolean"
          },
          "hero_image_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "hero_image_alt_text": {
            "type": [
              "string",
              "null"
            ]
          },
          "author_slug": {
            "type": [
              "string",
              "null"
            ]
          },
          "content_format": {
            "type": "string"
          },
          "related_hubs": {
            "type": "object",
            "description": "任意の JSON 値（string / number / boolean / null / array / object）。再帰構造は object + additionalProperties で表現。",
            "additionalProperties": true
          }
        },
        "required": [
          "id",
          "slug",
          "title",
          "body",
          "excerpt",
          "category",
          "author_name",
          "status",
          "published_at",
          "view_count",
          "tags",
          "thumbnail_url",
          "created_at",
          "updated_at"
        ]
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "examples": [
                  "not_found"
                ]
              },
              "message": {
                "type": "string",
                "examples": [
                  "Not Found"
                ]
              },
              "message_key": {
                "type": "string",
                "examples": [
                  "common.error.not_found"
                ]
              },
              "request_id": {
                "type": "string",
                "examples": [
                  "7d4e5…-…"
                ]
              },
              "param": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "リクエスト上のエラー対象フィールド名（zod path[0] 等）。root レベルエラーは null。",
                "examples": [
                  "email"
                ]
              },
              "doc_url": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "エラーコードに対応するドキュメント URL。未整備時は null。",
                "examples": [
                  null
                ]
              }
            },
            "required": [
              "code",
              "message",
              "request_id"
            ]
          }
        },
        "required": [
          "error"
        ]
      },
      "ArticleUpdate": {
        "type": "object",
        "properties": {
          "slug": {
            "type": [
              "string",
              "null"
            ]
          },
          "title": {
            "type": "string"
          },
          "body": {
            "type": [
              "string",
              "null"
            ]
          },
          "excerpt": {
            "type": [
              "string",
              "null"
            ]
          },
          "category": {
            "type": "string"
          },
          "author_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "author_slug": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string"
          },
          "published_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "thumbnail_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "hero_image_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "hero_image_alt_text": {
            "type": [
              "string",
              "null"
            ]
          },
          "content_format": {
            "type": "string"
          },
          "publish_to_web": {
            "type": "boolean"
          },
          "publish_to_app": {
            "type": "boolean"
          },
          "related_hubs": {
            "type": "object",
            "description": "任意の JSON 値（string / number / boolean / null / array / object）。再帰構造は object + additionalProperties で表現。",
            "additionalProperties": true
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "AdminAd": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "ad_type": {
            "type": "string"
          },
          "placement": {
            "type": "string"
          },
          "banner_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "link_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "alt_text": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string"
          },
          "start_date": {
            "type": [
              "string",
              "null"
            ]
          },
          "end_date": {
            "type": [
              "string",
              "null"
            ]
          },
          "impressions": {
            "type": "integer"
          },
          "clicks": {
            "type": "integer"
          },
          "memo": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string"
          },
          "updated_at": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "name",
          "ad_type",
          "placement",
          "banner_url",
          "link_url",
          "alt_text",
          "status",
          "start_date",
          "end_date",
          "impressions",
          "clicks",
          "memo",
          "created_at",
          "updated_at"
        ]
      },
      "AdUpdate": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "ad_type": {
            "type": "string"
          },
          "placement": {
            "type": "string"
          },
          "banner_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "link_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "alt_text": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string"
          },
          "start_date": {
            "type": [
              "string",
              "null"
            ]
          },
          "end_date": {
            "type": [
              "string",
              "null"
            ]
          },
          "memo": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "IgSlideCategory": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string"
          },
          "label": {
            "type": "string"
          },
          "prefix": {
            "type": [
              "string",
              "null"
            ]
          },
          "sort_order": {
            "type": "integer"
          },
          "is_deleted": {
            "type": "integer"
          }
        },
        "required": [
          "code",
          "label",
          "prefix",
          "sort_order",
          "is_deleted"
        ]
      },
      "IgPostCategory": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string"
          },
          "label": {
            "type": "string"
          },
          "color": {
            "type": [
              "string",
              "null"
            ]
          },
          "sort_order": {
            "type": "integer"
          },
          "is_deleted": {
            "type": "integer"
          }
        },
        "required": [
          "code",
          "label",
          "color",
          "sort_order",
          "is_deleted"
        ]
      },
      "IgTag": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "color": {
            "type": [
              "string",
              "null"
            ]
          },
          "usage_count": {
            "type": "integer"
          }
        },
        "required": [
          "id",
          "name",
          "color",
          "usage_count"
        ]
      },
      "IgTemplate": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "code": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "slide_type": {
            "type": "string"
          },
          "html_body": {
            "type": "string"
          },
          "slot_schema": {
            "type": "string"
          },
          "sample_content": {
            "type": "string",
            "default": "{}"
          },
          "sample_html": {
            "type": "string",
            "default": ""
          },
          "uses_parking_lot": {
            "type": "integer",
            "default": 0
          },
          "genre": {
            "type": "string",
            "enum": [
              "parking",
              "useful_info"
            ],
            "default": "parking"
          },
          "sort_order": {
            "type": "integer"
          },
          "is_active": {
            "type": "integer"
          }
        },
        "required": [
          "id",
          "code",
          "name",
          "slide_type",
          "html_body",
          "slot_schema",
          "sort_order",
          "is_active"
        ]
      },
      "IgPostTemplate": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "code": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "slide_refs": {
            "type": "string"
          },
          "genre": {
            "type": "string",
            "enum": [
              "parking",
              "useful_info"
            ],
            "default": "parking"
          }
        },
        "required": [
          "id",
          "code",
          "name",
          "description",
          "slide_refs"
        ]
      },
      "IgSlide": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "campaign_id": {
            "type": "string"
          },
          "template_id": {
            "type": "string"
          },
          "slide_index": {
            "type": "integer"
          },
          "content": {
            "type": "string"
          },
          "html_override": {
            "type": [
              "string",
              "null"
            ]
          },
          "png_r2_key": {
            "type": [
              "string",
              "null"
            ]
          },
          "png_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "revision_notes": {
            "type": [
              "string",
              "null"
            ]
          },
          "parking_lot_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string"
          },
          "updated_at": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "campaign_id",
          "template_id",
          "slide_index",
          "content",
          "html_override",
          "png_r2_key",
          "png_url",
          "revision_notes",
          "created_at",
          "updated_at"
        ]
      },
      "IgCampaign": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "code": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "theme": {
            "type": [
              "string",
              "null"
            ]
          },
          "area": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string"
          },
          "scheduled_at": {
            "type": [
              "string",
              "null"
            ]
          },
          "ig_media_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "notes": {
            "type": [
              "string",
              "null"
            ]
          },
          "source_material": {
            "type": [
              "string",
              "null"
            ]
          },
          "post_category_code": {
            "type": [
              "string",
              "null"
            ]
          },
          "genre": {
            "type": "string",
            "enum": [
              "parking",
              "useful_info"
            ],
            "default": "parking"
          },
          "created_by": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string"
          },
          "updated_at": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "code",
          "title",
          "theme",
          "area",
          "status",
          "scheduled_at",
          "ig_media_id",
          "notes",
          "source_material",
          "post_category_code",
          "created_by",
          "created_at",
          "updated_at"
        ]
      },
      "IgCaption": {
        "type": [
          "object",
          "null"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "campaign_id": {
            "type": "string"
          },
          "body": {
            "type": [
              "string",
              "null"
            ]
          },
          "hashtags": {
            "type": [
              "string",
              "null"
            ]
          },
          "draft_body": {
            "type": [
              "string",
              "null"
            ]
          },
          "generated_at": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string"
          },
          "updated_at": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "campaign_id",
          "body",
          "hashtags",
          "draft_body",
          "generated_at",
          "created_at",
          "updated_at"
        ]
      },
      "IgCompetitorSnapshot": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "campaign_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "source_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "account_handle": {
            "type": [
              "string",
              "null"
            ]
          },
          "raw_notes": {
            "type": [
              "string",
              "null"
            ]
          },
          "ai_ideas": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_by": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "campaign_id",
          "source_url",
          "account_handle",
          "raw_notes",
          "ai_ideas",
          "created_by",
          "created_at"
        ]
      },
      "SnsMetricsSeriesItem": {
        "type": "object",
        "properties": {
          "week": {
            "type": "string",
            "examples": [
              "2026-W15"
            ]
          },
          "followers": {
            "type": "integer"
          },
          "engagement_rate": {
            "type": "number"
          }
        },
        "required": [
          "week",
          "followers",
          "engagement_rate"
        ]
      },
      "SnsMetricsPlatform": {
        "type": "object",
        "properties": {
          "platform": {
            "type": "string",
            "enum": [
              "instagram",
              "x"
            ]
          },
          "followers": {
            "type": "integer"
          },
          "engagement_rate": {
            "type": "number"
          },
          "weekly_diff_followers": {
            "type": "integer"
          },
          "weekly_diff_engagement_pt": {
            "type": "number"
          },
          "monthly_diff_followers": {
            "type": "integer"
          },
          "series": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SnsMetricsSeriesItem"
            }
          }
        },
        "required": [
          "platform",
          "followers",
          "engagement_rate",
          "weekly_diff_followers",
          "weekly_diff_engagement_pt",
          "monthly_diff_followers",
          "series"
        ]
      },
      "SyncMeta": {
        "type": "object",
        "properties": {
          "synced": {
            "type": "boolean"
          },
          "synced_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "source": {
            "type": "string",
            "enum": [
              "db",
              "stub",
              "ga4",
              "x_api",
              "sns_snapshot",
              "mixed"
            ]
          },
          "reason": {
            "type": "string"
          }
        },
        "required": [
          "synced",
          "synced_at",
          "source"
        ],
        "description": "response の同期ステータス envelope。feature flag / 0 埋め固定値を統一する。"
      },
      "SnsMetricsResponse": {
        "type": "object",
        "properties": {
          "platforms": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SnsMetricsPlatform"
            }
          },
          "_meta": {
            "$ref": "#/components/schemas/SyncMeta"
          }
        },
        "required": [
          "platforms",
          "_meta"
        ]
      },
      "PostCalendarDay": {
        "type": "object",
        "properties": {
          "date": {
            "type": "string",
            "examples": [
              "2026-04-01"
            ]
          },
          "instagram": {
            "type": "object",
            "properties": {
              "draft": {
                "type": "integer"
              },
              "scheduled": {
                "type": "integer"
              },
              "published": {
                "type": "integer"
              }
            },
            "required": [
              "draft",
              "scheduled",
              "published"
            ]
          },
          "x": {
            "type": "object",
            "properties": {
              "draft": {
                "type": "integer"
              },
              "scheduled": {
                "type": "integer"
              },
              "published": {
                "type": "integer"
              }
            },
            "required": [
              "draft",
              "scheduled",
              "published"
            ]
          }
        },
        "required": [
          "date",
          "instagram",
          "x"
        ]
      },
      "PostCalendarResponse": {
        "type": "object",
        "properties": {
          "days": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PostCalendarDay"
            }
          },
          "_meta": {
            "$ref": "#/components/schemas/SyncMeta"
          }
        },
        "required": [
          "days",
          "_meta"
        ]
      },
      "ArticlesPvItem": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "slug": {
            "type": [
              "string",
              "null"
            ]
          },
          "title": {
            "type": "string"
          },
          "pv": {
            "type": "integer"
          },
          "pv_diff_pct": {
            "type": "number"
          }
        },
        "required": [
          "id",
          "slug",
          "title",
          "pv",
          "pv_diff_pct"
        ]
      },
      "ArticlesPvResponse": {
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ArticlesPvItem"
            }
          },
          "sources": {
            "type": "object",
            "properties": {
              "direct": {
                "type": "integer"
              },
              "google": {
                "type": "integer"
              },
              "twitter": {
                "type": "integer"
              },
              "facebook": {
                "type": "integer"
              },
              "other": {
                "type": "integer"
              }
            },
            "required": [
              "direct",
              "google",
              "twitter",
              "facebook",
              "other"
            ]
          },
          "_meta": {
            "$ref": "#/components/schemas/SyncMeta"
          }
        },
        "required": [
          "items",
          "sources",
          "_meta"
        ]
      },
      "AdsCtrDaily": {
        "type": "object",
        "properties": {
          "date": {
            "type": "string"
          },
          "clicks": {
            "type": "integer"
          }
        },
        "required": [
          "date",
          "clicks"
        ]
      },
      "AdsCtrItem": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "start_date": {
            "type": [
              "string",
              "null"
            ]
          },
          "end_date": {
            "type": [
              "string",
              "null"
            ]
          },
          "impressions": {
            "type": "integer"
          },
          "clicks": {
            "type": "integer"
          },
          "ctr": {
            "type": "number"
          },
          "days_remaining": {
            "type": "integer"
          },
          "daily": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AdsCtrDaily"
            }
          }
        },
        "required": [
          "id",
          "name",
          "start_date",
          "end_date",
          "impressions",
          "clicks",
          "ctr",
          "days_remaining",
          "daily"
        ]
      },
      "AdsCtrResponse": {
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AdsCtrItem"
            }
          },
          "_meta": {
            "$ref": "#/components/schemas/SyncMeta"
          }
        },
        "required": [
          "items",
          "_meta"
        ]
      },
      "MarketingSubscriber": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "email": {
            "type": "string"
          },
          "source": {
            "type": [
              "string",
              "null"
            ]
          },
          "lang": {
            "type": [
              "string",
              "null"
            ]
          },
          "subscribed_at": {
            "type": "string"
          },
          "unsubscribed_at": {
            "type": [
              "string",
              "null"
            ]
          },
          "meta": {},
          "created_at": {
            "type": "string"
          },
          "updated_at": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "email",
          "source",
          "lang",
          "subscribed_at",
          "unsubscribed_at",
          "created_at",
          "updated_at"
        ]
      },
      "MarketingSubscriberUpdateInput": {
        "type": "object",
        "properties": {
          "email": {
            "type": "string",
            "format": "email"
          },
          "source": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 100
          },
          "lang": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 10
          },
          "meta": {
            "type": "object",
            "additionalProperties": {
              "type": "object",
              "description": "任意の JSON 値（string / number / boolean / null / array / object）。再帰構造は object + additionalProperties で表現。",
              "additionalProperties": true
            }
          },
          "unsubscribed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string",
              "maxLength": 50
            },
            "maxItems": 50
          }
        }
      },
      "BroadcastRow": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "title": {
            "type": "string"
          },
          "subject": {
            "type": [
              "string",
              "null"
            ]
          },
          "body_markdown": {
            "type": [
              "string",
              "null"
            ]
          },
          "segment": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string"
          },
          "engine": {
            "type": [
              "string",
              "null"
            ]
          },
          "scheduled_at": {
            "type": [
              "string",
              "null"
            ]
          },
          "sent_at": {
            "type": [
              "string",
              "null"
            ]
          },
          "delivered": {
            "type": "integer"
          },
          "opened": {
            "type": "integer"
          },
          "clicked": {
            "type": "integer"
          },
          "created_at": {
            "type": "string"
          },
          "updated_at": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "title",
          "subject",
          "body_markdown",
          "segment",
          "status",
          "engine",
          "scheduled_at",
          "sent_at",
          "delivered",
          "opened",
          "clicked",
          "created_at",
          "updated_at"
        ]
      },
      "MarketingBroadcast": {
        "allOf": [
          {
            "$ref": "#/components/schemas/BroadcastRow"
          },
          {
            "type": "object",
            "properties": {
              "open_rate": {
                "type": "number"
              },
              "click_rate": {
                "type": "number"
              }
            },
            "required": [
              "open_rate",
              "click_rate"
            ]
          }
        ]
      },
      "BroadcastEngine": {
        "type": "string",
        "enum": [
          "resend",
          "sendgrid",
          "supabase"
        ]
      },
      "MarketingBroadcastCreateInput": {
        "type": "object",
        "properties": {
          "title": {
            "type": "string",
            "minLength": 1,
            "maxLength": 300
          },
          "subject": {
            "type": "string",
            "maxLength": 300
          },
          "body_markdown": {
            "type": "string",
            "maxLength": 200000
          },
          "segment": {
            "type": "string",
            "maxLength": 100
          },
          "engine": {
            "$ref": "#/components/schemas/BroadcastEngine"
          }
        },
        "required": [
          "title"
        ]
      },
      "MarketingBroadcastUpdateInput": {
        "type": "object",
        "properties": {
          "title": {
            "type": "string",
            "minLength": 1,
            "maxLength": 300
          },
          "subject": {
            "type": "string",
            "maxLength": 300
          },
          "body_markdown": {
            "type": "string",
            "maxLength": 200000
          },
          "body_html": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 500000
          },
          "segment": {
            "type": "string",
            "maxLength": 100
          },
          "engine": {
            "allOf": [
              {
                "$ref": "#/components/schemas/BroadcastEngine"
              },
              {
                "type": [
                  "string",
                  "null"
                ]
              }
            ]
          },
          "scheduled_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "scheduled",
              "sending",
              "sent",
              "failed"
            ]
          }
        }
      },
      "MarketingXScheduleRule": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "dow": {
            "type": "array",
            "items": {
              "type": "integer"
            }
          },
          "hour": {
            "type": "integer"
          },
          "minute": {
            "type": "integer"
          },
          "source_kind": {
            "type": "string"
          },
          "source_ref": {
            "type": [
              "string",
              "null"
            ]
          },
          "category": {
            "type": [
              "string",
              "null"
            ]
          },
          "enabled": {
            "type": "boolean"
          },
          "last_fired_at": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string"
          },
          "weekdays": {
            "type": "array",
            "items": {
              "type": "integer"
            }
          },
          "sourceType": {
            "type": "string"
          },
          "sourceLabel": {
            "type": [
              "string",
              "null"
            ]
          },
          "categoryFilter": {
            "type": "string"
          },
          "lastFiredAt": {
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "name",
          "dow",
          "hour",
          "minute",
          "source_kind",
          "source_ref",
          "category",
          "enabled",
          "last_fired_at",
          "created_at",
          "weekdays",
          "sourceType",
          "sourceLabel",
          "categoryFilter",
          "lastFiredAt",
          "createdAt"
        ]
      },
      "MarketingXScheduleRuleInput": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          },
          "dow": {
            "type": "array",
            "items": {
              "type": "integer",
              "minimum": 0,
              "maximum": 6
            },
            "minItems": 1,
            "maxItems": 7
          },
          "hour": {
            "type": "integer",
            "minimum": 0,
            "maximum": 23
          },
          "minute": {
            "type": "integer",
            "minimum": 0,
            "maximum": 59
          },
          "source_kind": {
            "type": "string",
            "enum": [
              "template",
              "draft_pool",
              "ai_generate",
              "ai"
            ]
          },
          "source_ref": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 500
          },
          "category": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 100
          },
          "enabled": {
            "type": "boolean"
          },
          "weekdays": {
            "type": "array",
            "items": {
              "type": "integer",
              "minimum": 0,
              "maximum": 6
            },
            "minItems": 1,
            "maxItems": 7
          },
          "sourceType": {
            "type": "string",
            "enum": [
              "template",
              "draft_pool",
              "ai_generate",
              "ai"
            ]
          },
          "sourceLabel": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 500
          },
          "categoryFilter": {
            "type": "string",
            "maxLength": 100
          }
        }
      },
      "MarketingXListenRule": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "listen_kind": {
            "type": "string"
          },
          "target": {
            "type": "string"
          },
          "min_likes_threshold": {
            "type": [
              "integer",
              "null"
            ]
          },
          "must_contain": {
            "type": [
              "string",
              "null"
            ]
          },
          "must_not_contain": {
            "type": [
              "string",
              "null"
            ]
          },
          "action_kinds": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "reply_template": {
            "type": [
              "string",
              "null"
            ]
          },
          "cooldown_minutes": {
            "type": "integer"
          },
          "enabled": {
            "type": "boolean"
          },
          "last_fired_at": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string"
          },
          "type": {
            "type": "string"
          },
          "minLikes": {
            "type": "integer"
          },
          "keywordContains": {
            "type": "string"
          },
          "actions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "replyTemplate": {
            "type": [
              "string",
              "null"
            ]
          },
          "cooldownMinutes": {
            "type": "integer"
          },
          "lastFiredAt": {
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "name",
          "listen_kind",
          "target",
          "min_likes_threshold",
          "must_contain",
          "must_not_contain",
          "action_kinds",
          "reply_template",
          "cooldown_minutes",
          "enabled",
          "last_fired_at",
          "created_at",
          "type",
          "minLikes",
          "keywordContains",
          "actions",
          "replyTemplate",
          "cooldownMinutes",
          "lastFiredAt",
          "createdAt"
        ]
      },
      "XListenKind": {
        "type": "string",
        "enum": [
          "keyword",
          "account"
        ]
      },
      "MarketingXListenRuleInput": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          },
          "listen_kind": {
            "$ref": "#/components/schemas/XListenKind"
          },
          "target": {
            "type": "string",
            "minLength": 1,
            "maxLength": 500
          },
          "min_likes_threshold": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0
          },
          "must_contain": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 500
          },
          "must_not_contain": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 500
          },
          "action_kinds": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "like",
                "repost",
                "reply"
              ]
            },
            "maxItems": 3
          },
          "reply_template": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 4000
          },
          "cooldown_minutes": {
            "type": "integer",
            "minimum": 0,
            "maximum": 10080
          },
          "enabled": {
            "type": "boolean"
          },
          "type": {
            "$ref": "#/components/schemas/XListenKind"
          },
          "minLikes": {
            "type": "integer",
            "minimum": 0
          },
          "keywordContains": {
            "type": "string",
            "maxLength": 500
          },
          "actions": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "like",
                "repost",
                "reply"
              ]
            },
            "maxItems": 3
          },
          "replyTemplate": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 4000
          },
          "cooldownMinutes": {
            "type": "integer",
            "minimum": 0,
            "maximum": 10080
          }
        }
      },
      "MarketingXAccount": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "handle": {
            "type": "string"
          },
          "display_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "tier": {
            "type": [
              "string",
              "null"
            ]
          },
          "is_primary": {
            "type": "boolean"
          },
          "followers_count": {
            "type": "integer"
          },
          "has_token": {
            "type": "boolean"
          },
          "updated_at": {
            "type": [
              "string",
              "null"
            ]
          },
          "following": {
            "type": "integer"
          },
          "postsTotal": {
            "type": "integer"
          },
          "verified": {
            "type": "boolean"
          },
          "bio": {
            "type": [
              "string",
              "null"
            ]
          },
          "displayName": {
            "type": [
              "string",
              "null"
            ]
          },
          "followers": {
            "type": "integer"
          },
          "updatedAt": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "id",
          "handle",
          "display_name",
          "tier",
          "is_primary",
          "followers_count",
          "has_token",
          "updated_at",
          "following",
          "postsTotal",
          "verified",
          "bio",
          "displayName",
          "followers",
          "updatedAt"
        ]
      },
      "MarketingXPost": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "account_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "body": {
            "type": "string"
          },
          "thread_of": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "threadCount": {
            "type": "integer"
          },
          "status": {
            "type": "string"
          },
          "scheduled_at": {
            "type": [
              "string",
              "null"
            ]
          },
          "published_at": {
            "type": [
              "string",
              "null"
            ]
          },
          "x_tweet_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "impressions": {
            "type": "integer"
          },
          "likes": {
            "type": "integer"
          },
          "reposts": {
            "type": "integer"
          },
          "replies": {
            "type": "integer"
          },
          "error_message": {
            "type": [
              "string",
              "null"
            ]
          },
          "metrics_synced_at": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string"
          },
          "updated_at": {
            "type": "string"
          },
          "accountId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "threadOf": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "scheduledAt": {
            "type": [
              "string",
              "null"
            ]
          },
          "publishedAt": {
            "type": [
              "string",
              "null"
            ]
          },
          "xTweetId": {
            "type": [
              "string",
              "null"
            ]
          },
          "errorMessage": {
            "type": [
              "string",
              "null"
            ]
          },
          "metricsSyncedAt": {
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "type": "string"
          },
          "updatedAt": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "account_id",
          "body",
          "thread_of",
          "threadCount",
          "status",
          "scheduled_at",
          "published_at",
          "x_tweet_id",
          "impressions",
          "likes",
          "reposts",
          "replies",
          "error_message",
          "metrics_synced_at",
          "created_at",
          "updated_at",
          "accountId",
          "threadOf",
          "scheduledAt",
          "publishedAt",
          "xTweetId",
          "errorMessage",
          "metricsSyncedAt",
          "createdAt",
          "updatedAt"
        ]
      },
      "XPostStatus": {
        "type": "string",
        "enum": [
          "draft",
          "scheduled",
          "publishing",
          "published",
          "failed",
          "archived"
        ]
      },
      "MarketingXPostCreateInput": {
        "type": "object",
        "properties": {
          "account_id": {
            "type": "string",
            "format": "uuid"
          },
          "body": {
            "type": "string",
            "minLength": 1,
            "maxLength": 4000
          },
          "thread_of": {
            "type": "string",
            "format": "uuid"
          },
          "thread_order": {
            "type": "integer",
            "minimum": 0,
            "maximum": 50
          },
          "status": {
            "$ref": "#/components/schemas/XPostStatus"
          },
          "scheduled_at": {
            "type": "string",
            "format": "date-time"
          },
          "media_urls": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uri"
            },
            "maxItems": 4
          }
        },
        "required": [
          "account_id",
          "body"
        ]
      },
      "MarketingXPostUpdateInput": {
        "type": "object",
        "properties": {
          "body": {
            "type": "string",
            "minLength": 1,
            "maxLength": 4000
          },
          "thread_of": {
            "type": "string",
            "format": "uuid"
          },
          "thread_order": {
            "type": "integer",
            "minimum": 0,
            "maximum": 50
          },
          "status": {
            "$ref": "#/components/schemas/XPostStatus"
          },
          "scheduled_at": {
            "type": "string",
            "format": "date-time"
          },
          "media_urls": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uri"
            },
            "maxItems": 4
          }
        }
      },
      "MarketingXAutomationLog": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "rule_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "rule_kind": {
            "type": [
              "string",
              "null"
            ]
          },
          "rule_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "fired_at": {
            "type": "string"
          },
          "target_tweet_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "author": {
            "type": [
              "string",
              "null"
            ]
          },
          "action": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": [
              "string",
              "null"
            ]
          },
          "error": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "id",
          "rule_id",
          "rule_kind",
          "rule_name",
          "fired_at",
          "target_tweet_id",
          "author",
          "action",
          "status",
          "error"
        ]
      },
      "MarketingXCompetitor": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "handle": {
            "type": "string"
          },
          "display_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "memo": {
            "type": [
              "string",
              "null"
            ]
          },
          "followers_count": {
            "type": "integer"
          },
          "posts_per_day": {
            "type": [
              "number",
              "null"
            ]
          },
          "added_at": {
            "type": "string"
          },
          "displayName": {
            "type": "string"
          },
          "followers": {
            "type": "integer"
          },
          "postsPerWeek": {
            "type": "number"
          },
          "engagementRate": {
            "type": "number"
          },
          "topPost": {
            "type": "object",
            "properties": {
              "body": {
                "type": "string"
              },
              "likes": {
                "type": "integer"
              },
              "postedAt": {
                "type": "string"
              }
            },
            "required": [
              "body",
              "likes",
              "postedAt"
            ]
          },
          "addedAt": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "handle",
          "display_name",
          "memo",
          "followers_count",
          "posts_per_day",
          "added_at",
          "displayName",
          "followers",
          "postsPerWeek",
          "engagementRate",
          "topPost",
          "addedAt"
        ]
      },
      "IntegrationProvider": {
        "type": "string",
        "enum": [
          "meta",
          "x",
          "ga4",
          "search_console",
          "resend",
          "sendgrid",
          "supabase_email"
        ]
      },
      "MarketingIntegrationEnvelope": {
        "type": "object",
        "properties": {
          "provider": {
            "$ref": "#/components/schemas/IntegrationProvider"
          },
          "status": {
            "type": "string"
          },
          "connected": {
            "type": "boolean"
          },
          "updated_at": {
            "type": [
              "string",
              "null"
            ]
          },
          "last_checked_at": {
            "type": [
              "string",
              "null"
            ]
          },
          "last_error": {
            "type": [
              "string",
              "null"
            ]
          },
          "public_fields": {
            "type": "object",
            "additionalProperties": {}
          },
          "has_secret": {
            "type": "object",
            "additionalProperties": {
              "type": "boolean"
            }
          }
        },
        "required": [
          "provider",
          "status",
          "connected",
          "updated_at",
          "last_checked_at",
          "last_error",
          "public_fields",
          "has_secret"
        ]
      },
      "IntegrationStatus": {
        "type": "string",
        "enum": [
          "connected",
          "not_configured",
          "error",
          "pending"
        ]
      },
      "MarketingBrand": {
        "type": "object",
        "properties": {
          "logo_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "primary_color": {
            "type": [
              "string",
              "null"
            ]
          },
          "secondary_color": {
            "type": [
              "string",
              "null"
            ]
          },
          "ng_words": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "default_hashtags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "default_signature": {
            "type": [
              "string",
              "null"
            ]
          },
          "updated_at": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "logo_url",
          "primary_color",
          "secondary_color",
          "ng_words",
          "default_hashtags",
          "default_signature",
          "updated_at"
        ]
      },
      "MarketingBrandPutInput": {
        "type": "object",
        "properties": {
          "logo_url": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 2000
          },
          "primary_color": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 30
          },
          "secondary_color": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 30
          },
          "ng_words": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "string",
              "maxLength": 100
            },
            "maxItems": 500
          },
          "default_hashtags": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "string",
              "maxLength": 100
            },
            "maxItems": 100
          },
          "default_signature": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 2000
          }
        }
      },
      "MarketingAsset": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "kind": {
            "type": "string",
            "enum": [
              "image",
              "video",
              "gif",
              "logo",
              "icon",
              "banner"
            ]
          },
          "mime_type": {
            "type": [
              "string",
              "null"
            ]
          },
          "r2_key": {
            "type": "string"
          },
          "public_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "thumbnail_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "image_url_optimized": {
            "type": [
              "string",
              "null"
            ]
          },
          "image_url_srcset": {
            "type": [
              "string",
              "null"
            ]
          },
          "image_url_picture": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "avif": {
                "type": "string"
              },
              "webp": {
                "type": "string"
              },
              "fallback_src": {
                "type": "string"
              }
            },
            "required": [
              "avif",
              "webp",
              "fallback_src"
            ]
          },
          "width": {
            "type": [
              "integer",
              "null"
            ]
          },
          "height": {
            "type": [
              "integer",
              "null"
            ]
          },
          "byte_size": {
            "type": [
              "integer",
              "null"
            ]
          },
          "duration_seconds": {
            "type": [
              "integer",
              "null"
            ]
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "alt_text": {
            "type": [
              "string",
              "null"
            ]
          },
          "uploaded_by": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "created_at": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "name",
          "kind",
          "mime_type",
          "r2_key",
          "public_url",
          "width",
          "height",
          "byte_size",
          "duration_seconds",
          "tags",
          "alt_text",
          "uploaded_by",
          "created_at"
        ]
      },
      "NotificationKind": {
        "type": "string",
        "enum": [
          "x_post_failed",
          "newsletter_failed",
          "campaign_start",
          "campaign_end",
          "automation_fired",
          "integration_error"
        ]
      },
      "NotificationSeverity": {
        "type": "string",
        "enum": [
          "info",
          "warning",
          "error",
          "success"
        ]
      },
      "MarketingNotification": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "user_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "kind": {
            "$ref": "#/components/schemas/NotificationKind"
          },
          "title": {
            "type": "string"
          },
          "body": {
            "type": [
              "string",
              "null"
            ]
          },
          "severity": {
            "$ref": "#/components/schemas/NotificationSeverity"
          },
          "read_at": {
            "type": [
              "string",
              "null"
            ]
          },
          "link": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "user_id",
          "kind",
          "title",
          "body",
          "severity",
          "read_at",
          "link",
          "created_at"
        ]
      },
      "MarketingActivityLog": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "user_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "action": {
            "type": "string"
          },
          "target_kind": {
            "type": [
              "string",
              "null"
            ]
          },
          "target_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "detail": {},
          "created_at": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "user_id",
          "action",
          "target_kind",
          "target_id",
          "created_at"
        ]
      },
      "CampaignObjective": {
        "type": [
          "string",
          "null"
        ],
        "enum": [
          "awareness",
          "traffic",
          "conversion",
          "engagement"
        ]
      },
      "CampaignStatus": {
        "type": "string",
        "enum": [
          "planning",
          "active",
          "paused",
          "completed",
          "archived"
        ]
      },
      "MarketingCampaignTargetKpis": {
        "type": [
          "object",
          "null"
        ],
        "additionalProperties": {
          "anyOf": [
            {
              "type": "number"
            },
            {
              "type": "string"
            },
            {
              "type": "boolean"
            },
            {
              "type": "null"
            },
            {
              "type": "null"
            }
          ]
        }
      },
      "MarketingCampaign": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "objective": {
            "$ref": "#/components/schemas/CampaignObjective"
          },
          "status": {
            "$ref": "#/components/schemas/CampaignStatus"
          },
          "start_date": {
            "type": "string"
          },
          "end_date": {
            "type": [
              "string",
              "null"
            ]
          },
          "channels": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "target_kpis": {
            "$ref": "#/components/schemas/MarketingCampaignTargetKpis"
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "created_by": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "created_at": {
            "type": "string"
          },
          "updated_at": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "name",
          "description",
          "objective",
          "status",
          "start_date",
          "end_date",
          "channels",
          "target_kpis",
          "tags",
          "created_by",
          "created_at",
          "updated_at"
        ]
      },
      "MarketingCampaignCreateInput": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          },
          "description": {
            "type": "string",
            "maxLength": 5000
          },
          "objective": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CampaignObjective"
              },
              {
                "type": "string"
              }
            ]
          },
          "status": {
            "$ref": "#/components/schemas/CampaignStatus"
          },
          "start_date": {
            "type": "string",
            "minLength": 1
          },
          "end_date": {
            "type": "string"
          },
          "channels": {
            "type": "array",
            "items": {
              "type": "string",
              "maxLength": 50
            },
            "maxItems": 50
          },
          "target_kpis": {
            "type": "object",
            "additionalProperties": {
              "anyOf": [
                {
                  "type": "number"
                },
                {
                  "type": "string"
                },
                {
                  "type": "boolean"
                },
                {
                  "type": "null"
                },
                {
                  "type": "null"
                }
              ]
            }
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string",
              "maxLength": 50
            },
            "maxItems": 50
          }
        },
        "required": [
          "name",
          "start_date"
        ]
      },
      "MarketingCampaignLinkedResource": {
        "type": [
          "object",
          "null"
        ],
        "properties": {
          "title": {
            "type": "string"
          },
          "thumbnail_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "title"
        ]
      },
      "MarketingCampaignItem": {
        "type": "object",
        "properties": {
          "campaign_id": {
            "type": "string",
            "format": "uuid"
          },
          "item_kind": {
            "type": "string",
            "enum": [
              "instagram_post",
              "x_post",
              "article",
              "ad",
              "newsletter"
            ]
          },
          "item_ref": {
            "type": "string"
          },
          "item_id": {
            "type": "string"
          },
          "linked_at": {
            "type": "string"
          },
          "linked_resource": {
            "$ref": "#/components/schemas/MarketingCampaignLinkedResource"
          }
        },
        "required": [
          "campaign_id",
          "item_kind",
          "item_ref",
          "item_id",
          "linked_at"
        ]
      },
      "MarketingCampaignMetrics": {
        "type": "object",
        "properties": {
          "campaign_id": {
            "type": "string",
            "format": "uuid"
          },
          "totals": {
            "type": "object",
            "properties": {
              "articles": {
                "type": "integer"
              },
              "ads": {
                "type": "integer"
              },
              "x_posts": {
                "type": "integer"
              },
              "instagram_posts": {
                "type": "integer"
              },
              "newsletters": {
                "type": "integer"
              }
            },
            "required": [
              "articles",
              "ads",
              "x_posts",
              "instagram_posts",
              "newsletters"
            ]
          },
          "pv": {
            "type": "integer"
          },
          "ad_impressions": {
            "type": "integer"
          },
          "ad_clicks": {
            "type": "integer"
          },
          "ctr": {
            "type": "number"
          },
          "x_impressions": {
            "type": "integer"
          },
          "x_likes": {
            "type": "integer"
          },
          "newsletters_sent": {
            "type": "integer"
          }
        },
        "required": [
          "campaign_id",
          "totals",
          "pv",
          "ad_impressions",
          "ad_clicks",
          "ctr",
          "x_impressions",
          "x_likes",
          "newsletters_sent"
        ]
      },
      "MarketingCampaignDetail": {
        "allOf": [
          {
            "$ref": "#/components/schemas/MarketingCampaign"
          },
          {
            "type": "object",
            "properties": {
              "items": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/MarketingCampaignItem"
                }
              }
            },
            "required": [
              "items"
            ]
          }
        ]
      },
      "MarketingCampaignDetailWithMetrics": {
        "allOf": [
          {
            "$ref": "#/components/schemas/MarketingCampaignDetail"
          },
          {
            "type": "object",
            "properties": {
              "metrics": {
                "$ref": "#/components/schemas/MarketingCampaignMetrics"
              }
            }
          }
        ]
      },
      "MarketingCampaignUpdateInput": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          },
          "description": {
            "type": "string",
            "maxLength": 5000
          },
          "objective": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CampaignObjective"
              },
              {
                "type": "string"
              }
            ]
          },
          "status": {
            "$ref": "#/components/schemas/CampaignStatus"
          },
          "start_date": {
            "type": "string",
            "minLength": 1
          },
          "end_date": {
            "type": "string"
          },
          "channels": {
            "type": "array",
            "items": {
              "type": "string",
              "maxLength": 50
            },
            "maxItems": 50
          },
          "target_kpis": {
            "type": "object",
            "additionalProperties": {
              "anyOf": [
                {
                  "type": "number"
                },
                {
                  "type": "string"
                },
                {
                  "type": "boolean"
                },
                {
                  "type": "null"
                },
                {
                  "type": "null"
                }
              ]
            }
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string",
              "maxLength": 50
            },
            "maxItems": 50
          }
        }
      },
      "MarketingAnalyticsSummary": {
        "type": "object",
        "properties": {
          "from": {
            "type": "string"
          },
          "to": {
            "type": "string"
          },
          "totalPv": {
            "type": "integer"
          },
          "totalImpressions": {
            "type": "integer"
          },
          "totalClicks": {
            "type": "integer"
          },
          "ctr": {
            "type": "number"
          },
          "newsletters_sent": {
            "type": "integer"
          },
          "posts_published": {
            "type": "integer"
          },
          "campaigns_active": {
            "type": "integer"
          },
          "_meta": {
            "$ref": "#/components/schemas/SyncMeta"
          }
        },
        "required": [
          "from",
          "to",
          "totalPv",
          "totalImpressions",
          "totalClicks",
          "ctr",
          "newsletters_sent",
          "posts_published",
          "campaigns_active",
          "_meta"
        ]
      },
      "MarketingAnalyticsArticleRow": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "slug": {
            "type": [
              "string",
              "null"
            ]
          },
          "title": {
            "type": "string"
          },
          "category": {
            "type": [
              "string",
              "null"
            ]
          },
          "author_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "published_at": {
            "type": [
              "string",
              "null"
            ]
          },
          "pv": {
            "type": "integer"
          },
          "pv_diff_pct": {
            "type": "number"
          }
        },
        "required": [
          "id",
          "slug",
          "title",
          "category",
          "author_name",
          "published_at",
          "pv",
          "pv_diff_pct"
        ]
      },
      "MarketingAnalyticsAdRow": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "start_date": {
            "type": [
              "string",
              "null"
            ]
          },
          "end_date": {
            "type": [
              "string",
              "null"
            ]
          },
          "impressions": {
            "type": "integer"
          },
          "clicks": {
            "type": "integer"
          },
          "ctr": {
            "type": "number"
          },
          "days_total": {
            "type": "integer"
          },
          "days_elapsed": {
            "type": "integer"
          },
          "progress": {
            "type": "number"
          },
          "daily": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "date": {
                  "type": "string"
                },
                "clicks": {
                  "type": "integer"
                }
              },
              "required": [
                "date",
                "clicks"
              ]
            }
          }
        },
        "required": [
          "id",
          "name",
          "status",
          "start_date",
          "end_date",
          "impressions",
          "clicks",
          "ctr",
          "days_total",
          "days_elapsed",
          "progress",
          "daily"
        ]
      },
      "MarketingAnalyticsCrossChannelRow": {
        "type": "object",
        "properties": {
          "channel": {
            "type": "string"
          },
          "posts": {
            "type": "integer"
          },
          "impressions": {
            "type": "integer"
          },
          "likes": {
            "type": "integer"
          },
          "clicks": {
            "type": "integer"
          },
          "pv": {
            "type": "integer"
          }
        },
        "required": [
          "channel",
          "posts",
          "impressions",
          "likes",
          "clicks",
          "pv"
        ]
      },
      "MarketingContentPoolItem": {
        "type": "object",
        "properties": {
          "kind": {
            "type": "string",
            "enum": [
              "article",
              "instagram_post",
              "x_post",
              "ad",
              "newsletter",
              "asset"
            ]
          },
          "id": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "status": {
            "type": [
              "string",
              "null"
            ]
          },
          "thumbnail_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "updated_at": {
            "type": [
              "string",
              "null"
            ]
          },
          "meta": {
            "type": "object",
            "additionalProperties": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "number"
                },
                {
                  "type": "boolean"
                },
                {
                  "type": "null"
                },
                {
                  "type": "null"
                }
              ]
            }
          }
        },
        "required": [
          "kind",
          "id",
          "title",
          "status",
          "thumbnail_url",
          "updated_at"
        ]
      },
      "MarketingCalendarEvent": {
        "type": "object",
        "properties": {
          "kind": {
            "type": "string",
            "enum": [
              "instagram_post",
              "x_post",
              "article",
              "ad",
              "newsletter"
            ]
          },
          "id": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "status": {
            "type": [
              "string",
              "null"
            ]
          },
          "channel": {
            "type": "string",
            "enum": [
              "x",
              "article",
              "ad",
              "newsletter",
              "instagram"
            ]
          },
          "at": {
            "type": "string"
          }
        },
        "required": [
          "kind",
          "id",
          "title",
          "status",
          "channel",
          "at"
        ]
      },
      "MarketingCalendarDay": {
        "type": "object",
        "properties": {
          "date": {
            "type": "string"
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MarketingCalendarEvent"
            }
          }
        },
        "required": [
          "date",
          "items"
        ]
      },
      "MarketingCalendarResponse": {
        "type": "object",
        "properties": {
          "from": {
            "type": "string"
          },
          "to": {
            "type": "string"
          },
          "days": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MarketingCalendarDay"
            }
          },
          "_meta": {
            "$ref": "#/components/schemas/SyncMeta"
          }
        },
        "required": [
          "from",
          "to",
          "days",
          "_meta"
        ]
      },
      "MarketingArticleCategory": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string"
          },
          "label": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "sort_order": {
            "type": "integer"
          },
          "is_deleted": {
            "type": "boolean"
          },
          "created_at": {
            "type": "string"
          },
          "updated_at": {
            "type": "string"
          }
        },
        "required": [
          "code",
          "label",
          "description",
          "sort_order",
          "is_deleted",
          "created_at",
          "updated_at"
        ]
      },
      "MarketingArticleCategoryUpdateInput": {
        "type": "object",
        "properties": {
          "label": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 2000
          },
          "sort_order": {
            "type": "integer",
            "minimum": 0,
            "maximum": 10000
          }
        }
      },
      "MarketingStoreIntegration": {
        "type": "object",
        "properties": {
          "store": {
            "type": "string",
            "enum": [
              "play_store",
              "app_store"
            ]
          },
          "display_name": {
            "type": "string"
          },
          "package_or_app_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "vendor_number": {
            "type": [
              "string",
              "null"
            ]
          },
          "connection_status": {
            "type": "string",
            "enum": [
              "not_configured",
              "connected",
              "error"
            ]
          },
          "last_status_message": {
            "type": [
              "string",
              "null"
            ]
          },
          "last_synced_at": {
            "type": [
              "string",
              "null"
            ]
          },
          "last_sync_error": {
            "type": [
              "string",
              "null"
            ]
          },
          "updated_at": {
            "type": [
              "string",
              "null"
            ]
          },
          "_meta": {
            "type": "object",
            "properties": {
              "synced": {
                "type": "boolean"
              },
              "synced_at": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "source": {
                "type": "string"
              }
            },
            "required": [
              "synced",
              "synced_at",
              "source"
            ]
          }
        },
        "required": [
          "store",
          "display_name",
          "package_or_app_id",
          "vendor_number",
          "connection_status",
          "last_status_message",
          "last_synced_at",
          "last_sync_error",
          "updated_at",
          "_meta"
        ]
      },
      "MarketingStoreIntegrationUpdate": {
        "type": "object",
        "properties": {
          "package_or_app_id": {
            "type": "string",
            "minLength": 1
          },
          "vendor_number": {
            "type": "string",
            "minLength": 1
          }
        }
      }
    },
    "parameters": {}
  },
  "paths": {
    "/.well-known/apple-app-site-association": {
      "get": {
        "tags": [
          "well-known"
        ],
        "summary": "iOS Universal Links 設定 / iOS Universal Links Config",
        "description": "Apple が Universal Links 検証時に取得する JSON。\n\n- 認証不要・公開エンドポイント。\n- Content-Type は application/json 固定。\n- Cache-Control: public, max-age=86400（24 時間）。\n- `appIDs` は環境変数 `IOS_APP_ID`（形式: TEAMID.co.jp.parky.app）から取得。\n  または `IOS_APP_TEAM_ID` + `IOS_APP_BUNDLE_ID` を個別に指定（優先）。\n  すべて未設定時は `details` と `webcredentials.apps` を空配列にして返す。\n- 対応パス: /parking-lots/*, /parking-sessions/*, /share/parking/*, /articles/*, /auth/*",
        "responses": {
          "200": {
            "description": "Apple App Site Association",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AppleAppSiteAssociation"
                }
              }
            }
          }
        },
        "operationId": "webWellKnownAppleAppSiteAssociationList"
      }
    },
    "/.well-known/assetlinks.json": {
      "get": {
        "tags": [
          "well-known"
        ],
        "summary": "Android App Links 設定 / Android App Links Config",
        "description": "Android が App Links 検証時に取得する JSON。\n\n- 認証不要・公開エンドポイント。\n- Content-Type は application/json 固定。\n- Cache-Control: public, max-age=86400（24 時間）。\n- `package_name` は環境変数 `ANDROID_PACKAGE_NAME` から取得。\n- `sha256_cert_fingerprints` は環境変数 `ANDROID_APP_CERT_FINGERPRINTS`（カンマ区切り）から取得。\n  未設定時は空配列 `[]`（Android 側は App Links 検出しないだけで 200）。",
        "responses": {
          "200": {
            "description": "Digital Asset Links",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AssetLinksResponse"
                }
              }
            }
          }
        },
        "operationId": "webWellKnownAssetlinksList"
      }
    },
    "/v1/marketing/articles": {
      "get": {
        "tags": [
          "記事 / Articles"
        ],
        "summary": "記事一覧 / Article List",
        "description": "### 用途\n管理者向けに `articles` テーブルの記事を一覧する。公開済み（`published`）だけでなく\n下書き（`draft`）・予約・非公開も含めた全ステータスを返し、タイトル ILIKE / カテゴリ /\nステータスで絞り込める。並びは `updated_at DESC`。\n\n### 管理者ポータルでの使用タイミング\n- コンテンツ管理 > 記事管理 のメイン一覧テーブルを開いたとき\n- ステータスタブ（公開中 / 下書き / 予約）切替時\n- 検索ボックスへのキーワード入力（タイトル ILIKE 部分一致）\n- カテゴリドロップダウンでフィルタしたとき\n\n### 認証・認可\n`requireAdmin`。管理者ホワイトリストに登録された Auth ユーザーのみアクセス可。\n\n### 挙動・制約\n- ページング: `PageQuerySchema`（page / limit）\n- `total` は別 COUNT クエリで返却（同一フィルタで集計）\n- ソフトデリート列は持たないため物理削除前提\n\n### 関連\n- `POST /v1/admin/articles` — 新規作成\n- `PATCH /v1/admin/articles/{slug}` — 部分更新\n- `DELETE /v1/admin/articles/{slug}` — 物理削除",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^\\d+$",
              "description": "1 はじまりのページ番号",
              "examples": [
                "1"
              ]
            },
            "required": false,
            "name": "page",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "pattern": "^\\d+$",
              "description": "1 ページあたりの件数（最大 2000）",
              "examples": [
                "20"
              ]
            },
            "required": false,
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "cursor ページング利用時の不透明トークン。offset ページング（page/limit）とは排他。"
            },
            "required": false,
            "name": "cursor",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "status",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "category",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "q",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "一覧",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/AdminArticle"
                      }
                    },
                    "page": {
                      "type": "integer",
                      "minimum": 1
                    },
                    "limit": {
                      "type": "integer",
                      "minimum": 1
                    },
                    "total": {
                      "type": "integer",
                      "minimum": 0
                    },
                    "total_is_estimate": {
                      "type": "boolean",
                      "description": "true のとき total は pg_class.reltuples 由来の概算値（数千行ズレ得る）。exact COUNT のときは field 自体が省略される。"
                    }
                  },
                  "required": [
                    "items",
                    "page",
                    "limit",
                    "total"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingArticlesList"
      },
      "post": {
        "tags": [
          "記事 / Articles"
        ],
        "summary": "記事を作成 / Create Article",
        "description": "### 用途\n新規記事を `articles` テーブルに INSERT する。本文・カテゴリ・タグ・サムネイル URL 等を\nボディで自由に渡す（`passthrough`）。`status` を `draft` で作って後から `published` に\n切り替える運用を想定。\n\n### 管理者ポータルでの使用タイミング\n- コンテンツ管理 > 記事管理 の「新規作成」ボタン押下時\n- 一覧から複製（コピー＆編集）した直後の保存時\n- 外部 CMS / インポーター経由の自動取り込み\n\n### 認証・認可\n`requireAdmin`。\n\n### 挙動・制約\n- ボディは `passthrough` のため列名と一致するキーだけ採用される（タイポはそのまま列指定）\n- 作成成功時に `recordAdminActivityBestEffort` で `article.create` 監査ログを残す\n- 画像アップロードは別 API（assets 系）で先に行い、`thumbnail_url` をここで保存\n\n### 関連\n- `GET /v1/admin/articles` — 一覧\n- `PATCH /v1/admin/articles/{slug}` — 公開ステータス変更等",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ArticleUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "作成済み",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AdminArticle"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingArticlesCreate"
      }
    },
    "/v1/marketing/articles/{slug}": {
      "get": {
        "tags": [
          "記事 / Articles"
        ],
        "summary": "記事単体取得（タグ含む / natural key = articles.slug）",
        "description": "### 用途\n指定 slug の記事を 1 件取得する。タグも JOIN 済みで返却するので、\n編集画面の初期表示で `GET /v1/admin/articles?limit=...` → client 側 find の\n迂回ロジックを排除できる。\n\n### 管理者ポータルでの使用タイミング\n- 記事編集画面（`ArticleDetailPage` / `ArticleEditorPage`）の初期ロード\n- プレビュー / 差し戻し確認モーダルの表示\n\n### 認証・認可\n`requireAdmin`（list と同じ）。\n\n### 挙動・制約\n- path param `{slug}` は `articles.slug` (natural key, UNIQUE) を指定\n- articles テーブルはソフトデリート列を持たないため、単純に slug 一致で引く\n- 該当 slug が無い場合は `not_found`（404）\n- 返却 shape は list 各要素と同一（タグを article_tag_links 経由で集約）\n\n### 関連\n- `GET /v1/admin/articles` — 一覧\n- `PATCH /v1/admin/articles/{slug}` — 更新",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 200,
              "pattern": "^[a-z0-9_\\-/]+$"
            },
            "required": true,
            "name": "slug",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "記事詳細",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AdminArticle"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingArticlesGet"
      },
      "patch": {
        "tags": [
          "記事 / Articles"
        ],
        "summary": "記事を更新（natural key = articles.slug）",
        "description": "### 用途\n既存記事を部分更新する。リクエストボディに含まれた列だけが UPDATE 対象（postgres.js の\n`sql(body)` ヘルパー）。空ボディの場合は現行レコードをそのまま返す。\n\n### 管理者ポータルでの使用タイミング\n- 記事編集画面の「保存」ボタン押下時\n- 一覧画面でステータスをインライン切替したとき（draft ↔ published）\n- カテゴリ・タグ一括編集モーダルからのコミット\n\n### 認証・認可\n`requireAdmin`。\n\n### 挙動・制約\n- path param `{slug}` は `articles.slug` (natural key, UNIQUE) を指定\n- 公開フラグ: `status = 'published'` のとき `published_at` をボディ側で同時にセットする運用\n- 該当 slug が無い場合は `not_found`（404）\n- 監査ログ `article.update` に diff（送信ボディ）を記録\n\n### 関連\n- `POST /v1/admin/articles` — 新規作成\n- `DELETE /v1/admin/articles/{slug}` — 削除",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 200,
              "pattern": "^[a-z0-9_\\-/]+$"
            },
            "required": true,
            "name": "slug",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ArticleUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "更新後",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AdminArticle"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingArticlesUpdate"
      },
      "delete": {
        "tags": [
          "記事 / Articles"
        ],
        "summary": "記事を削除（natural key = articles.slug）",
        "description": "### 用途\n記事を物理削除する（`DELETE FROM articles`）。ソフトデリート列は持たないため復元不可。\n誤削除を防ぐためポータル側で確認モーダルを必ず挟むこと。\n\n### 管理者ポータルでの使用タイミング\n- 記事編集画面の「削除」ボタン → 確認モーダル承認時\n- 一覧画面の行アクションメニュー「削除」\n- 複数選択での一括削除（ループ呼び出し）\n\n### 認証・認可\n`requireAdmin`。\n\n### 挙動・制約\n- path param `{slug}` は `articles.slug` (natural key, UNIQUE) を指定\n- 物理削除（履歴テーブルや関連テーブルへの cascade は DB スキーマに依存）\n- 監査ログ `article.delete` にタイトルを残す（削除前に SELECT して保存）\n- 存在しない slug でも 204 を返す（冪等）\n\n### 関連\n- `PATCH /v1/admin/articles/{slug}` — ソフトに非公開化したい場合は `status` を変える",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 200,
              "pattern": "^[a-z0-9_\\-/]+$"
            },
            "required": true,
            "name": "slug",
            "in": "path"
          }
        ],
        "responses": {
          "204": {
            "description": "成功"
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingArticlesDelete"
      }
    },
    "/v1/marketing/ads": {
      "get": {
        "tags": [
          "広告 / Ads"
        ],
        "summary": "広告一覧 / Ad List",
        "description": "### 用途\nアプリ内バナー広告 (`ads`) の管理一覧。配信ステータス・名前 ILIKE で絞り込みでき、\nインプレッション / クリック数も同時に返す。\n\n### 認証・認可\n`requireAdmin`。",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^\\d+$",
              "description": "1 はじまりのページ番号",
              "examples": [
                "1"
              ]
            },
            "required": false,
            "name": "page",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "pattern": "^\\d+$",
              "description": "1 ページあたりの件数（最大 2000）",
              "examples": [
                "20"
              ]
            },
            "required": false,
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "cursor ページング利用時の不透明トークン。offset ページング（page/limit）とは排他。"
            },
            "required": false,
            "name": "cursor",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "status",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "q",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "一覧",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/AdminAd"
                      }
                    },
                    "page": {
                      "type": "integer",
                      "minimum": 1
                    },
                    "limit": {
                      "type": "integer",
                      "minimum": 1
                    },
                    "total": {
                      "type": "integer",
                      "minimum": 0
                    },
                    "total_is_estimate": {
                      "type": "boolean",
                      "description": "true のとき total は pg_class.reltuples 由来の概算値（数千行ズレ得る）。exact COUNT のときは field 自体が省略される。"
                    }
                  },
                  "required": [
                    "items",
                    "page",
                    "limit",
                    "total"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingAdsList"
      },
      "post": {
        "tags": [
          "広告 / Ads"
        ],
        "summary": "広告を作成 / Create Ad",
        "description": "新規広告枠を `ads` テーブルに登録する。",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AdUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "作成済み",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AdminAd"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingAdsCreate"
      }
    },
    "/v1/marketing/ads/{id}": {
      "get": {
        "tags": [
          "広告 / Ads"
        ],
        "summary": "広告詳細 / Ad Detail",
        "description": "### 用途\n指定 ID の広告レコードを 1 件返す。list の `limit` 超過で拾えないケースを避けるため、\n編集画面初期ロードは list ではなく本 endpoint を使うこと。\n\n### 認証・認可\n`requireAdmin`（list と同じ）。",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid",
              "examples": [
                "00000000-0000-0000-0000-000000000000"
              ]
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "広告詳細",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AdminAd"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingAdsGet"
      },
      "patch": {
        "tags": [
          "広告 / Ads"
        ],
        "summary": "広告を更新 / Update Ad",
        "description": "広告レコードを部分更新する。空ボディなら現行値をそのまま返却。",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid",
              "examples": [
                "00000000-0000-0000-0000-000000000000"
              ]
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AdUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "更新後",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AdminAd"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingAdsUpdate"
      },
      "delete": {
        "tags": [
          "広告 / Ads"
        ],
        "summary": "広告を削除 / Delete Ad",
        "description": "広告を物理削除する。",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid",
              "examples": [
                "00000000-0000-0000-0000-000000000000"
              ]
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "204": {
            "description": "成功"
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingAdsDelete"
      }
    },
    "/v1/marketing/instagram/categories": {
      "get": {
        "tags": [
          "Instagram / Instagram"
        ],
        "summary": "スライドカテゴリ一覧 / Slide Category List",
        "description": "### 用途\nInstagram スライドテンプレートを分類する **スライドカテゴリ**（cover / spot_detail / ranking / cta など）の一覧を返す。\nテンプレート登録画面のカテゴリセレクター、フィルタリング、AI 自動生成時の種別判定に使う。\nソフトデリート済み（`is_deleted = 1`）は含まれず、`sort_order` 昇順で返す。\n\n### 管理者ポータルでの使用タイミング\n- Instagram tool > テンプレート管理画面の「カテゴリ」セレクター描画時\n- スライドカテゴリ管理画面（CRUD）を開いたとき\n- 投稿一括生成ウィザードでテンプレ種別を選ばせるとき\n\n### 認証・認可\n`requireAdmin` 必須（管理者ポータルからのみアクセス可）。追加の権限スコープは不要。\n\n### 挙動・制約\nCloudflare D1（`INSTAGRAM_DB` binding）の `ig_slide_categories` テーブルを SELECT。\nParky Supabase とは完全分離されたデータストア。\n\n### 関連\n- `POST /v1/admin/instagram/categories` — 新規カテゴリ追加\n- `PATCH /v1/admin/instagram/categories/{code}` — ラベル/プレフィックス更新\n- `DELETE /v1/admin/instagram/categories/{code}` — ソフトデリート",
        "responses": {
          "200": {
            "description": "一覧",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/IgSlideCategory"
                  }
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingInstagramCategoriesList"
      },
      "post": {
        "tags": [
          "Instagram / Instagram"
        ],
        "summary": "スライドカテゴリを作成 / Create Slide Category",
        "description": "### 用途\n新しいスライドカテゴリを登録する。`code`（種別キー）はテンプレートとリンクする一意 ID で、\n`label`（日本語表示名）、`prefix`（命名規則のヒント）、`sort_order` を指定する。\n\n### 管理者ポータルでの使用タイミング\n- Instagram tool > 設定 > スライドカテゴリ管理 > 「+ 追加」ダイアログの保存時\n\n### 認証・認可\n`requireAdmin` 必須。\n\n### 挙動・制約\n`ig_slide_categories` に INSERT。`sort_order` 未指定時は 50 で挿入。`code` は UNIQUE 想定（重複時は SQLite が制約違反を返す）。\n\n### 関連\n- `GET /v1/admin/instagram/categories` — 一覧取得\n- `PATCH /v1/admin/instagram/categories/{code}` — 更新",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "code": {
                    "type": "string",
                    "minLength": 1
                  },
                  "label": {
                    "type": "string",
                    "minLength": 1
                  },
                  "prefix": {
                    "type": "string"
                  },
                  "sort_order": {
                    "type": "integer"
                  }
                },
                "required": [
                  "code",
                  "label"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "作成済み",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IgSlideCategory"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingInstagramCategoriesCreate"
      }
    },
    "/v1/marketing/instagram/categories/{code}": {
      "patch": {
        "tags": [
          "Instagram / Instagram"
        ],
        "summary": "スライドカテゴリを更新 / Update Slide Category",
        "description": "### 用途\n既存スライドカテゴリのラベル / プレフィックス / 並び順を部分更新する。`code` は変更不可。\n\n### 管理者ポータルでの使用タイミング\n- Instagram tool > 設定 > スライドカテゴリ管理 > 行のインライン編集 / モーダル保存時\n- ドラッグ&ドロップで `sort_order` を入れ替えたとき\n\n### 認証・認可\n`requireAdmin` 必須。\n\n### 挙動・制約\n送信されたフィールドだけ動的に SET 句を組み立てて UPDATE。`updated_at` は自動更新。\n対象が存在しない場合は 404 `not_found`。\n\n### 関連\n- `GET /v1/admin/instagram/categories` — 一覧\n- `DELETE /v1/admin/instagram/categories/{code}` — 削除",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "code",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "label": {
                    "type": "string"
                  },
                  "prefix": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "sort_order": {
                    "type": "integer"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "更新済み",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IgSlideCategory"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingInstagramCategoriesUpdate"
      },
      "delete": {
        "tags": [
          "Instagram / Instagram"
        ],
        "summary": "スライドカテゴリをソフト削除 / Soft Delete Slide Category",
        "description": "### 用途\nスライドカテゴリを論理削除する（`is_deleted = 1`）。物理削除はせず、過去のテンプレが参照している\n履歴コードを壊さないようにする。\n\n### 管理者ポータルでの使用タイミング\n- Instagram tool > 設定 > スライドカテゴリ管理 > 行のゴミ箱アイコンタップ\n\n### 認証・認可\n`requireAdmin` 必須。\n\n### 挙動・制約\n`UPDATE ig_slide_categories SET is_deleted = 1` のみ。テンプレ側の `slide_type` は影響を受けない。\n存在しない `code` を指定しても 200 を返す（冪等）。\n\n### 関連\n- `POST /v1/admin/instagram/categories` — 同じ code で再作成は不可（必要なら別 code で）",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "code",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "削除完了",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "success"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingInstagramCategoriesDelete"
      }
    },
    "/v1/marketing/instagram/post-categories": {
      "get": {
        "tags": [
          "Instagram / Instagram"
        ],
        "summary": "投稿カテゴリ一覧 / Post Category List",
        "description": "### 用途\n投稿（キャンペーン）単位の分類タグ「**投稿カテゴリ**」一覧を返す。\n「特集」「リール」「キャンペーン」などのジャンル分けで、ポータル側のフィルタや色分け表示に使う。\n\n### 管理者ポータルでの使用タイミング\n- Instagram tool > 投稿一覧画面のカテゴリフィルター\n- 投稿作成 / 編集モーダルの「カテゴリ」セレクター\n- 投稿カテゴリ管理画面の表示時\n\n### 認証・認可\n`requireAdmin` 必須。\n\n### 挙動・制約\nD1 `ig_post_categories` から `is_deleted = 0` のレコードを `sort_order` 昇順で返す。\n`color` は HEX カラーコード（バッジの色付けに使用）。\n\n### 関連\n- `POST /v1/admin/instagram/post-categories` — 新規追加\n- `PATCH /v1/admin/instagram/posts/{id}` — `post_category_code` で投稿に紐付け",
        "responses": {
          "200": {
            "description": "一覧",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/IgPostCategory"
                  }
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingInstagramPostCategoriesList"
      },
      "post": {
        "tags": [
          "Instagram / Instagram"
        ],
        "summary": "投稿カテゴリを作成 / Create Post Category",
        "description": "### 用途\n新しい投稿カテゴリ（特集・リール・キャンペーン等）を登録する。\n`code` は投稿レコードの `post_category_code` と紐付く。\n\n### 管理者ポータルでの使用タイミング\n- Instagram tool > 設定 > 投稿カテゴリ管理 > 「+ 追加」モーダル保存\n\n### 認証・認可\n`requireAdmin` 必須。\n\n### 挙動・制約\n`ig_post_categories` に INSERT。`color` は HEX 文字列（例: `#7c5cfc`）でバッジ色として使う。\n`sort_order` 未指定時は 50。\n\n### 関連\n- `GET /v1/admin/instagram/post-categories` — 一覧",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "code": {
                    "type": "string",
                    "minLength": 1
                  },
                  "label": {
                    "type": "string",
                    "minLength": 1
                  },
                  "color": {
                    "type": "string"
                  },
                  "sort_order": {
                    "type": "integer"
                  }
                },
                "required": [
                  "code",
                  "label"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "作成済み",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IgPostCategory"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingInstagramPostCategoriesCreate"
      }
    },
    "/v1/marketing/instagram/post-categories/{code}": {
      "patch": {
        "tags": [
          "Instagram / Instagram"
        ],
        "summary": "投稿カテゴリを更新 / Update Post Category",
        "description": "### 用途\n投稿カテゴリのラベル / 色 / 並び順を部分更新する。`code` は不変。\n\n### 管理者ポータルでの使用タイミング\n- Instagram tool > 設定 > 投稿カテゴリ管理 > インライン編集 / 並び替え\n- カラーピッカーでバッジ色を変更したとき\n\n### 認証・認可\n`requireAdmin` 必須。\n\n### 挙動・制約\n送信されたフィールドのみ動的 UPDATE。`updated_at` 自動更新。対象が存在しない場合 404。\n\n### 関連\n- `DELETE /v1/admin/instagram/post-categories/{code}` — ソフトデリート",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "code",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "label": {
                    "type": "string"
                  },
                  "color": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "sort_order": {
                    "type": "integer"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "更新済み",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IgPostCategory"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingInstagramPostCategoriesUpdate"
      },
      "delete": {
        "tags": [
          "Instagram / Instagram"
        ],
        "summary": "投稿カテゴリをソフト削除 / Soft Delete Post Category",
        "description": "### 用途\n投稿カテゴリを論理削除する（`is_deleted = 1`）。既存の投稿が `post_category_code` で参照していても\n履歴ラベルが残るよう物理削除はしない。\n\n### 管理者ポータルでの使用タイミング\n- Instagram tool > 設定 > 投稿カテゴリ管理 > 行のゴミ箱アイコン\n\n### 認証・認可\n`requireAdmin` 必須。\n\n### 挙動・制約\n`UPDATE ig_post_categories SET is_deleted = 1`。冪等（存在しなくても 200）。\n\n### 関連\n- `GET /v1/admin/instagram/post-categories` — 一覧（ソフトデリート除外）",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "code",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "削除",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "success"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingInstagramPostCategoriesDelete"
      }
    },
    "/v1/marketing/instagram/tags": {
      "get": {
        "tags": [
          "Instagram / Instagram"
        ],
        "summary": "タグ一覧 / Tag List",
        "description": "### 用途\nタグの一覧を返す（instagram_tag CRUD）。\n\n### 認証\n要 `requireAdmin`。\n\n### クエリパラメータ\n`limit`/`offset` でページング、`q` で検索（hooks 実装による）。\n\n### 備考\n- レスポンスは `{ items, total }` 形式\n- 追加フィルタは hooks 側で解釈する",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "offset",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "q",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "一覧",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/IgTag"
                  }
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingInstagramTagsList"
      },
      "post": {
        "tags": [
          "Instagram / Instagram"
        ],
        "summary": "タグを作成 / Create Tag",
        "description": "### 用途\nタグを 1 件作成する（instagram_tag CRUD）。\n\n### 認証\n要 `requireAdmin`。\n\n### 備考\n- ID はサーバー側で UUID 発行（hooks 実装依存）\n- admin_activity_logs への記録はこの endpoint では行わない",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 40
                  },
                  "color": {
                    "type": "string"
                  }
                },
                "required": [
                  "name"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "作成済み",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IgTag"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingInstagramTagsCreate"
      }
    },
    "/v1/marketing/instagram/tags/{slug}": {
      "get": {
        "tags": [
          "Instagram / Instagram"
        ],
        "summary": "タグ詳細 / Tag Detail",
        "description": "### 用途\n単一 タグの詳細を返す（instagram_tag CRUD）。\n\n### 認証\n要 `requireAdmin`。\n\n### パスパラメータ\n`slug`: 対象 id\n\n### 備考\n- 未存在は 404 `not_found`",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "slug",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "詳細",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IgTag"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingInstagramTagsGet"
      },
      "patch": {
        "tags": [
          "Instagram / Instagram"
        ],
        "summary": "タグを部分更新 / Partially Update Tag",
        "description": "### 用途\nタグを部分更新する（instagram_tag CRUD）。\n\n### 認証\n要 `requireAdmin`。\n\n### パスパラメータ\n`slug`: 対象 id\n\n### 備考\n- 未存在は 404 `not_found`\n- admin_activity_logs への記録はこの endpoint では行わない",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "slug",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "color": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "更新済み",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IgTag"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingInstagramTagsUpdate"
      },
      "delete": {
        "tags": [
          "Instagram / Instagram"
        ],
        "summary": "タグを削除 / Delete Tag",
        "description": "### 用途\nタグを削除する（instagram_tag CRUD）。\n\n### 認証\n要 `requireAdmin`。\n\n### パスパラメータ\n`slug`: 対象 id\n\n### 備考\n- 存在しない id でも 200（冪等）\n- admin_activity_logs への記録はこの endpoint では行わない",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "slug",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "削除成功",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "success"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingInstagramTagsDelete"
      }
    },
    "/v1/marketing/instagram/posts/{id}/tags": {
      "put": {
        "tags": [
          "Instagram / Instagram"
        ],
        "summary": "投稿のタグを一括設定 / Bulk Set Post Tags",
        "description": "`tag_ids` を真として `ig_campaign_tags` の差分を計算し原子的に適用。`usage_count` も整合性保って増減。`requireAdmin`。",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "tag_ids": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                },
                "required": [
                  "tag_ids"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "更新後タグ一覧",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/IgTag"
                  }
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingInstagramPostsTagsReplace"
      }
    },
    "/v1/marketing/instagram/templates/{id}/tags": {
      "put": {
        "tags": [
          "Instagram / Instagram"
        ],
        "summary": "テンプレートのタグを一括設定 / Bulk Set Template Tags",
        "description": "投稿側と同じ差分計算ロジックで `ig_template_tags` を更新。`requireAdmin`。",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "tag_ids": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                },
                "required": [
                  "tag_ids"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "更新後タグ一覧",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/IgTag"
                  }
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingInstagramTemplatesTagsReplace"
      }
    },
    "/v1/marketing/instagram/templates": {
      "get": {
        "tags": [
          "Instagram / Instagram"
        ],
        "summary": "テンプレート一覧 / Template List",
        "description": "### 用途\nテンプレートの一覧を返す（instagram_template CRUD）。\n\n### 認証\n要 `requireAdmin`。\n\n### クエリパラメータ\n`limit`/`offset` でページング、`q` で検索（hooks 実装による）。\n\n### 備考\n- レスポンスは `{ items, total }` 形式\n- 追加フィルタは hooks 側で解釈する",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "offset",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "q",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "一覧",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/IgTemplate"
                  }
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingInstagramTemplatesList"
      },
      "post": {
        "tags": [
          "Instagram / Instagram"
        ],
        "summary": "テンプレートを作成 / Create Template",
        "description": "### 用途\nテンプレートを 1 件作成する（instagram_template CRUD）。\n\n### 認証\n要 `requireAdmin`。\n\n### 備考\n- ID はサーバー側で UUID 発行（hooks 実装依存）\n- admin_activity_logs への記録はこの endpoint では行わない",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "code": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 20
                  },
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 100
                  },
                  "slide_type": {
                    "type": "string"
                  },
                  "html_body": {
                    "type": "string",
                    "minLength": 1
                  },
                  "slot_schema": {
                    "type": "string",
                    "default": "[]"
                  },
                  "sample_content": {
                    "type": "string",
                    "default": "{}"
                  },
                  "sample_html": {
                    "type": "string",
                    "default": ""
                  },
                  "sort_order": {
                    "type": "integer",
                    "default": 0
                  },
                  "uses_parking_lot": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 1
                  },
                  "genre": {
                    "type": "string",
                    "enum": [
                      "parking",
                      "useful_info"
                    ]
                  }
                },
                "required": [
                  "code",
                  "name",
                  "slide_type",
                  "html_body"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "作成済み",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IgTemplate"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingInstagramTemplatesCreate"
      }
    },
    "/v1/marketing/instagram/templates/{id}": {
      "get": {
        "tags": [
          "Instagram / Instagram"
        ],
        "summary": "テンプレート詳細 / Template Detail",
        "description": "### 用途\n単一 テンプレートの詳細を返す（instagram_template CRUD）。\n\n### 認証\n要 `requireAdmin`。\n\n### パスパラメータ\n`id`: 対象 id\n\n### 備考\n- 未存在は 404 `not_found`",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "詳細",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IgTemplate"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingInstagramTemplatesGet"
      },
      "patch": {
        "tags": [
          "Instagram / Instagram"
        ],
        "summary": "テンプレートを部分更新 / Partially Update Template",
        "description": "### 用途\nテンプレートを部分更新する（instagram_template CRUD）。\n\n### 認証\n要 `requireAdmin`。\n\n### パスパラメータ\n`id`: 対象 id\n\n### 備考\n- 未存在は 404 `not_found`\n- admin_activity_logs への記録はこの endpoint では行わない",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 100
                  },
                  "slide_type": {
                    "type": "string"
                  },
                  "code": {
                    "type": "string"
                  },
                  "html_body": {
                    "type": "string"
                  },
                  "slot_schema": {
                    "type": "string"
                  },
                  "sample_content": {
                    "type": "string"
                  },
                  "sample_html": {
                    "type": "string"
                  },
                  "sort_order": {
                    "type": "integer"
                  },
                  "is_active": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 1
                  },
                  "uses_parking_lot": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 1
                  },
                  "genre": {
                    "type": "string",
                    "enum": [
                      "parking",
                      "useful_info"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "更新済み",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IgTemplate"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingInstagramTemplatesUpdate"
      },
      "delete": {
        "tags": [
          "Instagram / Instagram"
        ],
        "summary": "テンプレートを削除 / Delete Template",
        "description": "### 用途\nテンプレートを削除する（instagram_template CRUD）。\n\n### 認証\n要 `requireAdmin`。\n\n### パスパラメータ\n`id`: 対象 id\n\n### 備考\n- 存在しない id でも 204（冪等）\n- 使用中は 409、`?force=1` で cascade 削除\n- admin_activity_logs への記録はこの endpoint では行わない",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "id",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "0",
                "1"
              ]
            },
            "required": false,
            "name": "force",
            "in": "query"
          }
        ],
        "responses": {
          "204": {
            "description": "削除成功"
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "使用中のため削除不可",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "slide_count": {
                      "type": "number"
                    }
                  },
                  "required": [
                    "error",
                    "slide_count"
                  ]
                }
              }
            }
          }
        },
        "operationId": "marketingInstagramTemplatesDelete"
      }
    },
    "/v1/marketing/instagram/ai-providers": {
      "get": {
        "tags": [
          "Instagram / Instagram"
        ],
        "summary": "利用可能AIプロバイダー一覧 / Available AI Provider List",
        "description": "### 用途\nInstagram tool で使える AI プロバイダー（OpenAI / Anthropic / Workers AI 等）の最小情報を返す。\nセレクター描画用なので `vault_secret_id` 等のシークレット参照キーは含めない。\n\n### 管理者ポータルでの使用タイミング\n- AI 生成系操作（キャプション生成、スロット自動入力、HTML 修正等）の「プロバイダー選択」セレクター描画時\n- 初期表示時に priority 最高のものをデフォルト選択するため\n\n### 認証・認可\n`requireAdmin` 必須。Parky 共通の `ai_providers` テーブル（Supabase）を読むので Hyperdrive 経由。\n\n### 挙動・制約\n`SELECT id, provider_key, display_name, model_name, is_enabled, priority FROM ai_providers`\n`WHERE is_enabled = true ORDER BY priority DESC`。LLM 呼出本体は `loadProviders()` で\nconfig / vault_secret_id まで含めて別途取得する。\n\n### 関連\n- `POST /v1/admin/instagram/posts/{id}/generate-caption` — provider_id を指定可能\n- `POST /v1/admin/instagram/slides/{id}/generate-content` — provider_id を指定可能",
        "responses": {
          "200": {
            "description": "一覧",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "string"
                      },
                      "provider_key": {
                        "type": "string"
                      },
                      "display_name": {
                        "type": "string"
                      },
                      "model_name": {
                        "type": "string"
                      },
                      "is_enabled": {
                        "type": "boolean"
                      },
                      "priority": {
                        "type": "integer"
                      }
                    },
                    "required": [
                      "id",
                      "provider_key",
                      "display_name",
                      "model_name",
                      "is_enabled",
                      "priority"
                    ]
                  }
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingInstagramAiProvidersList"
      }
    },
    "/v1/marketing/instagram/templates/analyze-html": {
      "post": {
        "tags": [
          "Instagram / Instagram"
        ],
        "summary": "HTMLをAIでテンプレート化 / Analyze HTML with AI",
        "description": "### 用途\n既存のスライド HTML を LLM に食わせ、「可変部分」を `{{key}}` プレースホルダに\n置換した *テンプレート HTML* と、各プレースホルダの型定義 (`slot_schema`) を生成する。\n制作済みスライドを量産可能なテンプレートに昇格させる半自動ツール。\n\n### 管理者ポータルでの使用タイミング\n- Instagram tool のテンプレート作成画面で「既存 HTML から作る」選択時\n- ダッシュボードに貼った HTML スニペットに対する「テンプレ化」操作\n\n### 認証・認可\n要 `requireAdmin`。LLM 消費があるため `instagram:ai` 権限が望ましい。\n\n### 挙動・制約\n- LLM は `ai_providers` テーブルから取得（`provider_id` / `model` で指定可能、未指定なら優先順位 1 位）\n- 応答 JSON を抽出・検証し、`slot_schema.type` は text/textarea/number/url/image に正規化\n- JSON 不正時は `bad_gateway` (502) を返す\n- 使用量は `ai_usage_logs` に記録される（トークン数・コスト）\n- HTML 構造・CSS・class は完全保持、可変テキスト / 画像 URL / 数値のみ置換\n\n### 関連\n- `POST /v1/admin/instagram/post-templates` — テンプレート新規登録\n- `POST /v1/admin/instagram/templates/{id}/revise` — テンプレートを LLM でリバイズ",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "html": {
                    "type": "string",
                    "minLength": 10
                  },
                  "slide_type": {
                    "type": "string"
                  },
                  "hint": {
                    "type": "string"
                  },
                  "provider_id": {
                    "type": "string"
                  },
                  "model": {
                    "type": "string"
                  }
                },
                "required": [
                  "html"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "解析結果",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "html_body": {
                      "type": "string"
                    },
                    "slot_schema": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "key": {
                            "type": "string"
                          },
                          "label": {
                            "type": "string"
                          },
                          "type": {
                            "type": "string"
                          },
                          "required": {
                            "type": "boolean"
                          },
                          "placeholder": {
                            "type": "string"
                          }
                        },
                        "required": [
                          "key",
                          "label",
                          "type"
                        ]
                      }
                    },
                    "sample_content": {
                      "type": "object",
                      "additionalProperties": {
                        "type": "string"
                      }
                    }
                  },
                  "required": [
                    "html_body",
                    "slot_schema",
                    "sample_content"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingInstagramTemplatesAnalyzeHtmlCreate"
      }
    },
    "/v1/marketing/instagram/templates/{id}/revise": {
      "post": {
        "tags": [
          "Instagram / Instagram"
        ],
        "summary": "テンプレートをLLMで修正 / Revise Template with LLM",
        "description": "### 用途\nテンプレートの `html_body`（`{{key}}` プレースホルダ入り）または `sample_html`（完成形）を\nLLM で修正する。**保存せずプレビューのみ返却** — 管理者は差分を見てから手動で採用できる。\n\n### 管理者ポータルでの使用タイミング\n- テンプレート編集画面の「AI 提案」→「差分確認」→「採用 / 破棄」\n- 複数案生成して比較する導線\n\n### 認証\n要 `requireAdmin`。\n\n### 挙動・制約\n- `target='template'`: 修正時も `{{key}}` を**絶対に削除・改名しない**ようシステムプロンプトで制約\n- `target='sample'`: プレースホルダ制約なし（完成形を直に編集）\n- `current_html` 指定でユーザー未保存の修正を LLM に渡せる\n- 保存は別途 `PATCH /v1/admin/instagram/templates/{id}` で\n\n### 関連\n- `PATCH /v1/admin/instagram/templates/{id}` — 修正案を採用して保存",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "instructions": {
                    "type": "string",
                    "minLength": 1
                  },
                  "target": {
                    "type": "string",
                    "enum": [
                      "template",
                      "sample"
                    ],
                    "default": "template"
                  },
                  "current_html": {
                    "type": "string"
                  },
                  "provider_id": {
                    "type": "string"
                  },
                  "model": {
                    "type": "string"
                  }
                },
                "required": [
                  "instructions"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "修正案 HTML",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "html": {
                      "type": "string"
                    },
                    "usage": {
                      "type": "object",
                      "properties": {
                        "input_tokens": {
                          "type": "number"
                        },
                        "output_tokens": {
                          "type": "number"
                        }
                      },
                      "required": [
                        "input_tokens",
                        "output_tokens"
                      ]
                    }
                  },
                  "required": [
                    "html",
                    "usage"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingInstagramTemplatesReviseCreate"
      }
    },
    "/v1/marketing/instagram/post-templates": {
      "get": {
        "tags": [
          "Instagram / Instagram"
        ],
        "summary": "投稿テンプレート一覧 / Post Template List",
        "description": "### 用途\n**投稿テンプレート**（複数のスライドテンプレを束ねた雛形）の一覧を返す。\n「カバー → スポット詳細×N → ランキング → CTA」のような定型ストーリーを 1 レコードに保持し、\n`slide_refs` JSON で構成される。\n\n### 管理者ポータルでの使用タイミング\n- 投稿一括生成ウィザードの「投稿テンプレを選ぶ」セレクター\n- 投稿テンプレート管理画面\n\n### 認証・認可\n`requireAdmin` 必須。\n\n### 挙動・制約\n`SELECT id, code, name, description, slide_refs FROM ig_post_templates ORDER BY code ASC`。`slide_refs` は文字列 JSON のまま返す\n（クライアント側で `[{template_id, count}]` 形式に parse する）。\n\n### 関連\n- `POST /v1/admin/instagram/posts/{id}/generate-from-parking-lots` — テンプレを使った一括生成\n- `POST /v1/admin/instagram/posts/{id}/generate-all` — テンプレを使った AI 全体生成",
        "responses": {
          "200": {
            "description": "一覧",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/IgPostTemplate"
                  }
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingInstagramPostTemplatesList"
      },
      "post": {
        "tags": [
          "Instagram / Instagram"
        ],
        "summary": "投稿テンプレートを作成 / Create Post Template",
        "description": "### 用途\n新しい投稿テンプレートを登録する。`slide_refs` には\n`[{\"template_id\":\"<uuid>\",\"count\":N}, ...]` の JSON 文字列を渡し、AI 一括生成時の組み立て順 / 枚数を定義する。\n\n### 管理者ポータルでの使用タイミング\n- 投稿テンプレート管理 > 「+ 新規」モーダルの保存\n\n### 認証・認可\n`requireAdmin` 必須。\n\n### 挙動・制約\nid は `crypto.randomUUID()`。`slide_refs` は文字列保存（D1 に JSON 型は無いため）。\n`code` は識別子として UNIQUE 想定。\n\n### 関連\n- `PATCH /v1/admin/instagram/post-templates/{id}` — slide_refs 編集",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "code": {
                    "type": "string",
                    "minLength": 1
                  },
                  "name": {
                    "type": "string",
                    "minLength": 1
                  },
                  "description": {
                    "type": "string"
                  },
                  "slide_refs": {
                    "type": "string"
                  },
                  "genre": {
                    "type": "string",
                    "enum": [
                      "parking",
                      "useful_info"
                    ]
                  }
                },
                "required": [
                  "code",
                  "name",
                  "slide_refs"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "作成済み",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IgPostTemplate"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingInstagramPostTemplatesCreate"
      }
    },
    "/v1/marketing/instagram/post-templates/{id}": {
      "patch": {
        "tags": [
          "Instagram / Instagram"
        ],
        "summary": "投稿テンプレートを更新 / Update Post Template",
        "description": "### 用途\n投稿テンプレートのフィールド（code / name / description / slide_refs）を部分更新する。\n`slide_refs` を編集すると、以後の一括生成で使われるスライド構成が変わる。\n\n### 管理者ポータルでの使用タイミング\n- 投稿テンプレート管理 > 編集モーダル保存\n- スライド構成のドラッグ&ドロップ並び替え保存\n\n### 認証・認可\n`requireAdmin` 必須。\n\n### 挙動・制約\n送信されたフィールドのみ動的 SET。空ボディは 400。`updated_at` 自動更新。404 if not found。\n\n### 関連\n- `DELETE /v1/admin/instagram/post-templates/{id}` — 物理削除",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "code": {
                    "type": "string"
                  },
                  "name": {
                    "type": "string"
                  },
                  "description": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "slide_refs": {
                    "type": "string"
                  },
                  "genre": {
                    "type": "string",
                    "enum": [
                      "parking",
                      "useful_info"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "更新済み",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IgPostTemplate"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingInstagramPostTemplatesUpdate"
      },
      "delete": {
        "tags": [
          "Instagram / Instagram"
        ],
        "summary": "投稿テンプレートを削除 / Delete Post Template",
        "description": "### 用途\n投稿テンプレートを物理削除する。既に作成済みの投稿（キャンペーン）には影響しない\n（投稿側にはスライドが INSERT 済みで、テンプレへの FK は持たないため）。\n\n### 管理者ポータルでの使用タイミング\n- 投稿テンプレート管理画面のゴミ箱アイコン\n\n### 認証・認可\n`requireAdmin` 必須。\n\n### 挙動・制約\n`DELETE FROM ig_post_templates WHERE id = ?`。冪等。\n\n### 関連\n- `GET /v1/admin/instagram/post-templates` — 一覧",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "削除完了",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "success"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingInstagramPostTemplatesDelete"
      }
    },
    "/v1/marketing/instagram/posts": {
      "get": {
        "tags": [
          "Instagram / Instagram"
        ],
        "summary": "キャンペーン一覧 / Campaign List",
        "description": "### 用途\nキャンペーンの一覧を返す（instagram_campaign CRUD）。\n\n### 認証\n要 `requireAdmin`。\n\n### クエリパラメータ\n`limit`/`offset` でページング、`q` で検索（hooks 実装による）。\n\n### 備考\n- レスポンスは `{ items, total }` 形式\n- 追加フィルタは hooks 側で解釈する",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "status",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "offset",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "include",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "一覧",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "allOf": [
                          {
                            "$ref": "#/components/schemas/IgCampaign"
                          },
                          {
                            "type": "object",
                            "properties": {
                              "slides": {
                                "type": "array",
                                "items": {
                                  "$ref": "#/components/schemas/IgSlide"
                                }
                              },
                              "caption_body": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "slide_count": {
                                "type": "number"
                              },
                              "first_slide_id": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "first_slide_png_url": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "first_slide_template_id": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "first_slide_content": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "first_slide_html_override": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              }
                            }
                          }
                        ]
                      }
                    },
                    "total": {
                      "type": "number"
                    }
                  },
                  "required": [
                    "items",
                    "total"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingInstagramPostsList"
      },
      "post": {
        "tags": [
          "Instagram / Instagram"
        ],
        "summary": "キャンペーンを作成 / Create Campaign",
        "description": "### 用途\nキャンペーンを 1 件作成する（instagram_campaign CRUD）。\n\n### 認証\n要 `requireAdmin`。\n\n### 備考\n- ID はサーバー側で UUID 発行（hooks 実装依存）\n- admin_activity_logs への記録はこの endpoint では行わない",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "code": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 20
                  },
                  "title": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 200
                  },
                  "theme": {
                    "type": "string"
                  },
                  "area": {
                    "type": "string"
                  },
                  "notes": {
                    "type": "string"
                  },
                  "source_material": {
                    "type": "string"
                  },
                  "post_category_code": {
                    "type": "string"
                  },
                  "genre": {
                    "type": "string",
                    "enum": [
                      "parking",
                      "useful_info"
                    ]
                  }
                },
                "required": [
                  "code",
                  "title"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "作成済み",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IgCampaign"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingInstagramPostsCreate"
      }
    },
    "/v1/marketing/instagram/posts/{id}": {
      "get": {
        "tags": [
          "Instagram / Instagram"
        ],
        "summary": "キャンペーン詳細 / Campaign Detail",
        "description": "### 用途\n単一 キャンペーンの詳細を返す（instagram_campaign CRUD）。\n\n### 認証\n要 `requireAdmin`。\n\n### パスパラメータ\n`id`: 対象 id\n\n### 備考\n- 未存在は 404 `not_found`",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "詳細",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/IgCampaign"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "slides": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/IgSlide"
                          }
                        },
                        "caption": {
                          "$ref": "#/components/schemas/IgCaption"
                        }
                      },
                      "required": [
                        "slides",
                        "caption"
                      ]
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingInstagramPostsGet"
      },
      "patch": {
        "tags": [
          "Instagram / Instagram"
        ],
        "summary": "キャンペーンを部分更新 / Partially Update Campaign",
        "description": "### 用途\nキャンペーンを部分更新する（instagram_campaign CRUD）。\n\n### 認証\n要 `requireAdmin`。\n\n### パスパラメータ\n`id`: 対象 id\n\n### 備考\n- 未存在は 404 `not_found`\n- admin_activity_logs への記録はこの endpoint では行わない",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "title": {
                    "type": "string"
                  },
                  "theme": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "area": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "draft",
                      "review",
                      "scheduled",
                      "published",
                      "archived"
                    ]
                  },
                  "scheduled_at": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "notes": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "source_material": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "post_category_code": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "genre": {
                    "type": "string",
                    "enum": [
                      "parking",
                      "useful_info"
                    ]
                  },
                  "code": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "更新済み",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IgCampaign"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingInstagramPostsUpdate"
      },
      "delete": {
        "tags": [
          "Instagram / Instagram"
        ],
        "summary": "キャンペーンを削除 / Delete Campaign",
        "description": "### 用途\nキャンペーンを削除する（instagram_campaign CRUD）。\n\n### 認証\n要 `requireAdmin`。\n\n### パスパラメータ\n`id`: 対象 id\n\n### 備考\n- 存在しない id でも 204（冪等）\n- admin_activity_logs への記録はこの endpoint では行わない",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "204": {
            "description": "削除成功"
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingInstagramPostsDelete"
      }
    },
    "/v1/marketing/instagram/posts/{id}/slides": {
      "get": {
        "tags": [
          "Instagram / Instagram"
        ],
        "summary": "スライド一覧 / Slide List",
        "description": "### 用途\nキャンペーン配下のスライド (`ig_slides`) を `slide_index` 昇順で返す。\n\n### 認証\n要 `requireAdmin`。\n\n### 挙動・制約\n- `slide_index` 昇順、存在しなければ空配列",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "一覧",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/IgSlide"
                  }
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingInstagramPostsSlidesList"
      },
      "post": {
        "tags": [
          "Instagram / Instagram"
        ],
        "summary": "スライドを追加 / Add Slide",
        "description": "### 用途\nキャンペーンに新しいスライドを追加する。`content` 未指定 / 空 / `{}` の場合は\ntemplate の `sample_content` を初期値として流し込む。\n\n### 認証\n要 `requireAdmin`。\n\n### 挙動・制約\n- UUID 自動生成、created_at / updated_at は ISO\n- slide_index は呼出側指定（重複時は reorder が必要）",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "template_id": {
                    "type": "string"
                  },
                  "slide_index": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "content": {
                    "type": "string",
                    "default": "{}"
                  }
                },
                "required": [
                  "template_id",
                  "slide_index"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "作成済み",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IgSlide"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingInstagramPostsSlidesCreate"
      }
    },
    "/v1/marketing/instagram/posts/{id}/slides/reorder": {
      "patch": {
        "tags": [
          "Instagram / Instagram"
        ],
        "summary": "スライドを並び替え / Reorder Slides",
        "description": "### 用途\nスライドの表示順 (`slide_index`) を一括更新する。ドラッグ&ドロップ並び替え後の最終配列をまとめて送る。\n\n### 認証\n要 `requireAdmin`。\n\n### 挙動・制約\n- `UNIQUE(campaign_id, slide_index)` 制約を避けるため、一旦負の値ゾーン（`-1000000 - rowid`）にシフト → 本値に戻す 2 段戦略\n- D1 batch API で原子的に適用",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "items": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "slide_index": {
                          "type": "integer"
                        }
                      },
                      "required": [
                        "id",
                        "slide_index"
                      ]
                    }
                  }
                },
                "required": [
                  "items"
                ]
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "更新成功"
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingInstagramPostsSlidesReorder"
      }
    },
    "/v1/marketing/instagram/slides/{id}": {
      "patch": {
        "tags": [
          "Instagram / Instagram"
        ],
        "summary": "スライドを部分更新 / Partially Update Slide",
        "description": "### 用途\nスライドを部分更新する（instagram_slide CRUD）。\n\n### 認証\n要 `requireAdmin`。\n\n### パスパラメータ\n`id`: 対象 id\n\n### 備考\n- 未存在は 404 `not_found`\n- admin_activity_logs への記録はこの endpoint では行わない",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "content": {
                    "type": "string"
                  },
                  "html_override": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "png_r2_key": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "png_url": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "revision_notes": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "parking_lot_id": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "更新済み",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IgSlide"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingInstagramSlidesUpdate"
      },
      "delete": {
        "tags": [
          "Instagram / Instagram"
        ],
        "summary": "スライドを削除 / Delete Slide",
        "description": "### 用途\nスライドを削除する（instagram_slide CRUD）。\n\n### 認証\n要 `requireAdmin`。\n\n### パスパラメータ\n`id`: 対象 id\n\n### 備考\n- 存在しない id でも 204（冪等）\n- admin_activity_logs への記録はこの endpoint では行わない",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "204": {
            "description": "削除成功"
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingInstagramSlidesDelete"
      }
    },
    "/v1/marketing/instagram/slides/{id}/apply-parking-lot": {
      "post": {
        "tags": [
          "Instagram / Instagram"
        ],
        "summary": "スライドに駐車場を適用 / Apply Parking Lot to Slide",
        "description": "### 用途\nスライド単位で「この駐車場の情報を流し込む」運用。\n`parking_lots` テーブルから name / address / image / tags / 料金 等を取得し、\nテンプレの slot_schema に存在するキー（name, address, photo_url, category, description, price 等）へ決定論マッピングして `content` を更新する。\n\n### 挙動\n- `ig_slides.parking_lot_id` を更新\n- `content` の該当キーを上書き（無いキーは無視）\n- description は LLM に 1-2 文で生成させる（失敗しても slot 更新はする）\n- `html_override` は触らない（override 解除は別 PATCH で）",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "parking_lot_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "fill_description": {
                    "type": "boolean"
                  },
                  "provider_id": {
                    "type": "string"
                  },
                  "model": {
                    "type": "string"
                  }
                },
                "required": [
                  "parking_lot_id"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "更新後スライド",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IgSlide"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingInstagramSlidesApplyParkingLotCreate"
      }
    },
    "/v1/marketing/instagram/slides/{id}/duplicate": {
      "post": {
        "tags": [
          "Instagram / Instagram"
        ],
        "summary": "スライドを複製 / Duplicate Slide",
        "description": "### 用途\n既存スライドと同じ `template_id` / `content` / `html_override` を持つ新規スライドを\n作成し、元スライドの直後に挿入する（以降のスライドは +1 される）。PNG は意図的に複製しない\n（同じ画像が並ぶと混乱するため）。\n\n### 管理者ポータルでの使用タイミング\n- スライドエディタの「このスライドを複製」ボタン\n- 似たレイアウトのスライドを量産したいとき\n\n### 認証\n要 `requireAdmin`。\n\n### 挙動・制約\n- `UNIQUE(campaign_id, slide_index)` 衝突を避けるため、後続スライドを一時的に負の index\n  に逃してから挿入 → 最終 index に詰め直す 2 段階 batch\n- `revision_notes` は引き継がない（新規は空）\n- 存在しない id は 404 `not_found`\n\n### 関連\n- `POST /v1/admin/instagram/posts/{id}/slides` — ゼロから新規作成",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "複製後の新スライド",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IgSlide"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingInstagramSlidesDuplicate"
      }
    },
    "/v1/marketing/instagram/slides/{id}/upload-png": {
      "post": {
        "tags": [
          "Instagram / Instagram"
        ],
        "summary": "スライドPNGをアップロード / Upload Slide PNG",
        "description": "### 用途\nスライドをブラウザ側でレンダー→ PNG 化した画像を受け取り、R2 にアップロードする。\n同じキーに上書きすることで、再生成しても公開 URL が変わらず CDN キャッシュの再バインドも楽。\n\n### 管理者ポータルでの使用タイミング\n- スライドエディタの「PNG を確定」または「自動エクスポート」\n- バッチ書き出しツール\n\n### 認証\n要 `requireAdmin`。\n\n### 挙動・制約\n- R2 キーは `posts/<campaign_code>/slides/<NN>.png`（番号 2 桁ゼロパディング）\n- 公開 URL は `/cdn/ig/<key>?v=<timestamp>` で返す（キャッシュバスタ付き）\n- `ig_slides.png_r2_key` / `png_url` を自動更新\n- `INSTAGRAM_R2` binding が無いと 503\n\n### 関連\n- `PATCH /v1/admin/instagram/slides/{id}/confirm-png` — 既存 R2 オブジェクトを公式採用として記録",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary"
                  },
                  "campaign_code": {
                    "type": "string"
                  },
                  "slide_id": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "アップロード完了",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "r2_key": {
                      "type": "string"
                    },
                    "public_url": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "r2_key",
                    "public_url"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingInstagramSlidesUploadPngCreate"
      }
    },
    "/v1/marketing/instagram/upload-image": {
      "post": {
        "tags": [
          "Instagram / Instagram"
        ],
        "summary": "コンテンツ画像をアップロード / Upload Content Image",
        "description": "### 用途\nスライドのスロット（背景画像・商品写真など）に埋め込む汎用画像を R2 にアップロードする。\nファイル名と投稿コード (`campaign_code`) からタイムスタンプ+ハッシュ付き R2 キーを生成する。\n\n### 管理者ポータルでの使用タイミング\n- スライドエディタの画像スロットにドラッグ&ドロップ\n- 外部画像の差し替え導線\n\n### 認証\n要 `requireAdmin`。\n\n### 挙動・制約\n- ファイル名サニタイズ（非 ASCII/危険文字を `_` に置換、40 文字まで）\n- R2 キーにはタイムスタンプ (`YYYYMMDD-HHMMSS`) と 8 文字 UUID ハッシュを含め衝突回避\n- 公開 URL は `/cdn/ig/<key>` で返る\n- `INSTAGRAM_R2` binding が無いと 503\n\n### 関連\n- `POST /v1/admin/instagram/slides/{id}/upload-png` — スライド本体の PNG 保存\n- `POST /v1/admin/instagram/images/detect-sensitive` — センシティブ領域検出",
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary"
                  },
                  "campaign_code": {
                    "type": "string"
                  },
                  "slide_id": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "アップロード完了",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "r2_key": {
                      "type": "string"
                    },
                    "public_url": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "r2_key",
                    "public_url"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingInstagramUploadImageCreate"
      }
    },
    "/v1/marketing/instagram/slides/{id}/confirm-png": {
      "patch": {
        "tags": [
          "Instagram / Instagram"
        ],
        "summary": "PNGアップロード完了を記録 / Confirm PNG Upload",
        "description": "### 用途\nスライドに対応する R2 PNG の `r2_key` と `public_url` を DB に記録する確定用エンドポイント。\n`/upload-png` を使わずに別経路（presigned PUT 直送 / バッチ書き込み）でアップしたケースで使う。\n\n### 管理者ポータルでの使用タイミング\n- 外部ツール連携で PNG 書き出し済みの R2 オブジェクトを DB に紐付ける\n- バックフィルスクリプト\n\n### 認証\n要 `requireAdmin`。\n\n### 挙動・制約\n- `ig_slides.png_r2_key` / `png_url` / `updated_at` を UPDATE\n- R2 オブジェクトの存在検証は行わない（呼出し側責任）\n- 存在しない slide は 404 `not_found`\n\n### 関連\n- `POST /v1/admin/instagram/slides/{id}/upload-png` — Worker 経由でのアップロード + 自動確定",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "r2_key": {
                    "type": "string"
                  },
                  "public_url": {
                    "type": "string"
                  }
                },
                "required": [
                  "r2_key",
                  "public_url"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "更新後",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IgSlide"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingInstagramSlidesConfirmPngUpdate"
      }
    },
    "/v1/marketing/instagram/images/generate-ai": {
      "post": {
        "tags": [
          "Instagram / Instagram"
        ],
        "summary": "AI 画像生成 (Cloudflare Workers AI / Flux Schnell) → R2 保存",
        "description": "### 用途\nカバースライドの背景画像など、単一スロットに対して AI 画像を 1 枚生成する。\nCloudflare Workers AI の Flux Schnell モデルで生成し、R2 にアップして永続 URL を返す。\n\n### 認証\n要 `requireAdmin`。\n\n### 挙動・制約\n- size は square (1024x1024) / portrait (1024x1792) / landscape (1792x1024)\n- R2 キーは `posts/<campaign_code or 'shared'>/ai-gen/<timestamp>_<hash>.png`\n- 公開 URL は `/cdn/ig/<key>` で返る\n- `INSTAGRAM_R2` / `AI` binding が無いと 502\n- Flux Schnell は 4 step で高速 (体感 5-10 秒)",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "prompt": {
                    "type": "string",
                    "minLength": 3,
                    "maxLength": 2000
                  },
                  "campaign_code": {
                    "type": "string"
                  },
                  "size": {
                    "type": "string",
                    "enum": [
                      "square",
                      "portrait",
                      "landscape"
                    ]
                  }
                },
                "required": [
                  "prompt"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "生成完了",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "public_url": {
                      "type": "string"
                    },
                    "r2_key": {
                      "type": "string"
                    },
                    "prompt": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "public_url",
                    "r2_key",
                    "prompt"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "bad_gateway",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingInstagramImagesGenerateAiCreate"
      }
    },
    "/v1/marketing/instagram/slides/{id}/generate-content": {
      "post": {
        "tags": [
          "Instagram / Instagram"
        ],
        "summary": "スライドのコンテンツをLLMで自動生成 / Generate Slide Content with LLM",
        "description": "### 用途\nスライドのテンプレート `slot_schema`（キーと型定義）を LLM に渡し、参考素材から各スロットの\nテキスト / 画像 URL / 数値を自動生成する。既存 `content` はマージで保持。\n\n### 管理者ポータルでの使用タイミング\n- スライドエディタの「AI に書かせる」ボタン\n- 特定フィールドのみ再生成（`target_keys` 指定）\n- 新規スライド作成直後の初期埋め\n\n### 認証\n要 `requireAdmin`。LLM 消費あり。\n\n### 挙動・制約\n- 素材: `source_material` 優先、未指定なら `campaign.source_material` → `campaign.notes` の順でフォールバック\n- `persist_source=true` で campaign に素材を保存（次回デフォルト化）\n- プロバイダ: `ai_providers` から取得、`provider_id` / `model` で明示指定可\n- `ai_usage_logs` に使用量が記録される\n\n### 関連\n- `POST /v1/admin/instagram/slides/{id}/revise` — 既存 content を指示に沿ってリバイズ\n- `POST /v1/admin/instagram/posts/{id}/generate-all` — 全スライド一括生成",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "source_material": {
                    "type": "string"
                  },
                  "persist_source": {
                    "type": "boolean"
                  },
                  "hint": {
                    "type": "string"
                  },
                  "target_keys": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "provider_id": {
                    "type": "string"
                  },
                  "model": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "生成された content（対象キーのみ。既存値は保持してマージ）",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "content": {
                      "type": "object",
                      "additionalProperties": {
                        "type": "string"
                      }
                    }
                  },
                  "required": [
                    "content"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingInstagramSlidesGenerateContentCreate"
      }
    },
    "/v1/marketing/instagram/slides/{id}/revise": {
      "post": {
        "tags": [
          "Instagram / Instagram"
        ],
        "summary": "スライドHTMLをLLMで修正 / Revise Slide HTML with LLM",
        "description": "### 用途\nスライドの HTML（`html_override` または テンプレ `html_body`）を、\n自然言語指示 (`instructions`) に従って LLM が修正する。結果は `html_override` に保存され、\n`revision_notes` に指示内容が記録される。\n\n### 管理者ポータルでの使用タイミング\n- スライドエディタ「AI に修正させる」パネル\n- 「もう少し派手に」「ブランドカラーを強調」などの軽微な調整\n\n### 認証\n要 `requireAdmin`。\n\n### 挙動・制約\n- システムプロンプトで「修正後 HTML のみ」を要求、コードフェンスは除去\n- `html_override` に保存 → テンプレ `html_body` より優先される\n- `ai_usage_logs` に使用量記録\n- 元に戻したい場合は PATCH で `html_override: null` を送る\n\n### 関連\n- `PATCH /v1/admin/instagram/slides/{id}` — html_override の手動編集・クリア",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "instructions": {
                    "type": "string",
                    "minLength": 1
                  },
                  "provider_id": {
                    "type": "string"
                  },
                  "model": {
                    "type": "string"
                  }
                },
                "required": [
                  "instructions"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "修正済みHTML",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "html": {
                      "type": "string"
                    },
                    "usage": {
                      "type": "object",
                      "properties": {
                        "input_tokens": {
                          "type": "number"
                        },
                        "output_tokens": {
                          "type": "number"
                        }
                      },
                      "required": [
                        "input_tokens",
                        "output_tokens"
                      ]
                    }
                  },
                  "required": [
                    "html",
                    "usage"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingInstagramSlidesReviseCreate"
      }
    },
    "/v1/marketing/instagram/images/detect-sensitive": {
      "post": {
        "tags": [
          "Instagram / Instagram"
        ],
        "summary": "画像から敏感情報領域を検出 / Detect Sensitive Areas in Image",
        "description": "### 用途\n画像 URL を Cloudflare Workers AI に投げ、**顔 / ナンバープレート候補領域**\nのバウンディングボックスを返す。モザイク / ぼかし処理の初期候補として利用。\n\n### 管理者ポータルでの使用タイミング\n- 画像を投稿に取り込むとき、センシティブ領域を事前検出して編集画面に反映\n- モザイクエディタの「自動検出」ボタン\n\n### 認証\n要 `requireAdmin`。Workers AI 消費あり（`env.AI` binding が必要）。\n\n### 挙動・制約\n- Workers AI は専用の顔 / プレート検出モデルを持たないため、\n  `@cf/facebook/detr-resnet-50`（汎用物体検出）で person / car / truck / bus を検出し、\n  person box の上部 30% を **顔候補**、車の下部 15% 中央を **プレート候補** として返す\n- ヒューリスティック初期候補なので、管理者が**必ず手動で確認・修正**する前提\n- `min_score` で DETR 信頼度フィルタ（既定 0.5）\n- `image_width/height` 指定時は正規化した座標を返す\n\n### 関連\n- `POST /v1/admin/instagram/upload-image` — 検出対象の画像アップロード",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "image_url": {
                    "type": "string",
                    "format": "uri"
                  },
                  "image_width": {
                    "type": "integer",
                    "exclusiveMinimum": 0
                  },
                  "image_height": {
                    "type": "integer",
                    "exclusiveMinimum": 0
                  },
                  "min_score": {
                    "type": "number",
                    "minimum": 0,
                    "maximum": 1
                  }
                },
                "required": [
                  "image_url"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "検出された候補領域（相対座標 0-1）",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "boxes": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "label": {
                            "type": "string",
                            "enum": [
                              "face",
                              "plate"
                            ]
                          },
                          "x": {
                            "type": "number"
                          },
                          "y": {
                            "type": "number"
                          },
                          "width": {
                            "type": "number"
                          },
                          "height": {
                            "type": "number"
                          },
                          "score": {
                            "type": "number"
                          },
                          "source": {
                            "type": "string"
                          }
                        },
                        "required": [
                          "label",
                          "x",
                          "y",
                          "width",
                          "height",
                          "score",
                          "source"
                        ]
                      }
                    }
                  },
                  "required": [
                    "boxes"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingInstagramImagesDetectSensitiveCreate"
      }
    },
    "/v1/marketing/instagram/posts/{id}/generate-all": {
      "post": {
        "tags": [
          "Instagram / Instagram"
        ],
        "summary": "テンプレートから投稿をAIで組み立て / Generate Full Post with AI",
        "description": "### 用途\nキャンペーンに対して、投稿テンプレート（`post_template_id`）またはテンプレ ID 配列\n（`template_ids`）を元に、複数スライド + content + キャプションを **AI がまとめて生成する**\nオールインワン生成エンドポイント。\n\n### 管理者ポータルでの使用タイミング\n- 新規キャンペーン作成後の「AI で一括生成」ボタン\n- テンプレ変更時の全面再生成\n\n### 認証\n要 `requireAdmin`。LLM 消費量が大きい操作。\n\n### 挙動・制約\n- `spot_count` で生成するスライド枚数を制御（0〜10、既定 5）\n- 既存スライドは削除してから再生成（トランザクション）\n- `ai_usage_logs` に生成量・コストが記録される\n- 失敗時は途中まで生成されたスライドが残る可能性あり（呼出し側でリセット推奨）\n- `skip_ai: true` を指定すると LLM 呼出をスキップし content=空のスライドだけ作成する。\n  「空の投稿を作成 + 投稿テンプレ適用」モード用途で利用。\n\n### 関連\n- `POST /v1/admin/instagram/slides/{id}/generate-content` — 個別スライド生成\n- `POST /v1/admin/instagram/posts/{id}/generate-from-parking-lots` — 駐車場ベース生成",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "post_template_id": {
                    "type": "string"
                  },
                  "template_ids": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "spot_count": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 10
                  },
                  "hint": {
                    "type": "string"
                  },
                  "provider_id": {
                    "type": "string"
                  },
                  "model": {
                    "type": "string"
                  },
                  "skip_ai": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "作成完了",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "created_slides": {
                      "type": "number"
                    }
                  },
                  "required": [
                    "created_slides"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingInstagramPostsGenerateAllCreate"
      }
    },
    "/v1/marketing/instagram/posts/{id}/generate-from-parking-lots": {
      "post": {
        "tags": [
          "Instagram / Instagram"
        ],
        "summary": "駐車場から投稿を一括生成 / Generate Posts from Parking Lots",
        "description": "### 用途\n駐車場 ID 配列（最大 10 件）から、駐車場情報を Parky Postgres から引いて、\n投稿テンプレートの各スライドに **決定論的にマッピング** して content + PNG 指示を生成する。\n「スポット紹介投稿」を短時間で量産するための専用導線。\n\n### 管理者ポータルでの使用タイミング\n- 駐車場ランキング記事の投稿化\n- 特集エリアの駐車場をピックアップしてスライド化\n- カバー + ランキング + 詳細スライド群を一気に作る\n\n### 認証\n要 `requireAdmin`。\n\n### 挙動・制約\n- `parking_lot_ids` は 1〜10 件\n- 駐車場情報（name / address / images / rating / tags 等）を Postgres から bulk fetch\n- ランキング系テンプレは順序も決定論的（配列の並び順がそのまま順位）\n- 既存スライドは削除してから生成\n- 画像は R2 既存参照を流用（新規アップロードは不要）\n\n### 関連\n- `POST /v1/admin/instagram/posts/{id}/generate-all` — テンプレだけから汎用生成",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "parking_lot_ids": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "minItems": 1,
                    "maxItems": 20
                  },
                  "post_template_id": {
                    "type": "string"
                  },
                  "cover_title": {
                    "type": "string"
                  },
                  "cover_area": {
                    "type": "string"
                  },
                  "hint": {
                    "type": "string"
                  },
                  "provider_id": {
                    "type": "string"
                  },
                  "model": {
                    "type": "string"
                  }
                },
                "required": [
                  "parking_lot_ids"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "作成完了",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "created_slides": {
                      "type": "number"
                    },
                    "slide_ids": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  },
                  "required": [
                    "created_slides",
                    "slide_ids"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingInstagramPostsGenerateFromParkingLotsCreate"
      }
    },
    "/v1/marketing/instagram/posts/{id}/generate-caption": {
      "post": {
        "tags": [
          "Instagram / Instagram"
        ],
        "summary": "キャプションをLLMで生成 / Generate Caption with LLM",
        "description": "### 用途\nキャンペーンのタイトル・エリア・スライド content 要約を元に、Instagram 投稿用の\nキャプション本文と推奨ハッシュタグ配列を LLM に生成させる。\n\n### 管理者ポータルでの使用タイミング\n- 投稿編集画面の「AI でキャプション作成」ボタン\n- スライド編集後にキャプションを再生成する導線\n\n### 認証\n要 `requireAdmin`。\n\n### 挙動・制約\n- スライド全件の `content` を収集してコンテキスト化\n- 出力: `{ caption: string, hashtags: string[] }`\n- DB への保存はしない（`PATCH /posts/{id}/caption` で明示的に保存）\n- `ai_usage_logs` に使用量記録\n\n### 関連\n- `PATCH /v1/admin/instagram/posts/{id}/caption` — 生成結果を保存\n- `POST /v1/admin/instagram/posts/{id}/generate-ideas` — タイトル案生成",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "hint": {
                    "type": "string"
                  },
                  "provider_id": {
                    "type": "string"
                  },
                  "model": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "生成されたキャプション",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "caption": {
                      "type": "string"
                    },
                    "hashtags": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  },
                  "required": [
                    "caption",
                    "hashtags"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingInstagramPostsGenerateCaptionCreate"
      }
    },
    "/v1/marketing/instagram/posts/{id}/caption": {
      "patch": {
        "tags": [
          "Instagram / Instagram"
        ],
        "summary": "キャプションを手動更新 / Update Caption Manually",
        "description": "### 用途\nキャンペーンの `ig_captions`（キャプション本文 / ハッシュタグ）を upsert する。\nAI 生成結果の保存、または手動編集内容の反映に使う。\n\n### 管理者ポータルでの使用タイミング\n- 投稿編集画面のキャプションエディタ「保存」\n- `/generate-caption` 生成結果の「この内容で保存」採用時\n\n### 認証\n要 `requireAdmin`。\n\n### 挙動・制約\n- `INSERT ... ON CONFLICT(campaign_id) DO UPDATE` で upsert\n- `body` / `hashtags` いずれか未指定なら既存値を COALESCE で保持\n- 1 キャンペーンにつき 1 行（UNIQUE 制約）\n\n### 関連\n- `POST /v1/admin/instagram/posts/{id}/generate-caption` — AI 生成（保存しない）",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "body": {
                    "type": "string"
                  },
                  "hashtags": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "更新後",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/IgCaption"
                    },
                    {
                      "type": "object"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingInstagramPostsCaptionUpdate"
      }
    },
    "/v1/marketing/instagram/posts/{id}/generate-ideas": {
      "post": {
        "tags": [
          "Instagram / Instagram"
        ],
        "summary": "競合分析とアイデア生成 / Generate Competitor Analysis & Ideas",
        "description": "### 用途\n競合アカウントの投稿メモや参考 URL から、Parky の Instagram コンテンツ企画案\n（タイトル / コンセプト / フック）を 5〜8 件 LLM に提案させる企画ブレスト用エンドポイント。\n\n### 管理者ポータルでの使用タイミング\n- 新規企画検討画面の「AI にアイデア出しさせる」ボタン\n- 他アカウント（`account_handle`）の URL を貼って類似企画を量産\n\n### 認証\n要 `requireAdmin`。\n\n### 挙動・制約\n- `notes` 必須、`area`・`source_url`・`account_handle` 任意\n- 出力は `{ ideas: [{ title, concept, hook }, ...] }`（常に JSON 配列のみ）\n- DB 保存なし（管理者が選んで `POST /v1/admin/instagram/posts` で新規作成する想定）\n- `ai_usage_logs` に使用量記録\n\n### 関連\n- `POST /v1/admin/instagram/posts` — 選んだアイデアでキャンペーン作成",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "notes": {
                    "type": "string",
                    "minLength": 1
                  },
                  "area": {
                    "type": "string"
                  },
                  "source_url": {
                    "type": "string"
                  },
                  "account_handle": {
                    "type": "string"
                  },
                  "provider_id": {
                    "type": "string"
                  },
                  "model": {
                    "type": "string"
                  }
                },
                "required": [
                  "notes"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "生成されたアイデア一覧",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ideas": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "title": {
                            "type": "string"
                          },
                          "concept": {
                            "type": "string"
                          },
                          "hook": {
                            "type": "string"
                          }
                        },
                        "required": [
                          "title",
                          "concept",
                          "hook"
                        ]
                      }
                    }
                  },
                  "required": [
                    "ideas"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingInstagramPostsGenerateIdeasCreate"
      }
    },
    "/v1/marketing/instagram/posts/{id}/snapshots": {
      "get": {
        "tags": [
          "Instagram / Instagram"
        ],
        "summary": "競合分析スナップショット一覧 / Competitor Analysis Snapshots",
        "description": "### 用途\nキャンペーンに紐づく **競合分析スナップショット** (`ig_competitor_snapshots`) 一覧を返す。\n`/generate-ideas` で生成したアイデアと、その時参照した競合 URL / メモを履歴として保存する。\n\n### 管理者ポータルでの使用タイミング\n- 企画タブの「過去の競合分析」セクション\n- アイデア生成履歴の参照\n\n### 認証\n要 `requireAdmin`。\n\n### 挙動・制約\n- `created_at DESC` 降順（新しいスナップショットが上）\n- スナップショットは `/generate-ideas` 実行時に自動で保存される\n\n### 関連\n- `POST /v1/admin/instagram/posts/{id}/generate-ideas` — 新規スナップショット生成",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "一覧",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/IgCompetitorSnapshot"
                  }
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingInstagramPostsSnapshotsList"
      }
    },
    "/v1/marketing/dashboard/sns-metrics": {
      "get": {
        "tags": [
          "ダッシュボード / Dashboard"
        ],
        "summary": "SNS指標 / SNS Metrics",
        "description": "SNS フォロワー数とエンゲージメント率の時系列を返す。Marketing Portal のダッシュボード\nトップに表示する。現状は `sns_follower_snapshots` テーブルが未作成のため、各 platform\nとも followers=0 / series は 12 週分の 0 埋めを返す。\n\nTICKET: PARKY-SNS-SNAPSHOT-INGEST",
        "responses": {
          "200": {
            "description": "SNS 指標",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SnsMetricsResponse"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingDashboardSnsMetricsList"
      }
    },
    "/v1/marketing/dashboard/post-calendar": {
      "get": {
        "tags": [
          "ダッシュボード / Dashboard"
        ],
        "summary": "投稿カレンダー / Post Calendar",
        "description": "指定月の各日について Instagram / X の 下書き・予約・公開 件数を返す。\nIG は D1 `ig_campaigns` に居るためここでは 0 を返す。\nX は `x_posts.status` + `scheduled_at` / `published_at` で集計。\n\nTICKET: PARKY-IG-MIRROR",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^\\d{4}$"
            },
            "required": true,
            "name": "year",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "pattern": "^\\d{1,2}$"
            },
            "required": true,
            "name": "month",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "カレンダー",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PostCalendarResponse"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingDashboardPostCalendarList"
      }
    },
    "/v1/marketing/dashboard/articles-pv": {
      "get": {
        "tags": [
          "ダッシュボード / Dashboard"
        ],
        "summary": "記事PVランキング / Article PV Ranking",
        "description": "公開記事を `view_count` 降順で上位 10 件返す。\n\nTICKET: PARKY-GA4-INTEGRATION",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "enum": [
                "yesterday",
                "7d",
                "30d"
              ]
            },
            "required": false,
            "name": "period",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "PV ランキング",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ArticlesPvResponse"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingDashboardArticlesPvList"
      }
    },
    "/v1/marketing/dashboard/ads-ctr": {
      "get": {
        "tags": [
          "ダッシュボード / Dashboard"
        ],
        "summary": "広告CTR / Ad CTR",
        "description": "`ads.status = 'active'` の広告について impressions / clicks / CTR と残日数を返す。\ndaily は将来 GA4 / 内部集計連携時に実装するため現状は空配列。",
        "responses": {
          "200": {
            "description": "広告 CTR",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AdsCtrResponse"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingDashboardAdsCtrList"
      }
    },
    "/v1/marketing/newsletter/subscribers": {
      "get": {
        "tags": [
          "ニュースレター / Newsletter"
        ],
        "summary": "購読者一覧 / Subscriber List",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^\\d+$",
              "description": "1 はじまりのページ番号",
              "examples": [
                "1"
              ]
            },
            "required": false,
            "name": "page",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "pattern": "^\\d+$",
              "description": "1 ページあたりの件数（最大 2000）",
              "examples": [
                "20"
              ]
            },
            "required": false,
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "cursor ページング利用時の不透明トークン。offset ページング（page/limit）とは排他。"
            },
            "required": false,
            "name": "cursor",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "active",
                "unsubscribed"
              ]
            },
            "required": false,
            "name": "status",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "search",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "一覧",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/MarketingSubscriber"
                      }
                    },
                    "page": {
                      "type": "integer",
                      "minimum": 1
                    },
                    "limit": {
                      "type": "integer",
                      "minimum": 1
                    },
                    "total": {
                      "type": "integer",
                      "minimum": 0
                    },
                    "total_is_estimate": {
                      "type": "boolean",
                      "description": "true のとき total は pg_class.reltuples 由来の概算値（数千行ズレ得る）。exact COUNT のときは field 自体が省略される。"
                    }
                  },
                  "required": [
                    "items",
                    "page",
                    "limit",
                    "total"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingNewsletterSubscribersList"
      },
      "post": {
        "tags": [
          "ニュースレター / Newsletter"
        ],
        "summary": "購読者を追加 / Add Subscriber",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email"
                  },
                  "source": {
                    "type": "string",
                    "maxLength": 100
                  },
                  "lang": {
                    "type": "string",
                    "maxLength": 10
                  },
                  "meta": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "object",
                      "description": "任意の JSON 値（string / number / boolean / null / array / object）。再帰構造は object + additionalProperties で表現。",
                      "additionalProperties": true
                    }
                  }
                },
                "required": [
                  "email"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "追加/更新後",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MarketingSubscriber"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingNewsletterSubscribersCreate"
      }
    },
    "/v1/marketing/newsletter/subscribers/tag-summary": {
      "get": {
        "tags": [
          "ニュースレター / Newsletter"
        ],
        "summary": "購読者タグサマリー / Subscriber Tag Summary",
        "responses": {
          "200": {
            "description": "タグ別の購読者数",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "tag": {
                            "type": "string"
                          },
                          "count": {
                            "type": "integer"
                          }
                        },
                        "required": [
                          "tag",
                          "count"
                        ]
                      }
                    }
                  },
                  "required": [
                    "items"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingNewsletterSubscribersTagSummaryList"
      }
    },
    "/v1/marketing/newsletter/subscribers/summary": {
      "get": {
        "tags": [
          "ニュースレター / Newsletter"
        ],
        "summary": "購読者サマリー / Subscriber Summary",
        "responses": {
          "200": {
            "description": "購読者サマリー",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "total": {
                      "type": "integer"
                    },
                    "active": {
                      "type": "integer"
                    },
                    "unsubscribed": {
                      "type": "integer"
                    },
                    "bounced": {
                      "type": "integer"
                    },
                    "new_this_month": {
                      "type": "integer"
                    },
                    "unsubscribe_rate": {
                      "type": "number"
                    },
                    "tag_breakdown": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "tag": {
                            "type": "string"
                          },
                          "count": {
                            "type": "integer"
                          }
                        },
                        "required": [
                          "tag",
                          "count"
                        ]
                      }
                    }
                  },
                  "required": [
                    "total",
                    "active",
                    "unsubscribed",
                    "bounced",
                    "new_this_month",
                    "unsubscribe_rate",
                    "tag_breakdown"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingNewsletterSubscribersSummary"
      }
    },
    "/v1/marketing/newsletter/subscribers/{id}": {
      "delete": {
        "tags": [
          "ニュースレター / Newsletter"
        ],
        "summary": "購読者を削除 / Delete Subscriber",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid",
              "examples": [
                "00000000-0000-0000-0000-000000000000"
              ]
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "204": {
            "description": "削除済み"
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingNewsletterSubscribersDelete"
      },
      "put": {
        "tags": [
          "ニュースレター / Newsletter"
        ],
        "summary": "購読者を更新 / Update Subscriber",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid",
              "examples": [
                "00000000-0000-0000-0000-000000000000"
              ]
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MarketingSubscriberUpdateInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "更新後",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MarketingSubscriber"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingNewsletterSubscribersReplace"
      }
    },
    "/v1/marketing/newsletter/subscribers/verify-reconfirm": {
      "post": {
        "tags": [
          "ニュースレター / Newsletter"
        ],
        "summary": "メール変更確認トークンを検証 / Verify Email Change Token",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "subscriberId": {
                    "type": "string",
                    "format": "uuid",
                    "examples": [
                      "00000000-0000-0000-0000-000000000000"
                    ]
                  },
                  "email": {
                    "type": "string",
                    "format": "email"
                  },
                  "token": {
                    "type": "string",
                    "minLength": 10,
                    "maxLength": 128
                  },
                  "issuedAt": {
                    "type": "integer",
                    "minimum": 0
                  }
                },
                "required": [
                  "subscriberId",
                  "email",
                  "token",
                  "issuedAt"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "verification 成功。meta.email_verified_at が確定済み。",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "subscriber": {
                      "$ref": "#/components/schemas/MarketingSubscriber"
                    }
                  },
                  "required": [
                    "ok",
                    "subscriber"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "token 不正 / expired 等",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "enum": [
                        false
                      ]
                    },
                    "reason": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "ok",
                    "reason"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingNewsletterSubscribersVerifyReconfirmCreate"
      }
    },
    "/v1/marketing/newsletter/broadcasts": {
      "get": {
        "tags": [
          "ニュースレター / Newsletter"
        ],
        "summary": "配信一覧 / Broadcast List",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^\\d+$",
              "description": "1 はじまりのページ番号",
              "examples": [
                "1"
              ]
            },
            "required": false,
            "name": "page",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "pattern": "^\\d+$",
              "description": "1 ページあたりの件数（最大 2000）",
              "examples": [
                "20"
              ]
            },
            "required": false,
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "cursor ページング利用時の不透明トークン。offset ページング（page/limit）とは排他。"
            },
            "required": false,
            "name": "cursor",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "draft",
                "scheduled",
                "sending",
                "sent",
                "failed"
              ]
            },
            "required": false,
            "name": "status",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "一覧",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/MarketingBroadcast"
                      }
                    },
                    "page": {
                      "type": "integer",
                      "minimum": 1
                    },
                    "limit": {
                      "type": "integer",
                      "minimum": 1
                    },
                    "total": {
                      "type": "integer",
                      "minimum": 0
                    },
                    "total_is_estimate": {
                      "type": "boolean",
                      "description": "true のとき total は pg_class.reltuples 由来の概算値（数千行ズレ得る）。exact COUNT のときは field 自体が省略される。"
                    }
                  },
                  "required": [
                    "items",
                    "page",
                    "limit",
                    "total"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingNewsletterBroadcastsList"
      },
      "post": {
        "tags": [
          "ニュースレター / Newsletter"
        ],
        "summary": "配信を作成 / Create Broadcast",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MarketingBroadcastCreateInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "作成済み",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MarketingBroadcast"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingNewsletterBroadcastsCreate"
      }
    },
    "/v1/marketing/newsletter/broadcasts/{id}": {
      "get": {
        "tags": [
          "ニュースレター / Newsletter"
        ],
        "summary": "配信詳細 / Broadcast Detail",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid",
              "examples": [
                "00000000-0000-0000-0000-000000000000"
              ]
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "配信",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MarketingBroadcast"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingNewsletterBroadcastsGet"
      },
      "put": {
        "tags": [
          "ニュースレター / Newsletter"
        ],
        "summary": "配信を更新 / Update Broadcast",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid",
              "examples": [
                "00000000-0000-0000-0000-000000000000"
              ]
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MarketingBroadcastUpdateInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "更新後",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MarketingBroadcast"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingNewsletterBroadcastsReplace"
      }
    },
    "/v1/marketing/newsletter/broadcasts/{id}/duplicate": {
      "post": {
        "tags": [
          "ニュースレター / Newsletter"
        ],
        "summary": "配信を複製 / Duplicate Broadcast",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid",
              "examples": [
                "00000000-0000-0000-0000-000000000000"
              ]
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "複製済み（新 draft）",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MarketingBroadcast"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingNewsletterBroadcastsDuplicate"
      }
    },
    "/v1/marketing/newsletter/broadcasts/{id}/send-test": {
      "post": {
        "tags": [
          "ニュースレター / Newsletter"
        ],
        "summary": "テスト配信 / Send Test Broadcast",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid",
              "examples": [
                "00000000-0000-0000-0000-000000000000"
              ]
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email"
                  }
                },
                "required": [
                  "email"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "ログに記録",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "ok"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingNewsletterBroadcastsSendTestCreate"
      }
    },
    "/v1/marketing/newsletter/broadcasts/{id}/send-now": {
      "post": {
        "tags": [
          "ニュースレター / Newsletter"
        ],
        "summary": "即時配信 / Send Broadcast Now",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid",
              "examples": [
                "00000000-0000-0000-0000-000000000000"
              ]
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "202": {
            "description": "dispatch を受理（バックグラウンド処理）",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "broadcast_id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "dispatched": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "ok",
                    "broadcast_id",
                    "dispatched"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingNewsletterBroadcastsSendNowCreate"
      }
    },
    "/v1/marketing/newsletter/broadcasts/{id}/schedule": {
      "post": {
        "tags": [
          "ニュースレター / Newsletter"
        ],
        "summary": "配信を予約 / Schedule Broadcast",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid",
              "examples": [
                "00000000-0000-0000-0000-000000000000"
              ]
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "scheduledAt": {
                    "type": "string",
                    "format": "date-time"
                  }
                },
                "required": [
                  "scheduledAt"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "予約済み",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MarketingBroadcast"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingNewsletterBroadcastsSchedule"
      }
    },
    "/v1/marketing/newsletter/broadcasts/{id}/cancel": {
      "post": {
        "tags": [
          "ニュースレター / Newsletter"
        ],
        "summary": "配信予約をキャンセル / Cancel Broadcast Schedule",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid",
              "examples": [
                "00000000-0000-0000-0000-000000000000"
              ]
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "キャンセル後",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MarketingBroadcast"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingNewsletterBroadcastsCancel"
      }
    },
    "/v1/marketing/x/schedule-rules": {
      "get": {
        "tags": [
          "X（旧Twitter）/ X (Twitter)"
        ],
        "summary": "Xスケジュールルール一覧 / X Schedule Rule List",
        "responses": {
          "200": {
            "description": "一覧",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/MarketingXScheduleRule"
                      }
                    }
                  },
                  "required": [
                    "items"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingXScheduleRulesList"
      },
      "post": {
        "tags": [
          "X（旧Twitter）/ X (Twitter)"
        ],
        "summary": "スケジュールルールを作成 / Create Schedule Rule",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MarketingXScheduleRuleInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "作成済み",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MarketingXScheduleRule"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingXScheduleRulesCreate"
      }
    },
    "/v1/marketing/x/schedule-rules/{id}": {
      "put": {
        "tags": [
          "X（旧Twitter）/ X (Twitter)"
        ],
        "summary": "スケジュールルールを更新 / Update Schedule Rule",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid",
              "examples": [
                "00000000-0000-0000-0000-000000000000"
              ]
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MarketingXScheduleRuleInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "更新後",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MarketingXScheduleRule"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingXScheduleRulesReplace"
      },
      "delete": {
        "tags": [
          "X（旧Twitter）/ X (Twitter)"
        ],
        "summary": "スケジュールルールを削除 / Delete Schedule Rule",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid",
              "examples": [
                "00000000-0000-0000-0000-000000000000"
              ]
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "204": {
            "description": "成功"
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingXScheduleRulesDelete"
      }
    },
    "/v1/marketing/x/schedule-rules/{id}/toggle": {
      "patch": {
        "tags": [
          "X（旧Twitter）/ X (Twitter)"
        ],
        "summary": "スケジュールルールを有効/無効 / Toggle Schedule Rule",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid",
              "examples": [
                "00000000-0000-0000-0000-000000000000"
              ]
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "更新後",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MarketingXScheduleRule"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingXScheduleRulesToggleUpdate"
      }
    },
    "/v1/marketing/x/listen-rules": {
      "get": {
        "tags": [
          "X（旧Twitter）/ X (Twitter)"
        ],
        "summary": "リスニングルール一覧 / Listen Rule List",
        "responses": {
          "200": {
            "description": "一覧",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/MarketingXListenRule"
                      }
                    }
                  },
                  "required": [
                    "items"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingXListenRulesList"
      },
      "post": {
        "tags": [
          "X（旧Twitter）/ X (Twitter)"
        ],
        "summary": "リスニングルールを作成 / Create Listen Rule",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MarketingXListenRuleInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "作成済み",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MarketingXListenRule"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingXListenRulesCreate"
      }
    },
    "/v1/marketing/x/listen-rules/{id}": {
      "put": {
        "tags": [
          "X（旧Twitter）/ X (Twitter)"
        ],
        "summary": "リスニングルールを更新 / Update Listen Rule",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid",
              "examples": [
                "00000000-0000-0000-0000-000000000000"
              ]
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MarketingXListenRuleInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "更新後",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MarketingXListenRule"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingXListenRulesReplace"
      },
      "delete": {
        "tags": [
          "X（旧Twitter）/ X (Twitter)"
        ],
        "summary": "リスニングルールを削除 / Delete Listen Rule",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid",
              "examples": [
                "00000000-0000-0000-0000-000000000000"
              ]
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "204": {
            "description": "成功"
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingXListenRulesDelete"
      }
    },
    "/v1/marketing/x/listen-rules/{id}/toggle": {
      "patch": {
        "tags": [
          "X（旧Twitter）/ X (Twitter)"
        ],
        "summary": "リスニングルールを有効/無効 / Toggle Listen Rule",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid",
              "examples": [
                "00000000-0000-0000-0000-000000000000"
              ]
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "更新後",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MarketingXListenRule"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingXListenRulesToggleUpdate"
      }
    },
    "/v1/marketing/x/insights": {
      "get": {
        "tags": [
          "X（旧Twitter）/ X (Twitter)"
        ],
        "summary": "Xインサイト / X Insights",
        "responses": {
          "200": {
            "description": "インサイト",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "followerTrend": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "day": {
                            "type": "integer"
                          },
                          "followers": {
                            "type": "integer"
                          },
                          "date": {
                            "type": "string"
                          }
                        },
                        "required": [
                          "day",
                          "followers"
                        ]
                      }
                    },
                    "topPosts": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "body": {
                            "type": "string"
                          },
                          "date": {
                            "type": "string"
                          },
                          "likes": {
                            "type": "integer"
                          },
                          "reposts": {
                            "type": "integer"
                          },
                          "replies": {
                            "type": "integer"
                          },
                          "impressions": {
                            "type": "integer"
                          }
                        },
                        "required": [
                          "body",
                          "date",
                          "likes",
                          "reposts",
                          "replies",
                          "impressions"
                        ]
                      }
                    },
                    "heatmap": {
                      "type": "array",
                      "items": {
                        "type": "array",
                        "items": {
                          "type": "integer"
                        }
                      }
                    },
                    "_meta": {
                      "$ref": "#/components/schemas/SyncMeta"
                    }
                  },
                  "required": [
                    "followerTrend",
                    "topPosts",
                    "heatmap",
                    "_meta"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingXInsightsList"
      }
    },
    "/v1/marketing/x/accounts": {
      "get": {
        "tags": [
          "X（旧Twitter）/ X (Twitter)"
        ],
        "summary": "Xアカウント一覧 / X Account List",
        "responses": {
          "200": {
            "description": "一覧",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/MarketingXAccount"
                      }
                    }
                  },
                  "required": [
                    "items"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingXAccountsList"
      },
      "post": {
        "tags": [
          "X（旧Twitter）/ X (Twitter)"
        ],
        "summary": "Xアカウントを登録 / Register X Account",
        "description": "X の OAuth 2.0 トークンを `x_accounts.access_token_encrypted` に保存する。\n\naccess_token は lib/crypto.ts の encryptSecret (AES-GCM, env.MARKETING_ENCRYPT_KEY)\nで暗号化してから DB に書き込む。読み出し側 (cron/x-*.ts) は decryptSecret で復号する。",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "handle": {
                    "type": "string"
                  },
                  "access_token": {
                    "type": "string"
                  },
                  "tier": {
                    "type": "string",
                    "enum": [
                      "free",
                      "basic",
                      "pro"
                    ]
                  }
                },
                "required": [
                  "handle",
                  "access_token"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "作成済み",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MarketingXAccount"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingXAccountsCreate"
      }
    },
    "/v1/marketing/x/accounts/{handle}": {
      "get": {
        "tags": [
          "X（旧Twitter）/ X (Twitter)"
        ],
        "summary": "Xアカウント詳細 / X Account Detail",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 15,
              "pattern": "^[A-Za-z0-9_]+$"
            },
            "required": true,
            "name": "handle",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "取得成功",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MarketingXAccount"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingXAccountsGet"
      },
      "patch": {
        "tags": [
          "X（旧Twitter）/ X (Twitter)"
        ],
        "summary": "Xアカウントを更新 / Update X Account",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 15,
              "pattern": "^[A-Za-z0-9_]+$"
            },
            "required": true,
            "name": "handle",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "display_name": {
                    "type": "string",
                    "maxLength": 50
                  },
                  "tier": {
                    "type": "string",
                    "enum": [
                      "free",
                      "basic",
                      "pro"
                    ]
                  },
                  "is_primary": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "更新済み",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MarketingXAccount"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingXAccountsUpdate"
      },
      "delete": {
        "tags": [
          "X（旧Twitter）/ X (Twitter)"
        ],
        "summary": "Xアカウントを削除 / Delete X Account",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 15,
              "pattern": "^[A-Za-z0-9_]+$"
            },
            "required": true,
            "name": "handle",
            "in": "path"
          }
        ],
        "responses": {
          "204": {
            "description": "削除済み"
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingXAccountsDelete"
      }
    },
    "/v1/marketing/x/oauth/start": {
      "get": {
        "tags": [
          "X（旧Twitter）/ X (Twitter)"
        ],
        "summary": "X OAuth開始URLを発行 / Get X OAuth Start URL",
        "responses": {
          "200": {
            "description": "OAuth URL",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "authorize_url": {
                      "type": "string"
                    },
                    "state": {
                      "type": "string"
                    },
                    "todo": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "authorize_url",
                    "state",
                    "todo"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingXOauthStart"
      }
    },
    "/v1/marketing/x/oauth/callback": {
      "get": {
        "tags": [
          "X（旧Twitter）/ X (Twitter)"
        ],
        "summary": "X OAuthコールバック / X OAuth Callback",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "code",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "state",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "スタブ",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "todo": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "ok",
                    "todo"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingXOauthCallback"
      }
    },
    "/v1/marketing/x/posts": {
      "get": {
        "tags": [
          "X（旧Twitter）/ X (Twitter)"
        ],
        "summary": "X投稿一覧 / X Post List",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "enum": [
                "draft",
                "scheduled",
                "publishing",
                "published",
                "failed",
                "archived"
              ]
            },
            "required": false,
            "name": "status",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "uuid",
              "examples": [
                "00000000-0000-0000-0000-000000000000"
              ]
            },
            "required": false,
            "name": "account_id",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "search",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "pattern": "^\\d+$"
            },
            "required": false,
            "name": "limit",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "一覧",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/MarketingXPost"
                      }
                    }
                  },
                  "required": [
                    "items"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingXPostsList"
      },
      "post": {
        "tags": [
          "X（旧Twitter）/ X (Twitter)"
        ],
        "summary": "X投稿を作成 / Create X Post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MarketingXPostCreateInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "作成済み",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MarketingXPost"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingXPostsCreate"
      }
    },
    "/v1/marketing/x/posts/{id}": {
      "get": {
        "tags": [
          "X（旧Twitter）/ X (Twitter)"
        ],
        "summary": "X投稿詳細 / X Post Detail",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid",
              "examples": [
                "00000000-0000-0000-0000-000000000000"
              ]
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "投稿",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MarketingXPost"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingXPostsGet"
      },
      "put": {
        "tags": [
          "X（旧Twitter）/ X (Twitter)"
        ],
        "summary": "X投稿を更新 / Update X Post",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid",
              "examples": [
                "00000000-0000-0000-0000-000000000000"
              ]
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MarketingXPostUpdateInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "更新後",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MarketingXPost"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingXPostsReplace"
      },
      "delete": {
        "tags": [
          "X（旧Twitter）/ X (Twitter)"
        ],
        "summary": "X投稿を削除 / Delete X Post",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid",
              "examples": [
                "00000000-0000-0000-0000-000000000000"
              ]
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "204": {
            "description": "成功"
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingXPostsDelete"
      }
    },
    "/v1/marketing/x/posts/{id}/schedule": {
      "post": {
        "tags": [
          "X（旧Twitter）/ X (Twitter)"
        ],
        "summary": "X投稿を予約 / Schedule X Post",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid",
              "examples": [
                "00000000-0000-0000-0000-000000000000"
              ]
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "scheduledAt": {
                    "type": "string",
                    "format": "date-time"
                  }
                },
                "required": [
                  "scheduledAt"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "予約済み",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MarketingXPost"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingXPostsSchedule"
      }
    },
    "/v1/marketing/x/posts/{id}/publish-now": {
      "post": {
        "tags": [
          "X（旧Twitter）/ X (Twitter)"
        ],
        "summary": "X投稿を即時公開 / Publish X Post Now",
        "description": "投稿を即時公開キューに入れる。status を 'publishing' に更新。\n実際の X API 呼び出しは Agent D の cron/x-scheduled-post が拾う。",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid",
              "examples": [
                "00000000-0000-0000-0000-000000000000"
              ]
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "公開中",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MarketingXPost"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingXPostsPublishNowCreate"
      }
    },
    "/v1/marketing/x/automation-log": {
      "get": {
        "tags": [
          "X（旧Twitter）/ X (Twitter)"
        ],
        "summary": "自動化ルール発火履歴 / Automation Log",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^\\d+$"
            },
            "required": false,
            "name": "limit",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "履歴",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/MarketingXAutomationLog"
                      }
                    },
                    "_meta": {
                      "$ref": "#/components/schemas/SyncMeta"
                    }
                  },
                  "required": [
                    "items",
                    "_meta"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingXAutomationLogList"
      }
    },
    "/v1/marketing/x/competitors": {
      "get": {
        "tags": [
          "X（旧Twitter）/ X (Twitter)"
        ],
        "summary": "競合アカウント一覧 / Competitor Account List",
        "responses": {
          "200": {
            "description": "一覧",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/MarketingXCompetitor"
                      }
                    }
                  },
                  "required": [
                    "items"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingXCompetitorsList"
      },
      "post": {
        "tags": [
          "X（旧Twitter）/ X (Twitter)"
        ],
        "summary": "競合アカウントを追加 / Add Competitor Account",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "handle": {
                    "type": "string"
                  },
                  "memo": {
                    "type": "string"
                  }
                },
                "required": [
                  "handle"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "追加済み",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MarketingXCompetitor"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingXCompetitorsCreate"
      }
    },
    "/v1/marketing/x/competitors/{handle}": {
      "delete": {
        "tags": [
          "X（旧Twitter）/ X (Twitter)"
        ],
        "summary": "競合アカウントを削除 / Delete Competitor Account",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "handle",
            "in": "path"
          }
        ],
        "responses": {
          "204": {
            "description": "成功"
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingXCompetitorsDelete"
      }
    },
    "/v1/marketing/integrations": {
      "get": {
        "tags": [
          "連携設定 / Integrations"
        ],
        "summary": "連携一覧 / Integration List",
        "description": "対応プロバイダー `(meta, x, ga4, search_console, resend, sendgrid, supabase_email)` の\n連携状態を envelope 形式で返す。DB に行が無い provider は `status='not_configured'` /\n`public_fields={}` / `has_secret={ <各 secret key>: false }` で擬似的に補完して返すので、\nフロントエンドは常に 7 行並べて表示できる。\n\n機密値はレスポンスに一切含めず、`has_secret` の boolean でのみ存在有無を FE に通知する。",
        "responses": {
          "200": {
            "description": "一覧",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/MarketingIntegrationEnvelope"
                      }
                    }
                  },
                  "required": [
                    "items"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingIntegrationsList"
      }
    },
    "/v1/marketing/integrations/{provider}": {
      "put": {
        "tags": [
          "連携設定 / Integrations"
        ],
        "summary": "連携設定を更新 / Update Integration",
        "description": "指定 provider の `public_fields` だけを PUT する。secret 系 (api_key 等) は\nこの endpoint では受け付けず、`/oauth/<provider>/start` または provider 専用\nendpoint (例: `/integrations/ga4/upload-sa-json`) を使うこと。\n\nbody の key は provider 別の PUBLIC_FIELDS allowlist に含まれる必要あり。\n含まれない key が混ざっていた場合は 400 を返す。",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "provider",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "status": {
                    "$ref": "#/components/schemas/IntegrationStatus"
                  },
                  "public_fields": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "object",
                      "description": "任意の JSON 値（string / number / boolean / null / array / object）。再帰構造は object + additionalProperties で表現。",
                      "additionalProperties": true
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "保存済み",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MarketingIntegrationEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingIntegrationsReplace"
      }
    },
    "/v1/marketing/brand": {
      "get": {
        "tags": [
          "ブランド設定 / Brand Settings"
        ],
        "summary": "ブランド設定を取得 / Get Brand Settings",
        "responses": {
          "200": {
            "description": "ブランド設定",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MarketingBrand"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingBrandList"
      },
      "put": {
        "tags": [
          "ブランド設定 / Brand Settings"
        ],
        "summary": "ブランド設定を更新 / Update Brand Settings",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MarketingBrandPutInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "保存済み",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MarketingBrand"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingBrandReplace"
      }
    },
    "/v1/marketing/oauth/meta/start": {
      "get": {
        "tags": [
          "OAuth認証 / OAuth"
        ],
        "summary": "Meta OAuth認可URLを発行 / Generate Meta OAuth URL",
        "responses": {
          "200": {
            "description": "authorize URL",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "url": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "url"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "too_many_requests",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "bad_gateway",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingOauthMetaStart"
      }
    },
    "/v1/marketing/oauth/meta/callback": {
      "get": {
        "tags": [
          "OAuth認証 / OAuth"
        ],
        "summary": "Meta OAuthコールバック / Meta OAuth Callback",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "code",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "state",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "error",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "error_description",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "HTML",
            "content": {
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "too_many_requests",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "bad_gateway",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingOauthMetaCallback"
      }
    },
    "/v1/marketing/oauth/x/start": {
      "get": {
        "tags": [
          "OAuth認証 / OAuth"
        ],
        "summary": "X OAuth認可URLを発行 / Generate X OAuth URL",
        "responses": {
          "200": {
            "description": "authorize URL",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "url": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "url"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "too_many_requests",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "bad_gateway",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingOauthXStart"
      }
    },
    "/v1/marketing/oauth/x/callback": {
      "get": {
        "tags": [
          "OAuth認証 / OAuth"
        ],
        "summary": "X OAuthコールバック / X OAuth Callback",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "code",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "state",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "error",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "error_description",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "HTML",
            "content": {
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "too_many_requests",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "bad_gateway",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingOauthXCallback"
      }
    },
    "/v1/marketing/oauth/search-console/start": {
      "get": {
        "tags": [
          "OAuth認証 / OAuth"
        ],
        "summary": "Search Console OAuth URLを発行 / Generate Search Console OAuth URL",
        "responses": {
          "200": {
            "description": "authorize URL",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "url": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "url"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "too_many_requests",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "bad_gateway",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingOauthSearchConsoleStart"
      }
    },
    "/v1/marketing/oauth/search-console/callback": {
      "get": {
        "tags": [
          "OAuth認証 / OAuth"
        ],
        "summary": "Search Console OAuthコールバック / Search Console OAuth Callback",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "code",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "state",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "error",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "error_description",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "HTML",
            "content": {
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "too_many_requests",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "bad_gateway",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingOauthSearchConsoleCallback"
      }
    },
    "/v1/marketing/integrations/ga4/upload-sa-json": {
      "post": {
        "tags": [
          "OAuth認証 / OAuth",
          "GA4 / GA4"
        ],
        "summary": "GA4 SAキーを登録 / Register GA4 Service Account Key",
        "description": "サービスアカウント JSON をそのまま body で受け取り、property_id と合わせて\nmarketing.marketing_integrations.config に保存する。GA4 Data API への疎通確認を行い、\n200 が返れば status='connected'、失敗なら status='error' + last_error。\n\nprivate_key は lib/crypto.ts の encryptSecret で AES-GCM 暗号化してから\nJSONB に埋め込む。読み出し側は decryptSecret で復号してから jose.importPKCS8 に渡す。",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "property_id": {
                    "type": "string",
                    "minLength": 1
                  },
                  "service_account": {
                    "type": "object",
                    "properties": {
                      "client_email": {
                        "type": "string",
                        "format": "email"
                      },
                      "private_key": {
                        "type": "string",
                        "minLength": 1
                      },
                      "project_id": {
                        "type": "string"
                      },
                      "type": {
                        "type": "string"
                      },
                      "private_key_id": {
                        "type": "string"
                      },
                      "client_id": {
                        "type": "string"
                      },
                      "auth_uri": {
                        "type": "string",
                        "format": "uri"
                      },
                      "token_uri": {
                        "type": "string",
                        "format": "uri"
                      },
                      "auth_provider_x509_cert_url": {
                        "type": "string",
                        "format": "uri"
                      },
                      "client_x509_cert_url": {
                        "type": "string",
                        "format": "uri"
                      },
                      "universe_domain": {
                        "type": "string"
                      }
                    },
                    "required": [
                      "client_email",
                      "private_key"
                    ]
                  },
                  "scopes": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                },
                "required": [
                  "property_id",
                  "service_account"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "接続完了",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "property_id": {
                      "type": "string"
                    },
                    "sa_email": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "ok",
                    "property_id",
                    "sa_email"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "bad_gateway",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingIntegrationsGa4UploadSaJsonCreate"
      }
    },
    "/v1/marketing/assets": {
      "get": {
        "tags": [
          "アセット / Assets"
        ],
        "summary": "アセット一覧 / Asset List",
        "description": "マーケ担当者向けのアセット管理画面用の一覧 API。\n- `kind` で種別フィルタ（image / video / gif / logo / icon / banner）\n- `tag` で単一タグフィルタ（tags @> ARRAY[tag]）\n- `search` で name / alt_text への ILIKE 検索\n- `limit` 省略時は 100（max 500）",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "enum": [
                "image",
                "video",
                "gif",
                "logo",
                "icon",
                "banner"
              ]
            },
            "required": false,
            "name": "kind",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "tag",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "search",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "pattern": "^\\d+$"
            },
            "required": false,
            "name": "limit",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "一覧",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/MarketingAsset"
                      }
                    }
                  },
                  "required": [
                    "items"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingAssetsList"
      }
    },
    "/v1/marketing/assets/{id}": {
      "get": {
        "tags": [
          "アセット / Assets"
        ],
        "summary": "アセット詳細 / Asset Detail",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid",
              "examples": [
                "00000000-0000-0000-0000-000000000000"
              ]
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "詳細",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MarketingAsset"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingAssetsGet"
      },
      "put": {
        "tags": [
          "アセット / Assets"
        ],
        "summary": "アセット更新（alt_text / tags / kind / name）",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid",
              "examples": [
                "00000000-0000-0000-0000-000000000000"
              ]
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 300
                  },
                  "kind": {
                    "type": "string",
                    "enum": [
                      "image",
                      "video",
                      "gif",
                      "logo",
                      "icon",
                      "banner"
                    ]
                  },
                  "alt_text": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 2000
                  },
                  "tags": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "maxLength": 100
                    },
                    "maxItems": 50
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "更新後",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MarketingAsset"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingAssetsReplace"
      },
      "delete": {
        "tags": [
          "アセット / Assets"
        ],
        "summary": "アセット削除 / Delete Asset",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid",
              "examples": [
                "00000000-0000-0000-0000-000000000000"
              ]
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "204": {
            "description": "成功"
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingAssetsDelete"
      }
    },
    "/v1/marketing/assets/upload": {
      "post": {
        "tags": [
          "アセット / Assets"
        ],
        "summary": "アップロードURLを発行 / Generate Upload URL",
        "description": "2 ステップアップロードの 1 段目。レスポンスの `presigned_url` にクライアントが\n直接 `PUT` し、完了後に `POST /assets/:id/confirm` で実サイズ等を確定させる。\n\n`r2_key` は `marketing/{yyyymm}/{uuid}_{sanitized-name}` 形式で衝突しない。\npresigned URL の TTL は app_config `storage.presigned_url_expiry_seconds`（既定 300 秒）。",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 300
                  },
                  "kind": {
                    "type": "string",
                    "enum": [
                      "image",
                      "video",
                      "gif",
                      "logo",
                      "icon",
                      "banner"
                    ]
                  },
                  "mime_type": {
                    "type": "string",
                    "minLength": 1
                  },
                  "byte_size": {
                    "type": "integer",
                    "exclusiveMinimum": 0
                  }
                },
                "required": [
                  "name",
                  "kind",
                  "mime_type"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "presigned URL",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "asset_id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "presigned_url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "r2_key": {
                      "type": "string"
                    },
                    "public_url": {
                      "type": "string"
                    },
                    "expires_in": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "asset_id",
                    "presigned_url",
                    "r2_key",
                    "public_url",
                    "expires_in"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingAssetsUploadCreate"
      }
    },
    "/v1/marketing/assets/{id}/confirm": {
      "post": {
        "tags": [
          "アセット / Assets"
        ],
        "summary": "アップロード完了を確定 / Confirm Upload",
        "description": "PUT 完了後にクライアントから呼び、実サイズ / 寸法 / タグ / alt_text を登録する。",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid",
              "examples": [
                "00000000-0000-0000-0000-000000000000"
              ]
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "width": {
                    "type": "integer"
                  },
                  "height": {
                    "type": "integer"
                  },
                  "byte_size": {
                    "type": "integer"
                  },
                  "duration_seconds": {
                    "type": "integer"
                  },
                  "alt_text": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "tags": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "確定後",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MarketingAsset"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingAssetsConfirmCreate"
      }
    },
    "/v1/marketing/notifications": {
      "get": {
        "tags": [
          "通知 / Notifications"
        ],
        "summary": "通知一覧 / Notification List",
        "description": "JWT の sub を admins から引いた admin user_id と、user_id IS NULL（全員向け）\nの行を新しい順に返す。`unread=true` で未読のみに絞り込める。",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "enum": [
                "true",
                "false"
              ]
            },
            "required": false,
            "name": "unread",
            "in": "query"
          },
          {
            "schema": {
              "$ref": "#/components/schemas/NotificationKind"
            },
            "required": false,
            "name": "kind",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "pattern": "^\\d+$"
            },
            "required": false,
            "name": "limit",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "一覧",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/MarketingNotification"
                      }
                    }
                  },
                  "required": [
                    "items"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingNotificationsList"
      }
    },
    "/v1/marketing/notifications/summary": {
      "get": {
        "tags": [
          "通知 / Notifications"
        ],
        "summary": "未読件数サマリー / Unread Count Summary",
        "responses": {
          "200": {
            "description": "サマリー",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "unread": {
                      "type": "integer"
                    },
                    "total": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "unread",
                    "total"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingNotificationsSummary"
      }
    },
    "/v1/marketing/notifications/{id}/read": {
      "post": {
        "tags": [
          "通知 / Notifications"
        ],
        "summary": "指定通知を既読 / Mark Notification as Read",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid",
              "examples": [
                "00000000-0000-0000-0000-000000000000"
              ]
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "既読化後",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MarketingNotification"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingNotificationsRead"
      }
    },
    "/v1/marketing/notifications/mark-all-read": {
      "post": {
        "tags": [
          "通知 / Notifications"
        ],
        "summary": "すべての通知を既読 / Mark All Notifications as Read",
        "responses": {
          "200": {
            "description": "既読化件数",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "updated": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "updated"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingNotificationsMarkAllRead"
      }
    },
    "/v1/marketing/notifications/{id}": {
      "delete": {
        "tags": [
          "通知 / Notifications"
        ],
        "summary": "通知を削除 / Delete Notification",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid",
              "examples": [
                "00000000-0000-0000-0000-000000000000"
              ]
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "204": {
            "description": "成功"
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingNotificationsDelete"
      }
    },
    "/v1/marketing/activity": {
      "get": {
        "tags": [
          "活動ログ / Activity Logs"
        ],
        "summary": "活動ログ一覧 / Activity Log List",
        "description": "マーケ操作の監査ログ閲覧。キャンペーン詳細や X ポスト詳細などから\n`target_kind` + `target_id` の組み合わせで絞り込むと、そのオブジェクトに\n対して誰が何をしたかの履歴が取れる。",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "target_kind",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "target_id",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": false,
            "name": "user_id",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "action",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "pattern": "^\\d+$"
            },
            "required": false,
            "name": "limit",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "一覧",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/MarketingActivityLog"
                      }
                    }
                  },
                  "required": [
                    "items"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingActivityList"
      }
    },
    "/v1/marketing/campaigns": {
      "get": {
        "tags": [
          "キャンペーン / Campaigns"
        ],
        "summary": "キャンペーン一覧 / Campaign List",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "enum": [
                "planning",
                "active",
                "paused",
                "completed",
                "archived"
              ]
            },
            "required": false,
            "name": "status",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "from",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "to",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "channel",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "pattern": "^\\d+$"
            },
            "required": false,
            "name": "limit",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "一覧",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "allOf": [
                          {
                            "$ref": "#/components/schemas/MarketingCampaign"
                          },
                          {
                            "type": "object",
                            "properties": {
                              "items_count": {
                                "type": "integer"
                              }
                            },
                            "required": [
                              "items_count"
                            ]
                          }
                        ]
                      }
                    }
                  },
                  "required": [
                    "items"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingCampaignsList"
      },
      "post": {
        "tags": [
          "キャンペーン / Campaigns"
        ],
        "summary": "キャンペーンを作成 / Create Campaign",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MarketingCampaignCreateInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "作成済み",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MarketingCampaign"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingCampaignsCreate"
      }
    },
    "/v1/marketing/campaigns/{id}": {
      "get": {
        "tags": [
          "キャンペーン / Campaigns"
        ],
        "summary": "キャンペーン詳細 / Campaign Detail",
        "description": "?include=metrics で metrics、?include=items.resource で items[].linked_resource を埋める。\nカンマ区切りで複数同時指定可（例: `?include=metrics,items.resource`）。\nshape は `GET /v1/marketing/campaigns/{id}/metrics` と完全一致（computeCampaignMetrics 共用）。\neditor 画面の `campaigns.get(id)` + `campaigns.metrics(id)` 並列 2 req を 1 req に畳むための導線。",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid",
              "examples": [
                "00000000-0000-0000-0000-000000000000"
              ]
            },
            "required": true,
            "name": "id",
            "in": "path"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "include",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "詳細",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MarketingCampaignDetailWithMetrics"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingCampaignsGet"
      },
      "put": {
        "tags": [
          "キャンペーン / Campaigns"
        ],
        "summary": "キャンペーンを更新 / Update Campaign",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid",
              "examples": [
                "00000000-0000-0000-0000-000000000000"
              ]
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MarketingCampaignUpdateInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "更新後",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MarketingCampaign"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingCampaignsReplace"
      },
      "delete": {
        "tags": [
          "キャンペーン / Campaigns"
        ],
        "summary": "キャンペーンをアーカイブ / Archive Campaign",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid",
              "examples": [
                "00000000-0000-0000-0000-000000000000"
              ]
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "204": {
            "description": "archived"
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingCampaignsDelete"
      }
    },
    "/v1/marketing/campaigns/{id}/items": {
      "post": {
        "tags": [
          "キャンペーン / Campaigns"
        ],
        "summary": "コンテンツをキャンペーンに紐付け（識別コードベース）",
        "description": "item_kind に応じて item_ref の参照キーが変わる:\n- article        → articles.slug\n- x_post         → x_posts.id (uuid)\n- newsletter     → newsletter_broadcasts.id (uuid)\n- ad             → ads.id (uuid)\n- instagram_post → Instagram 外部 post id\n\n`item_ref` を推奨。`item_id` も受理する。両方あれば `item_ref` を優先。",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid",
              "examples": [
                "00000000-0000-0000-0000-000000000000"
              ]
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "item_kind": {
                    "type": "string",
                    "enum": [
                      "instagram_post",
                      "x_post",
                      "article",
                      "ad",
                      "newsletter"
                    ]
                  },
                  "item_ref": {
                    "type": "string",
                    "minLength": 1
                  },
                  "item_id": {
                    "type": "string",
                    "minLength": 1
                  }
                },
                "required": [
                  "item_kind"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "紐付け済み",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MarketingCampaignItem"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingCampaignsItemsCreate"
      }
    },
    "/v1/marketing/campaigns/{id}/items/{item_kind}/{item_ref}": {
      "delete": {
        "tags": [
          "キャンペーン / Campaigns"
        ],
        "summary": "キャンペーンからコンテンツを解除 / Remove Content from Campaign",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid",
              "examples": [
                "00000000-0000-0000-0000-000000000000"
              ]
            },
            "required": true,
            "name": "id",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "instagram_post",
                "x_post",
                "article",
                "ad",
                "newsletter"
              ]
            },
            "required": true,
            "name": "item_kind",
            "in": "path"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "item_ref",
            "in": "path"
          }
        ],
        "responses": {
          "204": {
            "description": "unlinked"
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingCampaignsItemsDelete"
      }
    },
    "/v1/marketing/campaigns/{id}/metrics": {
      "get": {
        "tags": [
          "キャンペーン / Campaigns"
        ],
        "summary": "キャンペーン指標集計 / Campaign Metrics",
        "description": "紐付け済みの item_kind 別に関連テーブルを UNION ALL で一括 JOIN して集計する。\n現状は PV/IMP/CLK は参照先テーブルの累積値をそのまま合算 (期間フィルタは未実装)。\nGA4 連携後は日次 pv スナップショットと突合して期間内差分を算出する予定。\n\n`GET /v1/marketing/campaigns/{id}?include=metrics` でも同じ shape を埋め込みで取得可能。",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid",
              "examples": [
                "00000000-0000-0000-0000-000000000000"
              ]
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "集計",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MarketingCampaignMetrics"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingCampaignsMetrics"
      }
    },
    "/v1/marketing/analytics/summary": {
      "get": {
        "tags": [
          "分析 / Analytics"
        ],
        "summary": "分析サマリー / Analytics Summary",
        "description": "期間内 (from-to) のマーケ KPI を一括で返す。GA4 連携前のため記事 PV は累積値。\nfrom/to を省略した場合は直近 30 日 (UTC)。",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "from",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "to",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "サマリー",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MarketingAnalyticsSummary"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingAnalyticsSummary"
      }
    },
    "/v1/marketing/analytics/articles": {
      "get": {
        "tags": [
          "分析 / Analytics"
        ],
        "summary": "記事別PV一覧 / Article PV List",
        "description": "記事別 PV の一覧。デフォルト上限は 20 件（SummaryPage 等の「上位 N 件」用途）。\nAnalyticsArticlesPage のような全件表示用途のみ limit を明示すること（max 200）。",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "from",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "to",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "category",
            "in": "query"
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200
            },
            "required": false,
            "name": "limit",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "記事パフォーマンス",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "from": {
                      "type": "string"
                    },
                    "to": {
                      "type": "string"
                    },
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/MarketingAnalyticsArticleRow"
                      }
                    },
                    "_meta": {
                      "$ref": "#/components/schemas/SyncMeta"
                    }
                  },
                  "required": [
                    "from",
                    "to",
                    "items",
                    "_meta"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingAnalyticsArticlesList"
      }
    },
    "/v1/marketing/analytics/ads": {
      "get": {
        "tags": [
          "分析 / Analytics"
        ],
        "summary": "広告別指標 / Ad Performance Metrics",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "status",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "from",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "to",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "広告パフォーマンス",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/MarketingAnalyticsAdRow"
                      }
                    },
                    "_meta": {
                      "$ref": "#/components/schemas/SyncMeta"
                    }
                  },
                  "required": [
                    "items",
                    "_meta"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingAnalyticsAdsList"
      }
    },
    "/v1/marketing/analytics/cross-channel": {
      "get": {
        "tags": [
          "分析 / Analytics"
        ],
        "summary": "チャネル別サマリー / Cross-Channel Summary",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "from",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "to",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "チャネル別",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "from": {
                      "type": "string"
                    },
                    "to": {
                      "type": "string"
                    },
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/MarketingAnalyticsCrossChannelRow"
                      }
                    },
                    "_meta": {
                      "$ref": "#/components/schemas/SyncMeta"
                    }
                  },
                  "required": [
                    "from",
                    "to",
                    "items",
                    "_meta"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingAnalyticsCrossChannelList"
      }
    },
    "/v1/marketing/content-pool": {
      "get": {
        "tags": [
          "コンテンツプール / Content Pool"
        ],
        "summary": "コンテンツプール横断検索 / Search Content Pool",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "enum": [
                "article",
                "instagram_post",
                "x_post",
                "ad",
                "newsletter",
                "asset"
              ]
            },
            "required": false,
            "name": "kind",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "search",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "status",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "pattern": "^\\d+$"
            },
            "required": false,
            "name": "limit",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "検索結果",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/MarketingContentPoolItem"
                      }
                    },
                    "total": {
                      "type": "integer"
                    },
                    "_meta": {
                      "$ref": "#/components/schemas/SyncMeta"
                    }
                  },
                  "required": [
                    "items",
                    "total",
                    "_meta"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingContentPoolList"
      }
    },
    "/v1/marketing/content-pool/suggest": {
      "get": {
        "tags": [
          "コンテンツプール / Content Pool"
        ],
        "summary": "コンテンツ候補を提案 / Suggest Content Ideas",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "keyword",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "候補",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/MarketingContentPoolItem"
                      }
                    },
                    "_meta": {
                      "$ref": "#/components/schemas/SyncMeta"
                    }
                  },
                  "required": [
                    "items",
                    "_meta"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingContentPoolSuggestList"
      }
    },
    "/v1/marketing/calendar": {
      "get": {
        "tags": [
          "カレンダー / Calendar"
        ],
        "summary": "横断カレンダー / Cross-Channel Calendar",
        "description": "X / 記事 / 広告 / メール の予定・公開イベントを統合して日付別に返す。\n広告は start_date を起点として 1 日目のセルに配置 (期間中セル埋めは UI 側で展開)。\n\nTICKET: PARKY-IG-MIRROR\n  Instagram は D1 (`INSTAGRAM_DB`) にのみ存在し Postgres 側に mirror 未整備のため含めない。",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "from",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "to",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "カレンダー",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MarketingCalendarResponse"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingCalendarList"
      }
    },
    "/v1/marketing/article-categories": {
      "get": {
        "tags": [
          "記事カテゴリ / Article Categories"
        ],
        "summary": "記事カテゴリ一覧 / Article Category List",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "include_deleted",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "一覧",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/MarketingArticleCategory"
                      }
                    }
                  },
                  "required": [
                    "items"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingArticleCategoriesList"
      },
      "post": {
        "tags": [
          "記事カテゴリ / Article Categories"
        ],
        "summary": "記事カテゴリを作成 / Create Article Category",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "code": {
                    "type": "string"
                  },
                  "label": {
                    "type": "string"
                  },
                  "description": {
                    "type": "string"
                  },
                  "sort_order": {
                    "type": "integer"
                  }
                },
                "required": [
                  "code",
                  "label"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "作成済み",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MarketingArticleCategory"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingArticleCategoriesCreate"
      }
    },
    "/v1/marketing/article-categories/{code}": {
      "get": {
        "tags": [
          "記事カテゴリ / Article Categories"
        ],
        "summary": "記事カテゴリ詳細 / Article Category Detail",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "code",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "詳細",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MarketingArticleCategory"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingArticleCategoriesGet"
      },
      "put": {
        "tags": [
          "記事カテゴリ / Article Categories"
        ],
        "summary": "記事カテゴリを更新 / Update Article Category",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "code",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MarketingArticleCategoryUpdateInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "更新後",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MarketingArticleCategory"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingArticleCategoriesReplace"
      },
      "delete": {
        "tags": [
          "記事カテゴリ / Article Categories"
        ],
        "summary": "記事カテゴリをソフト削除 / Soft Delete Article Category",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "code",
            "in": "path"
          }
        ],
        "responses": {
          "204": {
            "description": "deleted"
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingArticleCategoriesDelete"
      }
    },
    "/v1/marketing/stores/{store}": {
      "get": {
        "tags": [
          "ストア連携 / Store Integrations"
        ],
        "summary": "ストア連携設定を取得 / Get Store Integration",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "enum": [
                "play_store",
                "app_store"
              ]
            },
            "required": true,
            "name": "store",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Store integration settings",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MarketingStoreIntegration"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingStoresGet"
      },
      "put": {
        "tags": [
          "ストア連携 / Store Integrations"
        ],
        "summary": "ストア連携設定を更新 / Update Store Integration",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "enum": [
                "play_store",
                "app_store"
              ]
            },
            "required": true,
            "name": "store",
            "in": "path"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MarketingStoreIntegrationUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated store integration",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MarketingStoreIntegration"
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingStoresReplace"
      }
    },
    "/v1/marketing/stores/{store}/sync": {
      "post": {
        "tags": [
          "ストア連携 / Store Integrations"
        ],
        "summary": "ストア同期をトリガー / Trigger Store Sync",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "enum": [
                "play_store",
                "app_store"
              ]
            },
            "required": true,
            "name": "store",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Sync triggered",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "_meta": {
                      "type": "object",
                      "properties": {
                        "synced": {
                          "type": "boolean"
                        },
                        "reason": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "synced",
                        "reason"
                      ]
                    }
                  },
                  "required": [
                    "ok",
                    "_meta"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingStoresSync"
      }
    },
    "/v1/marketing/pcolle/lots": {
      "get": {
        "tags": [
          "pcolle"
        ],
        "summary": "Pコレ 駐車場マスタ（現地確認済のみ）を取得",
        "description": "Marketing Portal の駐車場ピッカー (Pコレ ソース) が叩く endpoint。\nGoogle Sheets を CSV で fetch、確認状況=「現地確認済」の行だけを ParkingLotLite[] 互換\n形式で返す。写真は Apps Script Web App 経由の Drive 索引でファイル名→fileId 解決し、\nlh3.googleusercontent.com の preview/full URL を組み立てて pcolle.photos に格納する。\nWorker isolate 内 memory cache 10 分 TTL（強制再取得が要れば cache はクリアして再 deploy）。",
        "responses": {
          "200": {
            "description": "Pコレ 駐車場一覧",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "address": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "lat": {
                            "type": "number"
                          },
                          "lng": {
                            "type": "number"
                          },
                          "total_spaces": {
                            "type": [
                              "number",
                              "null"
                            ]
                          },
                          "structure": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "operating_hours": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "max_height_m": {
                            "type": [
                              "number",
                              "null"
                            ]
                          },
                          "max_width_m": {
                            "type": [
                              "number",
                              "null"
                            ]
                          },
                          "max_length_m": {
                            "type": [
                              "number",
                              "null"
                            ]
                          },
                          "max_weight_t": {
                            "type": [
                              "number",
                              "null"
                            ]
                          },
                          "min_clearance_cm": {
                            "type": [
                              "number",
                              "null"
                            ]
                          },
                          "operator_code": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "operator_name": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "entry_difficulty": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "max_daily_fee": {
                            "type": [
                              "number",
                              "null"
                            ]
                          },
                          "unit_rate": {
                            "type": [
                              "object",
                              "null"
                            ],
                            "properties": {
                              "price": {
                                "type": "number"
                              },
                              "per_minutes": {
                                "type": "number"
                              }
                            },
                            "required": [
                              "price",
                              "per_minutes"
                            ]
                          },
                          "hourly_fee": {
                            "type": [
                              "number",
                              "null"
                            ]
                          },
                          "tag_slugs": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          },
                          "is_24h": {
                            "type": "boolean"
                          },
                          "max_parking_duration_min": {
                            "type": [
                              "number",
                              "null"
                            ]
                          },
                          "entry_method": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "shape_type": {
                            "type": "string"
                          },
                          "pcolle": {
                            "type": "object",
                            "properties": {
                              "raw_id": {
                                "type": "string"
                              },
                              "prefecture": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "city": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "district": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "photos": {
                                "type": "array",
                                "items": {
                                  "type": "object",
                                  "properties": {
                                    "label": {
                                      "type": "string"
                                    },
                                    "file_id": {
                                      "type": "string"
                                    },
                                    "preview_url": {
                                      "type": "string"
                                    },
                                    "full_url": {
                                      "type": "string"
                                    }
                                  },
                                  "required": [
                                    "label",
                                    "file_id",
                                    "preview_url",
                                    "full_url"
                                  ]
                                }
                              },
                              "raw_pricing": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "pricing_structured": {
                                "type": [
                                  "object",
                                  "null"
                                ],
                                "properties": {
                                  "weekday_day": {
                                    "type": [
                                      "object",
                                      "null"
                                    ],
                                    "properties": {
                                      "unit_minutes": {
                                        "type": "number"
                                      },
                                      "unit_yen": {
                                        "type": "number"
                                      },
                                      "max_yen": {
                                        "type": [
                                          "number",
                                          "null"
                                        ]
                                      }
                                    },
                                    "required": [
                                      "unit_minutes",
                                      "unit_yen",
                                      "max_yen"
                                    ]
                                  },
                                  "weekday_night": {
                                    "type": [
                                      "object",
                                      "null"
                                    ],
                                    "properties": {
                                      "unit_minutes": {
                                        "type": "number"
                                      },
                                      "unit_yen": {
                                        "type": "number"
                                      },
                                      "max_yen": {
                                        "type": [
                                          "number",
                                          "null"
                                        ]
                                      }
                                    },
                                    "required": [
                                      "unit_minutes",
                                      "unit_yen",
                                      "max_yen"
                                    ]
                                  },
                                  "weekend_day": {
                                    "type": [
                                      "object",
                                      "null"
                                    ],
                                    "properties": {
                                      "unit_minutes": {
                                        "type": "number"
                                      },
                                      "unit_yen": {
                                        "type": "number"
                                      },
                                      "max_yen": {
                                        "type": [
                                          "number",
                                          "null"
                                        ]
                                      }
                                    },
                                    "required": [
                                      "unit_minutes",
                                      "unit_yen",
                                      "max_yen"
                                    ]
                                  },
                                  "weekend_night": {
                                    "type": [
                                      "object",
                                      "null"
                                    ],
                                    "properties": {
                                      "unit_minutes": {
                                        "type": "number"
                                      },
                                      "unit_yen": {
                                        "type": "number"
                                      },
                                      "max_yen": {
                                        "type": [
                                          "number",
                                          "null"
                                        ]
                                      }
                                    },
                                    "required": [
                                      "unit_minutes",
                                      "unit_yen",
                                      "max_yen"
                                    ]
                                  },
                                  "max_24h": {
                                    "type": [
                                      "number",
                                      "null"
                                    ]
                                  },
                                  "cheapest_per_30m": {
                                    "type": [
                                      "number",
                                      "null"
                                    ]
                                  },
                                  "cheapest_per_hour": {
                                    "type": [
                                      "number",
                                      "null"
                                    ]
                                  },
                                  "highest_max": {
                                    "type": [
                                      "number",
                                      "null"
                                    ]
                                  },
                                  "source": {
                                    "type": "string",
                                    "enum": [
                                      "llm",
                                      "regex",
                                      "none"
                                    ]
                                  }
                                },
                                "required": [
                                  "weekday_day",
                                  "weekday_night",
                                  "weekend_day",
                                  "weekend_night",
                                  "max_24h",
                                  "cheapest_per_30m",
                                  "cheapest_per_hour",
                                  "highest_max",
                                  "source"
                                ]
                              }
                            },
                            "required": [
                              "raw_id",
                              "prefecture",
                              "city",
                              "district",
                              "photos",
                              "raw_pricing",
                              "pricing_structured"
                            ]
                          }
                        },
                        "required": [
                          "id",
                          "name",
                          "address",
                          "lat",
                          "lng",
                          "total_spaces",
                          "structure",
                          "operating_hours",
                          "max_height_m",
                          "max_width_m",
                          "max_length_m",
                          "max_weight_t",
                          "min_clearance_cm",
                          "operator_code",
                          "operator_name",
                          "entry_difficulty",
                          "max_daily_fee",
                          "unit_rate",
                          "hourly_fee",
                          "tag_slugs",
                          "is_24h",
                          "max_parking_duration_min",
                          "entry_method",
                          "shape_type",
                          "pcolle"
                        ]
                      }
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "total_rows": {
                          "type": "number"
                        },
                        "confirmed_rows": {
                          "type": "number"
                        },
                        "drive_files": {
                          "type": "number"
                        },
                        "cached": {
                          "type": "boolean"
                        }
                      },
                      "required": [
                        "total_rows",
                        "confirmed_rows",
                        "drive_files",
                        "cached"
                      ]
                    }
                  },
                  "required": [
                    "items",
                    "meta"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "validation_error / bad_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "bad_gateway",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "marketingPcolleLotsList"
      }
    }
  },
  "webhooks": {}
}
