{
  "openapi": "3.1.0",
  "info": {
    "title": "pr402 Facilitator API",
    "version": "1.0.0",
    "description": "x402 v2 facilitator for Solana: discovery, optional unsigned tx build, verify, and settle.\n\n**Launch phase:** This service and its documentation are provided on an **experimental** basis \u2014 **use at your own risk**. Protocol, fees, allowlists, and availability may change.\n\n**Official deployments:** **Recommended** (concise hostnames, easier to remember) \u2014 **Production** (Solana Mainnet) `https://ipay.sh`, **Preview** (Solana Devnet) `https://preview.ipay.sh`. **Also available** (same service; **not** deprecated) \u2014 `https://agent.pay402.me`, `https://preview.agent.pay402.me`. Select the matching entry under **`servers`** below. Always confirm **`solanaNetwork`** and **`chainId`** via **`GET /api/v1/facilitator/health`** or **`GET /api/v1/facilitator/capabilities`** on the host you call.\n\n**RPC URLs:** Integrators needing the deployment\u2019s wallet RPC should read **`solanaWalletRpcUrl`** from **`GET /health`** at runtime. **Do not** copy RPC URLs or API keys from static markdown into your app; they are environment-specific and may rotate.\n\n**Who uses this API:**\n- **Seller / resource provider agents** \u2014 onboard UniversalSettle vaults, per-asset provisioning (`POST /onboard/provision`), preview PDAs, optional signed registry (`/onboard/challenge` + `POST /onboard`), publish correct **`payTo`** in 402 (`agent-integration.md` \u2192 Seller agents).\n- **Buyer / payer agents** \u2014 match `accepts[]`, build unsigned proofs, sign, `POST /verify` + `POST /settle` (`agent-integration.md` \u2192 Buyer agents).\n\n**`payTo` (pr402):** Settlement goes through **UniversalSettle** (exact) and/or **SLA-Escrow**. `payTo` must be the **on-chain PDA** for that scheme (vault rail / escrow account)\u2014not a bare seller wallet for proofs. The **`POST /build-sla-escrow-payment-tx`** response **`verifyBodyTemplate`** carries the canonical Escrow PDA in `payTo`.\n\n**SLA-Escrow `FundPayment.seller`:** Build and verify require `paymentRequirements.accepted.extra` (**or** mirrored `extra` on the proof) to include **`beneficiary`** or **`merchantWallet`** (base58). That pubkey is the on-chain **seller** encoded in `FundPayment` and must **not** equal the escrow PDA.\n\n**Who pays Solana transaction fees:** The x402 v2 specification centers facilitator-paid **landing** for the standard **`exact`** rail. **SLA-Escrow** is a pr402 extension: **`facilitatorPaysTransactionFees` defaults to `false`**, so the **buyer** pays network fees unless you opt in to facilitator sponsorship (`true`, two-signer flow like `build-exact-payment-tx`). **FundPayment** token principal always comes from the buyer\u2019s funded ATA; only the **SOL fee payer** toggles. **Operator gate:** even when a client sends `facilitatorPaysTransactionFees: true`, the HTTP build is **rejected (400)** unless the deployment sets **`PR402_SLA_ESCROW_ALLOW_FACILITATOR_FEE_SPONSORSHIP`** to `true`/`1`/`yes`.\n\n**Scheme strings (agents):** HTTP `402 accepts[]` and builder **requests** may use the short wire form (`exact`, `sla-escrow`) or namespaced aliases (`v2:solana:exact`, `v2:solana:sla-escrow`). **`verifyBodyTemplate`** from **`build-*-payment-tx`** always uses the **wire** scheme in `paymentPayload.accepted` and `paymentRequirements`. **`POST /verify`** and **`POST /settle`** accept either form for `x402Version: 2` and normalize aliases before verification. \n\n**Typical buyer flow:** (1) `GET /supported` or `GET /capabilities`. (2) `POST /build-exact-payment-tx` or `POST /build-sla-escrow-payment-tx`. (3) Sign locally; facilitator may fill fee-payer at settle when applicable. (4) Swap signed tx into `verifyBodyTemplate`; optional `correlationId`. (5) `POST /verify` then `POST /settle` with the **same** body.\n\n**Examples:** Schema **`X402V2VerifySettleBody`** includes a worked **`example`** (exact rail). **`BuildExactPaymentTxRequest`** includes an **`example`** request for the builder.\n\n**CORS:** `OPTIONS` on `/api/v1/facilitator/*` returns 204; JSON uses `Access-Control-Allow-Origin: *`.\n\nIntegration guide: `/agent-integration.md` (includes **Golden path** section for standard integration).\n\n**Agent manifest (machine-readable):** `/agent-payTo-semantics.json` \u2014 `payTo` resolution rules and `paymentMintAllowlist` metadata; the same path is advertised as `agentManifest.payToSemantics` on `GET /capabilities`.\n\n**SLA-Escrow reference oracle (`oracle-qa`):** When `features.slaEscrow` is true, `GET /capabilities` may include `slaEscrowOracleQa` (profile id `x402/oracle-qa/api-quality/v1`, normative spec URL, optional `defaultOperatorPubkey`, optional evidence-registry note). That pubkey is only a **hint**\u2014`FundPayment.oracleAuthority` must match the operator you intend to resolve delivery.",
    "license": {
      "name": "Apache-2.0"
    }
  },
  "tags": [
    {
      "name": "x402 Core",
      "description": "Discovery and settlement core: **`/health`**, **`/capabilities`**, **`/supported`**, **`/verify`**, **`/settle`**, **`/discovery`**. Called by **buyer agents** (and sometimes sellers for discovery). Align requests with the **`servers`** entry you chose (Mainnet production vs Devnet preview)."
    },
    {
      "name": "pr402 Extension: Tx Build",
      "description": "**Buyer agents:** **`/build-exact-payment-tx`**, **`/build-sla-escrow-payment-tx`** \u2014 unsigned `VersionedTransaction` + `verifyBodyTemplate`. Eliminates Solana SDK work in the browser or agent runtime."
    },
    {
      "name": "pr402 Extension: Seller Onboarding",
      "description": "**Seller / resource providers:** **`/onboard/*`**, **`/onboard/provision`**, **`/upgrade`**. Prepare UniversalSettle / escrow PDAs and optional Postgres registry. Integration guide: **`/onboarding_guide.md`** on the same host."
    },
    {
      "name": "pr402 Extension: Operations",
      "description": "**Operators:** sweep, vault snapshot, cron helpers. Not part of the buyer x402 payment path."
    }
  ],
  "externalDocs": {
    "description": "x402 HTTP payment standard (v2)",
    "url": "https://github.com/coinbase/x402/blob/main/specs/x402-specification-v2.md"
  },
  "servers": [
    {
      "url": "https://ipay.sh",
      "description": "Production (Solana Mainnet). Recommended \u2014 concise hostname. Same stack as agent.pay402.me. Experimental \u2014 use at your own risk. No trailing slash."
    },
    {
      "url": "https://preview.ipay.sh",
      "description": "Preview (Solana Devnet). Recommended \u2014 concise hostname. Same stack as preview.agent.pay402.me. Experimental. For integration testing and devnet programs."
    },
    {
      "url": "https://agent.pay402.me",
      "description": "Production (Solana Mainnet). Also available \u2014 not deprecated. Experimental \u2014 use at your own risk. No trailing slash in base URL."
    },
    {
      "url": "https://preview.agent.pay402.me",
      "description": "Preview (Solana Devnet). Also available \u2014 not deprecated. Experimental \u2014 use at your own risk. For integration testing and devnet programs."
    }
  ],
  "paths": {
    "/api/v1/facilitator/health": {
      "get": {
        "tags": [
          "x402 Core"
        ],
        "summary": "Health check",
        "description": "Liveness and dependency snapshot: `status`, `schemaVersion`, `database` (connected | disabled | error), `solanaRpc`, `solanaSlot`, `environment`, `solanaNetwork`, `solanaWalletRpcUrl` (deployment-specific wallet RPC \u2014 may include credentials; **read at runtime**, do not hardcode from docs). Not the same payload as `/supported`.",
        "operationId": "facilitatorHealth",
        "responses": {
          "200": {
            "description": "Health snapshot: `status`, `schemaVersion`, `database`, `solanaRpc`, `solanaSlot`, `environment`, `solanaNetwork`, `solanaWalletRpcUrl` (camelCase JSON). Distinct from `GET /supported`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "500": {
            "description": "Facilitator initialization or scheme error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JsonError"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/facilitator/supported": {
      "get": {
        "tags": [
          "x402 Core"
        ],
        "summary": "Supported payment kinds (x402 discovery)",
        "description": "Returns active schemes (`kinds[]`), extensions, and signer hints for the configured chain. Scheme names are canonical short form: `exact` for UniversalSettle, `sla-escrow` for SLA-Escrow.",
        "operationId": "facilitatorSupported",
        "responses": {
          "200": {
            "description": "SupportedResponse",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JsonError"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/facilitator/capabilities": {
      "get": {
        "tags": [
          "x402 Core"
        ],
        "summary": "Discovery bundle",
        "description": "JSON with `schemaVersion`, `x402Version`, `chainId`, `feePayer`, embedded `supported`, feature flags, `httpEndpoints`, `agentManifest` (static doc paths), and x402 spec link. When **`features.slaEscrow`** is true, **`slaEscrowOracleQa`** may be present: pointers to the **`x402/oracle-qa/api-quality/v1`** normative spec, optional deployment-advertised **`defaultOperatorPubkey`** (maps to `FundPayment.oracleAuthority` when using that operator), and an optional **`evidenceRegistryNote`** for hash-bound artifact hosting expectations.",
        "operationId": "facilitatorCapabilities",
        "responses": {
          "200": {
            "description": "Capabilities object",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JsonError"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/facilitator/verify": {
      "post": {
        "tags": [
          "x402 Core"
        ],
        "summary": "Verify payment payload",
        "description": "Validates the x402 v2 proof against `paymentRequirements`. Optional `X-Correlation-ID` or body `correlationId` for DB audit linking. For **`x402Version: 2`**, `paymentPayload.accepted.scheme` and `paymentRequirements.scheme` may use the wire values **`exact`** / **`sla-escrow`** or the handler aliases **`v2:solana:exact`** / **`v2:solana:sla-escrow`**; the facilitator normalizes aliases to wire form before typed verification.",
        "operationId": "facilitatorVerify",
        "parameters": [
          {
            "name": "X-Correlation-ID",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/X402V2VerifySettleBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Verify success (`valid`, `payer`, optional `correlationId`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VerifyResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request or verification failure",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FacilitatorErrorBody"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/facilitator/settle": {
      "post": {
        "tags": [
          "x402 Core"
        ],
        "summary": "Settle payment",
        "description": "Same JSON shape as `/verify`. Submits or confirms on-chain settlement (scheme-specific). Reuse the **same** body and correlation id as verify. For v2 bodies, handler scheme aliases (`v2:solana:exact`, `v2:solana:sla-escrow`) are normalized to wire `exact` / `sla-escrow` like `/verify`. Idempotent: if the transaction is already on-chain, returns success. Rebuild unsigned tx (`retry build` error) if signing or retry is delayed and blockhash expires.",
        "operationId": "facilitatorSettle",
        "parameters": [
          {
            "name": "X-Correlation-ID",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/X402V2VerifySettleBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Settle success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SettleResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request or settlement failure",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FacilitatorErrorBody"
                }
              }
            }
          },
          "502": {
            "description": "Upstream / build / RPC failure (some routes)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FacilitatorErrorBody"
                }
              }
            }
          },
          "500": {
            "description": "Internal error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FacilitatorErrorBody"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/facilitator/build-exact-payment-tx": {
      "post": {
        "tags": [
          "pr402 Extension: Tx Build"
        ],
        "summary": "Build unsigned exact (UniversalSettle) payment tx",
        "description": "Returns base64 bincode `VersionedTransaction` (unsigned) and `verifyBodyTemplate`. Payer signs token authority; facilitator is fee payer at `/settle` when `feePayer` in extra matches deployment. **Request** `accepted.scheme` may be `exact` or `v2:solana:exact`. **`verifyBodyTemplate`** always uses wire **`exact`** in both `paymentPayload.accepted.scheme` and `paymentRequirements.scheme` (aliases are normalized).",
        "operationId": "buildExactPaymentTx",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BuildExactPaymentTxRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Unsigned tx + template",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BuildPaymentTxResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request or unsupported mint",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FacilitatorErrorBody"
                }
              }
            }
          },
          "501": {
            "description": "Not implemented",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FacilitatorErrorBody"
                }
              }
            }
          },
          "502": {
            "description": "RPC error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FacilitatorErrorBody"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/facilitator/build-sla-escrow-payment-tx": {
      "post": {
        "tags": [
          "pr402 Extension: Tx Build"
        ],
        "summary": "Build unsigned SLA-Escrow FundPayment tx",
        "description": "**Default `facilitatorPaysTransactionFees: false`:** buyer pays Solana network fees (single-signer shell). This extension does not assume the facilitator subsidizes gas on every payment (unlike the x402-documented **`exact`** rail). **`facilitatorPaysTransactionFees: true`** requires the operator to set **`PR402_SLA_ESCROW_ALLOW_FACILITATOR_FEE_SPONSORSHIP`** on the deployment; otherwise the request returns **400**. When allowed, that mode uses facilitator fee payer + partial buyer sign (two-signer pattern as `build-exact-payment-tx`). **`accepted.extra`** must include **`beneficiary`** or **`merchantWallet`**. Response `verifyBodyTemplate` sets **`payTo`** to the canonical **Escrow PDA**. SPL: legacy Token and Token-2022 **plain** mints only (82-byte mint). **Request** `accepted.scheme` may be `sla-escrow` or `v2:solana:sla-escrow`. **`verifyBodyTemplate`** always uses wire **`sla-escrow`** in both `paymentPayload.accepted.scheme` and `paymentRequirements.scheme`.",
        "operationId": "buildSlaEscrowPaymentTx",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BuildSlaEscrowPaymentTxRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Unsigned tx + template + paymentUid",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BuildPaymentTxResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FacilitatorErrorBody"
                }
              }
            }
          },
          "501": {
            "description": "Not implemented",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FacilitatorErrorBody"
                }
              }
            }
          },
          "502": {
            "description": "RPC error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FacilitatorErrorBody"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/facilitator/oracle/build-confirm": {
      "post": {
        "tags": [
          "pr402 Extension: Tx Build"
        ],
        "summary": "Build unsigned SLA-Escrow ConfirmOracle transaction",
        "description": "Returns base64-bincode **unsigned** `VersionedTransaction` for the **`oracleAuthority`** key to sign and land on-chain (`ConfirmOracle`). Requires SLA-Escrow (`ESCROW_PROGRAM_ID`) on the deployment. Uses the same build-endpoint rate limit as **`/build-*-payment-tx`** (returns **429** when exceeded).",
        "operationId": "buildOracleConfirmTx",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BuildOracleConfirmTxRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Unsigned oracle confirm transaction + PDAs",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BuildOracleConfirmTxResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FacilitatorErrorBody"
                }
              }
            }
          },
          "429": {
            "description": "Build endpoint rate limit (see Retry-After)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FacilitatorErrorBody"
                }
              }
            }
          },
          "501": {
            "description": "SLA-Escrow not configured",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FacilitatorErrorBody"
                }
              }
            }
          },
          "502": {
            "description": "RPC error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FacilitatorErrorBody"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/facilitator/onboard/challenge": {
      "get": {
        "tags": [
          "pr402 Extension: Seller Onboarding"
        ],
        "summary": "Onboard signing challenge",
        "description": "Returns a message to sign for proof-of-control (wallet ownership). Required for `POST /onboard` (DB registration). Requires `PR402_ONBOARD_HMAC_SECRET` on server.",
        "operationId": "facilitatorOnboardChallenge",
        "parameters": [
          {
            "name": "wallet",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "description": "Seller wallet pubkey (base58)"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Challenge payload",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OnboardChallengeResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request"
          },
          "503": {
            "description": "Onboard not configured"
          }
        }
      }
    },
    "/api/v1/facilitator/onboard/provision": {
      "post": {
        "tags": [
          "pr402 Extension: Seller Onboarding"
        ],
        "summary": "Build seller provisioning transaction (per asset)",
        "description": "Returns an unsigned base64-bincode `VersionedTransaction` to provision the UniversalSettle SplitVault (if needed) and the payment surface for the requested **asset**: native SOL uses the virtual mint `11111111111111111111111111111111`; SPL assets get an idempotent vault ATA create when missing. **Agent-oriented default:** callers often start with **`USDC`** (stablecoin settlements). **Idempotent:** repeat calls for the same `(wallet, asset)` return `alreadyProvisioned: true` and omit `transaction` when no on-chain work remains. **Aliases:** `SOL`, `USDC`, `USDT` resolve to cluster-appropriate mints (on devnet, `USDT` requires an explicit mint). Mint must pass `PR402_ALLOWED_PAYMENT_MINTS` when configured. **Note:** `WSOL` is intentionally not a friendly alias on this facilitator — see /onboarding_guide for the rationale; use `SOL` or `USDC` instead.",
        "operationId": "facilitatorOnboardProvision",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OnboardProvisionRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Provisioning instructions / optional unsigned transaction",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SellerProvisionTxResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid wallet, asset, allowlist, or malformed body"
          },
          "429": {
            "description": "Build endpoint rate limit (see Retry-After)"
          },
          "500": {
            "description": "Error"
          }
        }
      }
    },
    "/api/v1/facilitator/onboard": {
      "get": {
        "tags": [
          "pr402 Extension: Seller Onboarding"
        ],
        "summary": "Onboard preview \u2014 all schemes (read-only)",
        "description": "Returns vault/sol_storage preview for **all** registered schemes (exact + sla-escrow if configured). No DB writes. For lightweight single-scheme lookup, prefer `GET /discovery`. For DB-persisted registration, use the challenge \u2192 POST flow.",
        "operationId": "facilitatorOnboardPreview",
        "parameters": [
          {
            "name": "wallet",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OnboardResponse",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OnboardResponse"
                }
              }
            }
          },
          "400": {
            "description": "Missing wallet"
          },
          "500": {
            "description": "Error"
          }
        }
      },
      "post": {
        "tags": [
          "pr402 Extension: Seller Onboarding"
        ],
        "summary": "Submit signed onboard proof (DB registration)",
        "description": "Body: wallet, message, signature (from `/onboard/challenge` flow); optional **`asset`** (default **USDC**) selects the single settlement rail stored in `resource_providers` for that wallet. **Policy:** one payment asset per merchant wallet; use a different wallet for another coin. Verifies proof-of-control and persists vault PDAs when `DATABASE_URL` is set. This is the full registration step \u2014 unlike `GET /onboard` (preview) or `GET /discovery` (lightweight lookup), this writes to the database.",
        "operationId": "facilitatorOnboardSubmit",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OnboardSubmitBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OnboardResponse",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OnboardResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid JSON or body"
          },
          "401": {
            "description": "Signature verification failed"
          },
          "503": {
            "description": "Onboard secret not configured"
          },
          "500": {
            "description": "Error"
          }
        }
      }
    },
    "/api/v1/facilitator/onboard/retire": {
      "post": {
        "tags": [
          "pr402 Extension: Seller Onboarding"
        ],
        "summary": "Opt-out / retire a seller registration",
        "description": "Off-chain retirement. Same HMAC challenge + wallet-signed message as `POST /onboard`; sets `retired_at` / `inactive` / `listing_opt_in=false` so the wallet drops out of the public directory and future settle attempts warn. No on-chain write — the SplitVault remains usable; existing payments still settle. Use this before rotating to a new seller wallet.",
        "operationId": "facilitatorOnboardRetire",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OnboardRetireBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Retirement result",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OnboardRetireResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid body"
          },
          "401": {
            "description": "Signature verification failed"
          },
          "503": {
            "description": "Registry disabled (DATABASE_URL unset) or HMAC secret missing"
          }
        }
      }
    },
    "/api/v1/facilitator/providers": {
      "get": {
        "tags": [
          "pr402 Extension: Seller Onboarding"
        ],
        "summary": "Public seller directory (paged)",
        "description": "Lists verified, unretired sellers that have opted into public visibility (`listing_opt_in = true`). Each row contains only seller-declared metadata (`serviceUrl`, `displayName`, `description`, `tags`, `serviceMetadata`) plus the settlement rail pubkeys required to construct a 402 line. Pagination uses `cursor` (RFC3339 `updated_at`; rows returned are `updated_at < cursor`). Response includes a `notice` string making clear the facilitator only verifies wallet control — it does not vet the advertised service.",
        "operationId": "facilitatorProvidersList",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "description": "RFC3339 timestamp; server returns rows with updated_at < cursor"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Public directory page",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicProvidersPage"
                }
              }
            }
          },
          "503": {
            "description": "Registry disabled (DATABASE_URL unset)"
          }
        }
      }
    },
    "/api/v1/facilitator/providers/{wallet}": {
      "get": {
        "tags": [
          "pr402 Extension: Seller Onboarding"
        ],
        "summary": "Public seller directory \u2014 single-wallet lookup",
        "description": "Returns a single listing for the given wallet. Same visibility filters as the list endpoint. 404 when no public listing exists.",
        "operationId": "facilitatorProviderGet",
        "parameters": [
          {
            "name": "wallet",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "Seller wallet pubkey (base58)"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Public directory entry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicProviderSingle"
                }
              }
            }
          },
          "404": {
            "description": "No public listing for this wallet"
          },
          "503": {
            "description": "Registry disabled (DATABASE_URL unset)"
          }
        }
      }
    },
    "/api/v1/facilitator/seller/payments": {
      "post": {
        "tags": [
          "pr402 Extension: Seller Onboarding"
        ],
        "summary": "Wallet-authenticated seller payment history",
        "description": "Returns recent `payment_attempts` rows for the authenticated seller wallet. Uses the same HMAC challenge + wallet-signed message as `POST /onboard` so only the wallet owner can read their own history.",
        "operationId": "facilitatorSellerPayments",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SellerPaymentsRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Page of seller payments",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SellerPaymentsPage"
                }
              }
            }
          },
          "400": {
            "description": "Missing or invalid parameters"
          },
          "401": {
            "description": "Signature verification failed"
          },
          "503": {
            "description": "Registry disabled (DATABASE_URL unset) or HMAC secret missing"
          }
        }
      }
    },
    "/api/v1/facilitator/discovery": {
      "get": {
        "tags": [
          "pr402 Extension: Seller Onboarding"
        ],
        "summary": "Lightweight single-scheme discovery (read-only)",
        "description": "Programmatic discovery of `payTo` address (vault PDA) and metadata for a **single** scheme. Read-only, no DB writes, no auth. Prefer this over `GET /onboard` when you only need one scheme's `payTo`. Returns `SchemeOnboardInfo` with `vaultPda`, `feeBps`, `isSovereign`, etc.",
        "operationId": "facilitatorDiscovery",
        "parameters": [
          {
            "name": "wallet",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "description": "Seller wallet pubkey (base58)"
            }
          },
          {
            "name": "scheme",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "example": "exact",
              "description": "Payment scheme (e.g. exact or sla-escrow)"
            }
          },
          {
            "name": "asset",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "description": "Optional mint address for token-specific discovery"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Scheme discovery information",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SchemeOnboardInfo"
                }
              }
            }
          },
          "400": {
            "description": "Missing parameters"
          },
          "500": {
            "description": "Discovery failed"
          }
        }
      }
    },
    "/api/v1/facilitator/vault-snapshot": {
      "get": {
        "tags": [
          "pr402 Extension: Operations"
        ],
        "summary": "UniversalSettle vault snapshot (read-only RPC)",
        "description": "Query vault balances for a seller wallet. Requires UniversalSettle configured on deployment.",
        "operationId": "facilitatorVaultSnapshot",
        "parameters": [
          {
            "name": "wallet",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "spl_mint",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "spl_token_program",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "description": "Token-2022 program id when applicable"
            }
          },
          {
            "name": "spl_balance_scope",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "vault_ata",
                "owner_wallet"
              ],
              "default": "vault_ata",
              "description": "Use owner_wallet for getTokenAccountsByOwner-style SPL balance"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "VaultSnapshotResponse",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VaultSnapshotResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request or program not configured"
          }
        }
      }
    },
    "/api/v1/facilitator/sweep": {
      "post": {
        "tags": [
          "pr402 Extension: Operations"
        ],
        "summary": "Internal cron sweep execution (private)",
        "description": "Executes UniversalSettle sweep attempts for eligible provider rails. **Internal operations endpoint**: requires bearer auth and is intended for scheduler/cron use only. Supports `dryRun` for safe rollout.",
        "operationId": "facilitatorSweep",
        "security": [
          {
            "sweep_bearer": []
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SweepRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Sweep execution report",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SweepResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request / not configured",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FacilitatorErrorBody"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FacilitatorErrorBody"
                }
              }
            }
          },
          "503": {
            "description": "Missing required sweep auth parameter",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FacilitatorErrorBody"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/facilitator/upgrade": {
      "post": {
        "tags": [
          "pr402 Extension: Seller Onboarding"
        ],
        "summary": "Upgrade a Lite 402 to institutional format",
        "description": "Transforms a naive `PaymentRequired` body (where `payTo` is a bare seller wallet) into a fully institutional response with correct PDA-derived `payTo` and injected `extra` metadata. For `exact`: replaces `payTo` with the UniversalSettle SplitVault PDA and injects `feePayer`, `programId`, `configAddress`, `merchantWallet`. For `sla-escrow`: replaces `payTo` with the Escrow PDA and injects `oracleAuthorities`, `bankAddress`, `escrowProgramId`. Useful for sellers who want the facilitator to handle PDA derivation.",
        "operationId": "facilitatorUpgrade",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "PaymentRequired body with bare seller wallet as payTo"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Upgraded PaymentRequired with PDAs and extra metadata",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "PaymentRequired body with vault/escrow PDA as payTo and institutional extra"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request or upgrade failure",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FacilitatorErrorBody"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/facilitator/sweep-cron": {
      "get": {
        "tags": [
          "pr402 Extension: Operations"
        ],
        "summary": "Vercel cron sweep execution (private)",
        "description": "GET wrapper for serverless schedulers that cannot issue POST (e.g., Vercel Cron). Uses configured defaults equivalent to empty `POST /api/v1/facilitator/sweep` body.",
        "operationId": "facilitatorSweepCron",
        "security": [
          {
            "sweep_bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Sweep execution report",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SweepResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FacilitatorErrorBody"
                }
              }
            }
          },
          "503": {
            "description": "Missing required sweep auth parameter",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FacilitatorErrorBody"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "sweep_bearer": {
        "type": "http",
        "scheme": "bearer",
        "description": "Private scheduler token (`PR402_SWEEP_CRON_TOKEN`; DB parameter takes precedence over env)."
      }
    },
    "schemas": {
      "JsonError": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          }
        }
      },
      "FacilitatorErrorBody": {
        "type": "object",
        "description": "Error response from the facilitator.",
        "required": [
          "error",
          "code"
        ],
        "properties": {
          "error": {
            "type": "string",
            "description": "Human-readable error description"
          },
          "code": {
            "type": "string",
            "description": "Machine-readable error enum (e.g. BAD_REQUEST, INTERNAL_SERVER_ERROR)"
          },
          "correlationId": {
            "type": "string"
          }
        }
      },
      "X402V2VerifySettleBody": {
        "type": "object",
        "description": "x402 v2 JSON for `POST /verify` and `POST /settle`. `paymentPayload.accepted` and `paymentRequirements` must agree; each carries a `scheme` string. Use wire `exact` / `sla-escrow`, or `v2:solana:*` aliases (normalized server-side for v2). Objects returned by `verifyBodyTemplate` from build endpoints use wire schemes only.",
        "required": [
          "x402Version",
          "paymentPayload",
          "paymentRequirements"
        ],
        "properties": {
          "x402Version": {
            "type": "integer",
            "const": 2
          },
          "paymentPayload": {
            "type": "object",
            "additionalProperties": true
          },
          "paymentRequirements": {
            "type": "object",
            "additionalProperties": true
          },
          "correlationId": {
            "type": "string"
          }
        },
        "additionalProperties": true,
        "example": {
          "x402Version": 2,
          "paymentPayload": {
            "x402Version": 2,
            "accepted": {
              "scheme": "exact",
              "network": "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1",
              "amount": "50000",
              "payTo": "<SplitVault PDA from seller discovery \u2014 not a bare wallet>",
              "asset": "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU",
              "maxTimeoutSeconds": 300,
              "extra": {
                "merchantWallet": "<seller identity pubkey>",
                "feePayer": "<facilitator fee payer>",
                "programId": "<UniversalSettle program id>",
                "configAddress": "<config PDA>"
              }
            },
            "payload": {
              "transaction": "<base64(bincode(VersionedTransaction)) \u2014 replace with signed tx from build-exact response>"
            },
            "resource": {
              "url": "https://seller.example/api/premium",
              "description": "Paid API",
              "mimeType": "application/json"
            },
            "extensions": {}
          },
          "paymentRequirements": {
            "scheme": "exact",
            "network": "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1",
            "amount": "50000",
            "payTo": "<SplitVault PDA \u2014 must match proof>",
            "asset": "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU",
            "maxTimeoutSeconds": 300,
            "extra": {
              "merchantWallet": "<seller identity pubkey>"
            }
          }
        }
      },
      "ResourceRef": {
        "type": "object",
        "description": "x402 resource block",
        "properties": {
          "url": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "mimeType": {
            "type": "string"
          }
        },
        "additionalProperties": true
      },
      "BuildExactPaymentTxRequest": {
        "type": "object",
        "required": [
          "payer",
          "accepted",
          "resource"
        ],
        "properties": {
          "payer": {
            "type": "string",
            "description": "Buyer pubkey (base58)"
          },
          "accepted": {
            "type": "object",
            "description": "One accepts[] line; scheme: `exact` (canonical) or `v2:solana:exact` (accepted alias); must match deployment network"
          },
          "resource": {
            "$ref": "#/components/schemas/ResourceRef"
          },
          "skipSourceBalanceCheck": {
            "type": "boolean",
            "default": false
          },
          "autoWrapSol": {
            "type": "boolean",
            "description": "If true, inject wrap instructions when the payment mint is wrapped SOL"
          }
        },
        "additionalProperties": false,
        "example": {
          "payer": "<buyer pubkey base58>",
          "accepted": {
            "scheme": "exact",
            "network": "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1",
            "amount": "50000",
            "payTo": "<SplitVault PDA>",
            "asset": "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU",
            "maxTimeoutSeconds": 300,
            "extra": {}
          },
          "resource": {
            "url": "https://seller.example/api/premium",
            "description": "Paid API",
            "mimeType": "application/json"
          }
        }
      },
      "BuildSlaEscrowPaymentTxRequest": {
        "type": "object",
        "required": [
          "payer",
          "accepted",
          "resource",
          "slaHash",
          "oracleAuthority"
        ],
        "properties": {
          "payer": {
            "type": "string"
          },
          "accepted": {
            "type": "object",
            "description": "accepts[] line; scheme: `sla-escrow` (canonical) or `v2:solana:sla-escrow` (accepted alias). `extra` must include `beneficiary` or `merchantWallet`."
          },
          "resource": {
            "$ref": "#/components/schemas/ResourceRef"
          },
          "slaHash": {
            "type": "string",
            "description": "64 hex chars (32 bytes)"
          },
          "oracleAuthority": {
            "type": "string",
            "description": "Must appear in accepted.extra.oracleAuthorities"
          },
          "paymentUid": {
            "type": "string"
          },
          "skipSourceBalanceCheck": {
            "type": "boolean",
            "default": false
          },
          "facilitatorPaysTransactionFees": {
            "type": "boolean",
            "default": false,
            "description": "false (default): buyer pays Solana tx fees, one signer. true: facilitator fee payer, buyer second signer (opt-in; mirrors `build-exact-payment-tx` mechanics). Does not change FundPayment token amount \u2014 only who pays SOL network fees."
          },
          "autoWrapSol": {
            "type": "boolean",
            "description": "If true, inject wrap instructions when the payment mint is wrapped SOL"
          }
        },
        "additionalProperties": false
      },
      "BuildOracleConfirmTxRequest": {
        "type": "object",
        "required": [
          "oracleAuthority",
          "mint",
          "paymentUid",
          "deliveryHash",
          "resolutionState",
          "resolutionReason"
        ],
        "properties": {
          "oracleAuthority": {
            "type": "string",
            "description": "Oracle operator pubkey (base58); must match `FundPayment.oracleAuthority`"
          },
          "mint": {
            "type": "string",
            "description": "Payment SPL mint pubkey (base58)"
          },
          "paymentUid": {
            "type": "string"
          },
          "deliveryHash": {
            "type": "string",
            "description": "64 hex chars (32 bytes); on-chain delivery hash"
          },
          "resolutionHash": {
            "type": "string",
            "description": "Optional 64 hex chars (32 bytes). Omit or empty for all-zero placeholder."
          },
          "resolutionState": {
            "type": "integer",
            "minimum": 0,
            "maximum": 255,
            "description": "On-chain resolution state (program-specific enum as u8)"
          },
          "resolutionReason": {
            "type": "integer",
            "minimum": 0,
            "maximum": 65535,
            "description": "Rejection/approval reason code (u16)"
          },
          "feePayer": {
            "type": "string",
            "description": "Optional pubkey for SOL fee payer (defaults to `oracleAuthority`)"
          }
        },
        "additionalProperties": false
      },
      "BuildOracleConfirmTxResponse": {
        "type": "object",
        "required": [
          "transaction",
          "programId",
          "paymentPda"
        ],
        "properties": {
          "transaction": {
            "type": "string",
            "description": "base64(bincode VersionedTransaction), unsigned; sign with oracle keypair"
          },
          "programId": {
            "type": "string",
            "description": "SLA-Escrow program id (base58)"
          },
          "paymentPda": {
            "type": "string",
            "description": "Payment account PDA (base58)"
          }
        },
        "additionalProperties": false
      },
      "OnboardChallengeResponse": {
        "type": "object",
        "properties": {
          "wallet": {
            "type": "string"
          },
          "message": {
            "type": "string"
          },
          "expiresUnix": {
            "type": "integer"
          },
          "ttlSeconds": {
            "type": "integer"
          }
        }
      },
      "OnboardSubmitBody": {
        "type": "object",
        "required": [
          "wallet",
          "message",
          "signature"
        ],
        "properties": {
          "wallet": {
            "type": "string"
          },
          "message": {
            "type": "string"
          },
          "signature": {
            "type": "string",
            "description": "Ed25519 signature of `message`. Accepts **base58** (canonical Solana form) or **base64** (what browser wallet adapters return natively). Must decode to exactly 64 bytes; other encodings / lengths are rejected."
          },
          "asset": {
            "type": "string",
            "description": "Payment asset this seller registers in `resource_providers` (one per wallet). Default **USDC** (`SOL`, `USDC`, mint, \u2026 \u2014 same resolution as `POST /onboard/provision`)."
          },
          "discovery": {
            "$ref": "#/components/schemas/OnboardDiscoveryPayload"
          }
        }
      },
      "OnboardDiscoveryPayload": {
        "type": "object",
        "description": "Optional seller-declared discovery payload. All fields are optional; submit any subset to update only those columns. All limits are enforced server-side.",
        "properties": {
          "serviceUrl": {
            "type": "string",
            "description": "Seller's 402-gated endpoint URL. Must start with http:// or https://. Required for the row to appear in GET /providers.",
            "maxLength": 2048
          },
          "displayName": {
            "type": "string",
            "description": "Human-friendly name for the listing.",
            "maxLength": 64
          },
          "description": {
            "type": "string",
            "description": "Tweet-sized blurb.",
            "maxLength": 280
          },
          "tags": {
            "type": "array",
            "description": "Up to 5 tags; each is 1..=32 ASCII lowercase letters, digits, or '-'.",
            "maxItems": 5,
            "items": {
              "type": "string",
              "maxLength": 32,
              "pattern": "^[a-z0-9-]+$"
            }
          },
          "serviceMetadata": {
            "type": "object",
            "description": "Seller-defined opaque JSON object. Facilitator does not interpret. Max 4096 bytes when serialized."
          },
          "listingOptIn": {
            "type": "boolean",
            "description": "Must be true for the wallet to appear on GET /providers."
          }
        }
      },
      "OnboardRetireBody": {
        "type": "object",
        "required": [
          "wallet",
          "message",
          "signature"
        ],
        "description": "Same HMAC-bound, wallet-signed payload shape as POST /onboard. The facilitator only retires the wallet after signature verification passes.",
        "properties": {
          "wallet": {
            "type": "string"
          },
          "message": {
            "type": "string"
          },
          "signature": {
            "type": "string"
          }
        }
      },
      "OnboardRetireResponse": {
        "type": "object",
        "required": ["wallet", "retiredRowCount"],
        "properties": {
          "wallet": {
            "type": "string"
          },
          "retiredRowCount": {
            "type": "integer",
            "description": "Number of resource_providers rows updated. 0 = wallet was not in the registry."
          },
          "note": {
            "type": "string"
          }
        }
      },
      "PublicProviderEntry": {
        "type": "object",
        "required": [
          "walletPubkey",
          "settlementMode",
          "serviceUrl",
          "tags",
          "registrationVerifiedAt",
          "updatedAt"
        ],
        "properties": {
          "walletPubkey": { "type": "string" },
          "settlementMode": { "type": "string", "enum": ["native_sol", "spl"] },
          "splMint": { "type": "string" },
          "splitVaultPda": { "type": "string" },
          "serviceUrl": { "type": "string" },
          "displayName": { "type": "string" },
          "description": { "type": "string" },
          "tags": {
            "type": "array",
            "items": { "type": "string" }
          },
          "serviceMetadata": { "type": "object" },
          "registrationVerifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "PublicProvidersPage": {
        "type": "object",
        "required": ["entries", "notice"],
        "properties": {
          "entries": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/PublicProviderEntry" }
          },
          "nextCursor": {
            "type": "string",
            "format": "date-time",
            "description": "Pass as `?cursor=…` on the next request to page backwards."
          },
          "notice": {
            "type": "string",
            "description": "Facilitator disclaimer: wallet control is verified; the advertised service is not vetted."
          }
        }
      },
      "PublicProviderSingle": {
        "type": "object",
        "required": ["entry", "notice"],
        "properties": {
          "entry": { "$ref": "#/components/schemas/PublicProviderEntry" },
          "notice": { "type": "string" }
        }
      },
      "SellerPaymentsRequest": {
        "type": "object",
        "required": ["wallet", "message", "signature"],
        "description": "Same HMAC challenge + wallet-signed payload as POST /onboard.",
        "properties": {
          "wallet": { "type": "string" },
          "message": { "type": "string" },
          "signature": { "type": "string" },
          "limit": {
            "type": "integer",
            "minimum": 1,
            "maximum": 100,
            "default": 50
          },
          "cursor": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "SellerPaymentEntry": {
        "type": "object",
        "required": ["correlationId", "createdAt"],
        "properties": {
          "correlationId": { "type": "string" },
          "verifyAt": { "type": "string", "format": "date-time" },
          "verifyOk": { "type": "boolean" },
          "settleAt": { "type": "string", "format": "date-time" },
          "settleOk": { "type": "boolean" },
          "settlementSignature": { "type": "string" },
          "payerWallet": { "type": "string" },
          "scheme": { "type": "string" },
          "amount": { "type": "string" },
          "asset": { "type": "string" },
          "createdAt": { "type": "string", "format": "date-time" }
        }
      },
      "SellerPaymentsPage": {
        "type": "object",
        "required": ["entries"],
        "properties": {
          "entries": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/SellerPaymentEntry" }
          },
          "nextCursor": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ProvisioningStatus": {
        "type": "object",
        "required": [
          "asset",
          "recovered",
          "total"
        ],
        "properties": {
          "asset": {
            "type": "string",
            "example": "SOL"
          },
          "recovered": {
            "type": "string",
            "example": "5000000"
          },
          "total": {
            "type": "string",
            "example": "10000000"
          }
        }
      },
      "SchemeOnboardInfo": {
        "type": "object",
        "required": [
          "label",
          "role",
          "vaultPda",
          "solStoragePda",
          "feeBps",
          "status",
          "isSovereign"
        ],
        "properties": {
          "label": {
            "type": "string"
          },
          "role": {
            "type": "string"
          },
          "vaultPda": {
            "type": "string"
          },
          "solStoragePda": {
            "type": "string"
          },
          "tokenPda": {
            "type": "string",
            "nullable": true
          },
          "merchantWallet": {
            "type": "string",
            "description": "Authoritative merchant identity (base58)",
            "nullable": true
          },
          "feeBps": {
            "type": "string",
            "description": "Effective fee in basis points"
          },
          "status": {
            "type": "string",
            "example": "Sovereign"
          },
          "isSovereign": {
            "type": "boolean",
            "description": "Has 5 bps institutional discount"
          },
          "provisioningStatus": {
            "$ref": "#/components/schemas/ProvisioningStatus",
            "nullable": true
          }
        }
      },
      "OnboardResponse": {
        "type": "object",
        "required": [
          "wallet",
          "facilitator",
          "schemes"
        ],
        "properties": {
          "wallet": {
            "type": "string"
          },
          "facilitator": {
            "type": "string"
          },
          "schemes": {
            "type": "object",
            "additionalProperties": {
              "$ref": "#/components/schemas/SchemeOnboardInfo"
            }
          },
          "lifecycle": {
            "$ref": "#/components/schemas/SellerLifecycle",
            "description": "Seller lifecycle snapshot (Preview → Activate → Verify). Absent on older servers; always present on newer ones."
          }
        }
      },
      "SellerLifecycle": {
        "type": "object",
        "description": "Machine-readable seller lifecycle ladder state. `previewed` is always true when the response exists; `activated` reflects whether the exact-rail SplitVault is on-chain; `verified` reflects a `resource_providers` row with `registrationVerifiedAt` set. `nextStep` points at the first stage the seller can still act on.",
        "required": [
          "previewed",
          "activated",
          "verified"
        ],
        "properties": {
          "previewed": {
            "type": "boolean"
          },
          "activated": {
            "type": "boolean",
            "description": "True when the exact rail's SplitVault account exists on-chain. Independent of the sovereign fee tier, which is surfaced via `schemes.exact.isSovereign`."
          },
          "verified": {
            "type": "boolean",
            "description": "True when a `resource_providers` row with `registrationVerifiedAt IS NOT NULL` exists for this wallet. Disabled deployments (no `DATABASE_URL`) always report `false`."
          },
          "nextStep": {
            "type": "string",
            "nullable": true,
            "enum": [
              "activate",
              "verify"
            ],
            "description": "First pending stage. `null` when all three are complete."
          }
        }
      },
      "VaultSnapshotResponse": {
        "type": "object",
        "description": "UniversalSettle vault snapshot from `GET /vault-snapshot` (RPC-derived).",
        "required": [
          "seller",
          "programId",
          "splitVaultPda",
          "vaultSolStoragePda",
          "spendableLamports",
          "splAmountRaw",
          "splBalanceScope"
        ],
        "properties": {
          "seller": {
            "type": "string",
            "description": "Wallet pubkey (base58)"
          },
          "programId": {
            "type": "string"
          },
          "splitVaultPda": {
            "type": "string"
          },
          "vaultSolStoragePda": {
            "type": "string"
          },
          "spendableLamports": {
            "type": "integer",
            "format": "int64",
            "description": "Spendable SOL in lamports"
          },
          "vaultSplAta": {
            "type": "string",
            "nullable": true,
            "description": "Vault ATA for the queried mint, if any"
          },
          "splAmountRaw": {
            "type": "integer",
            "format": "int64",
            "description": "Raw SPL amount (0 if mint not queried or ATA missing)"
          },
          "splDecimals": {
            "type": "integer",
            "nullable": true
          },
          "splBalanceScope": {
            "type": "string",
            "enum": [
              "vault_ata",
              "owner_wallet"
            ],
            "description": "Reflects `spl_balance_scope` query (owner_wallet when using getTokenAccountsByOwner-style balance)"
          }
        },
        "additionalProperties": true
      },
      "SweepRequest": {
        "type": "object",
        "properties": {
          "limit": {
            "type": "integer",
            "minimum": 1,
            "maximum": 500,
            "default": 50
          },
          "cooldownSeconds": {
            "type": "integer",
            "minimum": 1,
            "default": 300
          },
          "requireRecentSettleWithinSeconds": {
            "type": "integer",
            "minimum": 60,
            "default": 86400
          },
          "dryRun": {
            "type": "boolean",
            "default": false
          }
        },
        "additionalProperties": false
      },
      "SweepItemResult": {
        "type": "object",
        "properties": {
          "wallet": {
            "type": "string"
          },
          "settlementMode": {
            "type": "string",
            "enum": [
              "native_sol",
              "spl"
            ]
          },
          "splMint": {
            "type": "string",
            "nullable": true
          },
          "availableRaw": {
            "type": "integer"
          },
          "thresholdRaw": {
            "type": "integer"
          },
          "status": {
            "type": "string",
            "enum": [
              "eligible",
              "ok",
              "skipped",
              "failed"
            ]
          },
          "action": {
            "type": "string"
          },
          "signature": {
            "type": "string",
            "nullable": true
          },
          "error": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "SweepResponse": {
        "type": "object",
        "properties": {
          "dryRun": {
            "type": "boolean"
          },
          "scanned": {
            "type": "integer"
          },
          "attempted": {
            "type": "integer"
          },
          "succeeded": {
            "type": "integer"
          },
          "skippedBelowThreshold": {
            "type": "integer"
          },
          "failed": {
            "type": "integer"
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SweepItemResult"
            }
          }
        },
        "additionalProperties": false
      },
      "UniversalSettleExtra": {
        "type": "object",
        "properties": {
          "programId": {
            "type": "string"
          },
          "configAddress": {
            "type": "string"
          },
          "feeBps": {
            "type": "string"
          },
          "merchantWallet": {
            "type": "string",
            "description": "IDENTITY: Required for PDA re-derivation"
          }
        }
      },
      "SLAEscrowExtra": {
        "type": "object",
        "properties": {
          "escrowProgramId": {
            "type": "string"
          },
          "bankAddress": {
            "type": "string"
          },
          "configAddress": {
            "type": "string"
          },
          "oracleAuthorities": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "merchantWallet": {
            "type": "string",
            "description": "Seller payout identity (base58); required if `beneficiary` omitted"
          },
          "beneficiary": {
            "type": "string",
            "description": "Preferred seller payout pubkey for `FundPayment.seller` (base58); overrides `merchantWallet` when both present"
          }
        }
      },
      "OnboardProvisionRequest": {
        "type": "object",
        "required": [
          "wallet",
          "asset"
        ],
        "properties": {
          "wallet": {
            "type": "string",
            "description": "Seller wallet pubkey (base58)"
          },
          "asset": {
            "type": "string",
            "description": "Asset to provision: `SOL`, `USDC`, `USDT`, or a base58 SPL mint (native SOL virtual mint `11111111111111111111111111111111` also accepted). Typical agent flows: **`USDC`** first."
          }
        },
        "additionalProperties": false
      },
      "SellerProvisionTxResponse": {
        "type": "object",
        "description": "UniversalSettle seller provisioning result; transaction omitted when already provisioned.",
        "required": [
          "schemaVersion",
          "wallet",
          "asset",
          "assetMint",
          "vaultPda",
          "solStoragePda",
          "alreadyProvisioned",
          "statusCode",
          "feePayer",
          "payer",
          "payerSignatureIndex",
          "notes"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string"
          },
          "wallet": {
            "type": "string"
          },
          "asset": {
            "type": "string",
            "description": "Friendly asset label after resolution"
          },
          "assetMint": {
            "type": "string",
            "description": "SPL mint base58; native SOL uses the all-ones virtual mint pubkey"
          },
          "vaultPda": {
            "type": "string"
          },
          "solStoragePda": {
            "type": "string"
          },
          "vaultTokenAta": {
            "type": "string",
            "description": "Vault-owned ATA for SPL mint when applicable"
          },
          "alreadyProvisioned": {
            "type": "boolean"
          },
          "statusCode": {
            "type": "string",
            "enum": [
              "ALREADY_PROVISIONED",
              "VAULT_AND_ATA",
              "VAULT_ONLY",
              "ATA_ONLY"
            ],
            "description": "Machine-readable summary of what this call will do (or already observed). Prefer this over parsing `notes[]`. `ALREADY_PROVISIONED` mirrors `alreadyProvisioned: true` (no `transaction`); the other three describe the shape of the unsigned tx returned."
          },
          "transaction": {
            "type": "string",
            "description": "base64(bincode VersionedTransaction), unsigned; absent when `alreadyProvisioned`"
          },
          "recentBlockhash": {
            "type": "string"
          },
          "recentBlockhashExpiresAt": {
            "type": "integer",
            "format": "int64"
          },
          "feePayer": {
            "type": "string"
          },
          "payer": {
            "type": "string"
          },
          "payerSignatureIndex": {
            "type": "integer"
          },
          "notes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "additionalProperties": true
      },
      "BuildPaymentTxResponse": {
        "type": "object",
        "description": "Unified response for transaction builders. `verifyBodyTemplate` is ready to POST to `/verify` and `/settle` after the buyer replaces `paymentPayload.payload.transaction` with the signed base64 tx; scheme fields inside the template are normalized to wire `exact` or `sla-escrow`.",
        "required": [
          "x402Version",
          "transaction",
          "recentBlockhash",
          "recentBlockhashExpiresAt",
          "feePayer",
          "payer",
          "payerSignatureIndex",
          "verifyBodyTemplate"
        ],
        "properties": {
          "x402Version": {
            "type": "integer"
          },
          "transaction": {
            "type": "string",
            "description": "base64(bincode VersionedTransaction), unsigned"
          },
          "recentBlockhash": {
            "type": "string"
          },
          "recentBlockhashExpiresAt": {
            "type": "integer",
            "format": "int64",
            "description": "Estimated UNIX epoch (seconds)"
          },
          "feePayer": {
            "type": "string"
          },
          "payer": {
            "type": "string"
          },
          "payerSignatureIndex": {
            "type": "integer"
          },
          "paymentUid": {
            "type": "string"
          },
          "verifyBodyTemplate": {
            "type": "object",
            "description": "Filled verify/settle body shape (`x402Version`, `paymentPayload`, `paymentRequirements`) with `scheme` set wire form (`exact` or `sla-escrow`); unsigned tx placeholder until the client signs."
          },
          "notes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "additionalProperties": true
      },
      "VerifyResponse": {
        "type": "object",
        "description": "Verify success response",
        "required": [
          "isValid"
        ],
        "properties": {
          "isValid": {
            "type": "boolean"
          },
          "invalidReason": {
            "type": "string"
          },
          "payer": {
            "type": "string"
          },
          "correlationId": {
            "type": "string"
          }
        },
        "additionalProperties": true
      },
      "SettleResponse": {
        "type": "object",
        "description": "Settle success or structured outcome (x402 v2 \u00a75.3). **`transaction`** is always a JSON **string**: base58 signature on Solana, or **`\"\"`** when omitted/unknown/error paths. Extension keys (e.g. `correlationId`) are allowed.",
        "required": [
          "success",
          "transaction"
        ],
        "properties": {
          "success": {
            "type": "boolean"
          },
          "errorReason": {
            "type": "string"
          },
          "payer": {
            "type": "string"
          },
          "transaction": {
            "type": "string",
            "description": "On-chain settlement signature when present; empty string when not applicable (per x402 \u00a75.3)"
          },
          "network": {
            "type": "string",
            "description": "CAIP-2 network id when applicable"
          },
          "correlationId": {
            "type": "string"
          }
        },
        "additionalProperties": true
      }
    }
  }
}
