openapi: "3.1.1"
info:
  title: "Voxgig Public API"
  version: "1.0.0"
  summary: "Read the Voxgig SDK catalogue and contact Voxgig, without an API key."
  description: "The public API behind voxgig.com. It is read-only apart from the contact form, needs no authentication, and is safe to call from an agent.\n\nVoxgig is an open-source SDK generator for API-first SaaS: it turns one OpenAPI description into six surfaces, an SDK, a CLI, an MCP Server, Agent Skills, a REPL, and a type-safe Semantic Model. This API exposes the catalogue of 600+ SDKs already generated that way, so you can find a worked example shaped like your own API.\n\nErrors are RFC 9457 problem details with media type `application/problem+json`. Every problem carries a stable `code`, and its `type` links to the matching section of https://voxgig.com/developers/errors.\n\nThere is also a Model Context Protocol server at `https://voxgig.com/mcp` if you would rather call Voxgig as a set of tools than as REST."
  termsOfService: "https://voxgig.com/notices"
  contact:
    name: Voxgig
    url: "https://voxgig.com/contact"
    email: "info@voxgig.com"
  license:
    name: MIT
    identifier: MIT
externalDocs:
  description: "Developer and agent resources"
  url: "https://voxgig.com/developers"
servers:
  - url: "https://voxgig.com"
    description: Production
security: []
tags:
  - name: catalogue
    description: "The Voxgig SDK catalogue: 600+ SDKs generated from real public OpenAPI descriptions."
  - name: contact
    description: "Send a message to a human at Voxgig."
  - name: service
    description: "Service health and machine-readable descriptions."
  - name: agents
    description: "Endpoints intended for AI agents."
