{
  "openapi": "3.1.0",
  "info": {
    "title": "Changelog State API",
    "version": "1.0.0",
    "contact": {
      "name": "Changelog State API",
      "email": "changelogstateapi@proton.me",
      "url": "https://github.com/xs10chill"
    },
    "description": "Changelog State API — structured release and changelog intelligence for agent-critical vendors. It answers two questions: what shipped, and what changed. Breaking is a flag quoted from the source when the source states it — not a prediction that the caller's production will fail. Paid endpoints are metered per call over x402 v2 at a flat 0.01 USDC, settled on whichever chain you pay from — the 402 challenge and this document's `x-payment-info.accepts` list every one this origin takes. No API keys and no signup: pay per call or use the free coarse catalog. This API does not track SaaS pricing pages or uptime status pages. Paid calls settle in USDC on Base or Solana.",
    "x-guidance": "Changelog State API answers two questions about the vendors an agent depends on: what shipped, and what changed. Start free: GET /api/monitors lists every tracked vendor with its source, extraction status, the headline release and date, how much is on record, and that vendor's paid URLs. GET /api/site/changelog/{slug} is the same coarse view for one vendor. Use those to decide whether anything is new. Pay only when you need the substance. GET /api/monitors/{slug} returns the structured recent releases including entry bodies, version strings, publish dates, source links and any breaking signal quoted from the source page; it is paginated with `limit`, `hasMore` and a `nextCursor` token you pass back as `cursor` to walk to older entries — every entry counted in `releaseCount` is reachable that way, one paid call per page — and each body is capped at 800 characters with `bodyTruncated` set when cut. GET /api/monitors/{slug}/changes returns the detected update history — new releases, version, date and title changes, and breaking signals; a pure wording reshuffle never produces a row, and `watchedSince` states when the baseline was captured so an empty history reads as \"nothing has changed\", not as a missing feed. Paid calls cost 0.01 USDC per call over x402 v2 on any chain the 402 challenge offers, with no API key, no signup and no account: request without payment to receive the 402 challenge, then repeat the request with a PAYMENT-SIGNATURE header. Payment is only captured once the response can actually be produced, so an unknown slug or an invalid query parameter costs nothing. Slugs are lowercase alphanumeric with dashes; take them from the free catalog rather than guessing. `{slug}` in the paths above is a placeholder, not an address: substitute a real slug before calling. On the paid routes the literal characters are answered with the 402 challenge so registries can probe the template, but a payment for them returns 404 and is never captured. Worked examples you can call as written: GET /api/site/changelog/openai, GET /api/monitors/openai and GET /api/monitors/openai/changes. This service does not track SaaS pricing pages and does not track uptime or status pages. The current fleet has 42 monitored vendors and projects: Anthropic, Auth0, Chainlink, Chrome, Circle, Clerk, Cloudflare, DeepSeek Harness, Discord, Docker, Figma, Foundry, Gemini, Geth, GitHub, Hugging Face, Kubernetes, LangChain, Linear, LiteLLM, MCP, NEAR, NEAR API JS, Next.js, Node.js, Notion, OpenAI, OpenZeppelin, Playwright, Prisma, React, Sentry, shadcn/ui, Stripe, Supabase, Twilio, TypeScript, Vercel, viem, Wormhole SDK, x402 and xAI. Paid calls settle in USDC on Base or Solana."
  },
  "x-agentcash-provenance": {
    "ownershipProofs": [
      "https://github.com/xs10chill"
    ]
  },
  "servers": [
    {
      "url": "https://changelog-state-api.replit.app",
      "description": "Production origin"
    }
  ],
  "tags": [
    {
      "name": "health",
      "description": "Liveness and readiness"
    },
    {
      "name": "catalog",
      "description": "Free coarse fleet catalog and teasers"
    },
    {
      "name": "intel",
      "description": "Paid structured release and change intelligence"
    },
    {
      "name": "feedback",
      "description": "Free structured feedback from agents and humans"
    }
  ],
  "paths": {
    "/api/healthz": {
      "get": {
        "operationId": "healthCheck",
        "tags": [
          "health"
        ],
        "summary": "Health check",
        "description": "Returns server health status. Free, no payment required. `status` answers whether the service is answering; the separate `payments` object reports whether paid calls can currently be settled, so a broken paywall is visible without reading it as an outage.",
        "security": [],
        "responses": {
          "200": {
            "description": "Healthy",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HealthStatus"
                }
              }
            }
          }
        }
      }
    },
    "/api/monitors": {
      "get": {
        "operationId": "listMonitors",
        "tags": [
          "catalog"
        ],
        "summary": "List tracked vendors (free, coarse)",
        "description": "Free coarse fleet catalog. Returns one card per tracked vendor: what it is, the official source being read, extraction status, the headline release and the times, how many releases and detected changes are on record, and the paid URLs for that vendor. Full entry bodies, structured release history and change history are paid. The headline can itself be a pre-release: `lastReleaseIsPrerelease` says so, for the vendor deep in a run of release candidates with no shipped version behind it, and `headlineLookback` counts how many of its most recent entries were searched for one before giving up.",
        "security": [],
        "responses": {
          "200": {
            "description": "Coarse catalog of every tracked vendor",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/MonitorSummary"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/site/changelog/{slug}": {
      "get": {
        "operationId": "getChangelogTeaser",
        "tags": [
          "catalog"
        ],
        "summary": "Coarse teaser for one vendor (free)",
        "description": "Free teaser for one vendor: identity, the official source being read, extraction status, the headline release, when it was last checked and last changed, how many releases and detected changes are on record, and the paid URLs. Never returns entry bodies, structured release history or change history. The headline can itself be a pre-release: `lastReleaseIsPrerelease` says so, and `headlineLookback` counts how many of the vendor's most recent entries were searched for a shipped version before settling for it. Substitute a slug from GET /api/monitors: anything that names no tracked vendor — including the literal `{slug}` placeholder of this template — returns 404. Worked example: GET /api/site/changelog/openai.",
        "security": [],
        "parameters": [
          {
            "$ref": "#/components/parameters/MonitorSlug"
          }
        ],
        "responses": {
          "200": {
            "description": "Coarse teaser",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChangelogTeaser"
                }
              }
            }
          },
          "404": {
            "description": "Not a tracked vendor: either an unknown slug or one that is not a well-formed slug at all, such as the literal `{slug}` placeholder.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/api/monitors/{slug}": {
      "get": {
        "operationId": "getMonitorDetail",
        "tags": [
          "intel"
        ],
        "summary": "Full structured recent releases (paid)",
        "description": "Paid. Returns the vendor's structured recent releases including entry bodies, version strings, publish dates, source links and any breaking signal the source page actually states. The default first page (limit=5) is curated: the headline release (the newest shipped stable) comes first, then any other breaking releases, then the newest remaining entries; fern, codegen, and unversioned machine tags are excluded when any real semver release exists. Sized by `limit` (default 5, maximum 50), which is echoed back alongside `hasMore`. Fully walkable: when `hasMore` is true the response carries a `nextCursor` token, and repeating the call with `cursor=<token>` returns the next older page rather than page one again — so every entry counted in `releaseCount` can be reached, however far back it goes. The token names a position in the newest-first order, not an offset, so a page already read never shifts under you: an entry arriving mid-walk sorts by its own publish date rather than renumbering the pages behind it. Entry bodies are capped at 800 characters and set `bodyTruncated` when cut; version and url are never dropped. Costs 0.01 USDC per call over x402 v2, payable on any chain the 402 challenge lists — each page is one call. Substitute a slug from GET /api/monitors: an unpaid request for a tracked vendor answers with the 402 challenge, while anything that names no vendor — an unknown or malformed slug — returns 404 with no challenge at all. The literal `{slug}` placeholder of this template is challenged so registries can probe it, but paying for it returns 404 and is never captured. Worked example: GET /api/monitors/openai. Payment is only captured once the response can be produced, so a 404 or an invalid query parameter (400) is never charged.",
        "security": [],
        "x-payment-info": {
          "price": {
            "mode": "fixed",
            "currency": "USD",
            "amount": "0.01"
          },
          "protocols": [
            {
              "x402": {
                "version": 2,
                "scheme": "exact",
                "network": "eip155:8453",
                "networkName": "Base",
                "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
                "assetSymbol": "USDC",
                "assetDecimals": 6,
                "atomicAmount": "10000",
                "payTo": "0xb23c3825de4ca8a479183092ae8d1d58d2736fc9",
                "facilitator": "https://facilitator.payai.network",
                "maxTimeoutSeconds": 120,
                "accepts": [
                  {
                    "network": "eip155:8453",
                    "networkName": "Base",
                    "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
                    "assetSymbol": "USDC",
                    "assetDecimals": 6,
                    "atomicAmount": "10000",
                    "payTo": "0xb23c3825de4ca8a479183092ae8d1d58d2736fc9",
                    "extra": {
                      "name": "USD Coin",
                      "version": "2"
                    }
                  },
                  {
                    "network": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",
                    "networkName": "Solana",
                    "asset": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
                    "assetSymbol": "USDC",
                    "assetDecimals": 6,
                    "atomicAmount": "10000",
                    "payTo": "6UNbfD8mgSqgFDxxwE29LfgKrBqrZBt4R4L4Resh4yct",
                    "extra": {
                      "feePayer": "CjNFTjvBhbJJd2B5ePPMHRLx1ELZpa8dwQgGL727eKww"
                    }
                  }
                ]
              }
            },
            {
              "x402": {
                "version": 1,
                "scheme": "exact",
                "network": "base",
                "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
                "assetSymbol": "USDC",
                "assetDecimals": 6,
                "maxAmountRequired": "10000",
                "payTo": "0xb23c3825de4ca8a479183092ae8d1d58d2736fc9",
                "facilitator": "https://facilitator.payai.network",
                "maxTimeoutSeconds": 120
              }
            }
          ]
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/MonitorSlug"
          },
          {
            "$ref": "#/components/parameters/ReleaseLimit"
          },
          {
            "$ref": "#/components/parameters/ReleaseCursor"
          }
        ],
        "responses": {
          "200": {
            "description": "Full structured release intel",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MonitorDetail"
                }
              }
            }
          },
          "400": {
            "description": "Invalid query parameter. No payment is charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "402": {
            "description": "Payment required — x402 v2 challenge",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentRequiredChallenge"
                }
              }
            }
          },
          "404": {
            "description": "Not a tracked vendor. An unknown or malformed slug is refused before the paywall, so there is no challenge and no charge. The literal `{slug}` placeholder is challenged unpaid and answered 404 when paid, and that payment is never captured.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/api/monitors/{slug}/changes": {
      "get": {
        "operationId": "getMonitorChanges",
        "tags": [
          "intel"
        ],
        "summary": "Detected changelog update history (paid)",
        "description": "Paid. Returns the detected changelog update history for the vendor — new releases, version changes, date changes, title changes and breaking signals quoted from the source. Pure wording reshuffle never produces a row, and neither does the first read of the source, over however few checks it takes that read to arrive in full: `watchedSince` says when the baseline was captured, so changeCount 0 means nothing has changed since that date rather than a missing feed. Sized by `limit` (default 50, maximum 200) and `hasMore`; this route carries no cursor, so rows older than the maximum page are counted in `changeCount` but are reachable only by narrowing the query with `since` or `type`. Costs 0.01 USDC per call over x402 v2, payable on any chain the 402 challenge lists. Substitute a slug from GET /api/monitors: an unpaid request for a tracked vendor answers with the 402 challenge, while anything that names no vendor — an unknown or malformed slug — returns 404 with no challenge at all. The literal `{slug}` placeholder of this template is challenged so registries can probe it, but paying for it returns 404 and is never captured. Worked example: GET /api/monitors/openai/changes. Payment is only captured once the response can be produced, so a 404 or an invalid query parameter (400) is never charged.",
        "security": [],
        "x-payment-info": {
          "price": {
            "mode": "fixed",
            "currency": "USD",
            "amount": "0.01"
          },
          "protocols": [
            {
              "x402": {
                "version": 2,
                "scheme": "exact",
                "network": "eip155:8453",
                "networkName": "Base",
                "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
                "assetSymbol": "USDC",
                "assetDecimals": 6,
                "atomicAmount": "10000",
                "payTo": "0xb23c3825de4ca8a479183092ae8d1d58d2736fc9",
                "facilitator": "https://facilitator.payai.network",
                "maxTimeoutSeconds": 120,
                "accepts": [
                  {
                    "network": "eip155:8453",
                    "networkName": "Base",
                    "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
                    "assetSymbol": "USDC",
                    "assetDecimals": 6,
                    "atomicAmount": "10000",
                    "payTo": "0xb23c3825de4ca8a479183092ae8d1d58d2736fc9",
                    "extra": {
                      "name": "USD Coin",
                      "version": "2"
                    }
                  },
                  {
                    "network": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",
                    "networkName": "Solana",
                    "asset": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
                    "assetSymbol": "USDC",
                    "assetDecimals": 6,
                    "atomicAmount": "10000",
                    "payTo": "6UNbfD8mgSqgFDxxwE29LfgKrBqrZBt4R4L4Resh4yct",
                    "extra": {
                      "feePayer": "CjNFTjvBhbJJd2B5ePPMHRLx1ELZpa8dwQgGL727eKww"
                    }
                  }
                ]
              }
            },
            {
              "x402": {
                "version": 1,
                "scheme": "exact",
                "network": "base",
                "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
                "assetSymbol": "USDC",
                "assetDecimals": 6,
                "maxAmountRequired": "10000",
                "payTo": "0xb23c3825de4ca8a479183092ae8d1d58d2736fc9",
                "facilitator": "https://facilitator.payai.network",
                "maxTimeoutSeconds": 120
              }
            }
          ]
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/MonitorSlug"
          },
          {
            "$ref": "#/components/parameters/ChangeLimit"
          },
          {
            "$ref": "#/components/parameters/ChangeSince"
          },
          {
            "$ref": "#/components/parameters/ChangeTypeFilter"
          }
        ],
        "responses": {
          "200": {
            "description": "Detected change history",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChangeFeed"
                }
              }
            }
          },
          "400": {
            "description": "Invalid query parameter. No payment is charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "402": {
            "description": "Payment required — x402 v2 challenge",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentRequiredChallenge"
                }
              }
            }
          },
          "404": {
            "description": "Not a tracked vendor. An unknown or malformed slug is refused before the paywall, so there is no challenge and no charge. The literal `{slug}` placeholder is challenged unpaid and answered 404 when paid, and that payment is never captured.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/api/feedback": {
      "post": {
        "operationId": "submitFeedback",
        "tags": [
          "feedback"
        ],
        "summary": "File structured feedback (free, no payment)",
        "description": "Free. Never answers 402, needs no key and no signup, and nothing is billed. Agents SHOULD POST here when they hit a missing feature, a 4xx/5xx, a product gap, or something that made the experience worse than it needed to be — say what you were trying to do and what would have made it better, and what you would buy that this API does not offer yet. Plain text only: markup in `title` or `message` is refused. `monitor_slug`, when present, must be a slug from GET /api/monitors. `endpoint` is recorded as a path and never fetched. A human operator reviews every filing; nothing filed here is executed, and nothing opens a pull request or issue anywhere. The raw IP is never stored, only a keyed hash of it, used for the per-IP limit of 10 filings an hour and for refusing an identical title and message filed again from the same IP within the hour.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FeedbackSubmission"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Filed. A human will review it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeedbackReceipt"
                }
              }
            }
          },
          "400": {
            "description": "Invalid filing: a missing or out-of-range field, an unknown `monitor_slug`, markup in `title` or `message`, a whitespace-only value, `metadata` over 4 KB, or an `endpoint` that is not a path.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "413": {
            "description": "Request body over 16 KB.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "More than 10 filings from this IP in the last hour, or the same title and message already filed from this IP within the hour.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "503": {
            "description": "Feedback is not configured on this deployment (no keyed hash secret), so nothing can be stored safely. Fails closed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "parameters": {
      "MonitorSlug": {
        "name": "slug",
        "in": "path",
        "required": true,
        "description": "Vendor slug, lowercase alphanumeric with dashes. Take one from GET /api/monitors and substitute it into the path: `{slug}` is a placeholder that names no vendor and never returns data. Worked example: GET /api/monitors/openai.",
        "example": "openai",
        "schema": {
          "type": "string",
          "minLength": 1,
          "maxLength": 64,
          "pattern": "^[a-z0-9][a-z0-9-]*$",
          "example": "openai"
        }
      },
      "ReportId": {
        "name": "reportId",
        "in": "path",
        "required": true,
        "description": "Stored report identifier.",
        "schema": {
          "type": "integer",
          "minimum": 1
        }
      },
      "FeedbackId": {
        "name": "feedbackId",
        "in": "path",
        "required": true,
        "description": "Feedback filing identifier.",
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "CaptureId": {
        "name": "captureId",
        "in": "path",
        "required": true,
        "description": "Settlement ledger row identifier.",
        "schema": {
          "type": "integer",
          "minimum": 1
        }
      },
      "ReleaseLimit": {
        "name": "limit",
        "in": "query",
        "required": false,
        "description": "Maximum number of release entries to return on this page. The page size, not a total: use `cursor` to walk past it.",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 50,
          "default": 5
        }
      },
      "ReleaseCursor": {
        "name": "cursor",
        "in": "query",
        "required": false,
        "description": "Opaque next-page token, copied verbatim from `nextCursor` on the previous response for this same vendor. Omit it — or send the empty string — to start at the newest entry. It names a position in the newest-first order rather than an offset, so a page you have already read never shifts under you: an entry that arrives mid-walk takes the place its own publish date gives it — above the cursor, on ground you have already covered, or below it, still ahead of you — instead of renumbering every page behind it. It does not expire; a walk can be paused and resumed. Do not construct or edit one: a token that is malformed, or was issued for a different vendor, is refused with 400 and is never charged.",
        "schema": {
          "type": "string",
          "maxLength": 512,
          "default": ""
        }
      },
      "ChangeLimit": {
        "name": "limit",
        "in": "query",
        "required": false,
        "description": "Maximum number of change rows to return.",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 200,
          "default": 50
        }
      },
      "ChangeSince": {
        "name": "since",
        "in": "query",
        "required": false,
        "description": "Only return changes detected at or after this instant.",
        "schema": {
          "type": "string",
          "format": "date-time"
        }
      },
      "ChangeTypeFilter": {
        "name": "type",
        "in": "query",
        "required": false,
        "description": "Restrict to a single change type.",
        "schema": {
          "type": "string",
          "enum": [
            "new_release",
            "version_changed",
            "date_changed",
            "title_changed",
            "breaking_signal"
          ]
        }
      }
    },
    "schemas": {
      "HealthStatus": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "description": "Whether this service is answering. Stays \"ok\" while the payment pipeline is broken: the free catalog and discovery documents are unaffected, and an uptime check must not read a paywall problem as the site being down."
          },
          "payments": {
            "$ref": "#/components/schemas/PaymentsProbeStatus"
          },
          "retention": {
            "$ref": "#/components/schemas/RetentionWindows"
          }
        },
        "required": [
          "status"
        ]
      },
      "RetentionWindows": {
        "description": "How long this origin keeps the parts of a payment it does not need forever. Published because this service's own privacy statement is written from it, and an operator can change it — a page that hardcoded the window would be able to promise a deletion a given deployment does not perform. Optional: a deployment that predates this field omits it.",
        "type": "object",
        "properties": {
          "settlementPayloadDays": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Days after a captured payment becomes terminal — response delivered, reconciled by the operator, or the authorization handed back — before the payer's signed payment message and the requirements it was settled against are erased from the settlement ledger. Everything else about the capture is kept. `null` means the erasure is switched off on this deployment and the signed message is kept indefinitely."
          }
        },
        "required": [
          "settlementPayloadDays"
        ]
      },
      "PaymentsProbeStatus": {
        "description": "Whether paid calls can currently be paid for, as of the last unfunded probe of the live 402 challenge. Reported separately from service health because the two fail independently — every free endpoint can be green while the paid half is unsellable. Optional: a deployment that predates this check omits it.",
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "description": "\"ok\" when the facilitator validated a payment carrying the live challenge and refused it only for having no balance. \"broken\" when it was refused for a reason this service controls, or the facilitator could not be used at all. \"unknown\" before the first probe.",
            "enum": [
              "ok",
              "broken",
              "unknown"
            ]
          },
          "classification": {
            "type": "string",
            "enum": [
              "healthy",
              "healthy_insufficient_balance",
              "declaration_rejected",
              "misconfigured",
              "unreachable",
              "unchecked"
            ]
          },
          "detail": {
            "type": "string"
          },
          "checkedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "target": {
            "type": [
              "string",
              "null"
            ],
            "description": "The paid operation whose 402 challenge the probe reproduces."
          },
          "networks": {
            "type": "array",
            "description": "Every chain the live 402 challenge currently offers, in the order it lists them. Built from the same configuration the challenge is, so it is correct from the moment the process is up and cannot disagree with what a payer is quoted — unlike `rails`, which is the last probe's findings. Optional: a deployment that predates this field omits it.",
            "items": {
              "type": "object",
              "properties": {
                "network": {
                  "type": "string",
                  "description": "CAIP-2 network id, as the x402 v2 challenge names it."
                },
                "name": {
                  "type": "string",
                  "description": "The chain's human-readable name, e.g. \"Base\"."
                }
              },
              "required": [
                "network",
                "name"
              ]
            }
          },
          "rails": {
            "type": "array",
            "description": "Per-rail probe findings for rails beyond the primary one. Empty on a single-rail origin, and empty before the first probe. Optional: a deployment that predates this field omits it.",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "description": "Stable rail id, e.g. \"solana\"."
                },
                "network": {
                  "type": "string",
                  "description": "CAIP-2 network id for the rail."
                },
                "status": {
                  "type": "string",
                  "description": "\"ok\" when nothing was found wrong with the rail. \"broken\" when it cannot currently take money. \"unknown\" when the check could not be completed — an unreachable node or a facilitator that returned no verdict — which is not a pass: the rail was neither confirmed nor ruled out this run.",
                  "enum": [
                    "ok",
                    "broken",
                    "unknown"
                  ]
                },
                "published": {
                  "type": "boolean",
                  "description": "Whether the rail is currently being advertised in the 402 challenge. A configured rail can be withheld — a Solana entry without the facilitator's fee payer cannot be turned into a transaction — so this is reported separately from `status`."
                },
                "verified": {
                  "type": "string",
                  "description": "How deeply the rail was checked, which is independent of `status`. \"payment\" means a real payment for this rail was built, signed and put to the facilitator for a verdict. \"declaration\" means only the facilitator's advertisement was read, so everything past the 402 is unproven — treat an \"ok\"/\"declaration\" rail as untested rather than as working. Optional: a deployment that predates this field omits it.",
                  "enum": [
                    "declaration",
                    "payment"
                  ]
                }
              },
              "required": [
                "id",
                "network",
                "status",
                "published"
              ]
            }
          }
        },
        "required": [
          "status",
          "classification",
          "detail",
          "checkedAt",
          "target"
        ]
      },
      "ApiError": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          }
        },
        "required": [
          "error"
        ]
      },
      "MonitorSummary": {
        "description": "Coarse free card — identity, source, extraction status, the headline release, how much is on record, and where the paid endpoints are. Never contains entry bodies or change history.",
        "type": "object",
        "properties": {
          "slug": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "homepageUrl": {
            "type": [
              "string",
              "null"
            ]
          },
          "sourceUrl": {
            "type": "string"
          },
          "sourceType": {
            "type": "string",
            "enum": [
              "github_releases",
              "atom",
              "rss",
              "json",
              "html"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "verifying",
              "ok",
              "error",
              "skipped"
            ],
            "description": "Extraction health, as of the last check. `ok` means that check read entries from the source; `error` means it failed; `pending` means the vendor has been seeded but never checked; `skipped` means the source URL was rejected at seed time. `verifying` is retired — it used to persist until two consecutive extracts matched, which made it a standing state for the vendors that ship most often — and is no longer emitted."
          },
          "lastReleaseTitle": {
            "type": [
              "string",
              "null"
            ],
            "description": "Title of the vendor's newest shipped release, or the exact string \"No usable release detected\" when the last crawl produced nothing this API will vouch for — an undated entry, or page navigation such as \"On this page\" mistaken for content. When the newest entry in the feed is a pre-release (rc, alpha, beta, dev, nightly) or a codegen tag such as fern-generation, the newest stable entry is reported here instead and the pre-release is named in lastPrereleaseTitle. Never a guess: if every entry searched is a pre-release, that pre-release is reported — flagged by lastReleaseIsPrerelease — rather than inventing a version the source never printed."
          },
          "lastReleaseAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When that release was published. Null whenever lastReleaseTitle is the fallback string: the two always agree."
          },
          "lastPrereleaseTitle": {
            "type": [
              "string",
              "null"
            ],
            "description": "The newer pre-release or codegen tag that was set aside in favour of the stable release above. Null when nothing was set aside. The entry itself is still returned by the paid endpoint, so history stays complete."
          },
          "lastReleaseIsPrerelease": {
            "type": "boolean",
            "description": "True when lastReleaseTitle is itself a pre-release or codegen tag rather than a shipped version — the case where no stable release was found within headlineLookback entries, which happens to a vendor counting down to a major version in a long run of candidates, and to a monitor added while that run fills its whole feed. The entry is still reported as the vendor's newest, because no version the source never printed is invented, but it is marked here so an unreleased beta cannot be read as the vendor's current version. False on an ordinary card, including one whose stable headline has a newer pre-release named in lastPrereleaseTitle."
          },
          "headlineLookback": {
            "type": [
              "integer",
              "null"
            ],
            "description": "How many of this vendor's most recent entries were searched for a shipped release before settling for the pre-release above. The search is bounded so a free catalog request cannot become a full-table read, so a stable release further back than this is out of reach and the pre-release stands. Null whenever lastReleaseIsPrerelease is false: nothing was looked past, so there is no bound to state."
          },
          "lastCheckedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "lastChangeAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When this vendor last changed something after we had already read it. Null until then: a monitor's first successful crawl is a baseline, not news, so a vendor that has published nothing since we started watching reports no change."
          },
          "releaseCount": {
            "type": "integer",
            "description": "Entries on record for this vendor, all of them paid."
          },
          "changeCount": {
            "type": "integer",
            "description": "Detected changes on record for this vendor."
          },
          "paid": {
            "$ref": "#/components/schemas/PaidPointer"
          }
        },
        "required": [
          "slug",
          "name",
          "homepageUrl",
          "sourceUrl",
          "sourceType",
          "status",
          "lastReleaseTitle",
          "lastReleaseAt",
          "lastPrereleaseTitle",
          "lastReleaseIsPrerelease",
          "headlineLookback",
          "lastCheckedAt",
          "lastChangeAt",
          "releaseCount",
          "changeCount",
          "paid"
        ]
      },
      "ChangelogTeaser": {
        "description": "Coarse free teaser — identity, source, the headline release and the times, how much is on record, and where the paid endpoints are. Entry bodies and change history are paid.",
        "type": "object",
        "properties": {
          "slug": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "homepageUrl": {
            "type": [
              "string",
              "null"
            ]
          },
          "sourceUrl": {
            "type": "string",
            "description": "The official feed or changelog page this vendor is read from."
          },
          "sourceType": {
            "type": "string",
            "enum": [
              "github_releases",
              "atom",
              "rss",
              "json",
              "html"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "verifying",
              "ok",
              "error",
              "skipped"
            ],
            "description": "Extraction health, as of the last check. `ok` means that check read entries from the source; `error` means it failed; `pending` means the vendor has been seeded but never checked; `skipped` means the source URL was rejected at seed time. `verifying` is retired — it used to persist until two consecutive extracts matched, which made it a standing state for the vendors that ship most often — and is no longer emitted."
          },
          "lastReleaseTitle": {
            "type": [
              "string",
              "null"
            ],
            "description": "Title of the vendor's newest shipped release, or the exact string \"No usable release detected\" when the last crawl produced nothing this API will vouch for. A pre-release or codegen tag at the top of the feed is passed over in favour of the newest stable entry, which is then named in lastPrereleaseTitle. Never a guess: when no stable entry is found within headlineLookback, the pre-release is reported and lastReleaseIsPrerelease says so."
          },
          "lastReleaseAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When that release was published. Null whenever lastReleaseTitle is the fallback string: the two always agree."
          },
          "lastPrereleaseTitle": {
            "type": [
              "string",
              "null"
            ],
            "description": "The newer pre-release or codegen tag that was set aside in favour of the stable release above. Null when nothing was set aside."
          },
          "lastReleaseIsPrerelease": {
            "type": "boolean",
            "description": "True when lastReleaseTitle is itself a pre-release or codegen tag rather than a shipped version, because no stable release was found within headlineLookback entries. The pre-release is still reported — no version the source never printed is invented — but it is marked here so an unreleased beta cannot be read as the vendor's current version."
          },
          "headlineLookback": {
            "type": [
              "integer",
              "null"
            ],
            "description": "How many of this vendor's most recent entries were searched for a shipped release before settling for the pre-release above. Null whenever lastReleaseIsPrerelease is false."
          },
          "lastCheckedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "lastChangeAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When this vendor last changed something after we had already read it. Null until then — the first crawl is a baseline, not news."
          },
          "releaseCount": {
            "type": "integer"
          },
          "changeCount": {
            "type": "integer"
          },
          "paid": {
            "$ref": "#/components/schemas/PaidPointer"
          }
        },
        "required": [
          "slug",
          "name",
          "homepageUrl",
          "sourceUrl",
          "sourceType",
          "status",
          "lastReleaseTitle",
          "lastReleaseAt",
          "lastPrereleaseTitle",
          "lastReleaseIsPrerelease",
          "headlineLookback",
          "lastCheckedAt",
          "lastChangeAt",
          "releaseCount",
          "changeCount",
          "paid"
        ]
      },
      "PaidPointer": {
        "type": "object",
        "properties": {
          "detail": {
            "type": "string",
            "description": "Absolute URL of the paid structured releases endpoint."
          },
          "changes": {
            "type": "string",
            "description": "Absolute URL of the paid change history endpoint."
          },
          "price": {
            "type": "string"
          },
          "currency": {
            "type": "string"
          },
          "network": {
            "type": "string",
            "description": "Primary settlement rail's x402 v2 network id, kept as a single string for backward compatibility. See `networks` for every rail this service will settle a payment on."
          },
          "networks": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Every x402 v2 network id this service will settle a payment on, primary rail first. Length 1 with a single configured rail, more when additional rails (e.g. Solana) are configured."
          },
          "protocol": {
            "type": "string"
          }
        },
        "required": [
          "detail",
          "changes",
          "price",
          "currency",
          "network",
          "networks",
          "protocol"
        ]
      },
      "ReleaseEntry": {
        "description": "One structured changelog entry. Bodies are capped at 800 characters; version and url are never dropped when the source stated them, so a capped entry is always one link away from its full text.",
        "type": "object",
        "properties": {
          "entryKey": {
            "type": "string"
          },
          "version": {
            "type": [
              "string",
              "null"
            ],
            "description": "Version string exactly as the source printed it. Null when the source stated none — never inferred."
          },
          "title": {
            "type": "string"
          },
          "url": {
            "type": [
              "string",
              "null"
            ],
            "description": "Link to the entry at the source, when the source gave one."
          },
          "body": {
            "type": [
              "string",
              "null"
            ],
            "description": "Entry text, at most 800 characters. A longer body is cut on a word boundary, ends with an ellipsis, and sets bodyTruncated."
          },
          "bodyTruncated": {
            "type": "boolean",
            "description": "True when body was cut at the 800-character cap. False when the body is complete or absent."
          },
          "publishedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "breaking": {
            "type": "boolean"
          },
          "breakingEvidence": {
            "type": [
              "string",
              "null"
            ]
          },
          "firstSeenAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "entryKey",
          "version",
          "title",
          "url",
          "body",
          "bodyTruncated",
          "publishedAt",
          "breaking",
          "breakingEvidence",
          "firstSeenAt"
        ]
      },
      "MonitorDetail": {
        "description": "The paid snapshot: the vendor's structured recent releases. The default first page (limit=5, no cursor) is curated — the headline release (newest stable, skipping codegen/fern/unversioned machine tags) comes first, then any other breaking releases, then the newest remaining entries. Pages requested with an explicit cursor use straight newest-first order so the full history is walkable. Sized, not trimmed — `limit` echoes the page size actually applied (default 5, maximum 50), `hasMore` says whether older entries remain after this page, and `nextCursor` is the token that fetches them. Walking `nextCursor` reaches every entry counted in `releaseCount`, so nothing on record is out of reach; each page is a separate paid call. Each entry body is capped at 800 characters and sets `bodyTruncated` when it was cut.",
        "type": "object",
        "properties": {
          "slug": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "homepageUrl": {
            "type": [
              "string",
              "null"
            ]
          },
          "sourceUrl": {
            "type": "string"
          },
          "sourceType": {
            "type": "string",
            "enum": [
              "github_releases",
              "atom",
              "rss",
              "json",
              "html"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "verifying",
              "ok",
              "error",
              "skipped"
            ],
            "description": "Extraction health, as of the last check. `ok` means that check read entries from the source; `error` means it failed; `pending` means the vendor has been seeded but never checked; `skipped` means the source URL was rejected at seed time. `verifying` is retired — it used to persist until two consecutive extracts matched, which made it a standing state for the vendors that ship most often — and is no longer emitted."
          },
          "lastReleaseTitle": {
            "type": [
              "string",
              "null"
            ],
            "description": "The vendor's newest shipped release — the same headline the free catalog reports, so the two tiers cannot disagree. A pre-release or codegen tag at the top of the feed is passed over in favour of the newest stable entry; both remain in releases[]. When no stable entry is found within headlineLookback, the pre-release is reported and lastReleaseIsPrerelease says so."
          },
          "lastReleaseAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "lastPrereleaseTitle": {
            "type": [
              "string",
              "null"
            ],
            "description": "The newer pre-release or codegen tag set aside in favour of the stable headline. Null when nothing was set aside."
          },
          "lastReleaseIsPrerelease": {
            "type": "boolean",
            "description": "True when lastReleaseTitle is itself a pre-release or codegen tag rather than a shipped version, because no stable release was found within headlineLookback entries — a vendor deep in a run of candidates, or a monitor added while that run fills its whole feed. The entry is still reported, since no version the source never printed is invented, but it is marked here so an unreleased beta cannot be read as the vendor's current version."
          },
          "headlineLookback": {
            "type": [
              "integer",
              "null"
            ],
            "description": "How many of this vendor's most recent entries were searched for a shipped release before settling for the pre-release above. The search is bounded, so a stable release further back than this is out of reach; releases[] still carries every entry on record, page by page. Null whenever lastReleaseIsPrerelease is false."
          },
          "lastCheckedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "lastSuccessAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "lastChangeAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "releaseCount": {
            "type": "integer",
            "description": "Total entries on record for this vendor, across all pages."
          },
          "limit": {
            "type": "integer",
            "description": "Page size applied to this response — the `limit` query parameter, or 5 when it was not supplied."
          },
          "hasMore": {
            "type": "boolean",
            "description": "True when older entries remain after this page. Nothing was silently dropped: raise limit (maximum 50) to widen the page, or pass nextCursor to fetch the next one."
          },
          "nextCursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Opaque token for the page after this one, or null when this page reached the oldest entry on record. Non-null exactly when hasMore is true. Send it back as the `cursor` query parameter — verbatim, for this same vendor — to continue the walk."
          },
          "releases": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ReleaseEntry"
            }
          }
        },
        "required": [
          "slug",
          "name",
          "homepageUrl",
          "sourceUrl",
          "sourceType",
          "status",
          "lastReleaseTitle",
          "lastReleaseAt",
          "lastPrereleaseTitle",
          "lastReleaseIsPrerelease",
          "headlineLookback",
          "lastCheckedAt",
          "lastSuccessAt",
          "lastChangeAt",
          "releaseCount",
          "limit",
          "hasMore",
          "nextCursor",
          "releases"
        ]
      },
      "ChangeReleaseRef": {
        "type": "object",
        "properties": {
          "title": {
            "type": "string"
          },
          "version": {
            "type": [
              "string",
              "null"
            ]
          },
          "url": {
            "type": [
              "string",
              "null"
            ]
          },
          "publishedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        },
        "required": [
          "title"
        ]
      },
      "ChangeEntry": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "changeType": {
            "type": "string",
            "enum": [
              "new_release",
              "version_changed",
              "date_changed",
              "title_changed",
              "breaking_signal"
            ]
          },
          "summary": {
            "type": "string"
          },
          "previousValue": {
            "type": [
              "string",
              "null"
            ]
          },
          "newValue": {
            "type": [
              "string",
              "null"
            ]
          },
          "evidence": {
            "type": [
              "string",
              "null"
            ],
            "description": "Literal source text backing a breaking signal, when present."
          },
          "detectedAt": {
            "type": "string",
            "format": "date-time"
          },
          "release": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/ChangeReleaseRef"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "id",
          "changeType",
          "summary",
          "detectedAt"
        ]
      },
      "ChangeFeed": {
        "description": "The paid change history: what this vendor altered after we had already read it — new entries, version and date changes, retitles, and breaking signals quoted from the source. Not a second copy of the snapshot, and not the first read of the source arriving one page at a time. An empty feed is an answer, not a gap: `watchedSince` says when the baseline was captured and `releaseCount` shows the source is being read, so changeCount 0 means \"nothing has changed since that date\", never \"the feed is missing\". Sized via `limit` (default 50, maximum 200) and `hasMore`. Unlike the snapshot this feed carries no cursor: rows older than the maximum page are counted in `changeCount` but are reached only by narrowing the query with `since` or `type`.",
        "type": "object",
        "properties": {
          "slug": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "changeCount": {
            "type": "integer",
            "description": "Total changes matching this query, across all pages. Zero means nothing has changed since watchedSince."
          },
          "watchedSince": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When this vendor's baseline was captured. Entries already on the page at that moment are history we read, not changes we detected, so they are in the paid snapshot rather than here. The same holds for an entry that a slow or paginated first read only surfaced on one of the next few checks, but only when the source dates it before this moment. Anything the vendor published after it, and anything the source does not date at all, is an arrival and appears here. Null only when the source has never been read successfully."
          },
          "releaseCount": {
            "type": "integer",
            "description": "Entries on record for this vendor. Non-zero with changeCount 0 means the source is being read and has simply not changed."
          },
          "limit": {
            "type": "integer",
            "description": "Page size applied to this response — the `limit` query parameter, or 50 when it was not supplied."
          },
          "hasMore": {
            "type": "boolean",
            "description": "True when changeCount exceeds the rows returned here — older rows exist on record. Raise limit (maximum 200) to widen the page, or narrow the query with since/type; there is no cursor on this feed."
          },
          "changes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ChangeEntry"
            }
          }
        },
        "required": [
          "slug",
          "name",
          "changeCount",
          "watchedSince",
          "releaseCount",
          "limit",
          "hasMore",
          "changes"
        ]
      },
      "PaymentRequirements": {
        "description": "A single x402 v2 accepts entry. Note the v2 field name `amount`; the v1 spelling was `maxAmountRequired`, and the resource description moved out of this object into the top-level `resource` field.",
        "type": "object",
        "properties": {
          "scheme": {
            "type": "string"
          },
          "network": {
            "type": "string",
            "description": "CAIP-2 network id."
          },
          "amount": {
            "type": "string",
            "description": "Price in atomic units of `asset`."
          },
          "asset": {
            "type": "string"
          },
          "payTo": {
            "type": "string"
          },
          "maxTimeoutSeconds": {
            "type": "integer"
          },
          "extra": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": true
          }
        },
        "required": [
          "scheme",
          "network",
          "amount",
          "asset",
          "payTo"
        ]
      },
      "ResourceInfo": {
        "description": "Describes the resource being paid for.",
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "description": "Absolute https URL of the paid resource."
          },
          "description": {
            "type": "string"
          },
          "mimeType": {
            "type": "string"
          },
          "serviceName": {
            "type": "string"
          }
        },
        "required": [
          "url"
        ]
      },
      "PaymentRequiredChallenge": {
        "description": "The x402 v2 `PaymentRequired` object. Also returned base64-encoded in the `PAYMENT-REQUIRED` response header, which is the canonical wire location for the HTTP transport.",
        "type": "object",
        "properties": {
          "x402Version": {
            "type": "integer"
          },
          "error": {
            "type": "string"
          },
          "resource": {
            "$ref": "#/components/schemas/ResourceInfo"
          },
          "accepts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PaymentRequirements"
            }
          },
          "extensions": {
            "type": "object",
            "additionalProperties": true
          }
        },
        "required": [
          "x402Version",
          "resource",
          "accepts"
        ]
      },
      "FeedbackAgent": {
        "type": "object",
        "description": "Who is filing, when it is an agent. All optional.",
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 120,
            "nullable": true
          },
          "model": {
            "type": "string",
            "maxLength": 120,
            "nullable": true
          },
          "is_agent": {
            "type": "boolean",
            "nullable": true
          }
        }
      },
      "FeedbackSubmission": {
        "type": "object",
        "description": "One structured filing. Plain text only. Unknown fields are ignored. Total body limit 16 KB.",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "bug",
              "missing_feature",
              "need",
              "request",
              "complaint"
            ]
          },
          "title": {
            "type": "string",
            "minLength": 8,
            "maxLength": 140
          },
          "message": {
            "type": "string",
            "minLength": 16,
            "maxLength": 4000
          },
          "severity": {
            "type": "string",
            "enum": [
              "low",
              "medium",
              "high",
              "blocker"
            ],
            "nullable": true
          },
          "monitor_slug": {
            "type": "string",
            "maxLength": 64,
            "nullable": true,
            "description": "A tracked vendor slug from GET /api/monitors, if the filing is about one."
          },
          "endpoint": {
            "type": "string",
            "maxLength": 200,
            "nullable": true,
            "description": "The path you called, e.g. /api/monitors/openai. Recorded, never fetched."
          },
          "method": {
            "type": "string",
            "enum": [
              "GET",
              "POST",
              "PUT",
              "PATCH",
              "DELETE"
            ],
            "nullable": true
          },
          "status_code": {
            "type": "integer",
            "minimum": 100,
            "maximum": 599,
            "nullable": true
          },
          "request_id": {
            "type": "string",
            "maxLength": 200,
            "nullable": true
          },
          "repro_steps": {
            "type": "string",
            "maxLength": 4000,
            "nullable": true
          },
          "expected": {
            "type": "string",
            "maxLength": 2000,
            "nullable": true
          },
          "actual": {
            "type": "string",
            "maxLength": 2000,
            "nullable": true
          },
          "agent": {
            "$ref": "#/components/schemas/FeedbackAgent"
          },
          "contact": {
            "type": "string",
            "maxLength": 200,
            "nullable": true,
            "description": "Optional. Stored only if you supply it."
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true,
            "nullable": true,
            "description": "Small JSON object, at most 4 KB serialised."
          }
        },
        "required": [
          "type",
          "title",
          "message"
        ]
      },
      "FeedbackReceipt": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "enum": [
              "new"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "status",
          "created_at"
        ]
      },
      "AdminFeedbackItem": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "product": {
            "type": "string"
          },
          "monitorSlug": {
            "type": "string",
            "nullable": true
          },
          "type": {
            "type": "string",
            "enum": [
              "bug",
              "missing_feature",
              "need",
              "request",
              "complaint"
            ]
          },
          "title": {
            "type": "string"
          },
          "message": {
            "type": "string"
          },
          "severity": {
            "type": "string",
            "nullable": true
          },
          "endpoint": {
            "type": "string",
            "nullable": true
          },
          "method": {
            "type": "string",
            "nullable": true
          },
          "statusCode": {
            "type": "integer",
            "nullable": true
          },
          "requestId": {
            "type": "string",
            "nullable": true
          },
          "reproSteps": {
            "type": "string",
            "nullable": true
          },
          "expected": {
            "type": "string",
            "nullable": true
          },
          "actual": {
            "type": "string",
            "nullable": true
          },
          "isAgent": {
            "type": "boolean"
          },
          "agentName": {
            "type": "string",
            "nullable": true
          },
          "agentModel": {
            "type": "string",
            "nullable": true
          },
          "contact": {
            "type": "string",
            "nullable": true
          },
          "actorUserId": {
            "type": "string",
            "nullable": true
          },
          "unauthenticated": {
            "type": "boolean"
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true,
            "nullable": true
          },
          "draftPrPrompt": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "new",
              "reviewed",
              "accepted",
              "implemented",
              "rejected",
              "spam"
            ]
          },
          "adminNotes": {
            "type": "string",
            "nullable": true
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          },
          "reviewedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        },
        "required": [
          "id",
          "product",
          "monitorSlug",
          "type",
          "title",
          "message",
          "severity",
          "endpoint",
          "method",
          "statusCode",
          "requestId",
          "reproSteps",
          "expected",
          "actual",
          "isAgent",
          "agentName",
          "agentModel",
          "contact",
          "actorUserId",
          "unauthenticated",
          "metadata",
          "draftPrPrompt",
          "status",
          "adminNotes",
          "createdAt",
          "updatedAt",
          "reviewedAt"
        ]
      },
      "AdminFeedbackListItem": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "enum": [
              "bug",
              "missing_feature",
              "need",
              "request",
              "complaint"
            ]
          },
          "title": {
            "type": "string"
          },
          "severity": {
            "type": "string",
            "nullable": true
          },
          "monitorSlug": {
            "type": "string",
            "nullable": true
          },
          "isAgent": {
            "type": "boolean"
          },
          "agentName": {
            "type": "string",
            "nullable": true
          },
          "unauthenticated": {
            "type": "boolean"
          },
          "status": {
            "type": "string",
            "enum": [
              "new",
              "reviewed",
              "accepted",
              "implemented",
              "rejected",
              "spam"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "type",
          "title",
          "severity",
          "monitorSlug",
          "isAgent",
          "agentName",
          "unauthenticated",
          "status",
          "createdAt"
        ]
      },
      "AdminFeedbackList": {
        "type": "object",
        "properties": {
          "total": {
            "type": "integer",
            "description": "Rows matching the filters, across every page."
          },
          "page": {
            "type": "integer"
          },
          "limit": {
            "type": "integer"
          },
          "hasMore": {
            "type": "boolean"
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AdminFeedbackListItem"
            }
          }
        },
        "required": [
          "total",
          "page",
          "limit",
          "hasMore",
          "items"
        ]
      },
      "AdminFeedbackTypeCounts": {
        "type": "object",
        "properties": {
          "bug": {
            "type": "integer"
          },
          "missing_feature": {
            "type": "integer"
          },
          "need": {
            "type": "integer"
          },
          "request": {
            "type": "integer"
          },
          "complaint": {
            "type": "integer"
          }
        },
        "required": [
          "bug",
          "missing_feature",
          "need",
          "request",
          "complaint"
        ]
      },
      "AdminFeedbackSummary": {
        "type": "object",
        "properties": {
          "total": {
            "type": "integer"
          },
          "new": {
            "type": "integer"
          },
          "agents": {
            "type": "integer"
          },
          "humans": {
            "type": "integer"
          },
          "byType": {
            "$ref": "#/components/schemas/AdminFeedbackTypeCounts"
          },
          "recent": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AdminFeedbackListItem"
            }
          }
        },
        "required": [
          "total",
          "new",
          "agents",
          "humans",
          "byType",
          "recent"
        ]
      },
      "AdminFeedbackUpdate": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "new",
              "reviewed",
              "accepted",
              "implemented",
              "rejected",
              "spam"
            ]
          },
          "admin_notes": {
            "type": "string",
            "maxLength": 4000,
            "nullable": true
          }
        }
      },
      "AdminSessionState": {
        "type": "object",
        "properties": {
          "authenticated": {
            "type": "boolean"
          }
        },
        "required": [
          "authenticated"
        ]
      },
      "AdminCredentials": {
        "type": "object",
        "properties": {
          "password": {
            "type": "string",
            "minLength": 1
          }
        },
        "required": [
          "password"
        ]
      },
      "RequestCounts": {
        "type": "object",
        "properties": {
          "total": {
            "type": "integer"
          },
          "free": {
            "type": "integer"
          },
          "askedToPay": {
            "type": "integer"
          },
          "paidSettled": {
            "type": "integer"
          },
          "notFound": {
            "type": "integer"
          },
          "badRequest": {
            "type": "integer"
          },
          "errors": {
            "type": "integer"
          },
          "paymentFailed": {
            "description": "Paid requests whose payment was presented but refused at verify or settle (`paid_verify_failed` + `paid_settle_failed`). A 402 challenge is never counted here.",
            "type": "integer"
          },
          "conversionRate": {
            "description": "Fleet-wide share of pay-demand (askedToPay + paidSettled) that converted to a settled payment, from 0 to 1 — across every request in the window, attributed to a vendor or not. Null when nobody was ever challenged to pay.",
            "type": [
              "number",
              "null"
            ]
          }
        },
        "required": [
          "total",
          "free",
          "askedToPay",
          "paidSettled",
          "notFound",
          "badRequest",
          "errors",
          "paymentFailed",
          "conversionRate"
        ]
      },
      "RevenueSummary": {
        "description": "Settled revenue only. Asked-to-pay is never counted.",
        "type": "object",
        "properties": {
          "settledCalls": {
            "type": "integer"
          },
          "settledUsdc": {
            "type": "string"
          },
          "pricePerCallUsdc": {
            "type": "string"
          }
        },
        "required": [
          "settledCalls",
          "settledUsdc",
          "pricePerCallUsdc"
        ]
      },
      "SlugAttribution": {
        "type": "object",
        "properties": {
          "slug": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "paidSettled": {
            "type": "integer"
          },
          "askedToPay": {
            "type": "integer"
          },
          "freeRequests": {
            "type": "integer"
          },
          "settledUsdc": {
            "description": "Settled-only revenue attributed to this vendor. Never includes an unsettled 200.",
            "type": "string"
          },
          "conversionRate": {
            "description": "Share of this vendor's pay-demand (askedToPay + paidSettled) that converted to a settled payment, from 0 to 1. Null when the vendor has never been challenged to pay, since there is no demand to rate.",
            "type": [
              "number",
              "null"
            ]
          }
        },
        "required": [
          "slug",
          "name",
          "paidSettled",
          "askedToPay",
          "freeRequests",
          "settledUsdc",
          "conversionRate"
        ]
      },
      "VendorTotals": {
        "type": "object",
        "description": "The same attribution as SlugAttribution, summed across every vendor in the window rather than a single one — the fleet-wide close for the Top Vendors table. Gated on whether the fleet has any traffic at all, never on whether topSlugs happens to list any rows, so a window whose traffic is otherwise unranked still gets a total.",
        "properties": {
          "paidSettled": {
            "type": "integer"
          },
          "askedToPay": {
            "type": "integer"
          },
          "freeRequests": {
            "type": "integer"
          },
          "settledUsdc": {
            "description": "Settled-only revenue summed across every vendor. Never includes an unsettled 200.",
            "type": "string"
          },
          "conversionRate": {
            "description": "Fleet-wide share of pay-demand (askedToPay + paidSettled) that converted to a settled payment, from 0 to 1. Null when no vendor was ever challenged to pay in the window.",
            "type": [
              "number",
              "null"
            ]
          }
        },
        "required": [
          "paidSettled",
          "askedToPay",
          "freeRequests",
          "settledUsdc",
          "conversionRate"
        ]
      },
      "UnattributedTraffic": {
        "type": "object",
        "description": "Traffic no vendor can be credited with — discovery documents and the free catalog carry no slug at all, and neither does an unknown-slug request that never joined to a monitor. Same categories as VendorTotals, computed the same way the archived PDF report's own \"No vendor (discovery, catalog)\" row is: the fleet-wide RequestCounts minus VendorTotals, never a count of its own.",
        "properties": {
          "paidSettled": {
            "type": "integer"
          },
          "askedToPay": {
            "type": "integer"
          },
          "freeRequests": {
            "type": "integer"
          },
          "settledUsdc": {
            "description": "Settled-only revenue with no vendor attribution. Never includes an unsettled 200.",
            "type": "string"
          },
          "conversionRate": {
            "description": "Share of unattributed pay-demand (askedToPay + paidSettled) that converted to a settled payment, from 0 to 1. Null when no unattributed request was ever challenged to pay.",
            "type": [
              "number",
              "null"
            ]
          }
        },
        "required": [
          "paidSettled",
          "askedToPay",
          "freeRequests",
          "settledUsdc",
          "conversionRate"
        ]
      },
      "AdminVendorsPage": {
        "type": "object",
        "description": "Every tracked vendor matching `search` (or every tracked vendor, when omitted) — including one with zero requests in the window — alphabetical by name by default, or sorted/ordered per `sort`/ `order`: this is the \"find any vendor, ranked any way\" view, not a fixed top-N. `total` counts every matching vendor regardless of `offset`/`limit`, so the client can render \"X of Y\" and page controls.",
        "properties": {
          "window": {
            "type": "string"
          },
          "search": {
            "type": [
              "string",
              "null"
            ]
          },
          "sort": {
            "type": "string",
            "enum": [
              "name",
              "freeRequests",
              "askedToPay",
              "paidSettled",
              "settledUsdc",
              "conversionRate"
            ]
          },
          "order": {
            "type": "string",
            "enum": [
              "asc",
              "desc"
            ]
          },
          "total": {
            "type": "integer",
            "description": "Matching vendors in total, independent of the page returned."
          },
          "offset": {
            "type": "integer"
          },
          "limit": {
            "type": "integer"
          },
          "hasMore": {
            "type": "boolean",
            "description": "True when vendors past this page still match — raise offset to see them."
          },
          "vendors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SlugAttribution"
            }
          }
        },
        "required": [
          "window",
          "search",
          "sort",
          "order",
          "total",
          "offset",
          "limit",
          "hasMore",
          "vendors"
        ]
      },
      "PathUsage": {
        "description": "Busiest routes by request volume, with dynamic segments collapsed (a real slug never fragments this).",
        "type": "object",
        "properties": {
          "path": {
            "type": "string"
          },
          "method": {
            "type": "string"
          },
          "total": {
            "type": "integer"
          }
        },
        "required": [
          "path",
          "method",
          "total"
        ]
      },
      "UnattributedPath": {
        "description": "One path that produced pay-demand no vendor can be credited with, and why. Unlike PathUsage these are literal paths, never collapsed to a route pattern: the whole point is to separate the documented `{slug}` placeholder replayed as an address from a real vendor whose row is merely missing its attribution, and collapsing them would hide exactly that.",
        "type": "object",
        "properties": {
          "path": {
            "type": "string"
          },
          "method": {
            "type": "string"
          },
          "recordedSlug": {
            "type": [
              "string",
              "null"
            ],
            "description": "The slug the row itself carries, when it carries one the catalog no longer has. Null for everything else — a row that named no vendor at the time it was served."
          },
          "askedToPay": {
            "type": "integer",
            "description": "402 challenges issued on this path in the window."
          },
          "lastAskedAt": {
            "type": "string",
            "format": "date-time",
            "description": "Most recent ask on this path. A cause that stopped accumulating reads as history; one still moving is a live source."
          },
          "cause": {
            "type": "string",
            "enum": [
              "missing_attribution",
              "deleted_vendor",
              "not_a_vendor"
            ],
            "description": "`missing_attribution` — a real, still-tracked vendor was resolved before the paywall opened, but the row predates 402 challenges carrying the slug; recoverable exactly by the vendor-attribution backfill. `deleted_vendor` — the row names a vendor the catalog no longer carries, so there is nothing to reconcile it to. `not_a_vendor` — the request never named a vendor at all, in practice the documented `{slug}` placeholder called as though it were a URL. The template is challenged on purpose, so registries can probe the documented path, and is never served, so this bucket keeps growing with registry and crawler probes."
          }
        },
        "required": [
          "path",
          "method",
          "recordedSlug",
          "askedToPay",
          "lastAskedAt",
          "cause"
        ]
      },
      "UnattributedPathBreakdown": {
        "description": "Where the unattributed pay-demand came from, so the warning card has somewhere to lead. `rows` is capped at the busiest handful; every count beside it is computed over all of them, so the cap can never understate the problem.",
        "type": "object",
        "properties": {
          "rows": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/UnattributedPath"
            }
          },
          "distinctPaths": {
            "type": "integer",
            "description": "How many distinct paths produced unattributed asks, of which `rows` shows the busiest."
          },
          "shownAsks": {
            "type": "integer",
            "description": "Asks covered by `rows` alone."
          },
          "totalAsks": {
            "type": "integer",
            "description": "Every unattributed ask in the window. Equal to `unattributed.askedToPay`, reached a different way — that one is a subtraction, this one a sum over the paths — so a disagreement between them is a real bug and not a rounding artefact."
          },
          "recoverableAsks": {
            "type": "integer",
            "description": "Asks a backfill run would move onto a real vendor (`cause` of `missing_attribution`), counted over every path rather than only the printed ones. The actionable slice of the warning."
          }
        },
        "required": [
          "rows",
          "distinctPaths",
          "shownAsks",
          "totalAsks",
          "recoverableAsks"
        ]
      },
      "ErrorLogEntry": {
        "description": "A real failure only. A 402 payment challenge and an unknown-slug 404 are ordinary traffic, not errors, and never appear here.",
        "type": "object",
        "properties": {
          "path": {
            "type": "string"
          },
          "method": {
            "type": "string"
          },
          "statusCode": {
            "type": "integer"
          },
          "outcome": {
            "type": "string"
          },
          "monitorSlug": {
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "failureReason": {
            "type": [
              "string",
              "null"
            ],
            "description": "Machine-readable verdict for a failed payment, e.g. `invalid_exact_evm_insufficient_balance` from the facilitator, or `payment_header_undecodable` when the payload never got that far. Null for failures that are not payment attempts."
          },
          "failureDetail": {
            "type": [
              "string",
              "null"
            ],
            "description": "The facilitator's own message, truncated. Never contains payment credentials."
          },
          "paymentHeader": {
            "type": [
              "string",
              "null"
            ],
            "description": "Which header the payment arrived in — `PAYMENT-SIGNATURE` (v2) or `X-PAYMENT` (v1)."
          },
          "paymentVersion": {
            "type": [
              "integer",
              "null"
            ],
            "description": "x402 protocol version the client spoke."
          },
          "payer": {
            "type": [
              "string",
              "null"
            ],
            "description": "Payer address, once decoding or the facilitator produced one."
          },
          "challengeIssued": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Whether the response told the caller how to pay. Null for rows recorded before this was tracked, which is not the same as false."
          }
        },
        "required": [
          "path",
          "method",
          "statusCode",
          "outcome",
          "monitorSlug",
          "createdAt",
          "failureReason",
          "failureDetail",
          "paymentHeader",
          "paymentVersion",
          "payer",
          "challengeIssued"
        ]
      },
      "FleetHealth": {
        "type": "object",
        "properties": {
          "total": {
            "type": "integer"
          },
          "ok": {
            "type": "integer"
          },
          "verifying": {
            "type": "integer",
            "description": "Retired, and expected to be 0. Nothing writes the `verifying` status any more; the bucket is kept so an existing dashboard or report does not lose a field it reads."
          },
          "pending": {
            "type": "integer"
          },
          "error": {
            "type": "integer"
          },
          "skipped": {
            "type": "integer"
          }
        },
        "required": [
          "total",
          "ok",
          "verifying",
          "pending",
          "error",
          "skipped"
        ]
      },
      "TimeseriesPoint": {
        "type": "object",
        "properties": {
          "date": {
            "type": "string"
          },
          "total": {
            "type": "integer"
          },
          "askedToPay": {
            "type": "integer"
          },
          "paidSettled": {
            "type": "integer"
          },
          "settledUsdc": {
            "type": "string"
          }
        },
        "required": [
          "date",
          "total",
          "askedToPay",
          "paidSettled",
          "settledUsdc"
        ]
      },
      "RecentChange": {
        "type": "object",
        "properties": {
          "slug": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "changeType": {
            "type": "string"
          },
          "summary": {
            "type": "string"
          },
          "detectedAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "slug",
          "name",
          "changeType",
          "summary",
          "detectedAt"
        ]
      },
      "AdminOverview": {
        "type": "object",
        "properties": {
          "window": {
            "type": "string"
          },
          "requests": {
            "$ref": "#/components/schemas/RequestCounts"
          },
          "revenue": {
            "$ref": "#/components/schemas/RevenueSummary"
          },
          "fleet": {
            "$ref": "#/components/schemas/FleetHealth"
          },
          "topSlugs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SlugAttribution"
            }
          },
          "vendorTotals": {
            "$ref": "#/components/schemas/VendorTotals"
          },
          "unattributed": {
            "$ref": "#/components/schemas/UnattributedTraffic"
          },
          "unattributedPaths": {
            "$ref": "#/components/schemas/UnattributedPathBreakdown"
          },
          "timeseries": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TimeseriesPoint"
            }
          },
          "recentChanges": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RecentChange"
            }
          },
          "busiestPaths": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PathUsage"
            }
          },
          "recentErrors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ErrorLogEntry"
            }
          },
          "schema": {
            "$ref": "#/components/schemas/SchemaDriftWatch"
          },
          "paymentFailures": {
            "$ref": "#/components/schemas/PaymentFailureWatch"
          },
          "unattributedDemand": {
            "$ref": "#/components/schemas/UnattributedDemandSignal"
          },
          "releases": {
            "$ref": "#/components/schemas/CaptureReleaseCounts"
          }
        },
        "required": [
          "window",
          "requests",
          "revenue",
          "fleet",
          "topSlugs",
          "vendorTotals",
          "unattributed",
          "unattributedPaths",
          "timeseries",
          "recentChanges",
          "busiestPaths",
          "recentErrors",
          "schema",
          "paymentFailures",
          "unattributedDemand",
          "releases"
        ]
      },
      "SchemaDriftWatch": {
        "type": "object",
        "description": "Whether the live database still matches the table definitions in code. The server refuses to boot against a database that contradicts them, but a column altered by hand while the service is running restarts nothing — so the catalog is re-checked on a slow cadence and the verdict recorded, rather than being discovered at the next deploy.",
        "properties": {
          "enabled": {
            "type": "boolean",
            "description": "False when MONITOR_SCHEMA_CHECK_HOURS is 0, in which case drift is only found at the next boot."
          },
          "intervalHours": {
            "type": "integer",
            "description": "Hours between checks (MONITOR_SCHEMA_CHECK_HOURS; 0 disables)."
          },
          "drifted": {
            "type": "boolean",
            "description": "The database is not what the code says it is, at either severity."
          },
          "breaking": {
            "type": "boolean",
            "description": "Something the code depends on is missing or contradicted, so requests touching those tables fail."
          },
          "advisory": {
            "type": "boolean",
            "description": "Real differences that cannot break a query on their own — an extra nullable column, a missing non-unique index."
          },
          "driftedSince": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the current mismatch was first observed."
          },
          "alertedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the operator was last notified about a breaking mismatch, and not yet told it was reconciled. Null while drifted means nobody has been told — either no channel is configured or the notification could not be delivered. Set while not drifted means the database is fine but the all-clear has not reached the operator yet; `detail` says so."
          },
          "lastCheckedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the catalog was last read. Null means it never has been."
          },
          "nextCheckAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "detail": {
            "type": "string",
            "description": "One-line explanation, naming the specific differences when there are any."
          }
        },
        "required": [
          "enabled",
          "intervalHours",
          "drifted",
          "breaking",
          "advisory",
          "detail"
        ]
      },
      "PaymentFailureWatch": {
        "type": "object",
        "description": "Whether paid calls are currently failing for a reason this service controls (a bad payment-header decoder, a wrong facilitator URL, a facilitator outage) as opposed to a payer's empty wallet, which is normal traffic and never reported here. Read from the recorded verdict of the continuous check, not re-probed on every dashboard poll.",
        "properties": {
          "enabled": {
            "type": "boolean",
            "description": "False when MONITOR_PAYMENT_FAILURE_CHECK_MINUTES is 0, in which case failures are only visible in the request log, not alerted."
          },
          "checkMinutes": {
            "type": "integer",
            "description": "Minutes between checks (MONITOR_PAYMENT_FAILURE_CHECK_MINUTES; 0 disables)."
          },
          "windowMinutes": {
            "type": "integer",
            "description": "How far back each check looks for failures."
          },
          "threshold": {
            "type": "integer",
            "description": "Failures required, per (reason, vendor), before this is reported."
          },
          "failing": {
            "type": "boolean",
            "description": "An operator-fault run of failures is currently outstanding."
          },
          "since": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the current run of failures was first seen. Null means none is outstanding."
          },
          "alertedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the operator was last notified about the current run, and not yet told it cleared. Null while failing means nobody has been told — either no channel is configured or the notification could not be delivered."
          },
          "lastCheckedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When request_logs was last read for this check. Null means it never has been."
          },
          "nextCheckAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "detail": {
            "type": "string",
            "description": "One-line explanation, naming the failing reason and vendor when there are any. Already scrubbed of anything secret-bearing, the same way every other alert-facing detail string is."
          }
        },
        "required": [
          "enabled",
          "checkMinutes",
          "windowMinutes",
          "threshold",
          "failing",
          "since",
          "alertedAt",
          "lastCheckedAt",
          "nextCheckAt",
          "detail"
        ]
      },
      "UnattributedDemandSignal": {
        "type": "object",
        "description": "Whether unattributed pay-demand — 402 challenges that named no tracked vendor — has grown disproportionate to the demand vendors generated in the same window. A quiet catalog picking up a handful of stray probes is normal; most of what callers are being charged for belonging to no vendor at all is a different fact, and one this signal raises actively instead of leaving an operator to notice it by eye in the Top Vendors table. Computed live from this same window's own `unattributed`/`vendorTotals` counts, not a separately scheduled check like the two watches above.",
        "properties": {
          "warning": {
            "type": "boolean",
            "description": "Unattributed pay-demand is disproportionately large relative to vendor-attributed demand."
          },
          "ratio": {
            "type": [
              "number",
              "null"
            ],
            "description": "Unattributed askedToPay divided by vendor-attributed askedToPay in this window. Null when vendors generated zero pay-demand at all, in which case the ratio is unbounded rather than a real number."
          },
          "minimumAsks": {
            "type": "integer",
            "description": "Unattributed asks below this are too little volume to judge, regardless of ratio."
          },
          "ratioThreshold": {
            "type": "number",
            "description": "Ratio at or above which unattributed demand counts as disproportionate."
          },
          "detail": {
            "type": "string",
            "description": "One-line explanation naming the counts behind the verdict."
          }
        },
        "required": [
          "warning",
          "ratio",
          "minimumAsks",
          "ratioThreshold",
          "detail"
        ]
      },
      "CaptureReleaseCounts": {
        "type": "object",
        "description": "Rejected payment authorizations released in this window, split by whether the service freed them on its own (`facilitatorAutoRelease.ts`) or an operator did after reviewing them. Computed live from `payment_captures.released_at` and its release note, never from a separately persisted counter, so a restart cannot lose it.",
        "properties": {
          "automatic": {
            "type": "integer"
          },
          "manual": {
            "type": "integer"
          },
          "total": {
            "type": "integer"
          }
        },
        "required": [
          "automatic",
          "manual",
          "total"
        ]
      },
      "PaymentLog": {
        "type": "object",
        "properties": {
          "totalCount": {
            "type": "integer",
            "description": "Every settled payment on record, not just those listed."
          },
          "limit": {
            "type": "integer"
          },
          "payments": {
            "type": "array",
            "description": "Newest settlement first.",
            "items": {
              "$ref": "#/components/schemas/PaymentLogEntry"
            }
          }
        },
        "required": [
          "totalCount",
          "limit",
          "payments"
        ]
      },
      "PaymentLogEntry": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "settledAt": {
            "type": "string",
            "format": "date-time"
          },
          "monitorSlug": {
            "type": [
              "string",
              "null"
            ],
            "description": "The monitor that was paid for; null when the route is not about one vendor."
          },
          "path": {
            "type": "string",
            "description": "The paid path, without host or query string."
          }
        },
        "required": [
          "id",
          "settledAt",
          "monitorSlug",
          "path"
        ]
      },
      "PaymentsHealth": {
        "type": "object",
        "properties": {
          "healthy": {
            "type": "boolean"
          },
          "classification": {
            "type": "string",
            "enum": [
              "healthy_insufficient_balance",
              "healthy",
              "declaration_rejected",
              "unreachable",
              "misconfigured"
            ]
          },
          "detail": {
            "type": "string"
          },
          "target": {
            "type": [
              "string",
              "null"
            ],
            "description": "The paid operation whose 402 challenge the probe reproduces."
          },
          "brokenSince": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "consecutiveFailures": {
            "type": "integer"
          },
          "intervalMinutes": {
            "type": "integer",
            "description": "Minutes between unprompted probes; 0 when only this endpoint probes."
          },
          "facilitatorUrl": {
            "type": "string"
          },
          "facilitatorName": {
            "type": "string"
          },
          "network": {
            "type": "string"
          },
          "asset": {
            "type": "string"
          },
          "priceUsdc": {
            "type": "string"
          },
          "payToConfigured": {
            "type": "boolean"
          },
          "payToAddress": {
            "type": [
              "string",
              "null"
            ]
          },
          "rails": {
            "type": "array",
            "description": "Every settlement rail the operator has configured, primary first, whether or not it is currently in the 402 challenge — see `published` on each entry. One facilitator and one price across all of them, so a rail that is unusable costs the sales of payers holding funds on that chain rather than breaking the paywall — `healthy` above stays true when only a secondary rail is down.",
            "items": {
              "$ref": "#/components/schemas/SettlementRailHealth"
            }
          },
          "checkedAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "healthy",
          "classification",
          "detail",
          "target",
          "brokenSince",
          "consecutiveFailures",
          "intervalMinutes",
          "facilitatorUrl",
          "facilitatorName",
          "network",
          "asset",
          "priceUsdc",
          "payToConfigured",
          "rails",
          "checkedAt"
        ]
      },
      "SettlementRailHealth": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Stable rail id, as used in logs and the capture ledger."
          },
          "networkName": {
            "type": "string"
          },
          "network": {
            "type": "string",
            "description": "CAIP-2 network id, exactly as published in the 402 challenge."
          },
          "asset": {
            "type": "string"
          },
          "payTo": {
            "type": "string"
          },
          "atomicAmount": {
            "type": "string"
          },
          "published": {
            "type": "boolean",
            "description": "Whether this rail is currently an entry in the 402 challenge. A configured rail can be withheld — a Solana accept is incomplete until the facilitator advertises a fee payer — and is listed here either way, because a configured rail that is being withheld is invisible otherwise."
          },
          "status": {
            "type": "string",
            "description": "\"ok\" when nothing was found wrong. \"broken\" when this rail cannot take money. \"unknown\" when the check could not be completed — an unreachable node, or a facilitator that returned no verdict — and so is explicitly not a pass: the rail was neither confirmed nor ruled out on the last run.",
            "enum": [
              "ok",
              "broken",
              "unknown"
            ]
          },
          "verified": {
            "type": "string",
            "description": "How deeply this rail was checked, independently of `status`. \"payment\" means a real signed payment was put to the facilitator for a verdict; \"declaration\" means only its advertisement was read. Null for the primary rail, whose depth is the probe's own.",
            "enum": [
              "declaration",
              "payment"
            ],
            "nullable": true
          },
          "detail": {
            "type": [
              "string",
              "null"
            ],
            "description": "What the last probe found for this rail. Null for the primary rail, whose verdict is the probe's own."
          }
        },
        "required": [
          "id",
          "networkName",
          "network",
          "asset",
          "payTo",
          "atomicAmount",
          "published",
          "status",
          "detail"
        ]
      },
      "AdminMonitorRow": {
        "type": "object",
        "properties": {
          "slug": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "verifying",
              "ok",
              "error",
              "skipped"
            ],
            "description": "Extraction health as of the last check: `ok` read entries, `error` failed, `pending` has never been checked, `skipped` was rejected at seed time. `verifying` is retired and no longer written."
          },
          "statusReason": {
            "type": [
              "string",
              "null"
            ]
          },
          "sourceType": {
            "type": "string"
          },
          "sourceUrl": {
            "type": "string"
          },
          "cleanStreak": {
            "type": "integer"
          },
          "consecutiveFailures": {
            "type": "integer"
          },
          "releaseCount": {
            "type": "integer"
          },
          "changeCount": {
            "type": "integer"
          },
          "lastCheckedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "lastSuccessAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "lastChangeAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "nextCheckAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "alertedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When a failure notification for the current outage was actually delivered to the operator. Null means the operator has not been told anything about this monitor."
          },
          "alertPending": {
            "type": "boolean",
            "description": "The monitor has failed enough consecutive checks to deserve a notification, but none has been delivered — the alert is either undeliverable or alerting is switched off. Nobody knows this vendor is broken."
          }
        },
        "required": [
          "slug",
          "name",
          "status",
          "sourceType",
          "sourceUrl",
          "cleanStreak",
          "consecutiveFailures",
          "releaseCount",
          "changeCount",
          "nextCheckAt",
          "alertPending"
        ]
      },
      "AlertingHealth": {
        "type": "object",
        "description": "Delivery health of the monitor failure-alert channel. Counters and timestamps prefixed \"last\" cover only the current server process (see `observedSince`); the monitor counts are read from the database and therefore survive a restart.",
        "properties": {
          "configured": {
            "type": "boolean",
            "description": "At least one channel (webhook or email) is set, so notifications will be attempted."
          },
          "webhookConfigured": {
            "type": "boolean",
            "description": "A webhook URL is set, so webhook notifications will be attempted."
          },
          "healthy": {
            "type": "boolean",
            "description": "True only when a delivery has actually succeeded, nothing has failed since, and no monitor is waiting on an undelivered alert. An unproven channel is not reported as healthy."
          },
          "classification": {
            "type": "string",
            "enum": [
              "not_configured",
              "unverified",
              "delivering",
              "failing",
              "undelivered"
            ]
          },
          "detail": {
            "type": "string",
            "description": "One-line explanation of the classification."
          },
          "webhookHost": {
            "type": [
              "string",
              "null"
            ],
            "description": "Host of the configured webhook. The full URL is a bearer credential and is never exposed."
          },
          "failureThreshold": {
            "type": "integer",
            "description": "Consecutive failed checks before a monitor is reported."
          },
          "observedSince": {
            "type": "string",
            "format": "date-time",
            "description": "When this server process started counting deliveries."
          },
          "lastAttemptAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "lastSuccessAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "lastFailureAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "lastFailureDetail": {
            "type": [
              "string",
              "null"
            ],
            "description": "Why the last delivery failed, with the credential redacted."
          },
          "delivered": {
            "type": "integer"
          },
          "failed": {
            "type": "integer"
          },
          "notifiedMonitors": {
            "type": "integer",
            "description": "Monitors whose current outage the operator has been told about."
          },
          "undeliveredMonitors": {
            "type": "integer",
            "description": "Monitors past the failure threshold with no notification delivered. Anything above zero is a silent outage."
          },
          "email": {
            "$ref": "#/components/schemas/EmailAlertHealth"
          },
          "heartbeat": {
            "$ref": "#/components/schemas/CrawlerHeartbeat"
          },
          "verification": {
            "$ref": "#/components/schemas/AlertChannelCheck"
          },
          "checkedAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "configured",
          "webhookConfigured",
          "email",
          "healthy",
          "classification",
          "detail",
          "failureThreshold",
          "observedSince",
          "delivered",
          "failed",
          "notifiedMonitors",
          "undeliveredMonitors",
          "heartbeat",
          "verification",
          "checkedAt"
        ]
      },
      "EmailAlertHealth": {
        "type": "object",
        "description": "Whether outage alerts also reach an inbox, via Resend. The API key is a bearer credential and is never returned; the destination address is returned only in masked form.",
        "properties": {
          "configured": {
            "type": "boolean",
            "description": "True only when both RESEND_API_KEY and MONITOR_ALERT_EMAIL are set. Half-configured counts as off."
          },
          "destination": {
            "type": [
              "string",
              "null"
            ],
            "description": "Masked destination, e.g. \"o•••r@example.com\"."
          },
          "fromAddress": {
            "type": "string",
            "description": "Sender address. Defaults to Resend's shared onboarding sender when MONITOR_ALERT_FROM_EMAIL is not set."
          },
          "fromConfigured": {
            "type": "boolean",
            "description": "False when the default onboarding sender is in use."
          },
          "lastAttemptAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "lastSuccessAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "lastFailureAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "lastFailureDetail": {
            "type": [
              "string",
              "null"
            ],
            "description": "Why the last email failed, with the API key and the raw destination redacted."
          },
          "delivered": {
            "type": "integer"
          },
          "failed": {
            "type": "integer"
          },
          "arrivalTracked": {
            "type": "boolean",
            "description": "Resend delivery events are being received (RESEND_WEBHOOK_SECRET is set), so a bounce would be noticed. When false, every field above means \"Resend accepted the request\" and nothing more — arrival is not being checked at all."
          },
          "bounced": {
            "type": "boolean",
            "description": "The last alert email Resend reported on bounced or was marked as spam, and nothing has arrived since. The channel is treated as broken even though the send succeeded."
          },
          "bouncedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When that bounce or complaint was reported."
          },
          "bounceDetail": {
            "type": [
              "string",
              "null"
            ],
            "description": "The provider's reason, with the API key and the raw destination redacted."
          },
          "confirmedDeliveryAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Newest alert email Resend confirmed as delivered to the mailbox — arrival, as opposed to acceptance."
          },
          "detail": {
            "type": "string",
            "description": "One-line explanation of the email channel's state, including which variable is missing when it is only half configured."
          }
        },
        "required": [
          "configured",
          "fromAddress",
          "fromConfigured",
          "delivered",
          "failed",
          "arrivalTracked",
          "bounced",
          "detail"
        ]
      },
      "CrawlerHeartbeat": {
        "type": "object",
        "description": "Liveness of the monitor scheduler itself, independent of any single vendor. A crawl loop that has stopped produces no failing monitor — a check that never runs never fails — while every endpoint keeps serving its last snapshot.",
        "properties": {
          "enabled": {
            "type": "boolean",
            "description": "Whether a stall would actually be reported. False when no webhook is set, when the threshold is 0, or when the scheduler is off."
          },
          "stalled": {
            "type": "boolean",
            "description": "No successful monitor check within the threshold."
          },
          "thresholdMinutes": {
            "type": "integer",
            "description": "Minutes without a successful check before the crawler is reported as stalled (MONITOR_HEARTBEAT_STALL_MINUTES; 0 disables it)."
          },
          "minutesSinceSuccess": {
            "type": "integer",
            "description": "Minutes since the newest successful check across the fleet, measured from process start when there has never been one."
          },
          "lastSuccessAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "lastCheckAttemptAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Newest check attempt, successful or not. Still moving while lastSuccessAt is frozen means every vendor is failing rather than the loop having stopped."
          },
          "activeMonitors": {
            "type": "integer",
            "description": "Monitors the scheduler is expected to be crawling."
          },
          "alertedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the operator was notified about the current stall. Stored in the database, so one stall produces one notification even across a restart."
          },
          "stalledSince": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "detail": {
            "type": "string",
            "description": "One-line explanation of the heartbeat state."
          }
        },
        "required": [
          "enabled",
          "stalled",
          "thresholdMinutes",
          "minutesSinceSuccess",
          "activeMonitors",
          "detail"
        ]
      },
      "AlertChannelCheck": {
        "type": "object",
        "description": "The unprompted proof that the alert channels themselves still accept a message. Every alarm this service has is silent when nothing is wrong, which is indistinguishable from a rotated webhook URL or a revoked mail key — so the channels are checked on a schedule, and the verdict is recorded in the database rather than in process memory.",
        "properties": {
          "enabled": {
            "type": "boolean",
            "description": "False when MONITOR_CHANNEL_CHECK_HOURS is 0 or no channel is configured, in which case a broken channel is only discovered during an outage."
          },
          "intervalHours": {
            "type": "integer",
            "description": "Hours between checks (MONITOR_CHANNEL_CHECK_HOURS; 0 disables)."
          },
          "channels": {
            "type": "array",
            "description": "Configured channels, named the way they are named everywhere else — a webhook host and a masked mailbox, never a credential.",
            "items": {
              "type": "string"
            }
          },
          "lastCheckedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the check last ran. Null means it never has."
          },
          "nextCheckAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "verified": {
            "type": "boolean",
            "description": "The last run found every configured channel accepting messages."
          },
          "degraded": {
            "type": "boolean",
            "description": "A channel refused and has not been proven since. Survives a restart, unlike the in-process delivery counters."
          },
          "degradedSince": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "notifiedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the operator was told about the current breakage, on whichever channel still worked. Null when nothing could be sent — every channel was broken."
          },
          "detail": {
            "type": "string",
            "description": "One-line explanation of the last check's outcome."
          }
        },
        "required": [
          "enabled",
          "intervalHours",
          "channels",
          "verified",
          "degraded",
          "detail"
        ]
      },
      "AlertTestResult": {
        "type": "object",
        "properties": {
          "delivered": {
            "type": "boolean",
            "description": "True when at least one configured channel accepted the test."
          },
          "webhookHost": {
            "type": [
              "string",
              "null"
            ]
          },
          "detail": {
            "type": "string",
            "description": "Combined one-line outcome across every attempted channel."
          },
          "attemptedAt": {
            "type": "string",
            "format": "date-time"
          },
          "webhook": {
            "$ref": "#/components/schemas/AlertChannelResult"
          },
          "email": {
            "$ref": "#/components/schemas/AlertChannelResult"
          }
        },
        "required": [
          "delivered",
          "detail",
          "attemptedAt",
          "webhook",
          "email"
        ]
      },
      "AlertChannelResult": {
        "type": "object",
        "description": "Outcome for one alert channel. With two channels, \"it failed\" is only actionable if the operator can see which one.",
        "properties": {
          "attempted": {
            "type": "boolean",
            "description": "False when the channel is not configured."
          },
          "delivered": {
            "type": "boolean"
          },
          "target": {
            "type": [
              "string",
              "null"
            ],
            "description": "Webhook host, or masked email destination. Never a full webhook URL or a raw address."
          },
          "detail": {
            "type": "string"
          }
        },
        "required": [
          "attempted",
          "delivered",
          "detail"
        ]
      },
      "ReportTotals": {
        "type": "object",
        "properties": {
          "requests": {
            "type": "integer"
          },
          "freeRequests": {
            "type": "integer"
          },
          "askedToPay": {
            "type": "integer"
          },
          "paidSettled": {
            "type": "integer"
          },
          "revenueUsdc": {
            "type": "string"
          },
          "uniqueSlugs": {
            "type": "integer"
          },
          "autoReleasedCaptures": {
            "type": "integer",
            "description": "Rejected payment authorizations the service freed on its own in this period, with no operator involved."
          },
          "manualReleasedCaptures": {
            "type": "integer",
            "description": "Rejected payment authorizations an operator freed by hand in this period."
          }
        },
        "required": [
          "requests",
          "freeRequests",
          "askedToPay",
          "paidSettled",
          "revenueUsdc",
          "uniqueSlugs",
          "autoReleasedCaptures",
          "manualReleasedCaptures"
        ]
      },
      "ReportSummary": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "periodStart": {
            "type": "string"
          },
          "periodEnd": {
            "type": "string"
          },
          "label": {
            "type": [
              "string",
              "null"
            ]
          },
          "sizeBytes": {
            "type": "integer"
          },
          "totals": {
            "$ref": "#/components/schemas/ReportTotals"
          },
          "prunedAt": {
            "description": "When this period's raw request logs were deleted, or null while they still exist. A pruned period is archive-only: its stored totals are the last account of that traffic, and its live analytics read zero regardless of how busy it was.",
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "prunedRows": {
            "description": "Rows that deletion removed; null until the period is pruned.",
            "type": [
              "integer",
              "null"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "pdfUrl": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "periodStart",
          "periodEnd",
          "sizeBytes",
          "totals",
          "prunedAt",
          "prunedRows",
          "createdAt",
          "pdfUrl"
        ]
      },
      "PaymentCapture": {
        "type": "object",
        "description": "One settlement ledger row: an authorization this service attempted to capture, what became of it, and what an operator decided about it.",
        "properties": {
          "id": {
            "type": "integer"
          },
          "status": {
            "description": "The ledger's own account of the authorization, unchanged by reconciliation. `settling` means the settle call's result was never seen, so it is unknown whether money moved.",
            "type": "string",
            "enum": [
              "settling",
              "settled",
              "undelivered",
              "delivered"
            ]
          },
          "payer": {
            "description": "The address that signed the authorization, lowercased.",
            "type": "string"
          },
          "nonce": {
            "description": "The authorization's nonce. Together with the payer it is what an operator quotes to the facilitator to reconcile a `settling` row that has no transaction reference.",
            "type": "string"
          },
          "protocolVersion": {
            "type": "integer"
          },
          "resourceUrl": {
            "type": "string"
          },
          "monitorSlug": {
            "type": [
              "string",
              "null"
            ]
          },
          "amountAtomic": {
            "type": "string"
          },
          "amountUsdc": {
            "description": "The same amount in USDC, as the operator reads money.",
            "type": "string"
          },
          "asset": {
            "type": "string"
          },
          "network": {
            "type": "string"
          },
          "transaction": {
            "description": "The facilitator's transaction reference, once settlement confirmed it. Null on a `settling` row.",
            "type": [
              "string",
              "null"
            ]
          },
          "settledAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "deliveredAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "ageSeconds": {
            "description": "How long the capture has been on the ledger. Age is the triage signal: a minute-old `settling` row may simply be in flight, a week-old one is a payer whose money is held.",
            "type": "integer"
          },
          "outstanding": {
            "description": "Unresolved and never confirmed delivered — the operator still owes this payer an answer.",
            "type": "boolean"
          },
          "facilitatorCheckedAt": {
            "description": "When this row was last asked about, if ever. Null until an operator asks the facilitator via `POST /admin/captures/{captureId}/check-facilitator`.",
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "facilitatorCheckResult": {
            "description": "What the last asking found. `settled` means the facilitator confirmed money moved (and `status` was advanced to match). `not_settled` means the facilitator rejected this exact authorization for a terminal reason — no money moved on that call. `inconclusive` means the facilitator could not be reached, or has not finished forming a verdict — this is deliberately distinct from `not_settled` and must never be read as \"nothing moved\".",
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "settled",
              "not_settled",
              "inconclusive",
              null
            ]
          },
          "facilitatorCheckDetail": {
            "description": "The human-readable account of what the last check found.",
            "type": [
              "string",
              "null"
            ]
          },
          "resolution": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "refunded",
              "fulfilled",
              "no_action_needed",
              null
            ]
          },
          "resolutionNote": {
            "type": [
              "string",
              "null"
            ]
          },
          "resolvedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "releasedAt": {
            "description": "When an operator handed this authorization back to the payer, after the facilitator confirmed the settlement was rejected. Null on every row still holding its claim. A released row keeps its `status` and its whole history, stops being `outstanding`, and no longer blocks a fresh attempt with the same authorization.",
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "releaseNote": {
            "description": "Why it was freed — the operator's own words, or the facilitator's verdict when they gave none.",
            "type": [
              "string",
              "null"
            ]
          },
          "releasable": {
            "description": "Whether `POST /admin/captures/{captureId}/release` would be accepted for this row: still `settling`, not already released, and the facilitator's last answer was `not_settled`. The same rule the server enforces, so the UI never has to re-derive it.",
            "type": "boolean"
          }
        },
        "required": [
          "id",
          "status",
          "payer",
          "nonce",
          "protocolVersion",
          "resourceUrl",
          "monitorSlug",
          "amountAtomic",
          "amountUsdc",
          "asset",
          "network",
          "transaction",
          "settledAt",
          "deliveredAt",
          "createdAt",
          "ageSeconds",
          "outstanding",
          "facilitatorCheckedAt",
          "facilitatorCheckResult",
          "facilitatorCheckDetail",
          "resolution",
          "resolutionNote",
          "resolvedAt",
          "releasedAt",
          "releaseNote",
          "releasable"
        ]
      },
      "PaymentCaptureList": {
        "type": "object",
        "properties": {
          "outstandingCount": {
            "description": "Captures across the whole ledger that no operator has closed out — neither reconciled nor released — not just the page below, so the headline does not move when the list is cut, and not affected by `status` or `search` either, so it does not move when the list is narrowed down.",
            "type": "integer"
          },
          "totalCount": {
            "description": "Rows matching the current `status` and `search`, resolved ones included when `status=all`. This is the total the page below is drawn from, not the whole ledger.",
            "type": "integer"
          },
          "status": {
            "description": "The `status` filter this response was generated with.",
            "type": "string",
            "enum": [
              "outstanding",
              "all"
            ]
          },
          "resolution": {
            "description": "The `resolution` filter this response was generated with, or null.",
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "refunded",
              "fulfilled",
              "no_action_needed",
              "unresolved",
              null
            ]
          },
          "search": {
            "description": "The `search` this response was generated with, or null.",
            "type": [
              "string",
              "null"
            ]
          },
          "limit": {
            "description": "Page size applied to this response.",
            "type": "integer"
          },
          "offset": {
            "description": "Rows skipped before this page — the `offset` echoed back.",
            "type": "integer"
          },
          "hasMore": {
            "description": "True when rows remain after this page. Request `offset + limit` to read them.",
            "type": "boolean"
          },
          "captures": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PaymentCapture"
            }
          }
        },
        "required": [
          "outstandingCount",
          "totalCount",
          "status",
          "resolution",
          "search",
          "limit",
          "offset",
          "hasMore",
          "captures"
        ]
      },
      "CaptureResolutionInput": {
        "type": "object",
        "properties": {
          "resolution": {
            "description": "`refunded` — the money was returned to the payer out of band. `fulfilled` — the payer did get what they paid for. `no_action_needed` — checked, and nothing is owed.",
            "type": "string",
            "enum": [
              "refunded",
              "fulfilled",
              "no_action_needed"
            ]
          },
          "note": {
            "description": "Free text an operator leaves for whoever reads this row next — a refund reference, what the facilitator said. Trimmed to 500 characters.",
            "type": "string",
            "maxLength": 500
          }
        },
        "required": [
          "resolution"
        ]
      },
      "CaptureReleaseInput": {
        "type": "object",
        "properties": {
          "note": {
            "description": "Why this authorization was freed, for whoever reads the row next. Trimmed to 500 characters. Omitted, the facilitator's own rejection detail is recorded as the reason instead, so the row always says why.",
            "type": "string",
            "maxLength": 500
          }
        }
      },
      "CaptureReleaseResult": {
        "type": "object",
        "properties": {
          "alreadyReleased": {
            "description": "True when the authorization had already been freed, so this call wrote nothing and the record returned is the original one.",
            "type": "boolean"
          },
          "capture": {
            "$ref": "#/components/schemas/PaymentCapture"
          }
        },
        "required": [
          "alreadyReleased",
          "capture"
        ]
      },
      "CaptureResolutionResult": {
        "type": "object",
        "properties": {
          "alreadyResolved": {
            "description": "True when the capture had already been reconciled, so this call wrote nothing and the record returned is the original one.",
            "type": "boolean"
          },
          "capture": {
            "$ref": "#/components/schemas/PaymentCapture"
          }
        },
        "required": [
          "alreadyResolved",
          "capture"
        ]
      },
      "CaptureBulkResolutionInput": {
        "type": "object",
        "properties": {
          "captureIds": {
            "description": "The captures to reconcile, listed explicitly. The list may span several pages of the ledger — an operator working an incident keeps their selection as they page — so it is capped well above one page, at what a single run can record and a single summary can honestly report. Duplicates are collapsed before anything is written, so the same id twice reports one outcome rather than reading as a race with another operator.",
            "type": "array",
            "items": {
              "type": "integer",
              "minimum": 1
            },
            "minItems": 1,
            "maxItems": 1000
          },
          "resolution": {
            "description": "The single resolution recorded against every row in `captureIds`. Same values, and same meanings, as the per-row call.",
            "type": "string",
            "enum": [
              "refunded",
              "fulfilled",
              "no_action_needed"
            ]
          },
          "note": {
            "description": "Free text recorded against every row in `captureIds` — usually what the incident was. Trimmed to 500 characters.",
            "type": "string",
            "maxLength": 500
          }
        },
        "required": [
          "captureIds",
          "resolution"
        ]
      },
      "CaptureBulkResolutionResult": {
        "type": "object",
        "properties": {
          "requested": {
            "description": "Distinct capture ids this run considered.",
            "type": "integer"
          },
          "resolved": {
            "description": "Of those, how many had the resolution recorded against them.",
            "type": "integer"
          },
          "skipped": {
            "description": "Rows this run deliberately left exactly as they were.",
            "type": "integer"
          },
          "skippedAlreadyResolved": {
            "description": "Skipped because the row was already reconciled — by an earlier run, or by another operator between the selection and this call. The original record is untouched.",
            "type": "integer"
          },
          "skippedDelivered": {
            "description": "Skipped because the capture was confirmed delivered, so there is nothing to reconcile.",
            "type": "integer"
          },
          "skippedMissing": {
            "description": "Skipped because no capture with that id exists.",
            "type": "integer"
          },
          "results": {
            "description": "What happened to each requested id, in the order they were processed, so the operator can see which specific rows were skipped rather than only how many.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CaptureBulkResolutionRow"
            }
          }
        },
        "required": [
          "requested",
          "resolved",
          "skipped",
          "skippedAlreadyResolved",
          "skippedDelivered",
          "skippedMissing",
          "results"
        ]
      },
      "CaptureBulkResolutionRow": {
        "type": "object",
        "properties": {
          "captureId": {
            "type": "integer"
          },
          "outcome": {
            "description": "`resolved` — the resolution was recorded on this row. `already_resolved` — it already carried one, and nothing was written. `delivered` — the payment was confirmed delivered, so there was nothing to reconcile. `not_found` — no such capture.",
            "type": "string",
            "enum": [
              "resolved",
              "already_resolved",
              "delivered",
              "not_found"
            ]
          }
        },
        "required": [
          "captureId",
          "outcome"
        ]
      },
      "CaptureFacilitatorCheckResult": {
        "type": "object",
        "properties": {
          "capture": {
            "description": "The capture with `facilitatorChecked*` (and, on a `settled` finding, `status`/`transaction`/`settledAt`) updated to reflect what was just found.",
            "$ref": "#/components/schemas/PaymentCapture"
          }
        },
        "required": [
          "capture"
        ]
      },
      "CaptureBulkFacilitatorCheckResult": {
        "type": "object",
        "properties": {
          "candidates": {
            "description": "Rows that were `settling` and unresolved when this run started — the whole outstanding backlog, including any this run's own bounds stopped it from reaching.",
            "type": "integer"
          },
          "attempted": {
            "description": "Of those, how many this run actually took up before it hit its row cap or its time budget. Always `checked` + `skipped`.",
            "type": "integer"
          },
          "remaining": {
            "description": "Candidates this run never reached, because it stopped at one of its bounds. Zero when it worked through the whole backlog. Everything it did check is already recorded, so running this again continues from here.",
            "type": "integer"
          },
          "stoppedReason": {
            "description": "Why the run ended. `complete` — every candidate was reached. `row_limit` — it reached the most rows one call will take on. `time_limit` — it ran out of its wall-clock budget, which is what a facilitator answering slowly (or not at all) looks like.",
            "type": "string",
            "enum": [
              "complete",
              "row_limit",
              "time_limit"
            ]
          },
          "checked": {
            "description": "Of the attempted rows, how many the facilitator was asked about.",
            "type": "integer"
          },
          "settled": {
            "description": "Of the checked rows, how many the facilitator confirmed settled.",
            "type": "integer"
          },
          "notSettled": {
            "description": "Of the checked rows, how many the facilitator rejected for a terminal reason — no money moved on that call.",
            "type": "integer"
          },
          "inconclusive": {
            "description": "Of the checked rows, how many came back inconclusive — the facilitator could not be reached, or has not finished forming a verdict. Distinct from `notSettled` and never to be read as \"nothing moved\".",
            "type": "integer"
          },
          "skipped": {
            "description": "Rows refused before ever reaching the facilitator.",
            "type": "integer"
          },
          "skippedTooRecent": {
            "description": "Skipped because the original settle attempt may still be resolving at the facilitator.",
            "type": "integer"
          },
          "skippedMissingPayload": {
            "description": "Skipped because the row predates stored payment payloads and has nothing to replay.",
            "type": "integer"
          },
          "skippedRaceResolved": {
            "description": "Skipped because the row stopped being an outstanding `settling` row — an operator resolved it, or another check already answered it — between this run starting and reaching it.",
            "type": "integer"
          }
        },
        "required": [
          "candidates",
          "attempted",
          "remaining",
          "stoppedReason",
          "checked",
          "settled",
          "notSettled",
          "inconclusive",
          "skipped",
          "skippedTooRecent",
          "skippedMissingPayload",
          "skippedRaceResolved"
        ]
      },
      "ReportPeriodInput": {
        "type": "object",
        "properties": {
          "periodEnd": {
            "type": "string",
            "description": "Inclusive last calendar day of the 90 day period, YYYY-MM-DD. Defaults to today when omitted.",
            "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
          },
          "label": {
            "type": "string",
            "maxLength": 120
          }
        }
      },
      "PruneResult": {
        "type": "object",
        "properties": {
          "pruned": {
            "description": "Rows this call deleted; zero when the period was already pruned.",
            "type": "integer"
          },
          "alreadyPruned": {
            "description": "True when the period had already been pruned, so this call deleted nothing and left the original record untouched.",
            "type": "boolean"
          },
          "prunedAt": {
            "description": "When the recorded deletion ran.",
            "type": "string",
            "format": "date-time"
          },
          "prunedRows": {
            "description": "Rows the recorded deletion removed.",
            "type": "integer"
          },
          "periodStart": {
            "type": "string"
          },
          "periodEnd": {
            "type": "string"
          }
        },
        "required": [
          "pruned",
          "alreadyPruned",
          "prunedAt",
          "prunedRows",
          "periodStart",
          "periodEnd"
        ]
      }
    }
  },
  "x-agentcash-guidance": {
    "llmsTxtUrl": "https://changelog-state-api.replit.app/llms.txt"
  }
}