---
openapi: 3.0.3
info:
  title: Substrate API
  version: '1.0'
  description: |
    Developer API for connecting healthcare systems to Substrate AI's
    autonomous revenue-cycle platform: submit encounters, launch claim-status
    workflows, fetch normalized snapshots, and configure webhook delivery for
    lifecycle events.

    For public, authless healthcare payer discovery from Claude, Codex, Cursor,
    VS Code, and other AI applications, use the separate [Substrate Payer Search
    MCP guide](https://docs.substrateai.com/mcp/). Never send PHI to the public
    payer MCP. Authenticated MCP tools for claim status, eligibility, portal
    workflows, claims, and appeals are onboarding beta partners now; [talk to
    the Substrate AI team](https://www.substrateai.com/get-started?utm_source=developer_docs&utm_medium=openapi&utm_campaign=authenticated_mcp_beta)
    to request access.
externalDocs:
  description: Substrate Payer Search MCP
  url: https://docs.substrateai.com/mcp/
servers:
- url: https://api.substrateai.com
  description: Production
security:
- bearerAuth: []
tags:
- name: Encounters
- name: Webhook Settings
paths:
  "/v1/encounters":
    get:
      tags:
      - Encounters
      operationId: listEncounters
      summary: Fetch encounters by ID
      description: |
        Returns encounter snapshots for the supplied prefixed IDs. With no
        `ids` parameter, returns an empty list (filter-only, not a full
        account index).
      parameters:
      - name: ids[]
        in: query
        description: Repeated query parameter; encounter prefix ID per value.
        required: false
        schema:
          type: array
          maxItems: 500
          items:
            type: string
            example: enc_abc123
      responses:
        '200':
          description: Encounters and per-input errors
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/EncounterListResponse"
        '401':
          "$ref": "#/components/responses/Unauthorized"
        '429':
          "$ref": "#/components/responses/RateLimited"
    post:
      tags:
      - Encounters
      operationId: createEncounters
      summary: Create or update encounters
      description: |
        Creates or updates encounters and triggers claim-status processing for
        newly created work. Accepts either a single encounter object or an
        array of encounters in one request. Up to 100 encounters per call.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              oneOf:
              - "$ref": "#/components/schemas/SingleEncounterRequest"
              - "$ref": "#/components/schemas/BatchEncountersRequest"
            examples:
              single:
                summary: Single encounter
                value:
                  encounter:
                    first_name: Jane
                    last_name: Doe
                    date_of_birth: 01/31/1990
                    payer_name: Aetna
                    member_id: M123
                    provider_id: '1234567890'
                    date_of_service: 03/20/2026
                    ehr_patient_account: EHR-12345
                    organization_name: Main Clinic
              batch:
                summary: Batch of encounters
                value:
                  encounters:
                  - first_name: Jane
                    last_name: Doe
                    date_of_birth: 01/31/1990
                    payer_name: Aetna
                    member_id: M123
                    provider_id: '1234567890'
                    date_of_service: 03/20/2026
      responses:
        '200':
          description: Encounters created or updated
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/EncounterListResponse"
        '400':
          "$ref": "#/components/responses/BadRequest"
        '401':
          "$ref": "#/components/responses/Unauthorized"
        '429':
          "$ref": "#/components/responses/RateLimited"
  "/v1/encounters/{id}":
    get:
      tags:
      - Encounters
      operationId: getEncounter
      summary: Fetch a single encounter
      parameters:
      - name: id
        in: path
        required: true
        description: Encounter prefix ID (e.g. `enc_abc123`).
        schema:
          type: string
      responses:
        '200':
          description: Encounter snapshot
          content:
            application/json:
              schema:
                type: object
                properties:
                  encounter:
                    "$ref": "#/components/schemas/Encounter"
        '401':
          "$ref": "#/components/responses/Unauthorized"
        '404':
          description: Encounter not found (or belongs to a different account)
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/Error"
        '429':
          "$ref": "#/components/responses/RateLimited"
  "/v1/webhook_settings":
    get:
      tags:
      - Webhook Settings
      operationId: getWebhookSettings
      summary: Read current webhook settings
      responses:
        '200':
          description: Current webhook settings for the account
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/WebhookSettings"
        '401':
          "$ref": "#/components/responses/Unauthorized"
    patch:
      tags:
      - Webhook Settings
      operationId: updateWebhookSettings
      summary: Update webhook settings
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                webhook_setting:
                  type: object
                  properties:
                    url:
                      type: string
                      format: uri
                      example: https://hooks.example.com/substrate
                    enabled:
                      type: boolean
      responses:
        '200':
          description: Updated webhook settings
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/WebhookSettings"
        '401':
          "$ref": "#/components/responses/Unauthorized"
        '422':
          "$ref": "#/components/responses/UnprocessableEntity"
  "/v1/webhook_settings/test_delivery":
    post:
      tags:
      - Webhook Settings
      operationId: sendWebhookTestDelivery
      summary: Send a test webhook
      description: |
        Substrate emits an `encounter.test` event to the configured URL so
        you can verify your receiver end-to-end. Returns the latest webhook
        settings (with `last_test_*` fields populated) on success.
      responses:
        '200':
          description: Test delivery sent
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/WebhookSettings"
        '401':
          "$ref": "#/components/responses/Unauthorized"
        '422':
          description: Test delivery failed (configuration or receiver error)
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/Error"
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: API token (`sub_live_*` in production, `sub_test_*` outside production)
  responses:
    Unauthorized:
      description: Missing or invalid bearer token
      content:
        application/json:
          schema:
            "$ref": "#/components/schemas/Error"
    BadRequest:
      description: Malformed request body or parameters
      content:
        application/json:
          schema:
            "$ref": "#/components/schemas/Error"
    UnprocessableEntity:
      description: Validation failed
      content:
        application/json:
          schema:
            "$ref": "#/components/schemas/ValidationErrors"
    RateLimited:
      description: |
        Per-token rate limit exceeded. Includes `Retry-After` and `RateLimit-*`
        headers so callers can self-pace.
      headers:
        Retry-After:
          schema:
            type: integer
        RateLimit-Limit:
          schema:
            type: integer
        RateLimit-Remaining:
          schema:
            type: integer
        RateLimit-Reset:
          schema:
            type: integer
      content:
        application/json:
          schema:
            "$ref": "#/components/schemas/Error"
  schemas:
    Error:
      type: object
      properties:
        error:
          type: string
      required:
      - error
    ValidationErrors:
      type: object
      properties:
        errors:
          type: array
          items:
            type: string
      required:
      - errors
    SingleEncounterRequest:
      type: object
      properties:
        encounter:
          "$ref": "#/components/schemas/EncounterInput"
      required:
      - encounter
    BatchEncountersRequest:
      type: object
      properties:
        encounters:
          type: array
          maxItems: 100
          items:
            "$ref": "#/components/schemas/EncounterInput"
      required:
      - encounters
    EncounterInput:
      type: object
      description: |
        Required: `first_name`, `last_name`, `date_of_birth`, `payer_name`,
        `provider_id`, and one of `date_of_service` / `first_date_of_service`.
        Other fields are optional context.
      properties:
        first_name:
          type: string
        last_name:
          type: string
        date_of_birth:
          type: string
          example: 01/31/1990
        payer_name:
          type: string
        member_id:
          type: string
        group_number:
          type: string
        policy_number:
          type: string
        provider_id:
          type: string
        date_of_service:
          type: string
          example: 03/20/2026
        first_date_of_service:
          type: string
          example: 03/20/2026
        ehr_patient_account:
          type: string
        org_level:
          type: string
        organization_name:
          type: string
        rendering_provider_npi:
          type: string
        billing_provider_npi:
          type: string
        ptan:
          type: string
        tax_id_number:
          type: string
        note:
          type: string
      required:
      - first_name
      - last_name
      - date_of_birth
      - payer_name
      - provider_id
    EncounterListResponse:
      type: object
      properties:
        encounters:
          type: array
          items:
            "$ref": "#/components/schemas/Encounter"
        errors:
          type: array
          items:
            "$ref": "#/components/schemas/EncounterError"
      required:
      - encounters
      - errors
    EncounterError:
      type: object
      properties:
        id:
          type: string
          description: The submitted ID (or input index for batch creates) that failed.
        index:
          type: integer
        message:
          type: string
      required:
      - message
    Encounter:
      type: object
      description: Encounter snapshot — top-level encounter attributes plus nested
        patient/claims/insurance/eligibility/payer-resolution.
      properties:
        id:
          type: string
          example: enc_abc123
        service_date:
          type: string
          format: date
          nullable: true
        first_service_date:
          type: string
          format: date
          nullable: true
        last_service_date:
          type: string
          format: date
          nullable: true
        internal_status:
          type: string
        has_payer_claim:
          type: boolean
        ehr_patient_account:
          type: string
          nullable: true
        organization_name:
          type: string
          nullable: true
        rendering_provider_npi:
          type: string
          nullable: true
        billing_provider_npi:
          type: string
          nullable: true
        payer_patient_account_number:
          type: string
          nullable: true
        tax_id_number:
          type: string
          nullable: true
        source_id:
          type: string
          nullable: true
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        patient:
          "$ref": "#/components/schemas/Patient"
        claims:
          type: array
          items:
            "$ref": "#/components/schemas/Claim"
        insurance_plans:
          type: array
          items:
            "$ref": "#/components/schemas/InsurancePlan"
        eligibility:
          type: array
          items:
            "$ref": "#/components/schemas/EligibilityCheck"
        payer_resolution:
          oneOf:
          - "$ref": "#/components/schemas/PayerResolution"
          - type: 'null'
      required:
      - id
    Patient:
      type: object
      properties:
        id:
          type: string
          example: pat_xyz789
        first_name:
          type: string
        middle_name:
          type: string
          nullable: true
        last_name:
          type: string
        date_of_birth:
          type: string
          format: date
        gender:
          type: string
          nullable: true
        address_line_1:
          type: string
          nullable: true
        address_line_2:
          type: string
          nullable: true
        city:
          type: string
          nullable: true
        state:
          type: string
          nullable: true
        zip_code:
          type: string
          nullable: true
        account_number:
          type: string
          nullable: true
        relationship:
          type: string
          nullable: true
        source:
          type: string
          nullable: true
        source_id:
          type: string
          nullable: true
        synced_at:
          type: string
          format: date-time
          nullable: true
        imported_at:
          type: string
          format: date-time
          nullable: true
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      required:
      - id
    Claim:
      type: object
      properties:
        id:
          type: string
          example: clm_123
        insurance_plan_id:
          type: string
          nullable: true
        status:
          type: string
          nullable: true
        network_status:
          type: string
          nullable: true
        claim_type:
          type: string
          nullable: true
        platform:
          type: string
          nullable: true
        claim_number:
          type: string
          nullable: true
        received_date:
          type: string
          format: date
          nullable: true
        finalized_date:
          type: string
          format: date
          nullable: true
        first_service_date:
          type: string
          format: date
          nullable: true
        last_service_date:
          type: string
          format: date
          nullable: true
        billed_amount_cents:
          type: integer
          nullable: true
        allowed_amount_cents:
          type: integer
          nullable: true
        paid_amount_cents:
          type: integer
          nullable: true
        copay_amount_cents:
          type: integer
          nullable: true
        coinsurance_amount_cents:
          type: integer
          nullable: true
        deductible_amount_cents:
          type: integer
          nullable: true
        ineligible_amount_cents:
          type: integer
          nullable: true
        patient_responsibility_cents:
          type: integer
          nullable: true
        payer_name:
          type: string
          nullable: true
        policy_number:
          type: string
          nullable: true
        billing_provider_name:
          type: string
          nullable: true
        billing_provider_npi:
          type: string
          nullable: true
        rendering_provider_name:
          type: string
          nullable: true
        rendering_provider_npi:
          type: string
          nullable: true
        patient_account_number:
          type: string
          nullable: true
        tax_id_number:
          type: string
          nullable: true
        ptan:
          type: string
          nullable: true
        source:
          type: string
          nullable: true
        synced_at:
          type: string
          format: date-time
          nullable: true
        imported_at:
          type: string
          format: date-time
          nullable: true
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        line_items:
          type: array
          items:
            "$ref": "#/components/schemas/LineItem"
      required:
      - id
    LineItem:
      type: object
      properties:
        procedure_code:
          type: string
          nullable: true
        revenue_code:
          type: string
          nullable: true
        line_number:
          type: integer
          nullable: true
        control_number:
          type: string
          nullable: true
        units:
          type: number
          nullable: true
        diagnosis_codes:
          type: array
          items:
            type: string
          nullable: true
        carc_codes:
          type: array
          items:
            type: string
          nullable: true
        rarc_codes:
          type: array
          items:
            type: string
          nullable: true
        remark_codes:
          type: array
          items:
            type: string
          nullable: true
        modifiers:
          type: array
          items:
            type: string
          nullable: true
        service_date:
          type: string
          format: date
          nullable: true
        processed_at:
          type: string
          format: date-time
          nullable: true
        billed_amount_cents:
          type: integer
          nullable: true
        paid_amount_cents:
          type: integer
          nullable: true
        allowed_amount_cents:
          type: integer
          nullable: true
        copay_amount_cents:
          type: integer
          nullable: true
        coinsurance_amount_cents:
          type: integer
          nullable: true
        deductible_amount_cents:
          type: integer
          nullable: true
        ineligible_amount_cents:
          type: integer
          nullable: true
        patient_responsibility_cents:
          type: integer
          nullable: true
        provider_writeoff_cents:
          type: integer
          nullable: true
        provider_discount_cents:
          type: integer
          nullable: true
        contracted_amount_cents:
          type: integer
          nullable: true
    InsurancePlan:
      type: object
      properties:
        id:
          type: string
          example: ip_456
        member_id:
          type: string
          nullable: true
        group_number:
          type: string
          nullable: true
        policy_number:
          type: string
          nullable: true
        payer_name:
          type: string
          nullable: true
        plan_payer_id:
          type: string
          nullable: true
        plan_type:
          type: string
          nullable: true
        platform:
          type: string
          nullable: true
        insurance_type:
          type: string
          nullable: true
        relationship:
          type: string
          nullable: true
        payer_state:
          type: string
          nullable: true
        subscriber_first_name:
          type: string
          nullable: true
        subscriber_middle_name:
          type: string
          nullable: true
        subscriber_last_name:
          type: string
          nullable: true
        subscriber_address_line_1:
          type: string
          nullable: true
        subscriber_address_line_2:
          type: string
          nullable: true
        subscriber_city:
          type: string
          nullable: true
        subscriber_state:
          type: string
          nullable: true
        subscriber_zip:
          type: string
          nullable: true
        source:
          type: string
          nullable: true
        source_id:
          type: string
          nullable: true
        synced_at:
          type: string
          format: date-time
          nullable: true
        imported_at:
          type: string
          format: date-time
          nullable: true
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      required:
      - id
    EligibilityCheck:
      type: object
      properties:
        id:
          type: integer
        member_id:
          type: string
          nullable: true
        status:
          type: string
          nullable: true
        error_message:
          type: string
          nullable: true
        has_pds_errors:
          type: boolean
          nullable: true
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    PayerResolution:
      type: object
      properties:
        id:
          type: integer
        status:
          type: string
          nullable: true
        confidence:
          type: number
          nullable: true
        resolution_method:
          type: string
          nullable: true
        resolution_tier:
          type: string
          nullable: true
        plan_type:
          type: string
          nullable: true
        insurance_type_code:
          type: string
          nullable: true
        resolved_at:
          type: string
          format: date-time
          nullable: true
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    WebhookSettings:
      type: object
      properties:
        url:
          type: string
          format: uri
          nullable: true
        enabled:
          type: boolean
        signing_secret:
          type: string
          description: |
            Account-scoped HMAC-SHA256 signing secret prefixed `sub_wh_*`.
            Used by your receiver to verify the `Substrate-Signature` header
            on outbound webhooks.
        last_test_at:
          type: string
          format: date-time
          nullable: true
        last_test_response_code:
          type: integer
          nullable: true
        last_test_error:
          type: string
          nullable: true
