openapi: 3.1.0
info:
  title: STEADYWRK Dispatch API
  version: "1.0.0"
  summary: The field-service dispatch capability of STEADYWRK, a sovereign AI company and attestation authority for autonomous agent work — public evals, trade index, quote, and order.
  description: >-
    The dispatch capability of STEADYWRK — a sovereign AI company built in Aqaba,
    Jordan, positioned as an attestation authority for autonomous agent work.
    Dispatch is a capability, not the identity: it is the operation that generates
    the attested, append-only work records.
    Real dispatch primitives behind the STEADYWRK MCP and A2A endpoints. The evals
    and trade-index reads are public and require no key. Quote and order are gated
    by a server-side dispatch key (ApiKeyAuth). Figures returned by the evals
    endpoint are self-reported estimates — read the "estimated" flag on the wire
    rather than treating them as a binding SLA. The same four capabilities are
    exposed as MCP tools at POST /api/mcp and as A2A skills at POST /api/a2a.
  contact:
    name: STEADYWRK
    email: v@steadywrk.org
    url: https://steadywrk.app/build
  license:
    name: Proprietary — see terms
    url: https://steadywrk.app/terms
servers:
  - url: https://steadywrk.app
    description: Production
externalDocs:
  description: Agent quickstart — wire the keyless evals tool into your agent
  url: https://steadywrk.app/build
tags:
  - name: dispatch-public
    description: Public, no-auth reads. Safe to call with zero setup.
  - name: dispatch-write
    description: Key-gated dispatch writes (ApiKeyAuth via x-api-key).
  - name: agent-endpoints
    description: JSON-RPC 2.0 transports for MCP tools and A2A skills.
paths:
  /api/dispatch/index:
    get:
      operationId: getDispatchIndex
      tags: [dispatch-public]
      summary: Trade-rate catalog (public)
      description: >-
        Catalog of the 8 dispatch trade verticals with routine-urgency baseline
        model rates. Rates are STEADYWRK model rates informed by BLS OES data
        (May 2024) — not BLS verbatim. No API key required.
      security: []
      responses:
        "200":
          description: Trade catalog.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/IndexResponse"
  /api/dispatch/analytics/evals:
    get:
      operationId: getDispatchEvals
      tags: [dispatch-public]
      summary: Rolling dispatch evals (public)
      description: >-
        Machine-readable evals: goal-fulfilment rates, operational latency, and
        trust signals for the selected rolling window. No API key required. The
        "estimated" flag is true while figures are canonical estimates and false
        only when a fresh, sufficient production sample backs them.
      security: []
      parameters:
        - name: period
          in: query
          required: false
          description: Rolling window. Defaults to rolling_30d.
          schema:
            type: string
            enum: [rolling_7d, rolling_30d, rolling_90d]
            default: rolling_30d
      responses:
        "200":
          description: Evals snapshot.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EvalsResponse"
  /api/dispatch/v1/quote:
    post:
      operationId: createDispatchQuote
      tags: [dispatch-write]
      summary: Instant dispatch quote
      description: >-
        Return an instant dispatch quote for a field-service job: matched
        contractor, estimated cost in cents, ETA tier, and validity window.
        Requires a dispatch API key.
      security:
        - ApiKeyAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/QuoteRequest"
      responses:
        "200":
          description: Quote result. "available" is false when no contractor matches.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/QuoteResponse"
        "401":
          description: Missing or invalid API key.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "422":
          description: Validation failed.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ValidationError"
        "429":
          description: Rate limit exceeded (20 req/min per IP).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "503":
          description: Dispatch API not configured on the server.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /api/dispatch/v1/order:
    post:
      operationId: createDispatchOrder
      tags: [dispatch-write]
      summary: Create a dispatch work order
      description: >-
        Create a work order from raw params, auto-match a contractor from the
        active pool, and return the tracked order with a masked contractor
        reference and status URL. Requires a dispatch API key.
      security:
        - ApiKeyAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/OrderRequest"
      responses:
        "201":
          description: Work order created and dispatched.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OrderResponse"
        "401":
          description: Missing or invalid API key.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "422":
          description: Validation failed or no contractor available.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ValidationError"
        "429":
          description: Rate limit exceeded (10 req/min per IP).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "503":
          description: Dispatch API not configured on the server.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /api/mcp:
    post:
      operationId: mcpJsonRpc
      tags: [agent-endpoints]
      summary: MCP Streamable-HTTP endpoint (JSON-RPC 2.0)
      description: >-
        Stateless JSON-RPC 2.0 transport. Methods: initialize, tools/list,
        tools/call (dispatch.quote, dispatch.order, dispatch.evals,
        dispatch.index), ping. GET returns 405. See https://steadywrk.app/mcp.json for the
        discovery manifest.
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/JsonRpcRequest"
      responses:
        "200":
          description: JSON-RPC response.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/JsonRpcResponse"
  /api/a2a:
    post:
      operationId: a2aJsonRpc
      tags: [agent-endpoints]
      summary: A2A endpoint (JSON-RPC 2.0, message/send)
      description: >-
        Agent2Agent transport (protocol 0.3.0). Send a message with a Data part
        {"skill":"dispatch.evals","input":{...}} to invoke a skill. The agent
        card lives at https://steadywrk.app/.well-known/agent-card.json.
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/JsonRpcRequest"
      responses:
        "200":
          description: JSON-RPC response carrying a Task.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/JsonRpcResponse"
