Stotles · OpenAPI Overlay 1.0.0

API Evangelist enhancements for the Stotles Public API

9 actions 9 updates update extends openapi/stotles-public-api-openapi.yml
Generated by API Evangelist Written by API Evangelist tooling for Stotles's API. It is a proposal applied on top of the contract, not a document Stotles publishes.
View Overlay File View on GitHub Overlay Specification

What the actions change

x-shape-warningx-apievangelistx-issuancex-key-propertiesx-graph-hubx-double-counting-hazardx-pagination-hazardx-cost-hint

Targets 9

$.info
$.servers
$.components.securitySchemes.apiKey
$.paths['/v1/notices/search'].get
$.paths['/v1/buyers/search'].get.parameters[?(@.name=='query')]
$.paths['/v1/suppliers/search'].get.parameters[?(@.name=='query')]
$.components.schemas.ProblemDetails
$.components.responses.RateLimited
$.tags

OpenAPI Overlay

Raw ↑
# authorship: generated by API Evangelist tooling. Stamped 2026-08-18
# on the file's own generator header (roadmap#64). An unmarked file is
# NOT assumed to be ours -- absence of evidence was never stamped.
x-method: generated
overlay: 1.0.0
info:
  title: API Evangelist enhancements for the Stotles Public API
  version: 1.0.0
