openapi: 3.1.0
info:
  title: Smart Contracts Escrow — Merchant API
  version: "2026-09-01"
  description: >
    Server-to-server API for stores (WooCommerce, Shopify, custom) to offer escrow-protected
    checkout. Guides: https://smartcontractsescrow.net/developers All money values are 2-dp decimal strings.
servers:
  - url: https://smartcontractsescrow.net/api/v1
security:
  - apiKey: []
paths:
  /account:
    get:
      summary: Verify credentials
      responses:
        "200":
          description: Merchant account
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Account" }
        "401": { $ref: "#/components/responses/Error" }
  /checkout/sessions:
    get:
      summary: List checkout sessions
      parameters:
        - { name: external_reference, in: query, schema: { type: string } }
        - $ref: "#/components/parameters/Limit"
        - $ref: "#/components/parameters/Cursor"
      responses:
        "200":
          description: Page of sessions
          content:
            application/json:
              schema:
                allOf:
                  - $ref: "#/components/schemas/ListEnvelope"
                  - properties:
                      data: { type: array, items: { $ref: "#/components/schemas/CheckoutSession" } }
    post:
      summary: Create a checkout session
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/CheckoutSessionCreate" }
      responses:
        "201":
          description: Created
          content:
            application/json:
              schema: { $ref: "#/components/schemas/CheckoutSession" }
        "400": { $ref: "#/components/responses/Error" }
        "409": { $ref: "#/components/responses/Error" }
  /checkout/sessions/{id}:
    get:
      summary: Retrieve a checkout session
      parameters: [{ $ref: "#/components/parameters/SessionId" }]
      responses:
        "200":
          description: Session
          content:
            application/json:
              schema: { $ref: "#/components/schemas/CheckoutSession" }
        "404": { $ref: "#/components/responses/Error" }
  /checkout/sessions/{id}/expire:
    post:
      summary: Expire an open session
      parameters: [{ $ref: "#/components/parameters/SessionId" }]
      responses:
        "200":
          description: Session (EXPIRED)
          content:
            application/json:
              schema: { $ref: "#/components/schemas/CheckoutSession" }
        "409": { $ref: "#/components/responses/Error" }
  /escrows:
    get:
      summary: List escrows created through your checkout sessions
      parameters:
        - { name: status, in: query, schema: { $ref: "#/components/schemas/EscrowStatus" } }
        - { name: external_reference, in: query, schema: { type: string } }
        - $ref: "#/components/parameters/Limit"
        - $ref: "#/components/parameters/Cursor"
      responses:
        "200":
          description: Page of escrows
          content:
            application/json:
              schema:
                allOf:
                  - $ref: "#/components/schemas/ListEnvelope"
                  - properties:
                      data: { type: array, items: { $ref: "#/components/schemas/Escrow" } }
  /escrows/{escrow_id}:
    get:
      summary: Retrieve an escrow
      parameters: [{ $ref: "#/components/parameters/EscrowId" }]
      responses:
        "200":
          description: Escrow
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Escrow" }
        "404": { $ref: "#/components/responses/Error" }
  /escrows/{escrow_id}/milestones/{milestone_id}/submit:
    post:
      summary: Submit delivery for a milestone (e.g. order shipped)
      parameters:
        - $ref: "#/components/parameters/EscrowId"
        - { name: milestone_id, in: path, required: true, schema: { type: string, format: uuid } }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [submission_details]
              properties:
                submission_details: { type: string, maxLength: 5000, example: "DHL 1234567890" }
      responses:
        "200":
          description: Updated escrow
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Escrow" }
        "400": { $ref: "#/components/responses/Error" }
        "409": { $ref: "#/components/responses/Error" }
  /events:
    get:
      summary: List events (reconciliation feed)
      parameters:
        - { name: type, in: query, schema: { $ref: "#/components/schemas/EventType" } }
        - $ref: "#/components/parameters/Limit"
        - $ref: "#/components/parameters/Cursor"
      responses:
        "200":
          description: Page of events
          content:
            application/json:
              schema:
                allOf:
                  - $ref: "#/components/schemas/ListEnvelope"
                  - properties:
                      data: { type: array, items: { $ref: "#/components/schemas/Event" } }
