openapi: 3.1.0
info:
  title: Black Cat VPN API
  version: "1.0"
  description: |
    Programmatic, account-free VPN provisioning for developers and AI agents.
    Pay on-chain (Solana SOL, Zcash ZEC, or X1 XNT) — no signup, no email, no card, no KYC.

    Flow:
      1. GET /plans                → catalog + live SOL/ZEC/XNT rate
      2. POST /invoices            → an invoice: exact crypto amount + recipient
      3. pay on-chain              → send the EXACT `amountCrypto` to `recipient`
                                     (or open `payUri`; SOL/X1 are matched by exact amount)
      4. GET /invoices/{id}        → poll until status == PAID
      5. use `subscriptionUrl`     → import into a VPN client (VLESS-Reality / Hysteria2)

    There is no hosted proxy — this provisions a real per-user VPN tunnel. Identity is
    the paying wallet; access is bound to the subscription and device-capped (fair use).
servers:
  - url: https://blackcat.is/api
paths:
  /plans:
    get:
      summary: Catalog + live rates
      description: Tiers, durations, active promo, and the current SOL/ZEC/XNT USD rate used to price invoices.
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  tiers: { type: array, items: { type: object } }
                  durations: { type: array, items: { type: object } }
                  intro:
                    type: [object, "null"]
                    description: Active promo (e.g. $1 first month), or null.
                  solUsd: { type: number, example: 79.34 }
                  zecUsd: { type: number, example: 442.1 }
                  x1Usd: { type: number, example: 0.5, description: "XNT (X1 native token) USD rate." }
  /invoices:
    post:
      summary: Create an invoice
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [planId, username]
              properties:
                planId:
                  type: string
                  description: e.g. "standard-1m", "standard-12m", "trial-30d" (promo).
                  example: standard-1m
                username:
                  type: string
                  pattern: "^[a-zA-Z0-9_-]{3,64}$"
                  description: Opaque account label you choose (no PII). This becomes the VPN identity.
                  example: agent-7f3a
                currency:
                  type: string
                  enum: [sol, zec, x1]
                  default: sol
                  description: "x1 = XNT, the native token of the X1 chain (an SVM fork); matched by exact amount like SOL."
                src:
                  type: string
                  description: Optional acquisition tag (attribution).
                  example: my-agent
      responses:
        "201":
          description: Invoice created (PENDING)
          content:
            application/json:
              schema:
                type: object
                properties:
                  invoiceId: { type: string, format: uuid }
                  plan: { type: string }
                  currency: { type: string, enum: [sol, zec, x1] }
                  amountCrypto:
                    type: string
                    description: EXACT amount to send, in the invoice currency (string, high precision).
                    example: "0.039442"
                  priceUsd: { type: string, example: "3" }
                  recipient:
                    type: string
                    description: Address to pay. SOL/X1 are matched by exact amount; ZEC uses a unique per-invoice t-address.
                  reference:
                    type: string
                    description: Solana reference pubkey (for on-chain matching when not in single-wallet mode).
                  payUri:
                    type: string
                    description: "solana:… or zcash:… payment URI (open in a wallet / build a tx from it)."
                  expiresAt: { type: string, format: date-time }
        "400":
          description: unknown planId / bad username
  /invoices/{id}:
    get:
      summary: Poll invoice status
      description: Poll until `status` is PAID, then read `subscriptionUrl`.
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: string, format: uuid }
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  invoiceId: { type: string }
                  status:
                    type: string
                    enum: [PENDING, PAID, EXPIRED]
                  currency: { type: string }
                  amountCrypto: { type: string }
                  txSignature: { type: [string, "null"] }
                  subscriptionUrl:
                    type: [string, "null"]
                    description: Present once PAID — import into a VPN client to get all nodes/protocols.
                  expiresAt: { type: string, format: date-time }
        "404":
          description: not found
