{
  "openapi": "3.1.0",
  "info": {
    "title": "Zuey.me Personal Linktree & Management API",
    "version": "1.0.0",
    "description": "Official programmatic API for Duy Nguyen (/zuey/) profile, links, layout ordering, themes, and agent tools.",
    "contact": {
      "name": "Duy Nguyen",
      "email": "hi@zuey.me",
      "url": "https://zuey.me"
    }
  },
  "servers": [
    {
      "url": "https://zuey.me",
      "description": "Production Edge Server"
    },
    {
      "url": "http://localhost:4321",
      "description": "Local Development Server"
    }
  ],
  "components": {
    "securitySchemes": {
      "BearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "API-Key",
        "description": "Pass secret API key as `Authorization: Bearer zuey_...`"
      },
      "ApiKeyHeader": {
        "type": "apiKey",
        "in": "header",
        "name": "X-API-Key",
        "description": "Pass secret API key in `X-API-Key` header"
      },
      "MemberSession": {
        "type": "apiKey",
        "in": "cookie",
        "name": "zuey_member",
        "description": "Member browser session (HttpOnly). Unsafe methods must be same-origin; member personal keys (`zk_...`) use BearerAuth instead."
      },
      "McpOAuth": {
        "type": "oauth2",
        "description": "OAuth 2.1 for MCP clients (PKCE S256, audience `<origin>/mcp`). Access tokens are only accepted by `/mcp`.",
        "flows": {
          "authorizationCode": {
            "authorizationUrl": "/oauth/authorize",
            "tokenUrl": "/oauth/token",
            "refreshUrl": "/oauth/token",
            "scopes": {
              "articles:read": "Member scope articles:read",
              "chat:write": "Member scope chat:write",
              "account:read": "Member scope account:read",
              "account:write": "Member scope account:write",
              "billing:read": "Member scope billing:read",
              "checkout:write": "Member scope checkout:write",
              "admin": "Admin tools (allowlisted admins only)"
            }
          }
        }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "success",
          "error"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": false
          },
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string",
                "description": "Stable machine-readable error code"
              },
              "message": {
                "type": "string"
              },
              "request_id": {
                "type": "string",
                "description": "Same value as the X-Request-Id response header; quote it when reporting a problem"
              }
            }
          }
        }
      },
      "Profile": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "handle": {
            "type": "string"
          },
          "email": {
            "type": "string"
          },
          "avatar_url": {
            "type": "string"
          },
          "intro_en": {
            "type": "string"
          },
          "intro_vi": {
            "type": "string"
          },
          "theme": {
            "type": "string"
          },
          "custom_css": {
            "type": "string"
          }
        }
      },
      "LinkItem": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "section": {
            "type": "string",
            "enum": [
              "blogs",
              "companies",
              "products",
              "socials"
            ]
          },
          "title_en": {
            "type": "string"
          },
          "title_vi": {
            "type": "string"
          },
          "subtitle_en": {
            "type": "string"
          },
          "subtitle_vi": {
            "type": "string"
          },
          "url": {
            "type": "string"
          },
          "icon": {
            "type": "string"
          },
          "order_index": {
            "type": "integer"
          },
          "is_active": {
            "type": "boolean"
          },
          "click_count": {
            "type": "integer"
          }
        }
      },
      "ReadItem": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "title": {
            "type": "string"
          },
          "author": {
            "type": "string",
            "nullable": true
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "domain": {
            "type": "string",
            "nullable": true
          },
          "site": {
            "type": "string",
            "nullable": true
          },
          "image": {
            "type": "string",
            "nullable": true
          },
          "source_kind": {
            "type": "string",
            "nullable": true
          },
          "language": {
            "type": "string",
            "nullable": true
          },
          "published": {
            "type": "string",
            "nullable": true
          },
          "tags": {
            "type": "string",
            "description": "Space-separated AnyMD tags"
          },
          "word_count": {
            "type": "integer",
            "nullable": true
          },
          "content_hash": {
            "type": "string",
            "nullable": true
          },
          "summary": {
            "type": "string",
            "nullable": true,
            "description": "AI-generated summary (may be inaccurate)"
          },
          "visible": {
            "type": "boolean"
          },
          "anymd_updated_at": {
            "type": "integer",
            "nullable": true
          },
          "synced_at": {
            "type": "string",
            "nullable": true
          },
          "created_at": {
            "type": "string"
          }
        }
      },
      "ReadsSyncRun": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "started_at": {
            "type": "string",
            "format": "date-time"
          },
          "finished_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "status": {
            "type": "string",
            "enum": [
              "running",
              "success",
              "partial",
              "failed"
            ]
          },
          "fetched": {
            "type": "integer"
          },
          "tagged": {
            "type": "integer"
          },
          "summarized": {
            "type": "integer"
          },
          "unchanged": {
            "type": "integer"
          },
          "hidden": {
            "type": "integer"
          },
          "error": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "SecretFinding": {
        "type": "object",
        "required": [
          "field",
          "kind",
          "excerpt"
        ],
        "properties": {
          "field": {
            "type": "string",
            "example": "steps[1].detail"
          },
          "kind": {
            "type": "string",
            "enum": [
              "openai_key",
              "github_token",
              "aws_access_key",
              "slack_token",
              "private_key",
              "bearer_token",
              "home_path",
              "email"
            ]
          },
          "excerpt": {
            "type": "string",
            "description": "Masked excerpt; never the full secret"
          }
        }
      },
      "WorkflowInput": {
        "type": "object",
        "required": [
          "expected_revision"
        ],
        "description": "For create: name, slug, summary, trigger, steps and expected_revision=0 are required. For update: any subset plus expected_revision.",
        "properties": {
          "expected_revision": {
            "type": "integer",
            "minimum": 0
          },
          "name": {
            "type": "string",
            "maxLength": 120
          },
          "slug": {
            "type": "string",
            "maxLength": 80,
            "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$"
          },
          "summary": {
            "type": "string",
            "maxLength": 500
          },
          "tools": {
            "type": "array",
            "maxItems": 20,
            "items": {
              "type": "string",
              "maxLength": 60
            }
          },
          "trigger": {
            "type": "string",
            "maxLength": 500
          },
          "steps": {
            "type": "array",
            "minItems": 1,
            "maxItems": 30,
            "items": {
              "type": "object",
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string",
                  "maxLength": 160
                },
                "detail": {
                  "type": "string",
                  "maxLength": 2000
                }
              }
            }
          },
          "metrics": {
            "type": "array",
            "maxItems": 12,
            "items": {
              "type": "object",
              "required": [
                "label",
                "value"
              ],
              "properties": {
                "label": {
                  "type": "string"
                },
                "value": {
                  "type": "string"
                }
              }
            }
          },
          "tags": {
            "type": "array",
            "maxItems": 12,
            "items": {
              "type": "string",
              "maxLength": 40
            }
          }
        }
      },
      "Workflow": {
        "type": "object",
        "description": "Public responses contain the content fields plus status=published and published_at. Admin responses add id, revision, published snapshot, has_unpublished_changes and findings.",
        "properties": {
          "name": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "summary": {
            "type": "string"
          },
          "tools": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "trigger": {
            "type": "string"
          },
          "steps": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "title": {
                  "type": "string"
                },
                "detail": {
                  "type": "string"
                }
              }
            }
          },
          "metrics": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "label": {
                  "type": "string"
                },
                "value": {
                  "type": "string"
                }
              }
            }
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "published"
            ]
          },
          "published_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "id": {
            "type": "string"
          },
          "revision": {
            "type": "integer"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "published": {
            "type": "object",
            "nullable": true,
            "description": "Current public snapshot (admin only)"
          },
          "has_unpublished_changes": {
            "type": "boolean"
          },
          "findings": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SecretFinding"
            }
          }
        }
      },
      "BookingSlot": {
        "type": "object",
        "properties": {
          "start": {
            "type": "string",
            "format": "date-time"
          },
          "end": {
            "type": "string",
            "format": "date-time"
          },
          "duration_min": {
            "type": "integer"
          }
        }
      },
      "BookingSlotList": {
        "type": "object",
        "properties": {
          "slots": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BookingSlot"
            }
          }
        }
      },
      "BookingHoldRequest": {
        "type": "object",
        "required": [
          "slot_start",
          "name",
          "email",
          "payment_method"
        ],
        "properties": {
          "slot_start": {
            "type": "string",
            "format": "date-time"
          },
          "name": {
            "type": "string",
            "maxLength": 120
          },
          "email": {
            "type": "string",
            "format": "email"
          },
          "company": {
            "type": "string",
            "maxLength": 160
          },
          "notes": {
            "type": "string",
            "maxLength": 2000
          },
          "timezone": {
            "type": "string",
            "description": "Guest IANA time zone"
          },
          "payment_method": {
            "type": "string",
            "enum": [
              "sepay",
              "paypal"
            ]
          }
        }
      },
      "BookingHoldResult": {
        "type": "object",
        "properties": {
          "booking": {
            "$ref": "#/components/schemas/BookingGuestView"
          },
          "manage_token": {
            "type": "string",
            "description": "Shown once; stored only as a hash"
          },
          "manage_url": {
            "type": "string"
          }
        }
      },
      "BookingGuestView": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "code": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "held",
              "confirmed",
              "expired",
              "cancelled",
              "needs_attention"
            ]
          },
          "slot_start": {
            "type": "string",
            "format": "date-time"
          },
          "slot_end": {
            "type": "string",
            "format": "date-time"
          },
          "duration_min": {
            "type": "integer"
          },
          "hold_expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "guest_name": {
            "type": "string"
          },
          "guest_email": {
            "type": "string"
          },
          "guest_timezone": {
            "type": "string",
            "nullable": true
          },
          "payment_method": {
            "type": "string",
            "enum": [
              "sepay",
              "paypal"
            ]
          },
          "amount_expected": {
            "type": "integer",
            "nullable": true,
            "description": "USD cents or VND"
          },
          "currency": {
            "type": "string",
            "nullable": true
          },
          "meet_url": {
            "type": "string",
            "nullable": true
          },
          "reschedule_count": {
            "type": "integer"
          },
          "can_reschedule": {
            "type": "boolean"
          },
          "reschedule_blocked_reason": {
            "type": "string",
            "nullable": true
          },
          "sepay": {
            "$ref": "#/components/schemas/SepayTransfer"
          }
        }
      },
      "BookingAdminView": {
        "type": "object",
        "description": "Full booking record (without the manage token hash), including fulfilment status fields.",
        "properties": {
          "id": {
            "type": "string"
          },
          "code": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "slot_start": {
            "type": "string"
          },
          "slot_end": {
            "type": "string"
          },
          "guest_name": {
            "type": "string"
          },
          "guest_email": {
            "type": "string"
          },
          "company": {
            "type": "string",
            "nullable": true
          },
          "notes": {
            "type": "string",
            "nullable": true
          },
          "payment_method": {
            "type": "string"
          },
          "amount_expected": {
            "type": "integer",
            "nullable": true
          },
          "amount_paid": {
            "type": "integer",
            "nullable": true
          },
          "currency": {
            "type": "string",
            "nullable": true
          },
          "payment_ref": {
            "type": "string",
            "nullable": true
          },
          "meet_status": {
            "type": "string",
            "nullable": true,
            "enum": [
              "sent",
              "unconfigured",
              "failed",
              null
            ]
          },
          "meet_error": {
            "type": "string",
            "nullable": true
          },
          "email_status": {
            "type": "string",
            "nullable": true,
            "enum": [
              "sent",
              "unconfigured",
              "failed",
              null
            ]
          },
          "email_error": {
            "type": "string",
            "nullable": true
          },
          "attention_reason": {
            "type": "string",
            "nullable": true,
            "description": "amount_mismatch | late_payment | admin"
          },
          "admin_note": {
            "type": "string",
            "nullable": true
          },
          "reschedule_count": {
            "type": "integer"
          }
        }
      },
      "BookingAdminList": {
        "type": "object",
        "properties": {
          "bookings": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BookingAdminView"
            }
          }
        }
      },
      "BookingAdminAction": {
        "type": "object",
        "required": [
          "id",
          "action"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "action": {
            "type": "string",
            "enum": [
              "cancel",
              "resolve",
              "mark_attention",
              "note"
            ]
          },
          "note": {
            "type": "string"
          }
        }
      },
      "BookingAvailability": {
        "type": "object",
        "properties": {
          "rules": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "weekday",
                "start_time",
                "end_time"
              ],
              "properties": {
                "weekday": {
                  "type": "integer",
                  "minimum": 0,
                  "maximum": 6
                },
                "start_time": {
                  "type": "string",
                  "example": "09:00"
                },
                "end_time": {
                  "type": "string",
                  "example": "17:00"
                },
                "timezone": {
                  "type": "string",
                  "default": "Asia/Ho_Chi_Minh"
                },
                "slot_minutes": {
                  "type": "integer",
                  "default": 90
                }
              }
            }
          },
          "exceptions": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "start_at",
                "end_at"
              ],
              "properties": {
                "start_at": {
                  "type": "string",
                  "format": "date-time"
                },
                "end_at": {
                  "type": "string",
                  "format": "date-time"
                },
                "reason": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "SepayTransfer": {
        "type": "object",
        "nullable": true,
        "properties": {
          "provider": {
            "type": "string",
            "enum": [
              "sepay"
            ]
          },
          "bank_account": {
            "type": "string"
          },
          "bank_code": {
            "type": "string"
          },
          "amount": {
            "type": "integer"
          },
          "currency": {
            "type": "string",
            "enum": [
              "VND"
            ]
          },
          "transfer_content": {
            "type": "string",
            "example": "ZBKAB23CD45"
          },
          "qr_url": {
            "type": "string"
          }
        }
      },
      "BookingCheckout": {
        "type": "object",
        "description": "PayPal: { provider, url (approval link), order_id, expires_at }. SePay: SepayTransfer fields plus expires_at.",
        "properties": {
          "provider": {
            "type": "string",
            "enum": [
              "sepay",
              "paypal"
            ]
          },
          "url": {
            "type": "string"
          },
          "order_id": {
            "type": "string"
          },
          "qr_url": {
            "type": "string"
          },
          "transfer_content": {
            "type": "string"
          },
          "amount": {
            "type": "integer"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "BookingCaptureResult": {
        "type": "object",
        "properties": {
          "booking": {
            "$ref": "#/components/schemas/BookingGuestView"
          },
          "capture_status": {
            "type": "string",
            "enum": [
              "confirmed",
              "pending",
              "not_approved",
              "declined",
              "needs_attention",
              "unchanged"
            ]
          }
        }
      },
      "BookingPaymentResult": {
        "type": "object",
        "properties": {
          "outcome": {
            "type": "string",
            "enum": [
              "ignored",
              "duplicate_event",
              "unmatched",
              "already_confirmed",
              "confirmed",
              "needs_attention"
            ]
          },
          "booking_id": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "SepayWebhook": {
        "type": "object",
        "required": [
          "id",
          "transferType",
          "transferAmount",
          "content"
        ],
        "properties": {
          "id": {
            "type": "integer"
          },
          "gateway": {
            "type": "string"
          },
          "transactionDate": {
            "type": "string"
          },
          "accountNumber": {
            "type": "string"
          },
          "content": {
            "type": "string"
          },
          "transferType": {
            "type": "string",
            "enum": [
              "in",
              "out"
            ]
          },
          "transferAmount": {
            "type": "number"
          },
          "referenceCode": {
            "type": "string"
          }
        }
      },
      "BlockParagraph": {
        "type": "object",
        "required": [
          "type",
          "text"
        ],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^[A-Za-z0-9_-]{1,64}$",
            "description": "Unique within the document; generated when omitted"
          },
          "type": {
            "type": "string",
            "const": "paragraph"
          },
          "text": {
            "type": "string",
            "maxLength": 20000,
            "description": "Inline text: **bold**, *italic*, ~~strike~~, ==highlight==, `code`, [label](https://...) — no HTML"
          }
        }
      },
      "BlockHeading": {
        "type": "object",
        "required": [
          "type",
          "level",
          "text"
        ],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^[A-Za-z0-9_-]{1,64}$",
            "description": "Unique within the document; generated when omitted"
          },
          "type": {
            "type": "string",
            "const": "heading"
          },
          "level": {
            "type": "integer",
            "enum": [
              1,
              2,
              3
            ]
          },
          "text": {
            "type": "string",
            "maxLength": 500
          }
        }
      },
      "BlockList": {
        "type": "object",
        "required": [
          "type",
          "items"
        ],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^[A-Za-z0-9_-]{1,64}$",
            "description": "Unique within the document; generated when omitted"
          },
          "type": {
            "type": "string",
            "const": "list"
          },
          "style": {
            "type": "string",
            "enum": [
              "bullet",
              "number"
            ]
          },
          "items": {
            "type": "array",
            "minItems": 1,
            "maxItems": 200,
            "items": {
              "type": "string",
              "maxLength": 20000,
              "description": "Inline text: **bold**, *italic*, ~~strike~~, ==highlight==, `code`, [label](https://...) — no HTML"
            }
          }
        }
      },
      "BlockChecklist": {
        "type": "object",
        "required": [
          "type",
          "items"
        ],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^[A-Za-z0-9_-]{1,64}$",
            "description": "Unique within the document; generated when omitted"
          },
          "type": {
            "type": "string",
            "const": "checklist"
          },
          "items": {
            "type": "array",
            "minItems": 1,
            "maxItems": 200,
            "items": {
              "type": "object",
              "required": [
                "text"
              ],
              "properties": {
                "text": {
                  "type": "string",
                  "maxLength": 20000,
                  "description": "Inline text: **bold**, *italic*, ~~strike~~, ==highlight==, `code`, [label](https://...) — no HTML"
                },
                "checked": {
                  "type": "boolean"
                }
              }
            }
          }
        }
      },
      "BlockQuote": {
        "type": "object",
        "required": [
          "type",
          "text"
        ],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^[A-Za-z0-9_-]{1,64}$",
            "description": "Unique within the document; generated when omitted"
          },
          "type": {
            "type": "string",
            "const": "quote"
          },
          "text": {
            "type": "string",
            "maxLength": 20000,
            "description": "Inline text: **bold**, *italic*, ~~strike~~, ==highlight==, `code`, [label](https://...) — no HTML"
          },
          "cite": {
            "type": "string",
            "maxLength": 500
          }
        }
      },
      "BlockCallout": {
        "type": "object",
        "required": [
          "type",
          "text"
        ],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^[A-Za-z0-9_-]{1,64}$",
            "description": "Unique within the document; generated when omitted"
          },
          "type": {
            "type": "string",
            "const": "callout"
          },
          "tone": {
            "type": "string",
            "enum": [
              "info",
              "warn",
              "tip"
            ]
          },
          "text": {
            "type": "string",
            "maxLength": 20000,
            "description": "Inline text: **bold**, *italic*, ~~strike~~, ==highlight==, `code`, [label](https://...) — no HTML"
          }
        }
      },
      "BlockCode": {
        "type": "object",
        "required": [
          "type",
          "code"
        ],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^[A-Za-z0-9_-]{1,64}$",
            "description": "Unique within the document; generated when omitted"
          },
          "type": {
            "type": "string",
            "const": "code"
          },
          "language": {
            "type": "string",
            "maxLength": 40
          },
          "code": {
            "type": "string",
            "maxLength": 50000
          }
        }
      },
      "BlockDivider": {
        "type": "object",
        "required": [
          "type"
        ],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^[A-Za-z0-9_-]{1,64}$",
            "description": "Unique within the document; generated when omitted"
          },
          "type": {
            "type": "string",
            "const": "divider"
          }
        }
      },
      "BlockImage": {
        "type": "object",
        "required": [
          "type",
          "url"
        ],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^[A-Za-z0-9_-]{1,64}$",
            "description": "Unique within the document; generated when omitted"
          },
          "type": {
            "type": "string",
            "const": "image"
          },
          "url": {
            "type": "string",
            "format": "uri",
            "pattern": "^https://",
            "maxLength": 2048
          },
          "alt": {
            "type": "string",
            "maxLength": 500
          },
          "caption": {
            "type": "string",
            "maxLength": 500
          }
        }
      },
      "BlockEmbed": {
        "type": "object",
        "required": [
          "type",
          "url"
        ],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^[A-Za-z0-9_-]{1,64}$",
            "description": "Unique within the document; generated when omitted"
          },
          "type": {
            "type": "string",
            "const": "embed"
          },
          "url": {
            "type": "string",
            "format": "uri",
            "pattern": "^https://",
            "maxLength": 2048
          },
          "provider": {
            "type": "string",
            "enum": [
              "youtube",
              "vimeo",
              "soundcloud",
              "spotify",
              "x",
              "facebook",
              "instagram",
              "tiktok",
              "linkedin",
              "generic"
            ],
            "description": "Detected from the URL when omitted. Rendered click-to-load for privacy."
          },
          "caption": {
            "type": "string",
            "maxLength": 500
          }
        }
      },
      "BlockTable": {
        "type": "object",
        "required": [
          "type",
          "headers",
          "rows"
        ],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^[A-Za-z0-9_-]{1,64}$",
            "description": "Unique within the document; generated when omitted"
          },
          "type": {
            "type": "string",
            "const": "table"
          },
          "headers": {
            "type": "array",
            "minItems": 1,
            "maxItems": 20,
            "items": {
              "type": "string",
              "maxLength": 500
            }
          },
          "rows": {
            "type": "array",
            "maxItems": 200,
            "items": {
              "type": "array",
              "items": {
                "type": "string",
                "maxLength": 20000,
                "description": "Inline text: **bold**, *italic*, ~~strike~~, ==highlight==, `code`, [label](https://...) — no HTML"
              },
              "description": "Same length as headers"
            }
          }
        }
      },
      "BlockChart": {
        "type": "object",
        "required": [
          "type",
          "kind",
          "labels",
          "series"
        ],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^[A-Za-z0-9_-]{1,64}$",
            "description": "Unique within the document; generated when omitted"
          },
          "type": {
            "type": "string",
            "const": "chart"
          },
          "kind": {
            "type": "string",
            "enum": [
              "bar",
              "line",
              "pie",
              "doughnut"
            ]
          },
          "title": {
            "type": "string",
            "maxLength": 500
          },
          "labels": {
            "type": "array",
            "minItems": 1,
            "maxItems": 100,
            "items": {
              "type": "string",
              "maxLength": 120
            }
          },
          "series": {
            "type": "array",
            "minItems": 1,
            "maxItems": 12,
            "items": {
              "type": "object",
              "required": [
                "name",
                "data"
              ],
              "properties": {
                "name": {
                  "type": "string",
                  "maxLength": 120
                },
                "data": {
                  "type": "array",
                  "items": {
                    "type": "number"
                  },
                  "description": "Same length as labels"
                }
              }
            }
          }
        }
      },
      "BlockDiagram": {
        "type": "object",
        "required": [
          "type",
          "source"
        ],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^[A-Za-z0-9_-]{1,64}$",
            "description": "Unique within the document; generated when omitted"
          },
          "type": {
            "type": "string",
            "const": "diagram"
          },
          "syntax": {
            "type": "string",
            "const": "mermaid"
          },
          "source": {
            "type": "string",
            "maxLength": 20000
          },
          "caption": {
            "type": "string",
            "maxLength": 500
          }
        }
      },
      "BlockSurvey": {
        "type": "object",
        "required": [
          "type",
          "question",
          "options"
        ],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^[A-Za-z0-9_-]{1,64}$",
            "description": "Unique within the document; generated when omitted"
          },
          "type": {
            "type": "string",
            "const": "survey"
          },
          "question": {
            "type": "string",
            "maxLength": 500
          },
          "options": {
            "type": "array",
            "minItems": 2,
            "maxItems": 10,
            "items": {
              "type": "object",
              "required": [
                "label"
              ],
              "properties": {
                "id": {
                  "type": "string",
                  "pattern": "^[A-Za-z0-9_-]{1,64}$",
                  "description": "Unique within the document; generated when omitted"
                },
                "label": {
                  "type": "string",
                  "maxLength": 200
                }
              }
            }
          },
          "allowMultiple": {
            "type": "boolean",
            "default": false
          }
        }
      },
      "BlockLayout": {
        "type": "object",
        "required": [
          "type",
          "cols",
          "children"
        ],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^[A-Za-z0-9_-]{1,64}$",
            "description": "Unique within the document; generated when omitted"
          },
          "type": {
            "type": "string",
            "const": "layout"
          },
          "variant": {
            "type": "string",
            "enum": [
              "columns",
              "grid",
              "bento"
            ]
          },
          "cols": {
            "type": "object",
            "required": [
              "base",
              "md",
              "lg"
            ],
            "properties": {
              "base": {
                "type": "integer",
                "minimum": 1,
                "maximum": 5
              },
              "md": {
                "type": "integer",
                "minimum": 1,
                "maximum": 5
              },
              "lg": {
                "type": "integer",
                "minimum": 1,
                "maximum": 5
              }
            },
            "description": "Columns at <768px, ≥768px, ≥1024px"
          },
          "gap": {
            "type": "string",
            "enum": [
              "sm",
              "md",
              "lg"
            ]
          },
          "children": {
            "type": "array",
            "minItems": 1,
            "maxItems": 12,
            "items": {
              "$ref": "#/components/schemas/LayoutChild"
            }
          }
        }
      },
      "BlockInteractive": {
        "type": "object",
        "required": [
          "type",
          "title"
        ],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^[A-Za-z0-9_-]{1,64}$",
            "description": "Unique within the document; generated when omitted"
          },
          "type": {
            "type": "string",
            "const": "interactive"
          },
          "title": {
            "type": "string",
            "maxLength": 500
          },
          "html": {
            "type": "string",
            "maxLength": 60000,
            "description": "Body HTML"
          },
          "css": {
            "type": "string",
            "maxLength": 60000
          },
          "js": {
            "type": "string",
            "maxLength": 60000,
            "description": "Runs in a sandboxed opaque-origin iframe with CSP connect-src none; call zuey.fetch(url) for allowlisted GET requests via the server proxy"
          },
          "height": {
            "type": "integer",
            "minimum": 120,
            "maximum": 1200
          },
          "caption": {
            "type": "string",
            "maxLength": 500
          }
        }
      },
      "BlockMath": {
        "type": "object",
        "required": [
          "type",
          "tex"
        ],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^[A-Za-z0-9_-]{1,64}$",
            "description": "Unique within the document; generated when omitted"
          },
          "type": {
            "type": "string",
            "const": "math"
          },
          "tex": {
            "type": "string",
            "maxLength": 10000,
            "description": "TeX source; shown as accessible source text"
          },
          "caption": {
            "type": "string",
            "maxLength": 500
          }
        }
      },
      "BlockGallery": {
        "type": "object",
        "required": [
          "type",
          "images"
        ],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^[A-Za-z0-9_-]{1,64}$",
            "description": "Unique within the document; generated when omitted"
          },
          "type": {
            "type": "string",
            "const": "gallery"
          },
          "images": {
            "type": "array",
            "minItems": 1,
            "maxItems": 24,
            "items": {
              "type": "object",
              "required": [
                "url"
              ],
              "properties": {
                "url": {
                  "type": "string",
                  "format": "uri",
                  "pattern": "^https://",
                  "maxLength": 2048
                },
                "alt": {
                  "type": "string",
                  "maxLength": 500
                },
                "caption": {
                  "type": "string",
                  "maxLength": 500
                }
              }
            }
          },
          "caption": {
            "type": "string",
            "maxLength": 500
          }
        }
      },
      "BlockAudio": {
        "type": "object",
        "required": [
          "type",
          "url"
        ],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^[A-Za-z0-9_-]{1,64}$",
            "description": "Unique within the document; generated when omitted"
          },
          "type": {
            "type": "string",
            "const": "audio"
          },
          "url": {
            "type": "string",
            "format": "uri",
            "pattern": "^https://",
            "maxLength": 2048
          },
          "title": {
            "type": "string",
            "maxLength": 500
          },
          "caption": {
            "type": "string",
            "maxLength": 500
          }
        }
      },
      "BlockVideo": {
        "type": "object",
        "required": [
          "type",
          "url"
        ],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^[A-Za-z0-9_-]{1,64}$",
            "description": "Unique within the document; generated when omitted"
          },
          "type": {
            "type": "string",
            "const": "video"
          },
          "url": {
            "type": "string",
            "format": "uri",
            "pattern": "^https://",
            "maxLength": 2048
          },
          "poster": {
            "type": "string",
            "format": "uri",
            "pattern": "^https://",
            "maxLength": 2048
          },
          "title": {
            "type": "string",
            "maxLength": 500
          },
          "caption": {
            "type": "string",
            "maxLength": 500
          }
        }
      },
      "BlockFile": {
        "type": "object",
        "required": [
          "type",
          "url",
          "name"
        ],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^[A-Za-z0-9_-]{1,64}$",
            "description": "Unique within the document; generated when omitted"
          },
          "type": {
            "type": "string",
            "const": "file"
          },
          "url": {
            "type": "string",
            "format": "uri",
            "pattern": "^https://",
            "maxLength": 2048
          },
          "name": {
            "type": "string",
            "maxLength": 500
          },
          "sizeBytes": {
            "type": "integer",
            "minimum": 0
          },
          "caption": {
            "type": "string",
            "maxLength": 500
          }
        }
      },
      "BlockBookmark": {
        "type": "object",
        "required": [
          "type",
          "url"
        ],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^[A-Za-z0-9_-]{1,64}$",
            "description": "Unique within the document; generated when omitted"
          },
          "type": {
            "type": "string",
            "const": "bookmark"
          },
          "url": {
            "type": "string",
            "format": "uri",
            "pattern": "^https://",
            "maxLength": 2048
          },
          "title": {
            "type": "string",
            "maxLength": 500
          },
          "description": {
            "type": "string",
            "maxLength": 1000
          },
          "image": {
            "type": "string",
            "format": "uri",
            "pattern": "^https://",
            "maxLength": 2048
          },
          "siteName": {
            "type": "string",
            "maxLength": 120
          }
        }
      },
      "BlockToggle": {
        "type": "object",
        "required": [
          "type",
          "summary",
          "blocks"
        ],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^[A-Za-z0-9_-]{1,64}$",
            "description": "Unique within the document; generated when omitted"
          },
          "type": {
            "type": "string",
            "const": "toggle"
          },
          "summary": {
            "type": "string",
            "maxLength": 500
          },
          "open": {
            "type": "boolean",
            "default": false
          },
          "blocks": {
            "type": "array",
            "maxItems": 50,
            "items": {
              "$ref": "#/components/schemas/Block"
            },
            "description": "May not contain toggle or layout blocks"
          }
        }
      },
      "Block": {
        "oneOf": [
          {
            "$ref": "#/components/schemas/BlockParagraph"
          },
          {
            "$ref": "#/components/schemas/BlockHeading"
          },
          {
            "$ref": "#/components/schemas/BlockList"
          },
          {
            "$ref": "#/components/schemas/BlockChecklist"
          },
          {
            "$ref": "#/components/schemas/BlockQuote"
          },
          {
            "$ref": "#/components/schemas/BlockCallout"
          },
          {
            "$ref": "#/components/schemas/BlockCode"
          },
          {
            "$ref": "#/components/schemas/BlockDivider"
          },
          {
            "$ref": "#/components/schemas/BlockImage"
          },
          {
            "$ref": "#/components/schemas/BlockEmbed"
          },
          {
            "$ref": "#/components/schemas/BlockTable"
          },
          {
            "$ref": "#/components/schemas/BlockChart"
          },
          {
            "$ref": "#/components/schemas/BlockDiagram"
          },
          {
            "$ref": "#/components/schemas/BlockSurvey"
          },
          {
            "$ref": "#/components/schemas/BlockLayout"
          },
          {
            "$ref": "#/components/schemas/BlockInteractive"
          },
          {
            "$ref": "#/components/schemas/BlockMath"
          },
          {
            "$ref": "#/components/schemas/BlockGallery"
          },
          {
            "$ref": "#/components/schemas/BlockAudio"
          },
          {
            "$ref": "#/components/schemas/BlockVideo"
          },
          {
            "$ref": "#/components/schemas/BlockFile"
          },
          {
            "$ref": "#/components/schemas/BlockBookmark"
          },
          {
            "$ref": "#/components/schemas/BlockToggle"
          }
        ],
        "discriminator": {
          "propertyName": "type",
          "mapping": {
            "paragraph": "#/components/schemas/BlockParagraph",
            "heading": "#/components/schemas/BlockHeading",
            "list": "#/components/schemas/BlockList",
            "checklist": "#/components/schemas/BlockChecklist",
            "quote": "#/components/schemas/BlockQuote",
            "callout": "#/components/schemas/BlockCallout",
            "code": "#/components/schemas/BlockCode",
            "divider": "#/components/schemas/BlockDivider",
            "image": "#/components/schemas/BlockImage",
            "embed": "#/components/schemas/BlockEmbed",
            "table": "#/components/schemas/BlockTable",
            "chart": "#/components/schemas/BlockChart",
            "diagram": "#/components/schemas/BlockDiagram",
            "survey": "#/components/schemas/BlockSurvey",
            "layout": "#/components/schemas/BlockLayout",
            "interactive": "#/components/schemas/BlockInteractive",
            "math": "#/components/schemas/BlockMath",
            "gallery": "#/components/schemas/BlockGallery",
            "audio": "#/components/schemas/BlockAudio",
            "video": "#/components/schemas/BlockVideo",
            "file": "#/components/schemas/BlockFile",
            "bookmark": "#/components/schemas/BlockBookmark",
            "toggle": "#/components/schemas/BlockToggle"
          }
        }
      },
      "LayoutChild": {
        "type": "object",
        "required": [
          "blocks"
        ],
        "properties": {
          "span": {
            "oneOf": [
              {
                "type": "integer",
                "minimum": 1,
                "maximum": 5
              },
              {
                "type": "object",
                "properties": {
                  "base": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 5
                  },
                  "md": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 5
                  },
                  "lg": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 5
                  }
                }
              }
            ]
          },
          "rowSpan": {
            "type": "integer",
            "minimum": 1,
            "maximum": 4
          },
          "blocks": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Block"
            }
          }
        }
      },
      "ArticleDocument": {
        "type": "object",
        "required": [
          "blocks"
        ],
        "properties": {
          "version": {
            "type": "integer",
            "const": 1
          },
          "blocks": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Block"
            }
          }
        },
        "description": "Layouts may nest at most 2 levels (a layout inside a layout is fine, a third level is rejected). Object span values must be ≤ cols at that breakpoint; a numeric span must be ≤ cols.lg and is clamped per breakpoint. Each chart series must have exactly as many data points as labels. A document may contain at most 500 blocks in total (nested blocks included). Interactive blocks: html/css/js at most 60000 characters each and 100000 combined; they run only in a sandboxed iframe and are rendered as a text fallback with a link in Markdown. Toggle blocks may contain any block except toggle and layout. Inline text supports **bold**, *italic*, ~~strike~~, ==highlight==, `code` and [label](https://...)."
      },
      "ArticleSummary": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "locale": {
            "type": "string",
            "enum": [
              "en",
              "vi",
              "zh",
              "ko",
              "ja"
            ],
            "description": "Locale of the edition in this response"
          },
          "primary_locale": {
            "type": "string",
            "enum": [
              "en",
              "vi",
              "zh",
              "ko",
              "ja"
            ]
          },
          "title": {
            "type": "string"
          },
          "excerpt": {
            "type": "string"
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Display names of topic_tags (public for every reader)"
          },
          "topic_tags": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "slug": {
                  "type": "string"
                },
                "name": {
                  "type": "string"
                }
              }
            }
          },
          "category": {
            "oneOf": [
              {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string"
                  },
                  "slug": {
                    "type": "string"
                  },
                  "name": {
                    "type": "string"
                  }
                }
              },
              {
                "type": "null"
              }
            ]
          },
          "labels": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "kind": {
                  "type": "string"
                },
                "slug": {
                  "type": "string"
                },
                "name": {
                  "type": "string"
                },
                "version": {
                  "type": [
                    "string",
                    "null"
                  ]
                }
              }
            },
            "description": "Approved labels for this edition (evidence is admin-only)"
          },
          "access": {
            "type": "string",
            "enum": [
              "free",
              "knowledges"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "published"
            ]
          },
          "revision": {
            "type": "integer",
            "description": "Article-wide revision for expected_revision"
          },
          "label_revision": {
            "type": "integer"
          },
          "published_revision": {
            "type": [
              "integer",
              "null"
            ]
          },
          "created_at": {
            "type": "string"
          },
          "updated_at": {
            "type": "string"
          },
          "published_at": {
            "type": [
              "string",
              "null"
            ]
          },
          "has_unpublished_changes": {
            "type": "boolean"
          },
          "available_locales": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "en",
                "vi",
                "zh",
                "ko",
                "ja"
              ]
            },
            "description": "Locales with a published, human-written edition"
          },
          "reading_minutes": {
            "type": "integer"
          },
          "cover_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "snippet": {
            "type": "string",
            "description": "Search snippet (only with ?q=), from text this caller may read; ** marks matches"
          },
          "editions": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "locale": {
                  "type": "string",
                  "enum": [
                    "en",
                    "vi",
                    "zh",
                    "ko",
                    "ja"
                  ]
                },
                "title": {
                  "type": "string"
                },
                "excerpt": {
                  "type": "string"
                },
                "status": {
                  "type": "string",
                  "enum": [
                    "draft",
                    "published"
                  ]
                },
                "revision": {
                  "type": "integer"
                },
                "published_revision": {
                  "type": [
                    "integer",
                    "null"
                  ]
                },
                "published_at": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "updated_at": {
                  "type": "string"
                },
                "has_unpublished_changes": {
                  "type": "boolean"
                },
                "reading_minutes": {
                  "type": "integer"
                }
              }
            },
            "description": "Admin responses only"
          }
        }
      },
      "Article": {
        "allOf": [
          {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "slug": {
                "type": "string"
              },
              "locale": {
                "type": "string",
                "enum": [
                  "en",
                  "vi",
                  "zh",
                  "ko",
                  "ja"
                ],
                "description": "Locale of the edition in this response"
              },
              "primary_locale": {
                "type": "string",
                "enum": [
                  "en",
                  "vi",
                  "zh",
                  "ko",
                  "ja"
                ]
              },
              "title": {
                "type": "string"
              },
              "excerpt": {
                "type": "string"
              },
              "tags": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "Display names of topic_tags (public for every reader)"
              },
              "topic_tags": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "slug": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    }
                  }
                }
              },
              "category": {
                "oneOf": [
                  {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "string"
                      },
                      "slug": {
                        "type": "string"
                      },
                      "name": {
                        "type": "string"
                      }
                    }
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "labels": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "kind": {
                      "type": "string"
                    },
                    "slug": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "version": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  }
                },
                "description": "Approved labels for this edition (evidence is admin-only)"
              },
              "access": {
                "type": "string",
                "enum": [
                  "free",
                  "knowledges"
                ]
              },
              "status": {
                "type": "string",
                "enum": [
                  "draft",
                  "published"
                ]
              },
              "revision": {
                "type": "integer",
                "description": "Article-wide revision for expected_revision"
              },
              "label_revision": {
                "type": "integer"
              },
              "published_revision": {
                "type": [
                  "integer",
                  "null"
                ]
              },
              "created_at": {
                "type": "string"
              },
              "updated_at": {
                "type": "string"
              },
              "published_at": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "has_unpublished_changes": {
                "type": "boolean"
              },
              "available_locales": {
                "type": "array",
                "items": {
                  "type": "string",
                  "enum": [
                    "en",
                    "vi",
                    "zh",
                    "ko",
                    "ja"
                  ]
                },
                "description": "Locales with a published, human-written edition"
              },
              "reading_minutes": {
                "type": "integer"
              },
              "cover_url": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "snippet": {
                "type": "string",
                "description": "Search snippet (only with ?q=), from text this caller may read; ** marks matches"
              },
              "editions": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "locale": {
                      "type": "string",
                      "enum": [
                        "en",
                        "vi",
                        "zh",
                        "ko",
                        "ja"
                      ]
                    },
                    "title": {
                      "type": "string"
                    },
                    "excerpt": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "draft",
                        "published"
                      ]
                    },
                    "revision": {
                      "type": "integer"
                    },
                    "published_revision": {
                      "type": [
                        "integer",
                        "null"
                      ]
                    },
                    "published_at": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "updated_at": {
                      "type": "string"
                    },
                    "has_unpublished_changes": {
                      "type": "boolean"
                    },
                    "reading_minutes": {
                      "type": "integer"
                    }
                  }
                },
                "description": "Admin responses only"
              }
            }
          },
          {
            "type": "object",
            "properties": {
              "document": {
                "$ref": "#/components/schemas/ArticleDocument"
              },
              "truncated": {
                "type": "boolean",
                "description": "True when the paid remainder was withheld for this viewer"
              },
              "preview": {
                "type": "boolean"
              },
              "locale_fallback": {
                "type": "boolean",
                "description": "The requested locale had no edition; another edition is returned"
              }
            }
          }
        ]
      },
      "ArticleInput": {
        "type": "object",
        "properties": {
          "slug": {
            "type": "string",
            "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$"
          },
          "title": {
            "type": "string",
            "maxLength": 200
          },
          "excerpt": {
            "type": "string",
            "maxLength": 500
          },
          "locale": {
            "type": "string",
            "enum": [
              "en",
              "vi",
              "zh",
              "ko",
              "ja"
            ],
            "description": "Edition to create/update (default: primary). Translations are written by people, never generated."
          },
          "primary_locale": {
            "type": "string",
            "enum": [
              "en",
              "vi",
              "zh",
              "ko",
              "ja"
            ]
          },
          "access": {
            "type": "string",
            "enum": [
              "free",
              "knowledges"
            ]
          },
          "tags": {
            "type": "array",
            "maxItems": 20,
            "items": {
              "type": "string",
              "maxLength": 40
            },
            "description": "Topic tag ids, slugs, aliases or names; unknown names create tags"
          },
          "category": {
            "type": [
              "string",
              "null"
            ],
            "description": "Category id or slug; null clears"
          },
          "document": {
            "$ref": "#/components/schemas/ArticleDocument"
          }
        }
      },
      "ArticleEdition": {
        "type": "object",
        "properties": {
          "locale": {
            "type": "string",
            "enum": [
              "en",
              "vi",
              "zh",
              "ko",
              "ja"
            ]
          },
          "title": {
            "type": "string"
          },
          "excerpt": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "published"
            ]
          },
          "revision": {
            "type": "integer"
          },
          "published_revision": {
            "type": [
              "integer",
              "null"
            ]
          },
          "published_at": {
            "type": [
              "string",
              "null"
            ]
          },
          "updated_at": {
            "type": "string"
          },
          "has_unpublished_changes": {
            "type": "boolean"
          },
          "reading_minutes": {
            "type": "integer"
          }
        }
      },
      "SurveyResults": {
        "type": "object",
        "properties": {
          "block_id": {
            "type": "string"
          },
          "question": {
            "type": "string"
          },
          "allow_multiple": {
            "type": "boolean"
          },
          "total_voters": {
            "type": "integer"
          },
          "voted": {
            "type": "boolean"
          },
          "options": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "label": {
                  "type": "string"
                },
                "count": {
                  "type": "integer"
                },
                "percent": {
                  "type": "number"
                }
              }
            }
          }
        }
      },
      "KnowledgeHit": {
        "type": "object",
        "properties": {
          "article_id": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "locale": {
            "type": "string",
            "enum": [
              "en",
              "vi",
              "zh",
              "ko",
              "ja"
            ]
          },
          "title": {
            "type": "string"
          },
          "excerpt": {
            "type": "string"
          },
          "snippet": {
            "type": "string",
            "description": "Matched text from a tier this caller may read; ** marks matches; never HTML"
          },
          "tier": {
            "type": "string",
            "enum": [
              "free",
              "preview",
              "full"
            ]
          },
          "access": {
            "type": "string",
            "enum": [
              "free",
              "knowledges"
            ]
          },
          "full_text": {
            "type": "boolean"
          },
          "published_revision": {
            "type": [
              "integer",
              "null"
            ]
          },
          "published_at": {
            "type": [
              "string",
              "null"
            ]
          },
          "score": {
            "type": "number"
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "bm25",
                "semantic"
              ]
            }
          },
          "url": {
            "type": "string"
          },
          "markdown_url": {
            "type": "string"
          }
        }
      },
      "KnowledgeSearchResult": {
        "type": "object",
        "properties": {
          "query": {
            "type": "string"
          },
          "locale": {
            "oneOf": [
              {
                "type": "string",
                "enum": [
                  "en",
                  "vi",
                  "zh",
                  "ko",
                  "ja"
                ]
              },
              {
                "type": "null"
              }
            ]
          },
          "semantic": {
            "type": "boolean",
            "description": "False means BM25 only (no Vectorize/AI binding, or embedding failed)"
          },
          "tiers": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "free",
                "preview",
                "full"
              ]
            },
            "description": "Index tiers searched; others were excluded before ranking"
          },
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/KnowledgeHit"
            }
          }
        }
      },
      "PublicTaxonomy": {
        "type": "object",
        "properties": {
          "locale": {
            "type": "string",
            "enum": [
              "en",
              "vi",
              "zh",
              "ko",
              "ja"
            ]
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "slug": {
                  "type": "string"
                },
                "name": {
                  "type": "string"
                },
                "published_count": {
                  "type": "integer"
                }
              }
            }
          },
          "categories": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "slug": {
                  "type": "string"
                },
                "name": {
                  "type": "string"
                },
                "published_count": {
                  "type": "integer"
                }
              }
            }
          },
          "labels": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "key": {
                  "type": "string",
                  "description": "<kind>:<slug>"
                },
                "kind": {
                  "type": "string",
                  "enum": [
                    "claim",
                    "freshness",
                    "domain",
                    "tool",
                    "model",
                    "workflow",
                    "mindset",
                    "extension"
                  ]
                },
                "slug": {
                  "type": "string"
                },
                "name": {
                  "type": "string"
                },
                "version": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "published_count": {
                  "type": "integer"
                }
              }
            }
          }
        }
      },
      "TopicTag": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "names": {
            "type": "object",
            "description": "Localized display names; at least one locale",
            "properties": {
              "en": {
                "type": "string",
                "maxLength": 60
              },
              "vi": {
                "type": "string",
                "maxLength": 60
              },
              "zh": {
                "type": "string",
                "maxLength": 60
              },
              "ko": {
                "type": "string",
                "maxLength": 60
              },
              "ja": {
                "type": "string",
                "maxLength": 60
              }
            }
          },
          "aliases": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "revision": {
            "type": "integer"
          },
          "article_count": {
            "type": "integer"
          },
          "created_at": {
            "type": "string"
          },
          "updated_at": {
            "type": "string"
          }
        }
      },
      "TopicTagInput": {
        "type": "object",
        "properties": {
          "names": {
            "type": "object",
            "description": "Localized display names; at least one locale",
            "properties": {
              "en": {
                "type": "string",
                "maxLength": 60
              },
              "vi": {
                "type": "string",
                "maxLength": 60
              },
              "zh": {
                "type": "string",
                "maxLength": 60
              },
              "ko": {
                "type": "string",
                "maxLength": 60
              },
              "ja": {
                "type": "string",
                "maxLength": 60
              }
            }
          },
          "slug": {
            "type": "string"
          },
          "aliases": {
            "type": "array",
            "maxItems": 10,
            "items": {
              "type": "string",
              "maxLength": 40
            }
          }
        }
      },
      "Category": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "names": {
            "type": "object",
            "description": "Localized display names; at least one locale",
            "properties": {
              "en": {
                "type": "string",
                "maxLength": 60
              },
              "vi": {
                "type": "string",
                "maxLength": 60
              },
              "zh": {
                "type": "string",
                "maxLength": 60
              },
              "ko": {
                "type": "string",
                "maxLength": 60
              },
              "ja": {
                "type": "string",
                "maxLength": 60
              }
            }
          },
          "position": {
            "type": "integer"
          },
          "revision": {
            "type": "integer"
          },
          "article_count": {
            "type": "integer"
          },
          "created_at": {
            "type": "string"
          },
          "updated_at": {
            "type": "string"
          }
        }
      },
      "CategoryInput": {
        "type": "object",
        "properties": {
          "names": {
            "type": "object",
            "description": "Localized display names; at least one locale",
            "properties": {
              "en": {
                "type": "string",
                "maxLength": 60
              },
              "vi": {
                "type": "string",
                "maxLength": 60
              },
              "zh": {
                "type": "string",
                "maxLength": 60
              },
              "ko": {
                "type": "string",
                "maxLength": 60
              },
              "ja": {
                "type": "string",
                "maxLength": 60
              }
            }
          },
          "slug": {
            "type": "string"
          },
          "position": {
            "type": "integer"
          }
        }
      },
      "TaxonomyLabel": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "kind": {
            "type": "string",
            "enum": [
              "claim",
              "freshness",
              "domain",
              "tool",
              "model",
              "workflow",
              "mindset",
              "extension"
            ]
          },
          "slug": {
            "type": "string"
          },
          "names": {
            "type": "object",
            "description": "Localized display names; at least one locale",
            "properties": {
              "en": {
                "type": "string",
                "maxLength": 60
              },
              "vi": {
                "type": "string",
                "maxLength": 60
              },
              "zh": {
                "type": "string",
                "maxLength": 60
              },
              "ko": {
                "type": "string",
                "maxLength": 60
              },
              "ja": {
                "type": "string",
                "maxLength": 60
              }
            }
          },
          "aliases": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "description": {
            "type": "string"
          },
          "version": {
            "type": [
              "string",
              "null"
            ]
          },
          "builtin": {
            "type": "boolean"
          },
          "approved": {
            "type": "boolean"
          },
          "approved_at": {
            "type": [
              "string",
              "null"
            ]
          },
          "approved_by": {
            "type": [
              "string",
              "null"
            ]
          },
          "revision": {
            "type": "integer"
          },
          "created_at": {
            "type": "string"
          },
          "updated_at": {
            "type": "string"
          }
        }
      },
      "TaxonomyLabelInput": {
        "type": "object",
        "properties": {
          "kind": {
            "type": "string",
            "enum": [
              "domain",
              "tool",
              "model",
              "workflow",
              "mindset",
              "extension"
            ]
          },
          "slug": {
            "type": "string"
          },
          "names": {
            "type": "object",
            "description": "Localized display names; at least one locale",
            "properties": {
              "en": {
                "type": "string",
                "maxLength": 60
              },
              "vi": {
                "type": "string",
                "maxLength": 60
              },
              "zh": {
                "type": "string",
                "maxLength": 60
              },
              "ko": {
                "type": "string",
                "maxLength": 60
              },
              "ja": {
                "type": "string",
                "maxLength": 60
              }
            }
          },
          "aliases": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "description": {
            "type": "string"
          },
          "version": {
            "type": [
              "string",
              "null"
            ]
          },
          "approve": {
            "type": "boolean",
            "description": "Approve an extension label"
          }
        }
      },
      "LabelEvidence": {
        "type": "object",
        "description": "claim:fact needs source_url (https), retrieved_at and claim; time-sensitive labels need as_of/review_date",
        "properties": {
          "source_url": {
            "type": "string"
          },
          "source_title": {
            "type": "string"
          },
          "retrieved_at": {
            "type": "string"
          },
          "as_of": {
            "type": "string"
          },
          "review_date": {
            "type": "string"
          },
          "version_applicability": {
            "type": "string"
          },
          "claim": {
            "type": "string"
          },
          "note": {
            "type": "string"
          }
        }
      },
      "LabelAssignment": {
        "type": "object",
        "required": [
          "scope"
        ],
        "properties": {
          "label_id": {
            "type": "string"
          },
          "label": {
            "type": "string",
            "description": "Input only: <kind>:<slug> instead of label_id"
          },
          "scope": {
            "type": "string",
            "enum": [
              "article",
              "locale",
              "revision",
              "block"
            ]
          },
          "locale": {
            "type": "string",
            "enum": [
              "en",
              "vi",
              "zh",
              "ko",
              "ja"
            ]
          },
          "edition_revision": {
            "type": "integer"
          },
          "block_id": {
            "type": "string",
            "description": "Required for scope block"
          },
          "evidence": {
            "$ref": "#/components/schemas/LabelEvidence"
          }
        }
      },
      "LabelState": {
        "type": "object",
        "properties": {
          "article_id": {
            "type": "string"
          },
          "label_revision": {
            "type": "integer"
          },
          "assignments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LabelAssignment"
            }
          },
          "history": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "label_revision": {
                  "type": "integer"
                },
                "assignments": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/LabelAssignment"
                  }
                },
                "source": {
                  "type": "string",
                  "enum": [
                    "manual",
                    "proposal",
                    "revert"
                  ]
                },
                "proposal_id": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "actor": {
                  "type": "string"
                },
                "reason": {
                  "type": "string"
                },
                "created_at": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "LabelProposal": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "job_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "article_id": {
            "type": "string"
          },
          "article_slug": {
            "type": "string"
          },
          "article_title": {
            "type": "string"
          },
          "locale": {
            "type": "string",
            "enum": [
              "en",
              "vi",
              "zh",
              "ko",
              "ja"
            ]
          },
          "edition_revision": {
            "type": "integer"
          },
          "current_edition_revision": {
            "type": [
              "integer",
              "null"
            ]
          },
          "base_label_revision": {
            "type": "integer"
          },
          "current_label_revision": {
            "type": "integer"
          },
          "before": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LabelAssignment"
            }
          },
          "proposed": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LabelAssignment"
            }
          },
          "questions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "rationale": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "deferred",
              "applied",
              "rejected",
              "stale"
            ]
          },
          "outdated": {
            "type": "boolean",
            "description": "The edition text changed after the proposal; it can no longer be approved"
          },
          "decided_by": {
            "type": [
              "string",
              "null"
            ]
          },
          "decided_at": {
            "type": [
              "string",
              "null"
            ]
          },
          "decision_reason": {
            "type": [
              "string",
              "null"
            ]
          },
          "applied_label_revision": {
            "type": [
              "integer",
              "null"
            ]
          },
          "created_at": {
            "type": "string"
          },
          "updated_at": {
            "type": "string"
          }
        }
      },
      "AuditJob": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "running",
              "paused",
              "completed",
              "failed",
              "cancelled"
            ]
          },
          "scope": {
            "type": "object",
            "properties": {
              "locales": {
                "type": "array",
                "items": {
                  "type": "string",
                  "enum": [
                    "en",
                    "vi",
                    "zh",
                    "ko",
                    "ja"
                  ]
                }
              },
              "article_ids": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "skip_unchanged": {
                "type": "boolean"
              }
            }
          },
          "cursor": {
            "type": "string",
            "description": "Last processed <article_id>|<locale>; the next run resumes after it"
          },
          "batch_size": {
            "type": "integer"
          },
          "processed": {
            "type": "integer"
          },
          "skipped": {
            "type": "integer"
          },
          "proposal_count": {
            "type": "integer"
          },
          "error_count": {
            "type": "integer"
          },
          "last_error": {
            "type": [
              "string",
              "null"
            ]
          },
          "model": {
            "type": "string"
          },
          "snapshot_at": {
            "type": "string"
          },
          "created_by": {
            "type": "string"
          },
          "created_at": {
            "type": "string"
          },
          "updated_at": {
            "type": "string"
          },
          "completed_at": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "TaxonomyAuditEntry": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "actor": {
            "type": "string"
          },
          "action": {
            "type": "string"
          },
          "target_type": {
            "type": "string"
          },
          "target_id": {
            "type": "string"
          },
          "before": {},
          "after": {},
          "reason": {
            "type": "string"
          },
          "created_at": {
            "type": "string"
          }
        }
      },
      "Member": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "email": {
            "type": "string"
          },
          "email_verified": {
            "type": "boolean"
          },
          "name": {
            "type": [
              "string",
              "null"
            ]
          },
          "avatar_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "locale": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "is_admin": {
            "type": "boolean"
          },
          "plans": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "knowledges",
                "ai",
                "combo",
                "community"
              ]
            }
          },
          "entitlements": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "read_full",
                "ai_chat",
                "community"
              ]
            }
          },
          "identities": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "provider": {
                  "type": "string"
                },
                "email": {
                  "type": [
                    "string",
                    "null"
                  ]
                }
              }
            }
          },
          "auth": {
            "type": "object",
            "properties": {
              "via": {
                "type": "string",
                "enum": [
                  "none",
                  "studio_session",
                  "admin_api_key",
                  "read_api_key",
                  "member_session",
                  "user_api_key",
                  "oauth_token"
                ],
                "description": "`oauth_token` for OAuth access tokens on /mcp, `user_api_key` for personal `zk_` keys"
              },
              "scopes": {
                "type": [
                  "array",
                  "null"
                ],
                "items": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "MemberSession": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "created_at": {
            "type": "string"
          },
          "last_seen_at": {
            "type": "string"
          },
          "expires_at": {
            "type": "string"
          },
          "user_agent": {
            "type": [
              "string",
              "null"
            ]
          },
          "current": {
            "type": "boolean"
          }
        }
      },
      "UserApiKey": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "prefix": {
            "type": "string",
            "description": "First characters of the secret, e.g. zk_AbCdEfG"
          },
          "name": {
            "type": "string"
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "articles:read",
                "chat:write",
                "account:read",
                "account:write",
                "billing:read",
                "checkout:write"
              ]
            }
          },
          "created_at": {
            "type": "string"
          },
          "expires_at": {
            "type": "string"
          },
          "last_used_at": {
            "type": [
              "string",
              "null"
            ]
          },
          "revoked_at": {
            "type": [
              "string",
              "null"
            ]
          },
          "replaced_by": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "expired",
              "revoked"
            ]
          }
        }
      },
      "UserApiKeyCreate": {
        "type": "object",
        "required": [
          "name",
          "scopes"
        ],
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 60
          },
          "scopes": {
            "type": "array",
            "minItems": 1,
            "items": {
              "type": "string",
              "enum": [
                "articles:read",
                "chat:write",
                "account:read",
                "account:write",
                "billing:read",
                "checkout:write"
              ]
            }
          },
          "expires_in_days": {
            "type": "integer",
            "minimum": 1,
            "maximum": 365,
            "default": 90
          }
        }
      },
      "PlansCatalog": {
        "type": "object",
        "properties": {
          "billing_configured": {
            "type": "boolean"
          },
          "missing": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "usd_vnd_rate": {
            "type": [
              "number",
              "null"
            ]
          },
          "card_plans": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "knowledges",
                "ai",
                "combo",
                "community"
              ]
            },
            "description": "Plans purchasable as a Dodo monthly card subscription (empty when Dodo is not configured)"
          },
          "plans": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "enum": [
                    "knowledges",
                    "ai",
                    "combo",
                    "community"
                  ]
                },
                "name": {
                  "type": "string"
                },
                "price_usd_cents": {
                  "type": "integer"
                },
                "entitlements": {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "enum": [
                      "read_full",
                      "ai_chat",
                      "community"
                    ]
                  }
                },
                "prices": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "months": {
                        "type": "integer"
                      },
                      "amount_usd_cents": {
                        "type": "integer"
                      },
                      "amount_vnd": {
                        "type": [
                          "integer",
                          "null"
                        ]
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "BillingOrderCheckout": {
        "allOf": [
          {
            "$ref": "#/components/schemas/BillingOrder"
          },
          {
            "type": "object",
            "properties": {
              "provider": {
                "type": "string",
                "enum": [
                  "sepay"
                ]
              },
              "status_url": {
                "type": "string"
              }
            }
          }
        ]
      },
      "CardSubscription": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "provider": {
            "type": "string",
            "enum": [
              "dodo"
            ]
          },
          "plan": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "knowledges",
              "ai",
              "combo",
              "community",
              null
            ]
          },
          "plan_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "active",
              "on_hold",
              "paused",
              "cancelled",
              "failed",
              "expired",
              "needs_attention"
            ]
          },
          "current_period_end": {
            "type": [
              "string",
              "null"
            ],
            "description": "Provider's next billing date: renewal date, or end of access once cancellation is scheduled"
          },
          "cancel_at_period_end": {
            "type": "boolean"
          },
          "amount_cents": {
            "type": [
              "integer",
              "null"
            ]
          },
          "currency": {
            "type": [
              "string",
              "null"
            ]
          },
          "attention_reason": {
            "type": [
              "string",
              "null"
            ]
          },
          "can_manage": {
            "type": "boolean"
          },
          "can_cancel": {
            "type": "boolean"
          },
          "created_at": {
            "type": "string"
          },
          "updated_at": {
            "type": "string"
          },
          "status_url": {
            "type": "string"
          }
        }
      },
      "CardCheckout": {
        "allOf": [
          {
            "$ref": "#/components/schemas/CardSubscription"
          },
          {
            "type": "object",
            "properties": {
              "checkout_url": {
                "type": "string",
                "description": "Dodo hosted checkout; redirect the member here"
              }
            }
          }
        ]
      },
      "BillingAttentionQueue": {
        "type": "object",
        "properties": {
          "orders": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "code": {
                  "type": "string"
                },
                "user_id": {
                  "type": "string"
                },
                "member_email": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "plan": {
                  "type": "string",
                  "enum": [
                    "knowledges",
                    "ai",
                    "combo",
                    "community"
                  ]
                },
                "plan_name": {
                  "type": "string"
                },
                "months": {
                  "type": "integer"
                },
                "amount_vnd": {
                  "type": "integer"
                },
                "amount_paid": {
                  "type": [
                    "integer",
                    "null"
                  ]
                },
                "attention_reason": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "`late_payment`, `underpaid`, `additional_payment`, …"
                },
                "payment_ref": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "expires_at": {
                  "type": "string"
                },
                "created_at": {
                  "type": "string"
                },
                "updated_at": {
                  "type": "string"
                }
              }
            }
          },
          "card_subscriptions": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "user_id": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "member_email": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "plan": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "plan_name": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "amount_cents": {
                  "type": [
                    "integer",
                    "null"
                  ]
                },
                "currency": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "attention_reason": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "`metadata_missing`, `metadata_mismatch`, `product_mismatch`, `amount_mismatch`"
                },
                "provider_subscription_id": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "current_period_end": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "last_event_at": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "created_at": {
                  "type": "string"
                },
                "updated_at": {
                  "type": "string"
                }
              }
            }
          },
          "card_note": {
            "type": "string"
          }
        }
      },
      "BillingOrder": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string",
            "description": "ZSB order code; must appear in the transfer content"
          },
          "plan": {
            "type": "string",
            "enum": [
              "knowledges",
              "ai",
              "combo",
              "community"
            ]
          },
          "months": {
            "type": "integer"
          },
          "amount_vnd": {
            "type": "integer"
          },
          "amount_usd_cents": {
            "type": "integer"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "paid",
              "expired",
              "needs_attention"
            ]
          },
          "expires_at": {
            "type": "string"
          },
          "paid_at": {
            "type": [
              "string",
              "null"
            ]
          },
          "amount_paid": {
            "type": [
              "integer",
              "null"
            ]
          },
          "attention_reason": {
            "type": [
              "string",
              "null"
            ]
          },
          "transfer": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "bank_account": {
                "type": "string"
              },
              "bank_code": {
                "type": "string"
              },
              "amount": {
                "type": "integer"
              },
              "transfer_content": {
                "type": "string"
              },
              "qr_url": {
                "type": "string"
              }
            }
          }
        }
      },
      "ChatSource": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "access": {
            "type": "string",
            "enum": [
              "free",
              "paid"
            ]
          },
          "scope": {
            "type": "string",
            "enum": [
              "full",
              "preview"
            ],
            "description": "preview = only the public part of a paid article was used (caller lacks read_full)"
          }
        }
      },
      "ChatSession": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "created_at": {
            "type": "string"
          },
          "updated_at": {
            "type": "string"
          },
          "running": {
            "type": "boolean"
          }
        }
      },
      "ChatMessage": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "role": {
            "type": "string",
            "enum": [
              "user",
              "assistant"
            ]
          },
          "content": {
            "type": "string"
          },
          "sources": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ChatSource"
            }
          },
          "status": {
            "type": "string",
            "enum": [
              "complete",
              "streaming",
              "cancelled",
              "error"
            ]
          },
          "error_code": {
            "type": [
              "string",
              "null"
            ]
          },
          "prompt_tokens": {
            "type": "integer"
          },
          "completion_tokens": {
            "type": "integer"
          },
          "est_cost_cents": {
            "type": "number"
          },
          "created_at": {
            "type": "string"
          }
        }
      },
      "ChatArtifact": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "session_id": {
            "type": "string"
          },
          "message_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "private",
              "attached"
            ]
          },
          "article_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "attached_at": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string"
          },
          "block": {
            "$ref": "#/components/schemas/BlockInteractive"
          }
        }
      },
      "ChatSessionDetail": {
        "type": "object",
        "properties": {
          "session": {
            "$ref": "#/components/schemas/ChatSession"
          },
          "messages": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ChatMessage"
            }
          },
          "artifacts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ChatArtifact"
            }
          }
        }
      },
      "ChatQuota": {
        "type": "object",
        "properties": {
          "month": {
            "type": "string",
            "description": "YYYY-MM in Asia/Saigon"
          },
          "used": {
            "type": "integer"
          },
          "limit": {
            "type": [
              "integer",
              "null"
            ],
            "description": "AI_MONTHLY_REQUEST_LIMIT (default 300); null for admins"
          },
          "remaining": {
            "type": [
              "integer",
              "null"
            ]
          }
        }
      },
      "OAuthError": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string"
          },
          "error_description": {
            "type": "string"
          }
        }
      },
      "ProtectedResourceMetadata": {
        "type": "object",
        "properties": {
          "resource": {
            "type": "string",
            "example": "https://zuey.me/mcp"
          },
          "authorization_servers": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "scopes_supported": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "bearer_methods_supported": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "header"
              ]
            }
          }
        }
      },
      "ClientRegistrationRequest": {
        "type": "object",
        "required": [
          "redirect_uris"
        ],
        "properties": {
          "redirect_uris": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "minItems": 1
          },
          "client_name": {
            "type": "string"
          },
          "client_uri": {
            "type": "string"
          },
          "token_endpoint_auth_method": {
            "type": "string",
            "enum": [
              "none",
              "client_secret_post",
              "client_secret_basic"
            ]
          },
          "grant_types": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "authorization_code",
                "refresh_token"
              ]
            }
          },
          "response_types": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "code"
              ]
            }
          },
          "scope": {
            "type": "string"
          }
        }
      },
      "TokenRequest": {
        "type": "object",
        "required": [
          "grant_type"
        ],
        "properties": {
          "grant_type": {
            "type": "string",
            "enum": [
              "authorization_code",
              "refresh_token"
            ]
          },
          "code": {
            "type": "string"
          },
          "redirect_uri": {
            "type": "string"
          },
          "code_verifier": {
            "type": "string",
            "minLength": 43,
            "maxLength": 128
          },
          "refresh_token": {
            "type": "string"
          },
          "scope": {
            "type": "string",
            "description": "Refresh only: narrow the granted scopes"
          },
          "resource": {
            "type": "string"
          },
          "client_id": {
            "type": "string"
          },
          "client_secret": {
            "type": "string"
          }
        }
      },
      "TokenResponse": {
        "type": "object",
        "required": [
          "access_token",
          "token_type",
          "expires_in",
          "scope"
        ],
        "properties": {
          "access_token": {
            "type": "string"
          },
          "token_type": {
            "type": "string",
            "enum": [
              "Bearer"
            ]
          },
          "expires_in": {
            "type": "integer"
          },
          "refresh_token": {
            "type": "string"
          },
          "scope": {
            "type": "string"
          }
        }
      },
      "ConnectedApp": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "client_id": {
            "type": "string"
          },
          "client_name": {
            "type": "string"
          },
          "client_uri": {
            "type": [
              "string",
              "null"
            ]
          },
          "registration": {
            "type": "string",
            "enum": [
              "dcr",
              "cimd"
            ]
          },
          "redirect_hosts": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "articles:read",
                "chat:write",
                "account:read",
                "account:write",
                "billing:read",
                "checkout:write",
                "admin"
              ]
            }
          },
          "granted_at": {
            "type": "string",
            "format": "date-time"
          },
          "last_used_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "active_tokens": {
            "type": "integer"
          }
        }
      },
      "NoticeInput": {
        "type": "object",
        "required": [
          "text"
        ],
        "properties": {
          "text": {
            "type": "object",
            "description": "Keyed by locale (en, vi, zh, ko, ja), each 1–500 characters; or a string together with `locale`",
            "additionalProperties": {
              "type": "string"
            }
          },
          "locale": {
            "type": "string",
            "enum": [
              "en",
              "vi",
              "zh",
              "ko",
              "ja"
            ]
          },
          "expression": {
            "type": "string",
            "enum": [
              "idle",
              "wave",
              "talking",
              "thinking",
              "happy",
              "surprised"
            ],
            "nullable": true
          },
          "target": {
            "type": "string",
            "enum": [
              "all",
              "members",
              "plan"
            ],
            "default": "all"
          },
          "plan": {
            "type": "string",
            "enum": [
              "knowledges",
              "ai",
              "combo",
              "community"
            ],
            "description": "Required when target = plan"
          },
          "starts_at": {
            "type": "string",
            "format": "date-time"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "Within 30 days of starts_at"
          },
          "ttl_hours": {
            "type": "number",
            "description": "Alternative to expires_at"
          }
        }
      },
      "Notice": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "text": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "expression": {
            "type": "string",
            "nullable": true
          },
          "target": {
            "type": "string",
            "enum": [
              "all",
              "members",
              "plan"
            ]
          },
          "target_plan": {
            "type": "string",
            "nullable": true
          },
          "starts_at": {
            "type": "string",
            "format": "date-time"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "status": {
            "type": "string",
            "enum": [
              "scheduled",
              "active",
              "expired"
            ]
          },
          "created_by": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "PublicNotice": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "text": {
            "type": "string"
          },
          "locale": {
            "type": "string",
            "enum": [
              "en",
              "vi",
              "zh",
              "ko",
              "ja"
            ]
          },
          "expression": {
            "type": "string",
            "nullable": true
          },
          "starts_at": {
            "type": "string",
            "format": "date-time"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "GithubActivity": {
        "type": "object",
        "properties": {
          "source": {
            "type": "string",
            "enum": [
              "cache",
              "live",
              "revalidated",
              "stale"
            ]
          },
          "error": {
            "nullable": true,
            "type": "object",
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              },
              "retry_after_seconds": {
                "type": "integer",
                "nullable": true
              }
            }
          },
          "snapshot": {
            "type": "object",
            "properties": {
              "user": {
                "type": "string"
              },
              "fetched_at": {
                "type": "string",
                "format": "date-time"
              },
              "pages_fetched": {
                "type": "integer"
              },
              "partial": {
                "type": "boolean"
              },
              "events": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "type": {
                      "type": "string"
                    },
                    "action": {
                      "type": "string",
                      "nullable": true
                    },
                    "repo": {
                      "type": "string"
                    },
                    "repo_url": {
                      "type": "string"
                    },
                    "url": {
                      "type": "string"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "ref": {
                      "type": "string",
                      "nullable": true
                    },
                    "ref_type": {
                      "type": "string",
                      "nullable": true
                    },
                    "title": {
                      "type": "string",
                      "nullable": true
                    },
                    "number": {
                      "type": "integer",
                      "nullable": true
                    },
                    "commits": {
                      "type": "integer",
                      "nullable": true
                    }
                  }
                }
              }
            }
          }
        }
      },
      "Weather": {
        "type": "object",
        "properties": {
          "place": {
            "nullable": true,
            "type": "object",
            "properties": {
              "name": {
                "type": "string"
              },
              "country": {
                "type": "string",
                "nullable": true
              },
              "admin1": {
                "type": "string",
                "nullable": true
              }
            }
          },
          "latitude": {
            "type": "number"
          },
          "longitude": {
            "type": "number"
          },
          "condition": {
            "type": "string",
            "enum": [
              "clear",
              "clouds",
              "fog",
              "rain",
              "snow",
              "storm"
            ]
          },
          "is_day": {
            "type": "boolean"
          },
          "weather_code": {
            "type": "integer"
          },
          "temperature_c": {
            "type": "number",
            "nullable": true
          },
          "wind_kmh": {
            "type": "number",
            "nullable": true
          },
          "observed_at": {
            "type": "string",
            "nullable": true
          },
          "source": {
            "type": "string",
            "enum": [
              "open-meteo"
            ]
          },
          "attribution": {
            "type": "string"
          }
        }
      },
      "CommunityMembership": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "chat": {
            "type": "string",
            "enum": [
              "en",
              "vi"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "invited",
              "joined",
              "left",
              "removed",
              "revoked"
            ]
          },
          "invite_link": {
            "type": "string",
            "nullable": true,
            "description": "Only while the invite is unused and unexpired"
          },
          "invite_expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "joined_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "CommunityStatus": {
        "type": "object",
        "properties": {
          "signed_in": {
            "type": "boolean"
          },
          "configured": {
            "type": "boolean"
          },
          "entitled": {
            "type": "boolean"
          },
          "chats": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "chat": {
                  "type": "string"
                },
                "available": {
                  "type": "boolean"
                }
              }
            }
          },
          "memberships": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CommunityMembership"
            }
          }
        }
      },
      "CommunitySweep": {
        "type": "object",
        "properties": {
          "checked": {
            "type": "integer"
          },
          "removed": {
            "type": "integer"
          },
          "revoked": {
            "type": "integer"
          },
          "errors": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "code": {
                  "type": "string"
                },
                "message": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    }
  },
  "paths": {
    "/api/v1/profile": {
      "get": {
        "summary": "Get Public Profile",
        "description": "Retrieves current biography, avatar, theme and contact info.",
        "responses": {
          "200": {
            "description": "Profile data",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "$ref": "#/components/schemas/Profile"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "put": {
        "summary": "Update Profile Info",
        "description": "Updates bio, name, avatar, or theme (Requires API key).",
        "security": [
          {
            "BearerAuth": []
          },
          {
            "ApiKeyHeader": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "intro_en": {
                    "type": "string"
                  },
                  "intro_vi": {
                    "type": "string"
                  },
                  "avatar_url": {
                    "type": "string"
                  },
                  "theme": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated successfully"
          },
          "401": {
            "description": "Unauthorized"
          }
        }
      }
    },
    "/api/v1/links": {
      "get": {
        "summary": "List All Links",
        "description": "Returns all link cards sorted by section and order index.",
        "responses": {
          "200": {
            "description": "List of links",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/LinkItem"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "Create a New Link",
        "description": "Adds a new link card to blogs, companies, or products.",
        "security": [
          {
            "BearerAuth": []
          },
          {
            "ApiKeyHeader": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "section",
                  "title_en",
                  "url"
                ],
                "properties": {
                  "section": {
                    "type": "string",
                    "enum": [
                      "blogs",
                      "companies",
                      "products"
                    ]
                  },
                  "title_en": {
                    "type": "string"
                  },
                  "title_vi": {
                    "type": "string"
                  },
                  "subtitle_en": {
                    "type": "string"
                  },
                  "subtitle_vi": {
                    "type": "string"
                  },
                  "url": {
                    "type": "string"
                  },
                  "icon": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Link created"
          },
          "401": {
            "description": "Unauthorized"
          }
        }
      }
    },
    "/api/v1/links/{id}": {
      "put": {
        "summary": "Update Link",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "ApiKeyHeader": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LinkItem"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated"
          },
          "404": {
            "description": "Not found"
          }
        }
      },
      "delete": {
        "summary": "Delete Link",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "ApiKeyHeader": []
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted"
          },
          "401": {
            "description": "Unauthorized"
          }
        }
      }
    },
    "/api/v1/links/reorder": {
      "post": {
        "summary": "Reorder Links",
        "description": "Updates ordering index for a list of link IDs.",
        "security": [
          {
            "BearerAuth": []
          },
          {
            "ApiKeyHeader": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "order"
                ],
                "properties": {
                  "order": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Reordered successfully"
          }
        }
      }
    },
    "/api/v1/theme": {
      "get": {
        "summary": "Get Theme Configuration",
        "responses": {
          "200": {
            "description": "Theme data"
          }
        }
      },
      "put": {
        "summary": "Update Theme Configuration",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Theme updated"
          }
        }
      }
    },
    "/api/v1/keys": {
      "get": {
        "summary": "List API Keys",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Keys list"
          }
        }
      },
      "post": {
        "summary": "Create API Key",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "responses": {
          "201": {
            "description": "Key created with secret token"
          }
        }
      }
    },
    "/api/v1/reads": {
      "get": {
        "tags": [
          "Zuey Reads"
        ],
        "summary": "List visible reads",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Search in title, summary, site and domain"
          },
          {
            "name": "source",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Filter by source_kind"
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 500,
              "default": 50
            }
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          },
          {
            "name": "include_hidden",
            "in": "query",
            "schema": {
              "type": "integer",
              "enum": [
                0,
                1
              ]
            },
            "description": "Admin only: include reads hidden after the zuey-reads tag was removed"
          }
        ],
        "responses": {
          "200": {
            "description": "Reads list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "items": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/ReadItem"
                          }
                        },
                        "total": {
                          "type": "integer"
                        },
                        "sources": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "source_kind": {
                                "type": "string"
                              },
                              "count": {
                                "type": "integer"
                              }
                            }
                          }
                        },
                        "last_synced_at": {
                          "type": "string",
                          "format": "date-time",
                          "nullable": true
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/reads/sync": {
      "post": {
        "tags": [
          "Zuey Reads"
        ],
        "summary": "Sync reads from AnyMD (admin)",
        "description": "Crawls the AnyMD library, keeps items tagged `zuey-reads`, summarizes new/changed content with Workers AI and hides items that lost the tag.",
        "security": [
          {
            "BearerAuth": []
          },
          {
            "ApiKeyHeader": []
          }
        ],
        "responses": {
          "200": {
            "description": "Sync run stats",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "$ref": "#/components/schemas/ReadsSyncRun"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "AnyMD or Workers AI upstream error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "ANYMD_API_KEY, AI or DB binding not configured",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/reads.md": {
      "get": {
        "tags": [
          "Zuey Reads"
        ],
        "summary": "Reads as Markdown",
        "responses": {
          "200": {
            "description": "Markdown list of reads",
            "content": {
              "text/markdown": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/workflows": {
      "get": {
        "tags": [
          "AI Workflows"
        ],
        "summary": "List workflows",
        "description": "Public: published snapshots only. include_drafts=1 requires admin and returns drafts with revision and findings.",
        "parameters": [
          {
            "name": "include_drafts",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "1",
                "true"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Workflow"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "AI Workflows"
        ],
        "summary": "Create a workflow draft",
        "description": "expected_revision must be 0. Returns secret findings; findings do not block saving a draft.",
        "security": [
          {
            "BearerAuth": []
          },
          {
            "ApiKeyHeader": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WorkflowInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "$ref": "#/components/schemas/Workflow"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "revision_conflict (error.current_revision) or slug_taken",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/workflows/{slug}": {
      "get": {
        "tags": [
          "AI Workflows"
        ],
        "summary": "Get a workflow",
        "description": "Public: published snapshot. include_drafts=1 (admin) returns the current draft.",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$"
            }
          },
          {
            "name": "include_drafts",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "$ref": "#/components/schemas/Workflow"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Workflow not found (or not published)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "AI Workflows"
        ],
        "summary": "Update the workflow draft",
        "description": "Partial update of the draft; slug is immutable. The public snapshot is unchanged until republished.",
        "security": [
          {
            "BearerAuth": []
          },
          {
            "ApiKeyHeader": []
          }
        ],
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WorkflowInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "$ref": "#/components/schemas/Workflow"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Workflow not found (or not published)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "revision_conflict (error.current_revision) or slug_taken",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "AI Workflows"
        ],
        "summary": "Soft-delete a workflow",
        "description": "expected_revision in the JSON body or the ?expected_revision query parameter.",
        "security": [
          {
            "BearerAuth": []
          },
          {
            "ApiKeyHeader": []
          }
        ],
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$"
            }
          },
          {
            "name": "expected_revision",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "slug": {
                          "type": "string"
                        },
                        "deleted": {
                          "type": "boolean"
                        },
                        "revision": {
                          "type": "integer"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Workflow not found (or not published)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "revision_conflict (error.current_revision) or slug_taken",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/workflows/{slug}/publish": {
      "post": {
        "tags": [
          "AI Workflows"
        ],
        "summary": "Publish the current draft",
        "security": [
          {
            "BearerAuth": []
          },
          {
            "ApiKeyHeader": []
          }
        ],
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "expected_revision",
                  "confirm"
                ],
                "properties": {
                  "expected_revision": {
                    "type": "integer"
                  },
                  "confirm": {
                    "type": "boolean",
                    "const": true
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "$ref": "#/components/schemas/Workflow"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Workflow not found (or not published)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "revision_conflict (error.current_revision) or slug_taken",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "secret_detected; error.findings lists SecretFinding items",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/booking/slots": {
      "get": {
        "tags": [
          "Business booking"
        ],
        "summary": "List open consultation slots (UTC)",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "days",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 60,
              "default": 60
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Open slots",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "data": {
                      "$ref": "#/components/schemas/BookingSlotList"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Payment provider not configured (`payment_unconfigured`, lists missing variables) or DB unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/booking/hold": {
      "post": {
        "tags": [
          "Business booking"
        ],
        "summary": "Hold a slot for 15 minutes",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BookingHoldRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Hold created; returns the one-time manage token",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "data": {
                      "$ref": "#/components/schemas/BookingHoldResult"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Slot taken, hold not active, or reschedule not allowed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Payment provider not configured (`payment_unconfigured`, lists missing variables) or DB unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/booking/{id}": {
      "get": {
        "tags": [
          "Business booking"
        ],
        "summary": "Get a booking (guest with manage token, or admin)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "token",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Guest view (with token) or admin view",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "data": {
                      "$ref": "#/components/schemas/BookingGuestView"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Booking not found or manage token invalid",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/booking/{id}/checkout": {
      "post": {
        "tags": [
          "Business booking"
        ],
        "summary": "Start payment for an active hold",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "token": {
                    "type": "string"
                  }
                },
                "required": [
                  "token"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "PayPal approval URL or SePay VietQR instructions",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "data": {
                      "$ref": "#/components/schemas/BookingCheckout"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Booking not found or manage token invalid",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Slot taken, hold not active, or reschedule not allowed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Payment provider error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Payment provider not configured (`payment_unconfigured`, lists missing variables) or DB unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/booking/{id}/capture": {
      "post": {
        "tags": [
          "Business booking"
        ],
        "summary": "Capture the PayPal order after the guest returns from PayPal",
        "description": "Called by the manage page on return. Never captures an expired hold. The booking is confirmed only when PayPal reports the capture as COMPLETED; the PAYMENT.CAPTURE.COMPLETED webhook confirms it too, idempotently by capture id.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "token": {
                    "type": "string",
                    "description": "Manage token"
                  }
                },
                "required": [
                  "token"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Booking state after the capture attempt",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "data": {
                      "$ref": "#/components/schemas/BookingCaptureResult"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Booking not found or manage token invalid",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Slot taken, hold not active, or reschedule not allowed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Payment provider error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Payment provider not configured (`payment_unconfigured`, lists missing variables) or DB unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/booking/{id}/reschedule": {
      "post": {
        "tags": [
          "Business booking"
        ],
        "summary": "Reschedule once (both times at least 48h away)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "token": {
                    "type": "string"
                  },
                  "slot_start": {
                    "type": "string",
                    "format": "date-time"
                  }
                },
                "required": [
                  "token",
                  "slot_start"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated booking",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "data": {
                      "$ref": "#/components/schemas/BookingGuestView"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Booking not found or manage token invalid",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Slot taken, hold not active, or reschedule not allowed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/booking/availability": {
      "get": {
        "tags": [
          "Business booking"
        ],
        "summary": "Get availability rules and exceptions",
        "security": [
          {
            "BearerAuth": []
          },
          {
            "ApiKeyHeader": []
          }
        ],
        "responses": {
          "200": {
            "description": "Availability",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "data": {
                      "$ref": "#/components/schemas/BookingAvailability"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "Business booking"
        ],
        "summary": "Replace availability rules and exceptions",
        "security": [
          {
            "BearerAuth": []
          },
          {
            "ApiKeyHeader": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BookingAvailability"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Saved availability",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "data": {
                      "$ref": "#/components/schemas/BookingAvailability"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/booking/admin": {
      "get": {
        "tags": [
          "Business booking"
        ],
        "summary": "List bookings",
        "security": [
          {
            "BearerAuth": []
          },
          {
            "ApiKeyHeader": []
          }
        ],
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "held",
                "confirmed",
                "expired",
                "cancelled",
                "needs_attention"
              ]
            }
          },
          {
            "name": "from",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "to",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 500
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Bookings",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "data": {
                      "$ref": "#/components/schemas/BookingAdminList"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Business booking"
        ],
        "summary": "Admin action on a booking (cancel never refunds automatically)",
        "security": [
          {
            "BearerAuth": []
          },
          {
            "ApiKeyHeader": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BookingAdminAction"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated booking",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "data": {
                      "$ref": "#/components/schemas/BookingAdminView"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Booking not found or manage token invalid",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Slot taken, hold not active, or reschedule not allowed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/webhooks/paypal": {
      "post": {
        "tags": [
          "Business booking"
        ],
        "summary": "PayPal webhook (verified through PayPal verify-webhook-signature; handles PAYMENT.CAPTURE.COMPLETED)",
        "parameters": [
          {
            "name": "paypal-transmission-id",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "paypal-transmission-time",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "paypal-transmission-sig",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "paypal-cert-url",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "paypal-auth-algo",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Event processed or ignored (idempotent by capture id)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "data": {
                      "$ref": "#/components/schemas/BookingPaymentResult"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Signature verification failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "PayPal verification API error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Payment provider not configured (`payment_unconfigured`, lists missing variables) or DB unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/webhooks/sepay": {
      "post": {
        "tags": [
          "Business booking"
        ],
        "summary": "SePay bank transfer webhook (Authorization: Apikey <key>)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SepayWebhook"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Event processed or ignored (idempotent)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "data": {
                      "$ref": "#/components/schemas/BookingPaymentResult"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Payment provider not configured (`payment_unconfigured`, lists missing variables) or DB unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/articles": {
      "get": {
        "tags": [
          "Articles"
        ],
        "summary": "List/discover published articles (admins: ?include_drafts=1). With q, the authorization-first knowledge search orders results (X-Search-Semantic: 1|0).",
        "parameters": [
          {
            "name": "include_drafts",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "1"
              ]
            }
          },
          {
            "name": "q",
            "in": "query",
            "schema": {
              "type": "string",
              "maxLength": 200
            }
          },
          {
            "name": "lang",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "en",
                "vi",
                "zh",
                "ko",
                "ja"
              ]
            },
            "description": "Preferred edition locale; articles without it show their primary edition"
          },
          {
            "name": "category",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Category slug or id"
          },
          {
            "name": "tag",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Topic tag slug or id"
          },
          {
            "name": "label",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "<kind>:<slug>, e.g. freshness:current"
          },
          {
            "name": "access",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "free",
                "knowledges"
              ]
            }
          },
          {
            "name": "sort",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "new",
                "old",
                "title",
                "relevance"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Articles",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ArticleSummary"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Articles"
        ],
        "summary": "Create a draft article",
        "security": [
          {
            "BearerAuth": []
          },
          {
            "ApiKeyHeader": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/ArticleInput"
                  },
                  {
                    "required": [
                      "slug",
                      "title"
                    ]
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created draft",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "$ref": "#/components/schemas/Article"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Revision conflict or slug taken (`revision_conflict`, `slug_taken`, `already_voted`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "`invalid_document` with `errors: [{path, message}]`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/articles/{slug}": {
      "get": {
        "tags": [
          "Articles"
        ],
        "summary": "Get one published edition (paywalled) or, for admins, its draft with ?draft=1",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "lang",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "en",
                "vi",
                "zh",
                "ko",
                "ja"
              ]
            },
            "description": "Edition locale; falls back to the primary edition (locale_fallback: true)"
          },
          {
            "name": "draft",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "1"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Article",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "$ref": "#/components/schemas/Article"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "Articles"
        ],
        "summary": "Update one edition draft (body.locale; a missing edition is created) and article metadata (optimistic concurrency)",
        "security": [
          {
            "BearerAuth": []
          },
          {
            "ApiKeyHeader": []
          }
        ],
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/ArticleInput"
                  },
                  {
                    "type": "object",
                    "required": [
                      "expected_revision"
                    ],
                    "properties": {
                      "expected_revision": {
                        "type": "integer"
                      }
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated draft",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "$ref": "#/components/schemas/Article"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Revision conflict or slug taken (`revision_conflict`, `slug_taken`, `already_voted`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "invalid_document",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Articles"
        ],
        "summary": "Soft-delete an article",
        "security": [
          {
            "BearerAuth": []
          },
          {
            "ApiKeyHeader": []
          }
        ],
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "deleted": {
                          "type": "boolean"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/articles/{slug}/publish": {
      "post": {
        "tags": [
          "Articles"
        ],
        "summary": "Publish one edition draft (locale, default primary)",
        "security": [
          {
            "BearerAuth": []
          },
          {
            "ApiKeyHeader": []
          }
        ],
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "expected_revision",
                  "confirm"
                ],
                "properties": {
                  "expected_revision": {
                    "type": "integer"
                  },
                  "confirm": {
                    "type": "boolean",
                    "const": true
                  },
                  "locale": {
                    "type": "string",
                    "enum": [
                      "en",
                      "vi",
                      "zh",
                      "ko",
                      "ja"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Published",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "$ref": "#/components/schemas/Article"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Revision conflict or slug taken (`revision_conflict`, `slug_taken`, `already_voted`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/articles/{slug}/editions": {
      "get": {
        "tags": [
          "Articles"
        ],
        "summary": "List every locale edition (draft and published state)",
        "security": [
          {
            "BearerAuth": []
          },
          {
            "ApiKeyHeader": []
          }
        ],
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Editions",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "slug": {
                          "type": "string"
                        },
                        "primary_locale": {
                          "type": "string",
                          "enum": [
                            "en",
                            "vi",
                            "zh",
                            "ko",
                            "ja"
                          ]
                        },
                        "revision": {
                          "type": "integer"
                        },
                        "editions": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/ArticleEdition"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Articles"
        ],
        "summary": "Delete one non-primary edition",
        "security": [
          {
            "BearerAuth": []
          },
          {
            "ApiKeyHeader": []
          }
        ],
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "locale",
                  "expected_revision"
                ],
                "properties": {
                  "locale": {
                    "type": "string",
                    "enum": [
                      "en",
                      "vi",
                      "zh",
                      "ko",
                      "ja"
                    ]
                  },
                  "expected_revision": {
                    "type": "integer"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Remaining article",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "$ref": "#/components/schemas/ArticleSummary"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`revision_conflict` or `primary_edition`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/articles/{slug}/revisions": {
      "get": {
        "tags": [
          "Articles"
        ],
        "summary": "Revision history; with ?revision=N&lang=xx the stored document (restore by saving it as the draft)",
        "security": [
          {
            "BearerAuth": []
          },
          {
            "ApiKeyHeader": []
          }
        ],
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "lang",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "en",
                "vi",
                "zh",
                "ko",
                "ja"
              ]
            },
            "description": "Edition locale; falls back to the primary edition (locale_fallback: true)"
          },
          {
            "name": "revision",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "History or document",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "oneOf": [
                        {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "revision": {
                                "type": "integer"
                              },
                              "locale": {
                                "type": "string",
                                "enum": [
                                  "en",
                                  "vi",
                                  "zh",
                                  "ko",
                                  "ja"
                                ]
                              },
                              "action": {
                                "type": "string"
                              },
                              "title": {
                                "type": "string"
                              },
                              "actor": {
                                "type": "string"
                              },
                              "created_at": {
                                "type": "string"
                              }
                            }
                          }
                        },
                        {
                          "type": "object",
                          "properties": {
                            "revision": {
                              "type": "integer"
                            },
                            "locale": {
                              "type": "string",
                              "enum": [
                                "en",
                                "vi",
                                "zh",
                                "ko",
                                "ja"
                              ]
                            },
                            "title": {
                              "type": "string"
                            },
                            "excerpt": {
                              "type": "string"
                            },
                            "document": {
                              "$ref": "#/components/schemas/ArticleDocument"
                            }
                          }
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/articles/{slug}/tags": {
      "put": {
        "tags": [
          "Articles"
        ],
        "summary": "Replace topic tags with existing tags",
        "security": [
          {
            "BearerAuth": []
          },
          {
            "ApiKeyHeader": []
          }
        ],
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "tags",
                  "expected_revision"
                ],
                "properties": {
                  "tags": {
                    "type": "array",
                    "maxItems": 20,
                    "items": {
                      "type": "string"
                    }
                  },
                  "expected_revision": {
                    "type": "integer"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Article",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "$ref": "#/components/schemas/ArticleSummary"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Revision conflict or slug taken (`revision_conflict`, `slug_taken`, `already_voted`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/articles/{slug}/labels": {
      "get": {
        "tags": [
          "Articles"
        ],
        "summary": "Label assignments with evidence and label history",
        "security": [
          {
            "BearerAuth": []
          },
          {
            "ApiKeyHeader": []
          }
        ],
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Label state",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "$ref": "#/components/schemas/LabelState"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "Articles"
        ],
        "summary": "Replace label assignments as a new label revision (strict: facts need evidence)",
        "security": [
          {
            "BearerAuth": []
          },
          {
            "ApiKeyHeader": []
          }
        ],
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "assignments",
                  "expected_label_revision"
                ],
                "properties": {
                  "assignments": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/LabelAssignment"
                    }
                  },
                  "expected_label_revision": {
                    "type": "integer"
                  },
                  "reason": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Label state",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "$ref": "#/components/schemas/LabelState"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`label_revision_conflict`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "`invalid_labels` with errors",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/articles/{slug}/labels/revert": {
      "post": {
        "tags": [
          "Articles"
        ],
        "summary": "Restore an earlier label revision as a new revision",
        "security": [
          {
            "BearerAuth": []
          },
          {
            "ApiKeyHeader": []
          }
        ],
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "to_label_revision",
                  "expected_label_revision"
                ],
                "properties": {
                  "to_label_revision": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "expected_label_revision": {
                    "type": "integer"
                  },
                  "reason": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Label state",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "$ref": "#/components/schemas/LabelState"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`label_revision_conflict`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/surveys/{blockId}/vote": {
      "post": {
        "tags": [
          "Articles"
        ],
        "summary": "Vote in a survey (one vote per person; sets an HttpOnly zuey_voter cookie for anonymous voters)",
        "parameters": [
          {
            "name": "blockId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "article_slug",
                  "option_ids"
                ],
                "properties": {
                  "article_slug": {
                    "type": "string"
                  },
                  "option_ids": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Vote recorded; aggregated results",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "$ref": "#/components/schemas/SurveyResults"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`survey_locked`: survey is in the members-only part",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`already_voted`; error.results contains current results",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "`survey_unconfigured` (SURVEY_HASH_SALT missing)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/surveys/{blockId}/results": {
      "get": {
        "tags": [
          "Articles"
        ],
        "summary": "Aggregated survey results",
        "parameters": [
          {
            "name": "blockId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "article_slug",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Results",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "$ref": "#/components/schemas/SurveyResults"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "survey_locked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/surveys/{blockId}/export.csv": {
      "get": {
        "tags": [
          "Articles"
        ],
        "summary": "Export individual votes as CSV (hashed voter ids, no IPs)",
        "security": [
          {
            "BearerAuth": []
          },
          {
            "ApiKeyHeader": []
          }
        ],
        "parameters": [
          {
            "name": "blockId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "article_slug",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "CSV",
            "content": {
              "text/csv": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/search": {
      "get": {
        "tags": [
          "Knowledge taxonomy"
        ],
        "summary": "Knowledge search (BM25 plus optional semantic). Authorization is applied before ranking: without read_full only free text and paid previews are searched.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 200
            }
          },
          {
            "name": "locale",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "en",
                "vi",
                "zh",
                "ko",
                "ja"
              ]
            },
            "description": "Restrict to one edition locale (alias: lang)"
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50,
              "default": 10
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Results",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "$ref": "#/components/schemas/KnowledgeSearchResult"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/search/reindex": {
      "post": {
        "tags": [
          "Knowledge taxonomy"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "ApiKeyHeader": []
          }
        ],
        "summary": "Rebuild the search index (and Vectorize vectors when bound) for every article",
        "responses": {
          "200": {
            "description": "Reindexed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "articles": {
                          "type": "integer"
                        },
                        "rows": {
                          "type": "integer"
                        },
                        "semantic": {
                          "type": "boolean"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/taxonomy": {
      "get": {
        "tags": [
          "Knowledge taxonomy"
        ],
        "summary": "Public discovery facets: tags, categories and approved labels used by published articles",
        "parameters": [
          {
            "name": "lang",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "en",
                "vi",
                "zh",
                "ko",
                "ja"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Facets",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "$ref": "#/components/schemas/PublicTaxonomy"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/taxonomy/tags": {
      "get": {
        "tags": [
          "Knowledge taxonomy"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "ApiKeyHeader": []
          }
        ],
        "summary": "List tags",
        "responses": {
          "200": {
            "description": "tags",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/TopicTag"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Knowledge taxonomy"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "ApiKeyHeader": []
          }
        ],
        "summary": "Create a tag. Slug, aliases and localized names must be unique across tags.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TopicTagInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "$ref": "#/components/schemas/TopicTag"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`revision_conflict` (stale expected_revision) or a duplicate slug/alias/name",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/taxonomy/tags/{id}": {
      "get": {
        "tags": [
          "Knowledge taxonomy"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "ApiKeyHeader": []
          }
        ],
        "summary": "Get one tag",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "tag",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "$ref": "#/components/schemas/TopicTag"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "Knowledge taxonomy"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "ApiKeyHeader": []
          }
        ],
        "summary": "Update a tag (optimistic concurrency)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/TopicTagInput"
                  },
                  {
                    "type": "object",
                    "required": [
                      "expected_revision"
                    ],
                    "properties": {
                      "expected_revision": {
                        "type": "integer"
                      }
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "$ref": "#/components/schemas/TopicTag"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`revision_conflict` (stale expected_revision) or a duplicate slug/alias/name",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Knowledge taxonomy"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "ApiKeyHeader": []
          }
        ],
        "summary": "Delete a tag",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "expected_revision",
            "in": "query",
            "schema": {
              "type": "integer"
            },
            "description": "Alternative to the body field"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "expected_revision": {
                    "type": "integer"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "deleted": {
                          "type": "boolean",
                          "const": true
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`revision_conflict` (stale expected_revision) or a duplicate slug/alias/name",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/taxonomy/categories": {
      "get": {
        "tags": [
          "Knowledge taxonomy"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "ApiKeyHeader": []
          }
        ],
        "summary": "List categorys",
        "responses": {
          "200": {
            "description": "categorys",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Category"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Knowledge taxonomy"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "ApiKeyHeader": []
          }
        ],
        "summary": "Create a category. Articles have at most one category.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CategoryInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "$ref": "#/components/schemas/Category"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`revision_conflict` (stale expected_revision) or a duplicate slug/alias/name",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/taxonomy/categories/{id}": {
      "get": {
        "tags": [
          "Knowledge taxonomy"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "ApiKeyHeader": []
          }
        ],
        "summary": "Get one category",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "category",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "$ref": "#/components/schemas/Category"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "Knowledge taxonomy"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "ApiKeyHeader": []
          }
        ],
        "summary": "Update a category (optimistic concurrency)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/CategoryInput"
                  },
                  {
                    "type": "object",
                    "required": [
                      "expected_revision"
                    ],
                    "properties": {
                      "expected_revision": {
                        "type": "integer"
                      }
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "$ref": "#/components/schemas/Category"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`revision_conflict` (stale expected_revision) or a duplicate slug/alias/name",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Knowledge taxonomy"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "ApiKeyHeader": []
          }
        ],
        "summary": "Delete a category",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "expected_revision",
            "in": "query",
            "schema": {
              "type": "integer"
            },
            "description": "Alternative to the body field"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "expected_revision": {
                    "type": "integer"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "deleted": {
                          "type": "boolean",
                          "const": true
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`revision_conflict` (stale expected_revision) or a duplicate slug/alias/name",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/taxonomy/labels": {
      "get": {
        "tags": [
          "Knowledge taxonomy"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "ApiKeyHeader": []
          }
        ],
        "summary": "List labels",
        "responses": {
          "200": {
            "description": "labels",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/TaxonomyLabel"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Knowledge taxonomy"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "ApiKeyHeader": []
          }
        ],
        "summary": "Create a label. claim/freshness are built in; `extension` labels stay unapproved until approve: true.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TaxonomyLabelInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "$ref": "#/components/schemas/TaxonomyLabel"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`revision_conflict` (stale expected_revision) or a duplicate slug/alias/name",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/taxonomy/labels/{id}": {
      "get": {
        "tags": [
          "Knowledge taxonomy"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "ApiKeyHeader": []
          }
        ],
        "summary": "Get one label",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "label",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "$ref": "#/components/schemas/TaxonomyLabel"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "Knowledge taxonomy"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "ApiKeyHeader": []
          }
        ],
        "summary": "Update a label (optimistic concurrency)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/TaxonomyLabelInput"
                  },
                  {
                    "type": "object",
                    "required": [
                      "expected_revision"
                    ],
                    "properties": {
                      "expected_revision": {
                        "type": "integer"
                      }
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "$ref": "#/components/schemas/TaxonomyLabel"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`revision_conflict` (stale expected_revision) or a duplicate slug/alias/name",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Knowledge taxonomy"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "ApiKeyHeader": []
          }
        ],
        "summary": "Delete an unused, non-built-in label",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "expected_revision",
            "in": "query",
            "schema": {
              "type": "integer"
            },
            "description": "Alternative to the body field"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "expected_revision": {
                    "type": "integer"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "deleted": {
                          "type": "boolean",
                          "const": true
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`revision_conflict` (stale expected_revision) or a duplicate slug/alias/name",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/taxonomy/proposals": {
      "get": {
        "tags": [
          "Knowledge taxonomy"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "ApiKeyHeader": []
          }
        ],
        "summary": "Label proposals for review",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "pending",
                "deferred",
                "applied",
                "rejected",
                "stale"
              ]
            }
          },
          {
            "name": "article_id",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "job_id",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Proposals",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/LabelProposal"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Knowledge taxonomy"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "ApiKeyHeader": []
          }
        ],
        "summary": "File a manual proposal for one edition (unverified facts are downgraded to needs-review with a question)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "article_slug",
                  "locale",
                  "assignments"
                ],
                "properties": {
                  "article_slug": {
                    "type": "string"
                  },
                  "locale": {
                    "type": "string",
                    "enum": [
                      "en",
                      "vi",
                      "zh",
                      "ko",
                      "ja"
                    ]
                  },
                  "assignments": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/LabelAssignment"
                    }
                  },
                  "rationale": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "$ref": "#/components/schemas/LabelProposal"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/taxonomy/proposals/{id}": {
      "get": {
        "tags": [
          "Knowledge taxonomy"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "ApiKeyHeader": []
          }
        ],
        "summary": "Get one proposal",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Proposal",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "$ref": "#/components/schemas/LabelProposal"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/taxonomy/proposals/{id}/decision": {
      "post": {
        "tags": [
          "Knowledge taxonomy"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "ApiKeyHeader": []
          }
        ],
        "summary": "Approve, edit, reject or defer a proposal. Approve/edit apply a new label revision; approving an applied proposal is idempotent.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "decision"
                ],
                "properties": {
                  "decision": {
                    "type": "string",
                    "enum": [
                      "approve",
                      "edit",
                      "reject",
                      "defer"
                    ]
                  },
                  "reason": {
                    "type": "string"
                  },
                  "confirm": {
                    "type": "boolean",
                    "const": true,
                    "description": "Required for approve/edit"
                  },
                  "expected_label_revision": {
                    "type": "integer",
                    "description": "Required for approve/edit"
                  },
                  "assignments": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/LabelAssignment"
                    },
                    "description": "Required for edit"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Decision",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "proposal": {
                          "$ref": "#/components/schemas/LabelProposal"
                        },
                        "labels": {
                          "oneOf": [
                            {
                              "$ref": "#/components/schemas/LabelState"
                            },
                            {
                              "type": "null"
                            }
                          ]
                        },
                        "already_applied": {
                          "type": "boolean"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`label_revision_conflict`, `edition_changed` (proposal became stale) or `proposal_closed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "`invalid_labels`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/taxonomy/audit-jobs": {
      "get": {
        "tags": [
          "Knowledge taxonomy"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "ApiKeyHeader": []
          }
        ],
        "summary": "Recent AI label audit jobs",
        "responses": {
          "200": {
            "description": "Jobs",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/AuditJob"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Knowledge taxonomy"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "ApiKeyHeader": []
          }
        ],
        "summary": "Queue an AI label audit over published editions; run it with /run in resumable batches",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "locales": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "en",
                        "vi",
                        "zh",
                        "ko",
                        "ja"
                      ]
                    }
                  },
                  "article_ids": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "skip_unchanged": {
                    "type": "boolean"
                  },
                  "batch_size": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 20
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "$ref": "#/components/schemas/AuditJob"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/taxonomy/audit-jobs/{id}": {
      "get": {
        "tags": [
          "Knowledge taxonomy"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "ApiKeyHeader": []
          }
        ],
        "summary": "Get one audit job",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Job",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "$ref": "#/components/schemas/AuditJob"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/taxonomy/audit-jobs/{id}/run": {
      "post": {
        "tags": [
          "Knowledge taxonomy"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "ApiKeyHeader": []
          }
        ],
        "summary": "Process the next batch from the cursor (errors pause the job; repeat until completed)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Job",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "$ref": "#/components/schemas/AuditJob"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`job_closed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "`ai_unavailable`: the Workers AI binding is not configured",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/taxonomy/audit-jobs/{id}/cancel": {
      "post": {
        "tags": [
          "Knowledge taxonomy"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "ApiKeyHeader": []
          }
        ],
        "summary": "Cancel a job (existing proposals stay for review)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Job",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "$ref": "#/components/schemas/AuditJob"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`job_closed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/taxonomy/audit-log": {
      "get": {
        "tags": [
          "Knowledge taxonomy"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "ApiKeyHeader": []
          }
        ],
        "summary": "Taxonomy audit trail",
        "parameters": [
          {
            "name": "target_type",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "tag",
                "category",
                "label",
                "article",
                "proposal",
                "audit_job"
              ]
            }
          },
          {
            "name": "target_id",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 500
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Entries",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/TaxonomyAuditEntry"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/members/auth/magic-link": {
      "post": {
        "tags": [
          "Members & account"
        ],
        "summary": "Email a 15-minute single-use sign-in link",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email"
                  },
                  "next": {
                    "type": "string",
                    "description": "Same-site path to open after sign-in"
                  }
                },
                "required": [
                  "email"
                ]
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Link sent",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "sent": {
                          "type": "boolean"
                        },
                        "expires_in_minutes": {
                          "type": "integer"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited` (retry_after_seconds)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "`email_failed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "`email_unconfigured` (RESEND_API_KEY missing)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/members/auth/magic-link/verify": {
      "post": {
        "tags": [
          "Members & account"
        ],
        "summary": "Consume a magic-link token and open a session (sets zuey_member)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "token": {
                    "type": "string"
                  }
                },
                "required": [
                  "token"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Signed in",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "next": {
                          "type": "string"
                        },
                        "created": {
                          "type": "boolean"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "`invalid_token` (unknown, used or expired)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/members/auth/{provider}": {
      "get": {
        "tags": [
          "Members & account"
        ],
        "summary": "Start Google or GitHub sign-in (browser redirect)",
        "parameters": [
          {
            "name": "provider",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "google",
                "github"
              ]
            }
          },
          {
            "name": "next",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "302": {
            "description": "Redirect to the provider; callback returns to /api/auth/{provider}/callback"
          }
        }
      }
    },
    "/api/members/auth/logout": {
      "post": {
        "tags": [
          "Members & account"
        ],
        "summary": "Sign out the current session",
        "security": [
          {
            "MemberSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Signed out",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "data": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/me": {
      "get": {
        "tags": [
          "Members & account"
        ],
        "summary": "Your profile, plans and entitlements (account:read)",
        "security": [
          {
            "MemberSession": []
          },
          {
            "BearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Member",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "data": {
                      "$ref": "#/components/schemas/Member"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`, `invalid_api_key`, `api_key_expired` or `api_key_revoked`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`insufficient_scope`, `entitlement_required`, `session_required` or `csrf_rejected`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "Members & account"
        ],
        "summary": "Update name, avatar URL or locale (account:write)",
        "security": [
          {
            "MemberSession": []
          },
          {
            "BearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 80
                  },
                  "avatar_url": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "uri"
                  },
                  "locale": {
                    "type": "string",
                    "enum": [
                      "vi",
                      "en"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated member",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "data": {
                      "$ref": "#/components/schemas/Member"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`, `invalid_api_key`, `api_key_expired` or `api_key_revoked`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`insufficient_scope`, `entitlement_required`, `session_required` or `csrf_rejected`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Members & account"
        ],
        "summary": "Delete your account (session only)",
        "security": [
          {
            "MemberSession": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "confirm_email": {
                    "type": "string"
                  }
                },
                "required": [
                  "confirm_email"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Deleted; billing records are retained without your email",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "data": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "`confirmation_required`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`, `invalid_api_key`, `api_key_expired` or `api_key_revoked`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`insufficient_scope`, `entitlement_required`, `session_required` or `csrf_rejected`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/me/email": {
      "post": {
        "tags": [
          "Members & account"
        ],
        "summary": "Request an email change (verification link sent to the new address; session only)",
        "security": [
          {
            "MemberSession": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "new_email": {
                    "type": "string",
                    "format": "email"
                  }
                },
                "required": [
                  "new_email"
                ]
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Verification sent",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "pending_email": {
                          "type": "string"
                        },
                        "expires_in_minutes": {
                          "type": "integer"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`, `invalid_api_key`, `api_key_expired` or `api_key_revoked`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`insufficient_scope`, `entitlement_required`, `session_required` or `csrf_rejected`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`email_taken`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited` (retry_after_seconds)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "`email_unconfigured`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/me/email/confirm": {
      "post": {
        "tags": [
          "Members & account"
        ],
        "summary": "Confirm an email change with the emailed token (the old address is notified)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "token": {
                    "type": "string"
                  }
                },
                "required": [
                  "token"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Email changed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "email": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "`invalid_token`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`email_taken`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/me/sessions": {
      "get": {
        "tags": [
          "Members & account"
        ],
        "summary": "Your signed-in sessions (account:read)",
        "security": [
          {
            "MemberSession": []
          },
          {
            "BearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Sessions",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/MemberSession"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`, `invalid_api_key`, `api_key_expired` or `api_key_revoked`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`insufficient_scope`, `entitlement_required`, `session_required` or `csrf_rejected`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Members & account"
        ],
        "summary": "Sign out all other sessions (session only)",
        "security": [
          {
            "MemberSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Revoked count",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "revoked": {
                          "type": "integer"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`, `invalid_api_key`, `api_key_expired` or `api_key_revoked`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`insufficient_scope`, `entitlement_required`, `session_required` or `csrf_rejected`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/me/sessions/{id}": {
      "delete": {
        "tags": [
          "Members & account"
        ],
        "summary": "Revoke one of your sessions (session only)",
        "security": [
          {
            "MemberSession": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Revoked",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "data": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`, `invalid_api_key`, `api_key_expired` or `api_key_revoked`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`insufficient_scope`, `entitlement_required`, `session_required` or `csrf_rejected`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found (also returned for resources owned by another member)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/me/activity": {
      "get": {
        "tags": [
          "Members & account"
        ],
        "summary": "Your account activity, newest first (account:read)",
        "security": [
          {
            "MemberSession": []
          },
          {
            "BearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Activity",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`, `invalid_api_key`, `api_key_expired` or `api_key_revoked`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`insufficient_scope`, `entitlement_required`, `session_required` or `csrf_rejected`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/me/export": {
      "get": {
        "tags": [
          "Members & account"
        ],
        "summary": "Download your data as JSON (account:read)",
        "security": [
          {
            "MemberSession": []
          },
          {
            "BearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Export",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "data": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`, `invalid_api_key`, `api_key_expired` or `api_key_revoked`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`insufficient_scope`, `entitlement_required`, `session_required` or `csrf_rejected`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/me/keys": {
      "get": {
        "tags": [
          "Members & account"
        ],
        "summary": "List your API keys (metadata only; session only)",
        "security": [
          {
            "MemberSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Keys",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/UserApiKey"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`, `invalid_api_key`, `api_key_expired` or `api_key_revoked`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`insufficient_scope`, `entitlement_required`, `session_required` or `csrf_rejected`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Members & account"
        ],
        "summary": "Create an API key; the secret is returned once (session only)",
        "security": [
          {
            "MemberSession": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UserApiKeyCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Key + one-time secret",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "secret": {
                          "type": "string"
                        },
                        "key": {
                          "$ref": "#/components/schemas/UserApiKey"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`, `invalid_api_key`, `api_key_expired` or `api_key_revoked`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`insufficient_scope`, `entitlement_required`, `session_required` or `csrf_rejected`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`key_limit_reached`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/me/keys/{id}": {
      "delete": {
        "tags": [
          "Members & account"
        ],
        "summary": "Revoke a key immediately (session only)",
        "security": [
          {
            "MemberSession": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Revoked key",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "data": {
                      "$ref": "#/components/schemas/UserApiKey"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`, `invalid_api_key`, `api_key_expired` or `api_key_revoked`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`insufficient_scope`, `entitlement_required`, `session_required` or `csrf_rejected`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found (also returned for resources owned by another member)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/me/keys/{id}/rotate": {
      "post": {
        "tags": [
          "Members & account"
        ],
        "summary": "Issue a replacement (same or narrower scopes); the old key works until you revoke it (session only)",
        "security": [
          {
            "MemberSession": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "scopes": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "articles:read",
                        "chat:write",
                        "account:read",
                        "account:write",
                        "billing:read",
                        "checkout:write"
                      ]
                    }
                  },
                  "expires_in_days": {
                    "type": "integer"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Replacement + one-time secret",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "secret": {
                          "type": "string"
                        },
                        "key": {
                          "$ref": "#/components/schemas/UserApiKey"
                        },
                        "previous": {
                          "$ref": "#/components/schemas/UserApiKey"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`, `invalid_api_key`, `api_key_expired` or `api_key_revoked`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`insufficient_scope`, `entitlement_required`, `session_required` or `csrf_rejected`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found (also returned for resources owned by another member)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`key_inactive` or `already_rotated`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/admin/members": {
      "get": {
        "tags": [
          "Members & account"
        ],
        "summary": "Admin: list/search members",
        "security": [
          {
            "BearerAuth": []
          },
          {
            "ApiKeyHeader": []
          },
          {
            "MemberSession": []
          }
        ],
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 50
            }
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Members",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "data": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`, `invalid_api_key`, `api_key_expired` or `api_key_revoked`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`insufficient_scope`, `entitlement_required`, `session_required` or `csrf_rejected`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/plans": {
      "get": {
        "tags": [
          "Membership billing"
        ],
        "summary": "Plan catalog with VND prepay prices",
        "responses": {
          "200": {
            "description": "Plans",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "data": {
                      "$ref": "#/components/schemas/PlansCatalog"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/billing/orders": {
      "get": {
        "tags": [
          "Membership billing"
        ],
        "summary": "Your orders (billing:read)",
        "security": [
          {
            "MemberSession": []
          },
          {
            "BearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Orders",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/BillingOrder"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`, `invalid_api_key`, `api_key_expired` or `api_key_revoked`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`insufficient_scope`, `entitlement_required`, `session_required` or `csrf_rejected`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Membership billing"
        ],
        "summary": "Start a purchase (checkout:write): SePay VietQR prepaid order, or a Dodo monthly card subscription checkout",
        "description": "`provider: sepay` (default) returns VietQR transfer details. `provider: dodo` returns a hosted `checkout_url`; months must be 1 or omitted. Either way, poll `status_url` for the real state.",
        "security": [
          {
            "MemberSession": []
          },
          {
            "BearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "plan"
                ],
                "properties": {
                  "plan": {
                    "type": "string",
                    "enum": [
                      "knowledges",
                      "ai",
                      "combo",
                      "community"
                    ]
                  },
                  "provider": {
                    "type": "string",
                    "enum": [
                      "sepay",
                      "dodo"
                    ],
                    "default": "sepay"
                  },
                  "months": {
                    "type": "integer",
                    "enum": [
                      1,
                      3,
                      6,
                      12
                    ],
                    "default": 1,
                    "description": "SePay only; card subscriptions are monthly"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Pending SePay order or pending card checkout",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "data": {
                      "oneOf": [
                        {
                          "$ref": "#/components/schemas/BillingOrderCheckout"
                        },
                        {
                          "$ref": "#/components/schemas/CardCheckout"
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`, `invalid_api_key`, `api_key_expired` or `api_key_revoked`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`insufficient_scope`, `entitlement_required`, `session_required` or `csrf_rejected`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`already_subscribed` (an active card subscription exists for this plan)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "`too_many_pending_orders`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "`payment_provider_error` (Dodo API failure)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "`billing_unconfigured` (SePay) or `payment_unconfigured` (Dodo); lists missing env names",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/billing/orders/{code}": {
      "get": {
        "tags": [
          "Membership billing"
        ],
        "summary": "One of your orders (billing:read; admins any)",
        "security": [
          {
            "MemberSession": []
          },
          {
            "BearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Order",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "data": {
                      "$ref": "#/components/schemas/BillingOrder"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`, `invalid_api_key`, `api_key_expired` or `api_key_revoked`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`insufficient_scope`, `entitlement_required`, `session_required` or `csrf_rejected`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found (also returned for resources owned by another member)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/billing/subscription": {
      "get": {
        "tags": [
          "Membership billing"
        ],
        "summary": "Your plans and effective entitlements (billing:read)",
        "security": [
          {
            "MemberSession": []
          },
          {
            "BearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Subscription",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "subscriptions": {
                          "type": "array",
                          "items": {
                            "type": "object"
                          }
                        },
                        "card_subscriptions": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/CardSubscription"
                          }
                        },
                        "active_plans": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "entitlements": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`, `invalid_api_key`, `api_key_expired` or `api_key_revoked`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`insufficient_scope`, `entitlement_required`, `session_required` or `csrf_rejected`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/billing/card/{id}": {
      "get": {
        "tags": [
          "Membership billing"
        ],
        "summary": "One of your card subscriptions (billing:read; admins any)",
        "security": [
          {
            "MemberSession": []
          },
          {
            "BearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^csub_[a-f0-9]{20}$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Card subscription",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "data": {
                      "$ref": "#/components/schemas/CardSubscription"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`, `invalid_api_key`, `api_key_expired` or `api_key_revoked`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`insufficient_scope`, `entitlement_required`, `session_required` or `csrf_rejected`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found (also returned for resources owned by another member)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/billing/card/{id}/portal": {
      "post": {
        "tags": [
          "Membership billing"
        ],
        "summary": "Open the Dodo customer portal (card, invoices, cancellation; session only)",
        "security": [
          {
            "MemberSession": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One-time portal link",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "url": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`, `invalid_api_key`, `api_key_expired` or `api_key_revoked`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`insufficient_scope`, `entitlement_required`, `session_required` or `csrf_rejected`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found (also returned for resources owned by another member)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`not_manageable` (no Dodo customer yet)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "`payment_provider_error`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "`payment_unconfigured`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/billing/card/{id}/cancel": {
      "post": {
        "tags": [
          "Membership billing"
        ],
        "summary": "Stop renewing at the end of the paid period; access continues until then (session only)",
        "security": [
          {
            "MemberSession": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Updated card subscription",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "data": {
                      "$ref": "#/components/schemas/CardSubscription"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`, `invalid_api_key`, `api_key_expired` or `api_key_revoked`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`insufficient_scope`, `entitlement_required`, `session_required` or `csrf_rejected`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found (also returned for resources owned by another member)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`not_cancellable` (not active)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "`payment_provider_error`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "`payment_unconfigured`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/webhooks/dodo": {
      "post": {
        "tags": [
          "Membership billing"
        ],
        "summary": "Dodo Payments webhook (Standard Webhooks signature, 5-minute window; subscription.* and payment.succeeded/failed)",
        "description": "Idempotent by `webhook-id`. Activation requires the configured product, the plan price and USD; anything else, or a payment without member metadata, is recorded as needs_attention.",
        "parameters": [
          {
            "name": "webhook-id",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "webhook-timestamp",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "webhook-signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "example": "v1,<base64>"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Event processed or ignored",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "outcome": {
                          "type": "string",
                          "enum": [
                            "duplicate_event",
                            "unmatched",
                            "ignored",
                            "stale_event",
                            "needs_attention",
                            "activated",
                            "deactivated",
                            "updated"
                          ]
                        },
                        "card_subscription_id": {
                          "type": [
                            "string",
                            "null"
                          ]
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "`invalid_signature`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "`payment_unconfigured` (DODO_WEBHOOK_SECRET missing)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/billing/reconcile": {
      "post": {
        "tags": [
          "Membership billing"
        ],
        "summary": "Admin: match recent SePay transactions to pending orders",
        "security": [
          {
            "BearerAuth": []
          },
          {
            "ApiKeyHeader": []
          },
          {
            "MemberSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Reconciliation result",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "checked": {
                          "type": "integer"
                        },
                        "matched": {
                          "type": "integer"
                        },
                        "results": {
                          "type": "array",
                          "items": {
                            "type": "object"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`, `invalid_api_key`, `api_key_expired` or `api_key_revoked`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`insufficient_scope`, `entitlement_required`, `session_required` or `csrf_rejected`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "`sepay_api_error`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "`reconcile_unconfigured` (SEPAY_API_TOKEN missing)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/billing/reminders": {
      "post": {
        "tags": [
          "Membership billing"
        ],
        "summary": "Admin/cron: send renewal reminders for plans ending within 7 days",
        "security": [
          {
            "BearerAuth": []
          },
          {
            "ApiKeyHeader": []
          },
          {
            "MemberSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Counts",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "data": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`, `invalid_api_key`, `api_key_expired` or `api_key_revoked`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`insufficient_scope`, `entitlement_required`, `session_required` or `csrf_rejected`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "`email_unconfigured`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/admin/billing/attention": {
      "get": {
        "tags": [
          "Membership billing"
        ],
        "summary": "Admin: payments needing attention (SePay orders and Dodo card subscriptions)",
        "security": [
          {
            "BearerAuth": []
          },
          {
            "ApiKeyHeader": []
          },
          {
            "MemberSession": []
          }
        ],
        "description": "Oldest first, at most 200 of each kind. Card rows are read-only (resolve them in the Dodo dashboard; `card_note` explains why).",
        "responses": {
          "200": {
            "description": "Attention queue",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "data": {
                      "$ref": "#/components/schemas/BillingAttentionQueue"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`, `invalid_api_key`, `api_key_expired` or `api_key_revoked`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`insufficient_scope`, `entitlement_required`, `session_required` or `csrf_rejected`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/admin/billing/orders/{code}/resolve": {
      "post": {
        "tags": [
          "Membership billing"
        ],
        "summary": "Admin: activate or dismiss a flagged SePay order",
        "security": [
          {
            "BearerAuth": []
          },
          {
            "ApiKeyHeader": []
          },
          {
            "MemberSession": []
          }
        ],
        "description": "`activate` marks the order paid now and grants/extends its plan through the normal fulfilment path (receipt email included). `dismiss` closes it as `expired` without access, keeping `attention_reason`. Repeating an action returns `already_activated` / `already_dismissed`; a dismissed order may still be activated. The admin and note are written to the member activity log.",
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "action": {
                    "type": "string",
                    "enum": [
                      "activate",
                      "dismiss"
                    ]
                  },
                  "note": {
                    "type": "string",
                    "maxLength": 500
                  }
                },
                "required": [
                  "action"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Resolution",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "outcome": {
                          "type": "string",
                          "enum": [
                            "activated",
                            "already_activated",
                            "dismissed",
                            "already_dismissed"
                          ]
                        },
                        "order": {
                          "$ref": "#/components/schemas/BillingOrder"
                        },
                        "subscription": {
                          "type": [
                            "object",
                            "null"
                          ],
                          "description": "Plan period after activation; null otherwise"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`, `invalid_api_key`, `api_key_expired` or `api_key_revoked`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`insufficient_scope`, `entitlement_required`, `session_required` or `csrf_rejected`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found (also returned for resources owned by another member)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`not_in_attention` (the order is pending, expired normally or already paid)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/chat/status": {
      "get": {
        "tags": [
          "Zuey AI"
        ],
        "summary": "What the chat UI should show for the caller (never 401/403)",
        "responses": {
          "200": {
            "description": "Status",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "signed_in": {
                          "type": "boolean"
                        },
                        "entitled": {
                          "type": "boolean"
                        },
                        "is_admin": {
                          "type": "boolean"
                        },
                        "configured": {
                          "type": "boolean"
                        },
                        "quota": {
                          "oneOf": [
                            {
                              "$ref": "#/components/schemas/ChatQuota"
                            },
                            {
                              "type": "null"
                            }
                          ]
                        },
                        "login_url": {
                          "type": "string"
                        },
                        "upgrade_url": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/chat/sessions": {
      "get": {
        "tags": [
          "Zuey AI"
        ],
        "summary": "List your chat sessions and this month's quota",
        "security": [
          {
            "MemberSession": []
          },
          {
            "BearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Sessions",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "sessions": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/ChatSession"
                          }
                        },
                        "quota": {
                          "$ref": "#/components/schemas/ChatQuota"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`entitlement_required` (plan without ai_chat), `insufficient_scope` (key lacks chat:write), `member_account_required`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Session not found (or not yours)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Zuey AI"
        ],
        "summary": "Start a chat session",
        "security": [
          {
            "MemberSession": []
          },
          {
            "BearerAuth": []
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "title": {
                    "type": "string",
                    "maxLength": 120
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "$ref": "#/components/schemas/ChatSession"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`entitlement_required` (plan without ai_chat), `insufficient_scope` (key lacks chat:write), `member_account_required`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Session not found (or not yours)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/chat/sessions/{id}": {
      "get": {
        "tags": [
          "Zuey AI"
        ],
        "summary": "One of your sessions with messages, sources and artifacts",
        "security": [
          {
            "MemberSession": []
          },
          {
            "BearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Session",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "$ref": "#/components/schemas/ChatSessionDetail"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`entitlement_required` (plan without ai_chat), `insufficient_scope` (key lacks chat:write), `member_account_required`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Session not found (or not yours)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "Zuey AI"
        ],
        "summary": "Rename a session",
        "security": [
          {
            "MemberSession": []
          },
          {
            "BearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "title"
                ],
                "properties": {
                  "title": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 120
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Renamed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "$ref": "#/components/schemas/ChatSession"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`entitlement_required` (plan without ai_chat), `insufficient_scope` (key lacks chat:write), `member_account_required`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Session not found (or not yours)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Zuey AI"
        ],
        "summary": "Delete a session (messages and artifacts are removed)",
        "security": [
          {
            "MemberSession": []
          },
          {
            "BearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "deleted": {
                          "type": "boolean"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`entitlement_required` (plan without ai_chat), `insufficient_scope` (key lacks chat:write), `member_account_required`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Session not found (or not yours)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`chat_run_in_progress`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/chat/sessions/{id}/messages": {
      "post": {
        "tags": [
          "Zuey AI"
        ],
        "summary": "Ask Zuey AI (Server-Sent Events stream)",
        "security": [
          {
            "MemberSession": []
          },
          {
            "BearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "description": "Streams `event: sources` ({sources}), `event: delta` ({text}) and finally `event: done` ({message_id, content, cancelled, artifacts, artifact_errors, usage}) or `event: error` ({message_id, code, message, retryable}). The first token typically takes 8–11 s; comment keep-alives are sent meanwhile. Closing the connection aborts the gateway run. Grounding only uses what the caller may read: without read_full, paid articles contribute their public preview only.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "message"
                ],
                "properties": {
                  "message": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 4000
                  },
                  "locale": {
                    "type": "string",
                    "enum": [
                      "en",
                      "vi",
                      "zh",
                      "ko",
                      "ja"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "SSE stream",
            "content": {
              "text/event-stream": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`entitlement_required` (plan without ai_chat), `insufficient_scope` (key lacks chat:write), `member_account_required`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Session not found (or not yours)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`chat_run_in_progress`: one reply at a time per session",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "`ai_quota_exceeded` with `limit`, `month`, `upgrade_url`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "`ai_unconfigured` (Dewee gateway env missing) or `database_unavailable`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/chat/sessions/{id}/stop": {
      "post": {
        "tags": [
          "Zuey AI"
        ],
        "summary": "Stop the reply streaming in this session (any tab or device)",
        "security": [
          {
            "MemberSession": []
          },
          {
            "BearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Stop requested",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "stopping": {
                          "type": "boolean"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`entitlement_required` (plan without ai_chat), `insufficient_scope` (key lacks chat:write), `member_account_required`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Session not found (or not yours)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/chat/export": {
      "get": {
        "tags": [
          "Zuey AI"
        ],
        "summary": "Download all your chats (sessions, messages, sources, artifacts, usage) as JSON",
        "security": [
          {
            "MemberSession": []
          },
          {
            "BearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Export",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "exported_at": {
                          "type": "string"
                        },
                        "sessions": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/ChatSessionDetail"
                          }
                        },
                        "usage": {
                          "type": "array",
                          "items": {
                            "type": "object"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`entitlement_required` (plan without ai_chat), `insufficient_scope` (key lacks chat:write), `member_account_required`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Session not found (or not yours)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/chat/artifacts/{id}/attach": {
      "post": {
        "tags": [
          "Zuey AI"
        ],
        "summary": "Admin: append a chat artifact to an article draft (publish separately)",
        "security": [
          {
            "BearerAuth": []
          },
          {
            "ApiKeyHeader": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "article_slug",
                  "expected_revision"
                ],
                "properties": {
                  "article_slug": {
                    "type": "string"
                  },
                  "expected_revision": {
                    "type": "integer"
                  },
                  "reason": {
                    "type": "string",
                    "description": "Required (and audited) for another member's artifact"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Attached",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "artifact_id": {
                          "type": "string"
                        },
                        "block_id": {
                          "type": "string"
                        },
                        "article": {
                          "$ref": "#/components/schemas/ArticleSummary"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Artifact or article not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`revision_conflict`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "`invalid_document`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/sandbox/fetch": {
      "post": {
        "tags": [
          "Zuey AI"
        ],
        "summary": "Fetch broker for sandboxed interactive blocks",
        "description": "HTTPS GET only, to hosts in SANDBOX_FETCH_ALLOWLIST; private/loopback addresses refused; redirects re-validated; no cookies or credentials forwarded; text/JSON only; 524288 bytes and 8000 ms caps; 60/min per member, 20/min per anonymous IP.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "url"
                ],
                "properties": {
                  "url": {
                    "type": "string",
                    "format": "uri"
                  },
                  "method": {
                    "type": "string",
                    "enum": [
                      "GET"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Upstream response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "url": {
                          "type": "string"
                        },
                        "status": {
                          "type": "integer"
                        },
                        "content_type": {
                          "type": "string"
                        },
                        "body": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "`invalid_url`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`host_not_allowed`, `private_address_blocked`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "`method_not_allowed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "`unsupported_content_type`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "`upstream_failed`, `upstream_too_large`, `upstream_redirect_blocked`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "`sandbox_fetch_unconfigured`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "504": {
            "description": "`upstream_timeout`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/admin/chat/sessions": {
      "get": {
        "tags": [
          "Zuey AI"
        ],
        "summary": "Admin: list/search all stored chat sessions with stats (audited)",
        "security": [
          {
            "BearerAuth": []
          },
          {
            "ApiKeyHeader": []
          }
        ],
        "parameters": [
          {
            "name": "reason",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 5,
              "maxLength": 500
            },
            "description": "Why access is needed; written to admin_access_audit (X-Admin-Reason header also accepted)"
          },
          {
            "name": "q",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "user_id",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Sessions and stats",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "sessions": {
                          "type": "array",
                          "items": {
                            "allOf": [
                              {
                                "$ref": "#/components/schemas/ChatSession"
                              },
                              {
                                "type": "object",
                                "properties": {
                                  "user_id": {
                                    "type": "string"
                                  },
                                  "user_email": {
                                    "type": [
                                      "string",
                                      "null"
                                    ]
                                  },
                                  "message_count": {
                                    "type": "integer"
                                  },
                                  "last_message_at": {
                                    "type": [
                                      "string",
                                      "null"
                                    ]
                                  }
                                }
                              }
                            ]
                          }
                        },
                        "stats": {
                          "type": "object",
                          "properties": {
                            "sessions": {
                              "type": "integer"
                            },
                            "messages": {
                              "type": "integer"
                            },
                            "month": {
                              "type": "string"
                            },
                            "active_users_month": {
                              "type": "integer"
                            },
                            "requests_month": {
                              "type": "integer"
                            },
                            "est_cost_cents_month": {
                              "type": "number"
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "`reason_required`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/admin/chat/sessions/{id}": {
      "get": {
        "tags": [
          "Zuey AI"
        ],
        "summary": "Admin: read one member's chat session (audited)",
        "security": [
          {
            "BearerAuth": []
          },
          {
            "ApiKeyHeader": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "reason",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 5,
              "maxLength": 500
            },
            "description": "Why access is needed; written to admin_access_audit (X-Admin-Reason header also accepted)"
          }
        ],
        "responses": {
          "200": {
            "description": "Session",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/ChatSessionDetail"
                        },
                        {
                          "type": "object",
                          "properties": {
                            "user_id": {
                              "type": "string"
                            }
                          }
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "`reason_required`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/.well-known/oauth-authorization-server": {
      "get": {
        "tags": [
          "OAuth & MCP"
        ],
        "summary": "Authorization server metadata (RFC 8414)",
        "security": [],
        "responses": {
          "200": {
            "description": "Metadata",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          }
        }
      }
    },
    "/.well-known/oauth-protected-resource/mcp": {
      "get": {
        "tags": [
          "OAuth & MCP"
        ],
        "summary": "Protected resource metadata for /mcp (RFC 9728); also served at /.well-known/oauth-protected-resource",
        "security": [],
        "responses": {
          "200": {
            "description": "Metadata",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProtectedResourceMetadata"
                }
              }
            }
          }
        }
      }
    },
    "/oauth/register": {
      "post": {
        "tags": [
          "OAuth & MCP"
        ],
        "summary": "Dynamic client registration (RFC 7591)",
        "security": [],
        "description": "Redirect URIs must be https, loopback http, or a private-use scheme; they are matched exactly at /oauth/authorize. Rate limited per IP.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ClientRegistrationRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Registered. `client_secret` is shown once (confidential clients only).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "description": "`invalid_redirect_uri` or `invalid_client_metadata`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OAuthError"
                }
              }
            }
          },
          "429": {
            "description": "`temporarily_unavailable` (too many registrations)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OAuthError"
                }
              }
            }
          }
        }
      }
    },
    "/oauth/authorize": {
      "get": {
        "tags": [
          "OAuth & MCP"
        ],
        "summary": "Authorization endpoint (browser): member sign-in, then consent screen",
        "security": [],
        "description": "Not callable from Try-it — open it in a browser. Errors about client_id/redirect_uri render a page; every other error redirects to the registered redirect_uri with `error`, `state` and `iss`.",
        "parameters": [
          {
            "name": "response_type",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "code"
              ]
            }
          },
          {
            "name": "client_id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "redirect_uri",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "code_challenge",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 43,
              "maxLength": 43
            }
          },
          {
            "name": "code_challenge_method",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "S256"
              ]
            }
          },
          {
            "name": "state",
            "in": "query",
            "schema": {
              "type": "string",
              "maxLength": 500
            }
          },
          {
            "name": "scope",
            "in": "query",
            "schema": {
              "type": "string",
              "description": "Space-separated subset of: articles:read chat:write account:read account:write billing:read checkout:write admin"
            }
          },
          {
            "name": "resource",
            "in": "query",
            "schema": {
              "type": "string",
              "example": "https://zuey.me/mcp"
            }
          }
        ],
        "responses": {
          "302": {
            "description": "Redirect to sign-in, consent, or the client redirect_uri"
          },
          "400": {
            "description": "HTML error page"
          }
        }
      }
    },
    "/oauth/token": {
      "post": {
        "tags": [
          "OAuth & MCP"
        ],
        "summary": "Token endpoint: authorization_code (+PKCE) and refresh_token (rotating)",
        "security": [],
        "description": "Public clients send `client_id` only; confidential (DCR) clients use HTTP Basic (`client_secret_basic`) or `client_secret`/`client_id` form fields (`client_secret_post`). Reusing a refresh token or authorization code revokes the whole token family.",
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/TokenRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Tokens",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TokenResponse"
                }
              }
            }
          },
          "400": {
            "description": "RFC 6749 error (`invalid_request`, `invalid_client`, `invalid_grant`, `unsupported_grant_type`, `invalid_scope`, `invalid_target`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OAuthError"
                }
              }
            }
          },
          "401": {
            "description": "RFC 6749 error (`invalid_request`, `invalid_client`, `invalid_grant`, `unsupported_grant_type`, `invalid_scope`, `invalid_target`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OAuthError"
                }
              }
            }
          }
        }
      }
    },
    "/oauth/revoke": {
      "post": {
        "tags": [
          "OAuth & MCP"
        ],
        "summary": "Token revocation (RFC 7009); revokes the token family",
        "security": [],
        "description": "Public clients send `client_id` only; confidential (DCR) clients use HTTP Basic (`client_secret_basic`) or `client_secret`/`client_id` form fields (`client_secret_post`).",
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "required": [
                  "token"
                ],
                "properties": {
                  "token": {
                    "type": "string"
                  },
                  "token_type_hint": {
                    "type": "string"
                  },
                  "client_id": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Revoked (also returned for unknown tokens)"
          },
          "400": {
            "description": "RFC 6749 error (`invalid_request`, `invalid_client`, `invalid_grant`, `unsupported_grant_type`, `invalid_scope`, `invalid_target`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OAuthError"
                }
              }
            }
          },
          "401": {
            "description": "RFC 6749 error (`invalid_request`, `invalid_client`, `invalid_grant`, `unsupported_grant_type`, `invalid_scope`, `invalid_target`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OAuthError"
                }
              }
            }
          }
        }
      }
    },
    "/oauth/connections": {
      "get": {
        "tags": [
          "OAuth & MCP"
        ],
        "summary": "Your connected apps (OAuth consents) — no token material",
        "security": [
          {
            "MemberSession": []
          },
          {
            "BearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Apps",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ConnectedApp"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Not signed in",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/oauth/connections/{id}": {
      "delete": {
        "tags": [
          "OAuth & MCP"
        ],
        "summary": "Disconnect an app: revoke consent and every token (session only)",
        "security": [
          {
            "MemberSession": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Disconnected",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "data": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "`session_required` or `csrf_rejected`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/mcp": {
      "post": {
        "tags": [
          "OAuth & MCP"
        ],
        "summary": "MCP Streamable HTTP endpoint (JSON-RPC 2.0)",
        "security": [
          {
            "McpOAuth": []
          },
          {
            "BearerAuth": []
          }
        ],
        "description": "Methods: `server/discover`, `initialize` (legacy versions), `ping`, `tools/list`, `tools/call`. For 2026-07-28 send `params._meta[\"io.modelcontextprotocol/protocolVersion\"]` and the matching `MCP-Protocol-Version` and `Mcp-Method` headers (`Mcp-Name` for tools/call). 401 responses carry `WWW-Authenticate: Bearer resource_metadata=\"…\"`; 403 with `error=\"insufficient_scope\"` names the scope to request. GET and DELETE answer 405 (no server-initiated stream, stateless server).",
        "parameters": [
          {
            "name": "MCP-Protocol-Version",
            "in": "header",
            "schema": {
              "type": "string",
              "enum": [
                "2026-07-28",
                "2025-11-25",
                "2025-06-18",
                "2025-03-26"
              ]
            }
          },
          {
            "name": "Mcp-Method",
            "in": "header",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Mcp-Name",
            "in": "header",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "jsonrpc",
                  "method"
                ],
                "properties": {
                  "jsonrpc": {
                    "type": "string",
                    "enum": [
                      "2.0"
                    ]
                  },
                  "id": {
                    "type": [
                      "string",
                      "integer"
                    ]
                  },
                  "method": {
                    "type": "string"
                  },
                  "params": {
                    "type": "object"
                  }
                },
                "example": {
                  "jsonrpc": "2.0",
                  "id": 1,
                  "method": "tools/list",
                  "params": {
                    "_meta": {
                      "io.modelcontextprotocol/protocolVersion": "2026-07-28",
                      "io.modelcontextprotocol/clientCapabilities": {}
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "JSON-RPC result or error",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "202": {
            "description": "Notification accepted"
          },
          "400": {
            "description": "Malformed JSON-RPC, header mismatch (-32020) or unsupported protocol version (-32022)"
          },
          "401": {
            "description": "Missing, invalid, expired or wrong-audience token (WWW-Authenticate challenge)"
          },
          "403": {
            "description": "Origin not allowed, or insufficient_scope step-up challenge"
          }
        }
      }
    },
    "/api/v1/notices/active": {
      "get": {
        "tags": [
          "Homepage experience"
        ],
        "summary": "Notices live now for this visitor",
        "description": "Audience is resolved from the member session (all, signed-in members, or one plan). Text falls back to English, then Vietnamese, when the requested locale has no edition.",
        "parameters": [
          {
            "name": "lang",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "en",
                "vi",
                "zh",
                "ko",
                "ja"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Active notices",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "locale": {
                          "type": "string"
                        },
                        "notices": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/PublicNotice"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "`database_unavailable`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/notices": {
      "get": {
        "tags": [
          "Homepage experience"
        ],
        "summary": "Admin: list notices",
        "security": [
          {
            "BearerAuth": []
          },
          {
            "ApiKeyHeader": []
          },
          {
            "MemberSession": []
          }
        ],
        "parameters": [
          {
            "name": "include_expired",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "1"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Notices",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "notices": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Notice"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "`database_unavailable`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Homepage experience"
        ],
        "summary": "Admin: send a visitor notice",
        "security": [
          {
            "BearerAuth": []
          },
          {
            "ApiKeyHeader": []
          },
          {
            "MemberSession": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/NoticeInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "data": {
                      "$ref": "#/components/schemas/Notice"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "`database_unavailable`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/notices/{id}/expire": {
      "post": {
        "tags": [
          "Homepage experience"
        ],
        "summary": "Admin: end a notice now (idempotent)",
        "security": [
          {
            "BearerAuth": []
          },
          {
            "ApiKeyHeader": []
          },
          {
            "MemberSession": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Expired",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "data": {
                      "$ref": "#/components/schemas/Notice"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "`notice_not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "`database_unavailable`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/github/activity": {
      "get": {
        "tags": [
          "Homepage experience"
        ],
        "summary": "Public GitHub events of mrgoonie",
        "description": "Up to 300 public events (GitHub keeps about 90 days). This is NOT a contribution calendar. Cached 15 minutes and ETag-revalidated; when GitHub is rate limited or down the last good snapshot is returned with `source: \"stale\"` and an `error`.",
        "responses": {
          "200": {
            "description": "Activity",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "data": {
                      "$ref": "#/components/schemas/GithubActivity"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "`github_rate_limited` with retry_after_seconds (no cached snapshot yet)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "`github_unavailable` (no cached snapshot yet)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/weather": {
      "get": {
        "tags": [
          "Homepage experience"
        ],
        "summary": "Current weather (Open-Meteo proxy)",
        "description": "Send `lat` and `lon` (rounded server-side to 1 decimal, ~11 km) or `city`. Data by Open-Meteo.com (CC BY 4.0).",
        "parameters": [
          {
            "name": "lat",
            "in": "query",
            "schema": {
              "type": "number",
              "minimum": -90,
              "maximum": 90
            }
          },
          {
            "name": "lon",
            "in": "query",
            "schema": {
              "type": "number",
              "minimum": -180,
              "maximum": 180
            }
          },
          {
            "name": "city",
            "in": "query",
            "schema": {
              "type": "string",
              "minLength": 2,
              "maxLength": 80
            }
          },
          {
            "name": "lang",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "en",
                "vi",
                "zh",
                "ko",
                "ja"
              ]
            },
            "description": "City-name language"
          }
        ],
        "responses": {
          "200": {
            "description": "Weather",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "data": {
                      "$ref": "#/components/schemas/Weather"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "`invalid_query`, `invalid_coordinates` or `invalid_city`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "`city_not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "`weather_unavailable`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/community": {
      "get": {
        "tags": [
          "Homepage experience"
        ],
        "summary": "Your Telegram community status",
        "security": [
          {
            "MemberSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Status",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "data": {
                      "$ref": "#/components/schemas/CommunityStatus"
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "`database_unavailable`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/community/invite": {
      "post": {
        "tags": [
          "Homepage experience"
        ],
        "summary": "Get a one-hour, single-use invite link ($29 plan)",
        "security": [
          {
            "MemberSession": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "chat": {
                    "type": "string",
                    "enum": [
                      "en",
                      "vi"
                    ]
                  }
                },
                "required": [
                  "chat"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Invite (an unexpired unused invite is returned again instead of creating a new one)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "data": {
                      "$ref": "#/components/schemas/CommunityMembership"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`entitlement_required` (community) or `csrf_rejected`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`already_joined`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited` or `telegram_rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "`telegram_error` or `telegram_unreachable`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "`community_unconfigured` (missing lists the env names, never values)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/community/sweep": {
      "post": {
        "tags": [
          "Homepage experience"
        ],
        "summary": "Admin/cron: remove lapsed members, retire stale invites",
        "security": [
          {
            "BearerAuth": []
          },
          {
            "ApiKeyHeader": []
          },
          {
            "MemberSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Sweep result",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "data": {
                      "$ref": "#/components/schemas/CommunitySweep"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Admin role required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "`community_unconfigured` (missing lists the env names, never values)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/community/telegram-webhook": {
      "post": {
        "tags": [
          "Homepage experience"
        ],
        "summary": "Telegram Bot API webhook (chat_member updates)",
        "description": "Register with setWebhook secret_token=TELEGRAM_WEBHOOK_SECRET and allowed_updates [\"chat_member\"]. Authenticated by the X-Telegram-Bot-Api-Secret-Token header.",
        "parameters": [
          {
            "name": "X-Telegram-Bot-Api-Secret-Token",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Processed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "outcome": {
                          "type": "string",
                          "enum": [
                            "joined",
                            "joined_then_removed",
                            "left",
                            "ignored"
                          ]
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "`invalid_webhook_secret`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "`community_unconfigured` (missing lists the env names, never values)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "tags": [
    {
      "name": "Zuey Reads",
      "description": "Curated reading list synced from the AnyMD library (tag `zuey-reads`) with AI-generated summaries. Summaries may be inaccurate; every item links to the original."
    },
    {
      "name": "AI Workflows",
      "description": "Zuey's AI workflows. Writes only touch the draft and require expected_revision (optimistic concurrency). Publishing copies the draft into a public snapshot, requires confirm: true and is blocked when secrets/PII are detected."
    },
    {
      "name": "Business booking",
      "description": "Zuey for Business: one-off $1,999 consultation (90-minute Google Meet). Slots are held for 15 minutes and confirmed only by a verified payment webhook."
    },
    {
      "name": "Articles",
      "description": "Block-based articles (rich blocks, layouts, paywall) and surveys"
    },
    {
      "name": "Knowledge taxonomy",
      "description": "Knowledge search (authorization before ranking), public discovery facets, and admin taxonomy: topic tags, categories, typed labels with evidence, AI label proposals with human review, resumable audit jobs, and the audit log."
    },
    {
      "name": "Members & account",
      "description": "Member sign-in (magic link, Google, GitHub), profile, sessions, activity, export/delete and personal API keys. Browser calls use the `zuey_member` HttpOnly cookie; unsafe methods must be same-origin (`Origin` header) or are rejected with `csrf_rejected`. Programmatic calls send `Authorization: Bearer zk_…` personal keys limited to scopes: articles:read, chat:write, account:read, account:write, billing:read, checkout:write. There is no admin scope. Key management, email change, session revocation and account deletion require the browser session."
    },
    {
      "name": "Membership billing",
      "description": "Monthly plans: Knowledges $9 (read_full), Zuey AI $9 (ai_chat), Kết hợp $19 (read_full + ai_chat), Cộng đồng $29 (+ community). SePay bank transfer prepays 1, 3, 6 or 12 months at USD × USD_VND_RATE rounded up to 1,000 VND per month (no discounts). When Dodo Payments is configured, a plan can also be a monthly USD card subscription that renews automatically and is cancellable from the account page. Plans are activated only by a verified SePay or Dodo webhook (or admin reconciliation), never by a checkout redirect."
    },
    {
      "name": "Zuey AI",
      "description": "Grounded chat with Zuey AI (Dewee gateway), private per-member sessions, interactive artifacts and the sandbox fetch proxy. Requires the ai_chat entitlement (Zuey AI, Kết hợp, Cộng đồng plans)."
    },
    {
      "name": "OAuth & MCP",
      "description": "OAuth 2.1 authorization server for MCP clients (authorization code + PKCE S256 only, rotating refresh tokens, RFC 8707 resource indicators, RFC 9207 `iss`). Clients are identified by a Client ID Metadata Document URL or registered via RFC 7591 Dynamic Client Registration. Access tokens (`zoa_…`) are audience-bound to `<origin>/mcp` and only accepted by the `/mcp` endpoint in the `Authorization` header. `/mcp` speaks MCP Streamable HTTP (JSON-RPC 2.0, JSON responses, stateless). Supported protocol versions: 2026-07-28, 2025-11-25, 2025-06-18, 2025-03-26. `/mcp` also accepts personal `zk_` keys and admin API keys; tools are filtered per caller and scope."
    },
    {
      "name": "Homepage experience",
      "description": "Visitor notices from Duy (shown in the mascot bubble), Telegram community access for the $29 plan, public GitHub activity of mrgoonie and an Open-Meteo weather proxy. Weather uses coarse coordinates (1 decimal) or a city name; nothing about the caller is stored server-side."
    }
  ]
}