webhooks:
  escrowEvent:
    post:
      summary: Event notification
      description: >
        Signed with `X-Escrow-Signature: t=<unix>,v1=<hex HMAC-SHA256(secret, "<t>.<raw body>")>`.
        Respond 2xx within 10 s. At-least-once, unordered delivery.
      parameters:
        - { name: X-Escrow-Signature, in: header, required: true, schema: { type: string } }
        - { name: X-Escrow-Event, in: header, required: true, schema: { $ref: "#/components/schemas/EventType" } }
        - { name: X-Escrow-Event-Id, in: header, required: true, schema: { type: string } }
      requestBody:
        content:
          application/json:
            schema: { $ref: "#/components/schemas/Event" }
      responses:
        "200": { description: Acknowledged }
components:
  securitySchemes:
    apiKey:
      type: http
      scheme: bearer
      bearerFormat: "sce_<lookup>_<secret>"
  parameters:
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      schema: { type: string, maxLength: 255 }
    SessionId:
      name: id
      in: path
      required: true
      schema: { type: string, example: cs_5b1e0c9a7d3f4e2a1b6c8d0e }
    EscrowId:
      name: escrow_id
      in: path
      required: true
      description: Escrow uuid (the TR- reference code is also accepted).
      schema: { type: string }
    Limit:
      name: limit
      in: query
      schema: { type: integer, minimum: 1, maximum: 100, default: 25 }
    Cursor:
      name: cursor
      in: query
      schema: { type: string }
  responses:
    Error:
      description: Error envelope
      content:
        application/json:
          schema: { $ref: "#/components/schemas/Error" }
  schemas:
    Money:
      type: string
      pattern: '^\d+\.\d{2}$'
      example: "149.99"
    Error:
      type: object
      required: [error]
      properties:
        error:
          type: object
          required: [type, code, message]
          properties:
            type:
              type: string
              enum: [invalid_request_error, authentication_error, permission_error, rate_limit_error, api_error]
            code: { type: string }
            message: { type: string }
            details: { type: object }
    ListEnvelope:
      type: object
      properties:
        object: { const: list }
        next: { type: [string, "null"] }
        previous: { type: [string, "null"] }
    Account:
      type: object
      properties:
        object: { const: account }
        id: { type: string, pattern: '^[1-9][0-9]{18}$', description: 19-digit account number (a string; exceeds JS safe-integer range) }
        seller_code: { type: string }
        trading_name: { type: string }
        verification_status: { type: string, enum: [UNVERIFIED, PENDING, VERIFIED, REJECTED] }
        api_key: { type: string }
        api_version: { type: string }
    LineItem:
      type: object
      required: [name, unit_amount]
      properties:
        name: { type: string, maxLength: 255 }
        quantity: { type: integer, minimum: 1, default: 1 }
        unit_amount: { $ref: "#/components/schemas/Money" }
        sku: { type: string, maxLength: 100 }
    MilestoneInput:
      type: object
      required: [title, amount]
      properties:
        title: { type: string, maxLength: 255 }
        description: { type: string }
        amount: { $ref: "#/components/schemas/Money" }
    CheckoutSessionCreate:
      type: object
      required: [title, amount]
      properties:
        title: { type: string, maxLength: 255 }
        description: { type: string }
        amount: { $ref: "#/components/schemas/Money" }
        currency: { type: string, enum: [USD], default: USD }
        line_items: { type: array, maxItems: 100, items: { $ref: "#/components/schemas/LineItem" } }
        milestones:
          type: array
          maxItems: 20
          description: Must sum to amount. Defaults to a single milestone.
          items: { $ref: "#/components/schemas/MilestoneInput" }
        external_reference: { type: string, maxLength: 255 }
        customer_email: { type: string, format: email }
        metadata: { type: object, additionalProperties: { type: string, maxLength: 500 }, maxProperties: 50 }
        success_url: { type: string, format: uri }
        cancel_url: { type: string, format: uri }
        platform: { type: string, maxLength: 32 }
        expires_in: { type: integer, minimum: 900, maximum: 604800, default: 86400 }
    CheckoutSession:
      type: object
      properties:
        id: { type: string }
        object: { const: checkout.session }
        status: { type: string, enum: [OPEN, COMPLETED, EXPIRED] }
        url: { type: [string, "null"], format: uri }
        title: { type: string }
        description: { type: string }
        currency: { type: string }
        amount: { $ref: "#/components/schemas/Money" }
        line_items: { type: array, items: { $ref: "#/components/schemas/LineItem" } }
        milestones: { type: array, items: { $ref: "#/components/schemas/MilestoneInput" } }
        external_reference: { type: string }
        customer_email: { type: string }
        metadata: { type: object, additionalProperties: { type: string } }
        success_url: { type: string }
        cancel_url: { type: string }
        platform: { type: string }
        escrow: { type: [string, "null"], format: uuid }
        expires_at: { type: integer }
        completed_at: { type: [integer, "null"] }
        created: { type: integer }
    EscrowStatus:
      type: string
      enum: [PENDING_ACCEPTANCE, PENDING_FUNDING, AWAITING_PAYMENT, WAITING_PAYMENT_CONFIRMATION, PARTIALLY_FUNDED,
             IN_ESCROW, WORK_IN_PROGRESS, COMPLETED, DISPUTED, DECLINED, CANCELLED, CLOSED, DEADLINE_EXPIRED]
    Milestone:
      type: object
      properties:
        id: { type: string, format: uuid }
        reference_code: { type: string, example: MS-40918273 }
        object: { const: milestone }
        title: { type: string }
        description: { type: string }
        amount: { $ref: "#/components/schemas/Money" }
        status: { type: string, enum: [PENDING, AWAITING_REVIEW, REVISION_REQUESTED, COMPLETED, DISPUTED] }
        submission_details: { type: string }
    Escrow:
      type: object
      properties:
        id: { type: string, format: uuid }
        reference_code: { type: string, example: TR-20261125.482913.10200, description: 'Human-readable; TR-<date>.<6 random digits>.<deal amount in cents>. Not a secret.' }
        dispute_reference: { type: [string, "null"], example: DS-73918264 }
        object: { const: escrow }
        title: { type: string }
        description: { type: string }
        status: { $ref: "#/components/schemas/EscrowStatus" }
        currency: { type: string }
        amount: { $ref: "#/components/schemas/Money" }
        fees: { $ref: "#/components/schemas/Money" }
        total: { $ref: "#/components/schemas/Money" }
        funded_amount: { $ref: "#/components/schemas/Money" }
        buyer: { type: [object, "null"], properties: { email: { type: string } } }
        checkout_session: { type: [string, "null"] }
        external_reference: { type: string }
        metadata: { type: object, additionalProperties: { type: string } }
        milestones: { type: array, items: { $ref: "#/components/schemas/Milestone" } }
        created: { type: integer }
    EventType:
      type: string
      enum:
        - checkout.session.completed
        - checkout.session.expired
        - escrow.created
        - escrow.funded
        - escrow.partially_funded
        - escrow.work_started
        - escrow.completed
        - escrow.disputed
        - escrow.cancelled
        - escrow.expired
        - escrow.updated
        - escrow.milestone.submitted
        - escrow.milestone.approved
        - escrow.milestone.revision_requested
        - escrow.milestone.disputed
        - escrow.milestone.updated
        - ping
    Event:
      type: object
      properties:
        id: { type: string, example: evt_9f2c1a7b3e4d5c6b7a8f9e0d }
        object: { const: event }
        type: { $ref: "#/components/schemas/EventType" }
        created: { type: integer }
        data:
          type: object
          properties:
            object:
              description: Escrow, CheckoutSession, or Milestone (+ escrow, checkout_session, external_reference)
              type: object