x-generated: '2026-08-14'
x-method: generated
x-source: >-
  Generated by the API Evangelist enrichment pipeline against
  openapi/stotles-public-api-openapi.yml (harvested verbatim from
  https://api.stotles.com/v1/openapi.json, HTTP 200, 2026-08-14). This overlay carries OUR
  annotations only; the original spec is never mutated.
extends: openapi/stotles-public-api-openapi.yml

x-notes: >-
  The Stotles spec is already well-annotated by its author — it documents pagination, RFC 9457
  errors, casing, money handling and versioning in info.description, and every enum carries
  x-enumDescriptions. This overlay therefore adds only what the provider does NOT state: the
  undocumented runtime signals we observed on live responses, the traversal semantics that make the
  entity graph navigable, the double-counting hazard on framework values, and links to the derived
  artifacts in this repo.

actions:
  - target: $.info
    description: Attach API Evangelist provenance and cross-links to the derived artifact set.
    update:
      x-apievangelist:
        profile: https://apis.io/provider/stotles
        harvested: '2026-08-14'
        harvested_from: https://api.stotles.com/v1/openapi.json
        artifacts:
          authentication: authentication/stotles-authentication.yml
          conventions: conventions/stotles-conventions.yml
          errors: errors/stotles-problem-types.yml
          data_model: data-model/stotles-data-model.yml
          lifecycle: lifecycle/stotles-lifecycle.yml
          rate_limits: rate-limits/stotles-rate-limits.yml
          mcp: mcp/stotles-mcp.yml
          tool_crosswalk: mcp/stotles-tool-crosswalk.yml
          skills: skills/_index.yml

  - target: $.servers
    description: >-
      Record the observed edge and CORS posture of the production server. Stotles pins
      Access-Control-Allow-Origin to https://app.stotles.com, which means this API cannot be called
      from a third-party browser client — an integration-shaping fact the spec does not state.
    update:
      - url: https://api.stotles.com
        description: Production
        x-observed:
          probed: '2026-08-14'
          edge: Amazon CloudFront
          http_versions: [h2, h3]
          cors_allow_origin: https://app.stotles.com
          browser_callable_by_third_parties: false
          hsts: false
          request_id_header: request-id
          note: >-
            `request-id` is returned on every response including 401s and is the identifier to quote
            to Stotles support. It is undocumented.

  - target: $.components.securitySchemes.apiKey
    description: Record how keys are obtained and the operational constraints on them.
    update:
      x-issuance:
        self_serve: false
        channel: Customer Success Manager
        statement: 'Keys are issued by Stotles — ask your Customer Success Manager.'
      x-key-properties:
        identity_granularity: organization
        prefix_published: false
        test_mode: false
        rotation_endpoint: false
        note: >-
          One long-lived static key per organization. No documented prefix, so the key is not
          recognizable by shape to secret scanners; no test key, so all development runs against
          production data; no self-service rotation.

  - target: $.paths['/v1/notices/search'].get
    description: >-
      Flag the two highest-consequence behaviours of the hub operation: it is the only way to
      traverse buyer/supplier/framework relationships, and unfiltered results double-count framework
      value.
    update:
      x-graph-hub: >-
        This API has no sub-resource paths. buyer -> notices, supplier -> notices and
        framework -> notices are ALL performed by re-calling this operation with buyer_id,
        supplier_id or framework_id. Expect it to dominate call volume, and budget it against the
        3 requests/second ceiling.
      x-double-counting-hazard: >-
        A framework-establishing notice and every call-off made under it both appear in unfiltered
        results, and both carry a `value`. Summing `value` across an unfiltered result set therefore
        overstates market size. Use framework_activity=only_call_offs or
        framework_activity=exclude_framework_agreements when totalling spend.
      x-pagination-hazard: >-
        A page shorter than `limit` is NOT the last page. Terminate only on next_cursor == null.
      x-cost-hint: >-
        limit defaults to 20 but accepts up to 50. Paging at 50 cuts request count by 60% against
        the 1,000/hour allowance.

  - target: $.paths['/v1/buyers/search'].get.parameters[?(@.name=='query')]
    description: >-
      Highlight the surface's sharpest inconsistency — `query` is a required single string here but
      an optional array on the notices search.
    update:
      x-shape-warning: >-
        On this operation `query` is a REQUIRED single string (2-200 chars). On
        /v1/notices/search the same parameter name is an OPTIONAL ARRAY of terms combined by
        `query_operator`. Same name, different type and requiredness. This is the most likely cause
        of a 400 for a client that generalizes one call pattern across the API.

  - target: $.paths['/v1/suppliers/search'].get.parameters[?(@.name=='query')]
    description: Mirror the required-single-string warning onto the supplier search.
    update:
      x-shape-warning: >-
        REQUIRED single string (2-200 chars), unlike the optional array `query` on
        /v1/notices/search.

  - target: $.components.schemas.ProblemDetails
    description: Bind the error envelope to the derived problem-type catalog and its handling matrix.
    update:
      x-catalog: errors/stotles-problem-types.yml
      x-type-uris-dereferenceable: false
      x-type-uris-note: >-
        The `type` URIs under https://api.stotles.com/problems/ are stable identifiers but return
        404 — no documentation is served at them. RFC 9457 permits this; the catalog in errors/
        substitutes for it.
      x-retry-matrix:
        '400': never — fix the request using errors[].parameter
        '401': never — credentials problem, escalate to a human
        '404': never — treat as absent, not as failure
        '429': yes — sleep exactly Retry-After seconds
        '500': yes — exponential backoff, log request-id

  - target: $.components.responses.RateLimited
    description: Record that Retry-After is the only rate-limit signal the API emits.
    update:
      x-signal-gap: >-
        No X-RateLimit-* or RFC 9239 RateLimit-* headers are emitted on successful responses. A
        client cannot see remaining budget; it can only discover the limit by crossing it. Documented
        limits are 1,000 requests/hour and 3 requests/second, both per API key.
      x-limits: {per_hour: 1000, per_second: 3, scope: per-api-key}

  - target: $.tags
    description: >-
      Note that the four tags map 1:1 onto the four addressable entities, and that no write
      operations exist on any of them.
    update:
      - name: Notices
        description: Public sector procurement notices.
        x-entity: Notice
        x-operations: {read: 2, write: 0}
      - name: Buyers
        description: Public sector buyers and their procurement activity.
        x-entity: Buyer
        x-operations: {read: 2, write: 0}
      - name: Suppliers
        description: Suppliers bidding for and winning public sector contracts.
        x-entity: Supplier
        x-operations: {read: 2, write: 0}
      - name: Frameworks
        description: Framework agreements and dynamic purchasing systems.
        x-entity: Framework
        x-operations: {read: 2, write: 0}
      - name: x-surface-summary
        description: >-
          8 operations, all GET, all read-only. No write path exists, which is why no idempotency
          mechanism is offered or needed — every request is safely retryable by HTTP method.