paths:
  "/api/sdk/catalog.json":
    get:
      operationId: list_sdk_catalog
      tags:
        - catalogue
      summary: "Download the whole Voxgig SDK catalogue in one JSON document, every generated example SDK with its slug, title, description, documentation URL and GitHub repository."
      description: "Use this when you want the full set and intend to filter it yourself. It is a static document regenerated on every deploy, so it is cheap to fetch and safe to cache.\n\nPrefer `search_sdk_catalog` when you are looking for a handful of matches; this document is several hundred kilobytes."
      responses:
        "200":
          description: "The complete catalogue."
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/SdkCatalog"
        default:
          "$ref": "#/components/responses/UnexpectedError"
  "/api/sdk/search":
    get:
      operationId: search_sdk_catalog
      tags:
        - catalogue
        - agents
      summary: "Search the Voxgig SDK catalogue by keyword and return the matching example SDKs, newest match ranking first, with a documentation URL and a raw README URL for each."
      description: "The keyword is matched against each entry's slug, title, description and topics, case-insensitively. Every whitespace-separated word in `q` must match somewhere in the entry.\n\nThis is the endpoint to reach for when a user asks whether Voxgig has an example for some kind of API. Follow `readme_url` on a result to read the generated README as markdown.\n\nOmit `q` to page through the whole catalogue."
      parameters:
        - "$ref": "#/components/parameters/SearchQuery"
        - "$ref": "#/components/parameters/SearchLimit"
        - "$ref": "#/components/parameters/SearchOffset"
      responses:
        "200":
          description: "Matching catalogue entries."
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/SdkSearchResult"
              example:
                query: weather
                total: 12
                limit: 2
                offset: 0
                results:
                  - slug: realtime-weather-sdk
                    title: "Realtime Weather SDK"
                    description: "Generated SDK for the Realtime Weather API."
                    url: "https://voxgig.com/voxgig-sdk/realtime-weather-sdk"
                    readme_url: "https://voxgig.com/voxgig-sdk/realtime-weather-sdk.md"
                    github_url: "https://github.com/voxgig-sdk/realtime-weather-sdk"
                    topics:
                      - weather
        "400":
          "$ref": "#/components/responses/BadRequest"
        default:
          "$ref": "#/components/responses/UnexpectedError"
  "/api/sdk/{slug}.json":
    get:
      operationId: get_sdk_entry
      tags:
        - catalogue
      summary: "Fetch one Voxgig catalogue entry by its slug, returning its title, description, topics, documentation URL, raw README URL and GitHub repository."
      description: "Slugs come from `search_sdk_catalog` or `list_sdk_catalog`. They always end in `-sdk`. The response does not embed the README; follow `readme_url` for that, which returns `text/markdown`."
      parameters:
        - "$ref": "#/components/parameters/SdkSlug"
      responses:
        "200":
          description: "The catalogue entry."
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/SdkEntry"
        "404":
          "$ref": "#/components/responses/NotFound"
        default:
          "$ref": "#/components/responses/UnexpectedError"
  "/voxgig-sdk/{slug}.md":
    get:
      operationId: get_sdk_readme
      tags:
        - catalogue
        - agents
      summary: "Fetch the generated README for one catalogue SDK as raw markdown, which is the fastest way to see what the generated code looks like."
      description: "Returns `text/markdown`. This is the same README published in the SDK's GitHub repository, served without any HTML around it."
      parameters:
        - "$ref": "#/components/parameters/SdkSlug"
      responses:
        "200":
          description: "The README, as markdown."
          content:
            text/markdown:
              schema:
                type: string
        "404":
          "$ref": "#/components/responses/NotFound"
        default:
          "$ref": "#/components/responses/UnexpectedError"
  "/api/contact":
    post:
      operationId: submit_contact_message
      tags:
        - contact
      summary: "Send a message to a human at Voxgig about the SDK generator, an API Experience engagement, or a podcast pitch, and get a reply within 48 hours."
      description: "Do not call this on a user's behalf without their explicit say-so, and never use it for unsolicited marketing. It reaches a real inbox.\n\nAll three fields are required. `message` is capped at 5000 characters. There is no rate limit published, but abuse is blocked at the edge.\n\nSend `application/json`. The endpoint also accepts form encodings for the browser form on /contact, but those are subject to a cross-site POST check and are refused with 403 unless the request carries a matching `Origin` header, so they are not usable programmatically."
      requestBody:
        required: true
        content:
          application/json:
            schema:
              "$ref": "#/components/schemas/ContactMessage"
            example:
              name: "Ada Lovelace"
              email: "ada@example.com"
              message: "We have an OpenAPI 3.1 spec and need Go and Python SDKs plus an MCP server. What does an engagement look like?"
      responses:
        "200":
          description: "The message was accepted and will be answered by a person."
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ContactAccepted"
              example:
                ok: true
        "400":
          "$ref": "#/components/responses/MissingFields"
        "403":
          description: "A form-encoded POST arrived without a matching Origin header and was refused as a cross-site form submission. Send application/json instead."
          content:
            text/plain:
              schema:
                type: string
        "405":
          "$ref": "#/components/responses/MethodNotAllowed"
        "413":
          "$ref": "#/components/responses/PayloadTooLarge"
        "415":
          "$ref": "#/components/responses/UnsupportedMediaType"
        default:
          "$ref": "#/components/responses/UnexpectedError"
  "/api/health":
    get:
      operationId: get_service_health
      tags:
        - service
      summary: "Check that the Voxgig site is up, and read the size of the SDK catalogue and the links to every machine-readable description it publishes."
      description: "Always returns 200 when the Worker is serving. Use it as a liveness probe and as a discovery document: the `links` member points at the OpenAPI description, the MCP endpoint and llms.txt."
      responses:
        "200":
          description: "The service is up."
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/HealthStatus"
              example:
                status: ok
                service: voxgig.com
                version: "1.0.0"
                sdk_count: 637
                links:
                  openapi: "https://voxgig.com/openapi.json"
                  mcp: "https://voxgig.com/mcp"
                  llms_txt: "https://voxgig.com/llms.txt"
                  developers: "https://voxgig.com/developers"
        default:
          "$ref": "#/components/responses/UnexpectedError"
  "/mcp":
    post:
      operationId: call_mcp_server
      tags:
        - agents
      summary: "Call the Voxgig Model Context Protocol server over Streamable HTTP with a JSON-RPC 2.0 message, exposing the SDK catalogue and product documentation as tools."
      description: "A stateless MCP endpoint. It supports `initialize`, `notifications/initialized`, `tools/list`, `tools/call`, `ping` and `server/discover`, and it does not require the handshake first: a cold `tools/list` works.\n\nSend `Content-Type: application/json` and `Accept: application/json, text/event-stream`. Responses are single JSON objects; no session id is issued or expected. Notifications are answered with 202 and an empty body.\n\nMost callers should point an MCP client at this URL rather than speak JSON-RPC by hand."
      requestBody:
        required: true
        content:
          application/json:
            schema:
              "$ref": "#/components/schemas/JsonRpcRequest"
            example:
              jsonrpc: "2.0"
              id: 1
              method: tools/list
      responses:
        "200":
          description: "A JSON-RPC response."
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/JsonRpcResponse"
        "202":
          description: "A JSON-RPC notification was accepted. No body."
        "400":
          "$ref": "#/components/responses/BadRequest"
        "403":
          description: "The Origin header was rejected, per the MCP transport security rules. A request with a non-safe method and no Content-Type at all is also refused here, by the site-wide cross-site check, so always send Content-Type: application/json."
          content:
            "application/problem+json":
              schema:
                "$ref": "#/components/schemas/Problem"
        "405":
          "$ref": "#/components/responses/MethodNotAllowed"
        default:
          "$ref": "#/components/responses/UnexpectedError"
  "/openapi.json":
    get:
      operationId: get_openapi_description
      tags:
        - service
      summary: "Fetch this OpenAPI 3.1 description of the Voxgig public API, which is the document you are reading."
      description: "The same document is available as YAML at `/openapi.yaml`."
      responses:
        "200":
          description: "The OpenAPI description."
          content:
            application/json:
              schema:
                type: object
  "/llms.txt":
    get:
      operationId: get_llms_txt
      tags:
        - agents
      summary: "Fetch the llms.txt index for voxgig.com, a short markdown summary of what Voxgig is, when an agent should reach for it, and where the machine-readable resources live."
      description: "Follows the llmstxt.org format. `/llms-full.txt` is the long form and lists every SDK in the catalogue."
      responses:
        "200":
          description: "The llms.txt document."
          content:
            text/plain:
              schema:
                type: string
  "/sdk/voxgig-sdk.csv":
    get:
      operationId: get_sdk_catalog_csv
      tags:
        - catalogue
      summary: "Download the Voxgig SDK catalogue as CSV, one row per SDK, with the upstream API's own website and OpenAPI URL where the README records them."
      description: "Columns: slug, title, voxgig_url, github_url, freepublicapis_url, source_openapi_url, source_website."
      responses:
        "200":
          description: "The catalogue, as CSV."
          content:
            text/csv:
              schema:
                type: string
