openapi: 3.1.0
info:
  title: SELAT Catalog API
  version: 1.0.0
  summary: Free, machine-readable discovery over SELAT's federated x402 + MPP capability catalog.
  description: |
    SELAT federates paid, machine-native API endpoints from x402 and MPP sources into one catalog.
    This API is free and unauthenticated. It never spends.

    - `GET /federated` returns the full catalog as a compressed envelope. Decode it as
      `gunzip(base64decode(data))` using the declared `compression` and `encoding`, and refresh your
      local cache by comparing `generatedAt`.
    - `GET /health` reports catalog freshness and size.

    Ranked search, per-service schemas, and live HTTP 402 price quotes are served by the hosted MCP
    server at `https://catalog.selat.ai/mcp` (Streamable HTTP, no auth).

    Paying happens locally, never through this API. Paid calls run via `@selat-ai/selat-cli`
    (`selat run "<intent>" --dry-run` to see the price, then `selat run` once the user approves),
    and settle from the user's own agent wallet. SELAT never holds keys or funds.
  contact:
    name: SELAT
    url: https://selat.ai
    email: hello@selat.ai
externalDocs:
  description: SELAT documentation
  url: https://selat.ai/docs
servers:
  - url: https://catalog.selat.ai/api/v1
tags:
  - name: catalog
    description: Free catalog discovery.
paths:
  /health:
    get:
      operationId: getCatalogHealth
      summary: Catalog freshness and size
      tags: [catalog]
      security: []
      responses:
        "200":
          description: Catalog health.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Health"
              example:
                ok: true
                storage: file
                latestFetchedAt: 1790993037987
                serviceCount: 3175
  /federated:
    get:
      operationId: getFederatedCatalog
      summary: Full federated x402 + MPP catalog
      description: |
        Returns the whole catalog (~33 MB decoded) as a gzip-compressed, base64-encoded payload.
        Cache it locally; the decoded `data` is a `FederatedCatalog`.
      tags: [catalog]
      security: []
      responses:
        "200":
          description: Compressed catalog envelope.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/FederatedEnvelope"
        "404":
          description: No catalog snapshot is available yet.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                ok: false
                message: catalog_not_found
components:
  schemas:
    Health:
      type: object
      required: [ok, storage, latestFetchedAt, serviceCount]
      properties:
        ok: { type: boolean }
        storage: { type: string, description: Snapshot storage backend. }
        latestFetchedAt: { type: integer, description: Unix epoch milliseconds of the latest catalog build. }
        serviceCount: { type: integer }
    FederatedEnvelope:
      type: object
      required: [ok, compression, encoding, generatedAt, data]
      properties:
        ok: { type: boolean }
        compression: { type: string, enum: [gzip] }
        encoding: { type: string, enum: [base64] }
        generatedAt: { type: string, format: date-time }
        data:
          type: string
          contentEncoding: base64
          contentMediaType: application/gzip
          description: base64(gzip(JSON)) of a `FederatedCatalog`.
          contentSchema:
            $ref: "#/components/schemas/FederatedCatalog"
    FederatedCatalog:
      type: object
      description: The decoded `data` payload of `FederatedEnvelope`.
      required: [fetchedAt, totals, services]
      properties:
        fetchedAt: { type: integer, description: Unix epoch milliseconds. }
        totals:
          type: object
          description: Build counters (raw, merged, filtered, enrichment applied).
          additionalProperties: true
        services:
          type: array
          items: { $ref: "#/components/schemas/Service" }
    Service:
      type: object
      required: [id, name, description, url, categories, tags, endpoints]
      properties:
        id: { type: string, description: Canonical service id (usually the provider host). }
        name: { type: string }
        description: { type: string }
        url: { type: string, format: uri }
        categories: { type: array, items: { type: string } }
        tags: { type: array, items: { type: string } }
        sources:
          type: array
          description: Upstream registries this service was federated from.
          items:
            type: object
            properties:
              catalog: { type: string }
              catalogId: { type: string }
        supports: { type: object, additionalProperties: true }
        docs: { type: object, additionalProperties: true }
        endpoints:
          type: array
          items: { $ref: "#/components/schemas/Endpoint" }
    Endpoint:
      type: object
      required: [method, path, fullUrl, description, payments]
      properties:
        method: { type: string }
        path: { type: string }
        fullUrl: { type: string, format: uri }
        description: { type: string }
        inputSchema:
          type: object
          description: Request shape (parameters and body JSON Schema) where known.
          properties:
            source: { type: string }
            method: { type: string }
            required: { type: array, items: { type: string } }
            parameters: { type: array, items: { type: object, additionalProperties: true } }
            body: { type: object, additionalProperties: true }
        queryParams: { type: array, items: { type: object, additionalProperties: true } }
        pathParams: { type: array, items: { type: object, additionalProperties: true } }
        usage: { type: object, additionalProperties: true }
        sourceCatalog: { type: string }
        payments:
          type: array
          items: { $ref: "#/components/schemas/Payment" }
    Payment:
      type: object
      description: One accepted way to pay this endpoint (protocol × scheme × network).
      required: [protocol, scheme, network, asset, payTo, amountBaseUnits, amountUsd]
      properties:
        protocol: { type: string, enum: [x402, mpp] }
        scheme: { type: string, examples: [exact] }
        network: { type: string, description: "CAIP-2 network id, e.g. eip155:8453 or tempo:4217." }
        asset: { type: string }
        payTo: { type: string }
        amountBaseUnits: { type: string }
        decimals: { type: integer }
        amountUsd: { type: number }
        domain: { type: string }
        sourceCatalog: { type: string }
    Error:
      type: object
      required: [ok, message]
      properties:
        ok: { type: boolean, const: false }
        message: { type: string }
