{
  "openapi": "3.1.0",
  "info": {
    "title": "Citely API",
    "version": "1.0.0",
    "description": "Discover and read expert Web3 legal / compliance / security risk reports. Listing is free; full text is behind an x402 paywall (USDC on Base Sepolia). See /SKILL.md for the agent reading flow.",
    "license": { "name": "Authors keep 100% — 0% platform fee" }
  },
  "servers": [{ "url": "https://citely-nine.vercel.app", "description": "Base Sepolia testnet deployment" }],
  "paths": {
    "/api/v1/articles": {
      "get": {
        "operationId": "listArticles",
        "summary": "List the catalog (free, metadata only)",
        "description": "Returns published reports newest-first, metadata only (no body). Combine the optional filters with AND.",
        "parameters": [
          { "name": "q", "in": "query", "required": false, "schema": { "type": "string" }, "description": "Free-text over title / summary / author / org / tags." },
          { "name": "tag", "in": "query", "required": false, "schema": { "type": "string" }, "description": "Substring match on any tag." },
          { "name": "author", "in": "query", "required": false, "schema": { "type": "string" }, "description": "Substring match on author name or org." }
        ],
        "responses": {
          "200": {
            "description": "Catalog page",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["count", "articles"],
                  "properties": {
                    "count": { "type": "integer" },
                    "articles": { "type": "array", "items": { "$ref": "#/components/schemas/CatalogItem" } }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/authors": {
      "get": {
        "operationId": "listAuthors",
        "summary": "List authors (free, derived from the attestation index)",
        "description": "Returns the published authors, grouped by name (most articles first). Derived from the same on-chain attestation index as /api/v1/articles, so it stays in sync. Never exposes the article body or the on-chain payout address.",
        "responses": {
          "200": {
            "description": "Authors",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["count", "authors"],
                  "properties": {
                    "count": { "type": "integer" },
                    "authors": { "type": "array", "items": { "$ref": "#/components/schemas/AuthorSummary" } }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/articles/{slug}": {
      "get": {
        "operationId": "readArticle",
        "summary": "Read a report (x402 paid)",
        "description": "First call returns 402 with payment requirements in the `payment-required` header (Base64 JSON: scheme exact, network eip155:84532, USDC asset, payTo, amount). Pay with an agent wallet (e.g. Cobo Agentic Wallet, protocol x402) and retry to get 200. The paying wallet must hold test USDC and must NOT be the article's author/payTo (self-send is rejected).",
        "parameters": [
          { "name": "slug", "in": "path", "required": true, "schema": { "type": "string", "pattern": "^[a-z0-9-]{1,80}$" }, "example": "yaoqian-crypto-liability" }
        ],
        "responses": {
          "200": {
            "description": "Paid: full report + companion + on-chain citation",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ArticlePaid" } } }
          },
          "402": {
            "description": "Payment Required — pay the quoted USDC amount and retry.",
            "headers": {
              "payment-required": {
                "description": "Base64-encoded JSON payment requirements (the x402 `accepts` block). Hand this to your wallet's x402 payment call.",
                "schema": { "type": "string" }
              }
            },
            "content": { "application/json": { "schema": { "type": "object" } } }
          },
          "404": {
            "description": "Unknown or unpublished slug.",
            "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" } } } } }
          }
        }
      }
    },
    "/api/v1/articles/{slug}/entitlement": {
      "post": {
        "summary": "Re-unlock a previously paid article by signing a wallet message (no payment).",
        "description": "Free. A returning reader who already paid proves ownership with a wallet signature; the server recovers the address, checks the payment log, and returns the same full article JSON as the paid 200. Human-reading lane.",
        "parameters": [
          { "name": "slug", "in": "path", "required": true, "schema": { "type": "string" } }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["message", "signature"],
                "properties": {
                  "message": { "type": "string", "description": "Output of buildEntitlementMessage: lines 文章/地址/时间(ISO)/nonce." },
                  "signature": { "type": "string", "description": "personal_sign of message by the paying wallet." }
                }
              }
            }
          }
        },
        "responses": {
          "200": { "description": "Full article JSON (slug, title, content, companion, starterPrompts, citation)." },
          "400": { "description": "Missing/invalid body." },
          "403": { "description": "bad_signature / slug_mismatch / expired / not_paid." },
          "404": { "description": "Unknown or unpublished slug." }
        }
      }
    },
    "/api/v1/sol/articles/{slug}": {
      "get": {
        "operationId": "readArticleSolana",
        "summary": "Read a report (x402 paid, Solana devnet)",
        "description": "Parallel Solana devnet lane — same paid content as /api/v1/articles/{slug}, settled in USDC-SPL on Solana devnet via the PayAI facilitator (x402 v2). First call returns 402 with the requirements in the `PAYMENT-REQUIRED` response header (Base64 JSON) and body. The payer signs an SPL transfer of `amount` of the USDC mint to `payTo` (gas is covered by the facilitator's `accepts[0].extra.feePayer`), then retries with the signed payload in the `PAYMENT-SIGNATURE` header to get 200. Fixed params: network solana-devnet (CAIP-2 solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1), asset USDC-SPL 4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU (6 decimals), payTo 6bMe95k9itoYTvef4mE9rCDw1K11BgzMmZxgvjjkoH9s. The payTo must already have a USDC ATA. Price reuses the article's on-chain priceUSDC.",
        "parameters": [
          { "name": "slug", "in": "path", "required": true, "schema": { "type": "string", "pattern": "^[a-z0-9-]{1,80}$" }, "example": "yaoqian-crypto-liability" }
        ],
        "responses": {
          "200": {
            "description": "Paid: full report + companion + on-chain citation (identical body to the Base lane).",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ArticlePaid" } } }
          },
          "402": {
            "description": "Payment Required — sign the USDC-SPL transfer per the requirements and retry with the PAYMENT-SIGNATURE header.",
            "headers": {
              "PAYMENT-REQUIRED": {
                "description": "Base64-encoded JSON payment requirements (x402 v2). Decode to read accepts[0]: { scheme: \"exact\", network, amount, payTo, asset, maxTimeoutSeconds, extra.feePayer }. Its presence is how the client selects the v2 protocol.",
                "schema": { "type": "string" }
              }
            },
            "content": { "application/json": { "schema": { "type": "object" } } }
          },
          "404": {
            "description": "Unknown or unpublished slug.",
            "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" } } } } }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "CatalogItem": {
        "type": "object",
        "required": ["slug", "title", "summary", "author", "tags", "price", "priceUSDC", "publishedAt", "attestationUID", "read"],
        "properties": {
          "slug": { "type": "string" },
          "title": { "type": "string" },
          "summary": { "type": "string" },
          "author": { "type": "string" },
          "authorOrg": { "type": "string" },
          "tags": { "type": "array", "items": { "type": "string" } },
          "price": { "type": "string", "example": "$0.30" },
          "priceUSDC": { "type": "string", "description": "Amount in USDC base units (6 decimals).", "example": "300000" },
          "publishedAt": { "type": "string" },
          "attestationUID": { "type": "string", "description": "EAS attestation UID.", "example": "0x…" },
          "read": { "type": "string", "description": "Path to fetch this article (prepend the origin and GET it).", "example": "/api/v1/articles/yaoqian-crypto-liability" }
        }
      },
      "AuthorSummary": {
        "type": "object",
        "required": ["name", "articleCount", "tags", "articles"],
        "properties": {
          "name": { "type": "string" },
          "org": { "type": "string" },
          "articleCount": { "type": "integer" },
          "tags": { "type": "array", "items": { "type": "string" }, "description": "Deduped union of tags across this author's articles." },
          "articles": {
            "type": "array",
            "items": {
              "type": "object",
              "required": ["slug", "title", "read"],
              "properties": {
                "slug": { "type": "string" },
                "title": { "type": "string" },
                "read": { "type": "string", "description": "Path to fetch this article (prepend the origin and GET it).", "example": "/api/v1/articles/yaoqian-crypto-liability" }
              }
            }
          }
        }
      },
      "ArticlePaid": {
        "type": "object",
        "required": ["slug", "title", "content", "companion", "starterPrompts", "citation"],
        "properties": {
          "slug": { "type": "string" },
          "title": { "type": "string" },
          "content": { "type": "string", "description": "Full article markdown." },
          "companion": { "type": "string", "description": "Paid 〔A〕 zone: 原文 / 术语表 (glossary) / 误区表 (misconceptions)." },
          "starterPrompts": {
            "type": "array",
            "description": "Reader 〔C〕 starter prompts an agent can run verbatim.",
            "items": {
              "type": "object",
              "required": ["title", "prompt"],
              "properties": { "title": { "type": "string" }, "prompt": { "type": "string" } }
            }
          },
          "citation": { "$ref": "#/components/schemas/Citation" }
        }
      },
      "Citation": {
        "type": "object",
        "required": ["author", "attestationUID", "publishedAt"],
        "properties": {
          "author": { "type": "string" },
          "attestationUID": { "type": "string" },
          "publishedAt": { "type": "string" }
        }
      }
    }
  }
}