components:
  parameters:
    SdkSlug:
      name: slug
      in: path
      required: true
      description: "The catalogue slug of the SDK, as returned by search_sdk_catalog. Always ends in `-sdk`."
      schema:
        type: string
        pattern: "^[a-z0-9][a-z0-9-]*$"
        minLength: 3
        maxLength: 120
        examples:
          - openaq-platform-sdk
          - github-rest-sdk
    SearchQuery:
      name: q
      in: query
      required: false
      description: "Keywords to match against slug, title, description and topics. Every whitespace-separated word must match. Omit to page through everything."
      schema:
        type: string
        maxLength: 200
        examples:
          - weather
          - "open data"
    SearchLimit:
      name: limit
      in: query
      required: false
      description: "Maximum number of results to return."
      schema:
        type: integer
        minimum: 1
        maximum: 100
        default: 20
    SearchOffset:
      name: offset
      in: query
      required: false
      description: "Number of matches to skip, for paging."
      schema:
        type: integer
        minimum: 0
        default: 0
  schemas:
    SdkEntry:
      title: "SDK catalogue entry"
      description: "One generated example SDK in the Voxgig catalogue."
      type: object
      properties:
        slug:
          type: string
          description: "Stable identifier, and the last path segment of the documentation URL."
          examples:
            - openaq-platform-sdk
        title:
          type: string
          description: "Human-readable name of the SDK."
          examples:
            - "OpenAQ Platform SDK"
        description:
          type: string
          description: "One-line summary of the upstream API the SDK wraps."
        url:
          type: string
          format: uri
          description: "Documentation page for this SDK on voxgig.com."
        readme_url:
          type: string
          format: uri
          description: "The generated README as raw markdown."
        github_url:
          type: string
          format: uri
          description: "The generated SDK's GitHub repository."
        topics:
          type: array
          description: "Topic tags taken from the generated repository."
          items:
            type: string
      required:
        - slug
        - title
        - url
        - readme_url
        - github_url
      additionalProperties: false
    SdkCatalog:
      title: "SDK catalogue"
      description: "The complete Voxgig SDK catalogue."
      type: object
      properties:
        generated_at:
          type: string
          format: date-time
          description: "When this document was built. It is rebuilt on every deploy."
        count:
          type: integer
          description: "Number of entries."
        sdks:
          type: array
          description: "Every catalogue entry, sorted by title."
          items:
            "$ref": "#/components/schemas/SdkEntry"
      required:
        - count
        - sdks
      additionalProperties: false
    SdkSearchResult:
      title: "SDK search result"
      description: "A page of catalogue entries matching a keyword search."
      type: object
      properties:
        query:
          type: string
          description: "The keywords that were searched for."
        total:
          type: integer
          description: "Total number of matches, before paging."
        limit:
          type: integer
          description: "The limit that was applied."
        offset:
          type: integer
          description: "The offset that was applied."
        results:
          type: array
          description: "This page of matches, most relevant ordering preserved from the catalogue."
          items:
            "$ref": "#/components/schemas/SdkEntry"
      required:
        - query
        - total
        - limit
        - offset
        - results
      additionalProperties: false
    HealthStatus:
      title: "Health status"
      description: "Liveness of voxgig.com, plus links to its machine-readable descriptions."
      type: object
      properties:
        status:
          type: string
          enum:
            - ok
          description: "Always `ok` when the service answers."
        service:
          type: string
          description: "The service that answered."
        version:
          type: string
          description: "Version of the deployed site."
        sdk_count:
          type: integer
          description: "Number of SDKs in the catalogue."
        links:
          type: object
          description: "Absolute URLs of the machine-readable resources."
          properties:
            openapi:
              type: string
              format: uri
            mcp:
              type: string
              format: uri
            llms_txt:
              type: string
              format: uri
            developers:
              type: string
              format: uri
          additionalProperties: false
      required:
        - status
        - service
        - links
      additionalProperties: false
    ContactMessage:
      title: "Contact message"
      description: "A message for a human at Voxgig."
      type: object
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 256
          description: "Who is writing. A real name, not a company name."
        email:
          type: string
          format: email
          minLength: 3
          maxLength: 256
          description: "Where the reply should go."
        message:
          type: string
          minLength: 1
          maxLength: 5000
          description: "What you want. Be specific; a person reads this."
      required:
        - name
        - email
        - message
      additionalProperties: false
    ContactAccepted:
      title: "Contact accepted"
      description: "Acknowledgement that the message was received."
      type: object
      properties:
        ok:
          type: boolean
          enum:
            - true
          description: "Always true on success."
      required:
        - ok
      additionalProperties: false
    JsonRpcRequest:
      title: "JSON-RPC request"
      description: "A JSON-RPC 2.0 request or notification, as used by MCP."
      type: object
      properties:
        jsonrpc:
          type: string
          enum:
            - "2.0"
          description: "Always the string \"2.0\"."
        id:
          description: "Correlates the response with this request. Omit for a notification."
          anyOf:
            - type: string
            - type: integer
        method:
          type: string
          description: "The MCP method to call."
          examples:
            - tools/list
            - tools/call
            - initialize
            - ping
            - server/discover
        params:
          type: object
          description: "Method parameters."
      required:
        - jsonrpc
        - method
      additionalProperties: false
    JsonRpcResponse:
      title: "JSON-RPC response"
      description: "A JSON-RPC 2.0 response. Exactly one of result or error is present."
      type: object
      properties:
        jsonrpc:
          type: string
          enum:
            - "2.0"
          description: "Always the string \"2.0\"."
        id:
          description: "The id of the request this answers. Null when the request was unparsable."
          anyOf:
            - type: string
            - type: integer
            - type: "null"
        result:
          type: object
          description: "The method's result. Present only on success."
        error:
          type: object
          description: "The failure. Present only when the call did not succeed."
          properties:
            code:
              type: integer
              description: "JSON-RPC 2.0 error code."
            message:
              type: string
              description: "Short description of the failure."
            data:
              description: "Extra context, shape depends on the code."
          required:
            - code
            - message
      required:
        - jsonrpc
        - id
      additionalProperties: false
    Problem:
      title: "Problem details"
      description: "An RFC 9457 problem details object. RFC 9457 requires no member; this API always sends type, title, status and code, and usually detail and resolution. Ignore members you do not recognise."
      type: object
      properties:
        type:
          type: string
          format: uri
          default: "about:blank"
          description: "URI identifying the problem type. Dereferences to the matching section of the error reference."
          examples:
            - "https://voxgig.com/developers/errors#missing_fields"
        title:
          type: string
          description: "Short, human-readable summary, stable for a given code."
          examples:
            - "Missing required fields"
        status:
          type: integer
          minimum: 100
          maximum: 599
          description: "The HTTP status code. Always matches the actual response status."
        code:
          type: string
          description: "Stable machine-readable identifier for the problem type."
          enum:
            - not_found
            - not_acceptable
            - method_not_allowed
            - unsupported_media_type
            - invalid_request
            - invalid_json
            - missing_fields
            - payload_too_large
            - unauthorized
            - server_error
        detail:
          type: string
          description: "Explanation specific to this occurrence. Do not parse it."
        instance:
          type: string
          format: uri-reference
          description: "The path this problem occurred on."
        resolution:
          type: string
          description: "One sentence telling you how to succeed next time."
        errors:
          type: array
          description: "Field-level failures, when the problem is a validation failure."
          items:
            type: object
            properties:
              field:
                type: string
              message:
                type: string
            required:
              - field
              - message
      required:
        - type
        - title
        - status
        - code
      additionalProperties: true
  responses:
    NotFound:
      description: "The resource does not exist."
      content:
        "application/problem+json":
          schema:
            "$ref": "#/components/schemas/Problem"
          example:
            type: "https://voxgig.com/developers/errors#not_found"
            title: "Not found"
            status: 404
            code: not_found
            detail: "No catalogue entry has that slug."
            resolution: "Check the slug with search_sdk_catalog, then retry."
    BadRequest:
      description: "A parameter was malformed or out of range."
      content:
        "application/problem+json":
          schema:
            "$ref": "#/components/schemas/Problem"
          example:
            type: "https://voxgig.com/developers/errors#invalid_request"
            title: "Invalid request"
            status: 400
            code: invalid_request
            detail: "limit must be an integer between 1 and 100."
            resolution: "Correct the parameter and retry."
    MissingFields:
      description: "A required field was absent or empty."
      content:
        "application/problem+json":
          schema:
            "$ref": "#/components/schemas/Problem"
          example:
            type: "https://voxgig.com/developers/errors#missing_fields"
            title: "Missing required fields"
            status: 400
            code: missing_fields
            detail: "name, email and message are all required."
            resolution: "Supply every field listed in errors, then retry."
            errors:
              - field: email
                message: required
    MethodNotAllowed:
      description: "The endpoint does not accept that HTTP method."
      content:
        "application/problem+json":
          schema:
            "$ref": "#/components/schemas/Problem"
          example:
            type: "https://voxgig.com/developers/errors#method_not_allowed"
            title: "Method not allowed"
            status: 405
            code: method_not_allowed
            detail: "This endpoint only accepts POST."
            resolution: "Retry with one of: POST."
    PayloadTooLarge:
      description: "The request body is larger than the endpoint accepts."
      content:
        "application/problem+json":
          schema:
            "$ref": "#/components/schemas/Problem"
          example:
            type: "https://voxgig.com/developers/errors#payload_too_large"
            title: "Payload too large"
            status: 413
            code: payload_too_large
            detail: "message is limited to 5000 characters."
            resolution: "Shorten the body and retry."
    UnsupportedMediaType:
      description: "The request Content-Type cannot be parsed."
      content:
        "application/problem+json":
          schema:
            "$ref": "#/components/schemas/Problem"
          example:
            type: "https://voxgig.com/developers/errors#unsupported_media_type"
            title: "Unsupported media type"
            status: 415
            code: unsupported_media_type
            detail: "Send application/json or application/x-www-form-urlencoded."
            resolution: "Set a supported Content-Type and retry."
    UnexpectedError:
      description: "An unexpected error."
      content:
        "application/problem+json":
          schema:
            "$ref": "#/components/schemas/Problem"
          example:
            type: "https://voxgig.com/developers/errors#server_error"
            title: "Server error"
            status: 500
            code: server_error
            detail: "Something failed on our side."
            resolution: "Retry once after a short pause."