components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: x-api-key
      description: Server-issued dispatch key. Required for quote and order only.
  schemas:
    Location:
      type: object
      required: [state]
      properties:
        state:
          type: string
          minLength: 2
          maxLength: 2
          description: Two-letter US state code (e.g. AZ, TX).
        zip:
          type: string
          maxLength: 10
        city:
          type: string
          maxLength: 255
    OrderLocation:
      allOf:
        - $ref: "#/components/schemas/Location"
        - type: object
          properties:
            address:
              type: string
              maxLength: 1000
            name:
              type: string
              maxLength: 500
    Urgency:
      type: string
      enum: [emergency, urgent, routine, scheduled]
      default: routine
    QuoteRequest:
      type: object
      required: [trade, location]
      properties:
        trade:
          type: string
          minLength: 1
          description: Trade code (plumbing, electrical, HVAC, etc.).
        location:
          $ref: "#/components/schemas/Location"
        urgency:
          $ref: "#/components/schemas/Urgency"
        nte_cents:
          type: integer
          minimum: 1
          description: Not-to-exceed ceiling, in cents.
        description:
          type: string
          maxLength: 2000
      example:
        trade: plumbing
        location:
          state: AZ
          zip: "85001"
        urgency: routine
    QuoteResponse:
      type: object
      properties:
        available:
          type: boolean
        quote:
          type: object
          properties:
            trade:
              type: string
            location:
              $ref: "#/components/schemas/Location"
            urgency:
              $ref: "#/components/schemas/Urgency"
            estimated_eta:
              type: string
              description: ETA band derived from urgency (e.g. "same day").
            best_match:
              type: object
              properties:
                estimated_cost_cents:
                  type: integer
                estimated_cost_usd:
                  type: string
                estimated_response_minutes:
                  type: integer
                match_score:
                  type: number
            alternates_count:
              type: integer
            generated_at:
              type: string
              format: date-time
            valid_for_minutes:
              type: integer
        message:
          type: string
          description: Present when available is false.
    OrderRequest:
      type: object
      required: [trade, location, description]
      properties:
        trade:
          type: string
          minLength: 1
        location:
          $ref: "#/components/schemas/OrderLocation"
        urgency:
          $ref: "#/components/schemas/Urgency"
        nte_cents:
          type: integer
          minimum: 1
        description:
          type: string
          minLength: 1
          maxLength: 5000
        callback_url:
          type: string
          format: uri
        reference_id:
          type: string
          maxLength: 255
      example:
        trade: electrical
        location:
          state: AZ
          city: Phoenix
        urgency: urgent
        description: Breaker panel fault at a commercial site.
    OrderResponse:
      type: object
      properties:
        created:
          type: boolean
        order:
          type: object
          properties:
            id:
              type: string
            status:
              type: string
              example: pending_dispatch
            trade:
              type: string
            location:
              $ref: "#/components/schemas/OrderLocation"
            urgency:
              $ref: "#/components/schemas/Urgency"
            quoted_cost_cents:
              type: integer
            quoted_cost_usd:
              type: string
            assigned_contractor_ref:
              type: string
              description: Masked contractor handle (e.g. SW-1a2b3c4d).
            estimated_response_minutes:
              type: integer
            created_at:
              type: string
              format: date-time
        tracking:
          type: object
          properties:
            status_url:
              type: string
              format: uri
    IndexResponse:
      type: object
      properties:
        trades:
          type: array
          items:
            $ref: "#/components/schemas/TradeRate"
        note:
          type: string
        updated_at:
          type: string
          format: date-time
    TradeRate:
      type: object
      properties:
        trade:
          type: string
        tradeLabel:
          type: string
        blsOccCode:
          type: string
        metro:
          type: string
        state:
          type: string
        urgency:
          type: string
        medianRateDollars:
          type: number
        p25RateDollars:
          type: number
        p75RateDollars:
          type: number
        sampleSize:
          type: integer
        period:
          type: string
    EvalsResponse:
      type: object
      description: >-
        Sourced from canonical metrics. The "estimated" flag is authoritative;
        latency figures stay canonical estimates (latency_source says so).
      properties:
        schema_version:
          type: string
        period:
          type: string
          enum: [rolling_7d, rolling_30d, rolling_90d]
        estimated:
          type: boolean
        goal_fulfillment:
          type: object
          properties:
            completion_rate:
              type: number
            nte_accuracy:
              type: number
            redispatch_rate:
              type: number
            human_override_rate:
              type: number
            source:
              type: string
              enum: [canonical_estimate, live_telemetry]
        operational:
          type: object
          properties:
            latency_source:
              type: string
            avg_dispatch_latency_ms:
              type: integer
            p95_dispatch_latency_ms:
              type: integer
            jobs_dispatched_30d:
              type: string
            active_providers:
              type: integer
            active_states:
              type: integer
        trust_signals:
          type: object
          properties:
            opsec_audit_last_passed:
              type: string
            canonical_metrics_drift:
              type: boolean
            build_hash:
              type: string
      example:
        schema_version: "2.1.4"
        period: rolling_30d
        estimated: true
        goal_fulfillment:
          completion_rate: 0.9
          human_override_rate: 0.03
          source: canonical_estimate
        operational:
          latency_source: canonical_estimate
          avg_dispatch_latency_ms: 340
          p95_dispatch_latency_ms: 890
    Error:
      type: object
      properties:
        error:
          type: string
    ValidationError:
      type: object
      properties:
        error:
          type: string
          example: Validation failed
        details:
          type: object
          additionalProperties: true
    JsonRpcRequest:
      type: object
      required: [jsonrpc, method]
      properties:
        jsonrpc:
          type: string
          const: "2.0"
        id:
          oneOf:
            - type: string
            - type: integer
            - type: "null"
        method:
          type: string
          description: e.g. tools/list, tools/call (MCP) or message/send (A2A).
        params:
          type: object
          additionalProperties: true
    JsonRpcResponse:
      type: object
      required: [jsonrpc]
      properties:
        jsonrpc:
          type: string
          const: "2.0"
        id:
          oneOf:
            - type: string
            - type: integer
            - type: "null"
        result:
          type: object
          additionalProperties: true
        error:
          type: object
          properties:
            code:
              type: integer
            message:
              type: string
