> ## Documentation Index
> Fetch the complete documentation index at: https://docs.useselo.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# Run a release test

> Run a paid release test. This route is protected by x402 v2 and costs 1.00 USDC on Algorand Mainnet.

1. Send the request without payment. Selo validates the body and the preflight first, then answers `402` with a `PAYMENT-REQUIRED` header: base64 JSON offering one `exact` requirement of `1000000` atomic USDC (ASA `31566704`) on `algorand:wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8=`, tagged `x402-global-challenge`.
2. Sign that requirement with an x402 v2 client and send the same request again with the `PAYMENT-SIGNATURE` header (base64 JSON payment payload). The x402 v1 header `X-PAYMENT` is not read.
3. Selo verifies the payment with the facilitator, claims the job, settles, and records the settlement transaction on the job. Only then does it pay your endpoint and run the five checks. The response arrives when the job finishes (wall clock 60 seconds), with a `PAYMENT-RESPONSE` header.

Send an `Idempotency-Key`. A repeat with the same `preflightId` and key returns the first job's result and never settles a second payment, even if it carries a newly signed payment. Without the header, Selo deduplicates on the signed payment itself.



## OpenAPI

````yaml /openapi.json post /v1/release-test
openapi: 3.1.0
info:
  title: Selo API
  version: 1.0.0
  summary: CI for x402. It pays your endpoint before your users do.
  description: >-
    Selo is a release gate for paid x402 APIs on Algorand. You authorize your
    endpoint, run a free preflight, then pay Selo 1.00 USDC over x402. Only
    after that payment settles does Selo pay your endpoint as a real x402
    client, run five release checks and return a deterministic PASS, FAIL or
    INCONCLUSIVE verdict with an Ed25519-signed attestation.


    There are no API keys. Access to a target is granted per target by
    authorization; the only paid route is `POST /v1/release-test`, paid with
    x402. Bodies are JSON, at most 64 KiB.
  contact:
    name: Selo
    url: https://useselo.xyz
servers:
  - url: https://api.useselo.xyz
    description: Production, Algorand Mainnet
security: []
tags:
  - name: Authorizations
    description: Prove you control the target before Selo sends it anything.
  - name: Preflight
    description: Check for free that a target can be tested and is within budget.
  - name: Release tests
    description: The paid x402 route that runs the release checks.
  - name: Reports
    description: Private, token-addressed reports of finished release tests.
  - name: Attestation keys
    description: Public keys that verify signed verdicts.
  - name: Discovery
    description: Machine-readable descriptions of Selo.
  - name: Health
    description: Liveness.
paths:
  /v1/release-test:
    post:
      tags:
        - Release tests
      summary: Run a release test
      description: >-
        Run a paid release test. This route is protected by x402 v2 and costs
        1.00 USDC on Algorand Mainnet.


        1. Send the request without payment. Selo validates the body and the
        preflight first, then answers `402` with a `PAYMENT-REQUIRED` header:
        base64 JSON offering one `exact` requirement of `1000000` atomic USDC
        (ASA `31566704`) on
        `algorand:wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8=`, tagged
        `x402-global-challenge`.

        2. Sign that requirement with an x402 v2 client and send the same
        request again with the `PAYMENT-SIGNATURE` header (base64 JSON payment
        payload). The x402 v1 header `X-PAYMENT` is not read.

        3. Selo verifies the payment with the facilitator, claims the job,
        settles, and records the settlement transaction on the job. Only then
        does it pay your endpoint and run the five checks. The response arrives
        when the job finishes (wall clock 60 seconds), with a `PAYMENT-RESPONSE`
        header.


        Send an `Idempotency-Key`. A repeat with the same `preflightId` and key
        returns the first job's result and never settles a second payment, even
        if it carries a newly signed payment. Without the header, Selo
        deduplicates on the signed payment itself.
      operationId: runReleaseTest
      parameters:
        - name: Idempotency-Key
          in: header
          required: false
          description: >-
            8 to 128 characters of `A-Z`, `a-z`, `0-9`, `_` or `-`. Scoped to
            the `preflightId`.
          schema:
            type: string
            pattern: ^[A-Za-z0-9_-]{8,128}$
            minLength: 8
            maxLength: 128
          example: release-2026-10-26-a
        - name: PAYMENT-SIGNATURE
          in: header
          required: false
          description: >-
            Base64-encoded JSON x402 v2 payment payload that pays the
            requirement from the 402. Omit it on the first request to receive
            the challenge.
          schema:
            type: string
          example: <base64 JSON payment payload>
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ReleaseTestRequest'
            examples:
              contract:
                summary: With a release contract and release ID
                value:
                  preflightId: pfl_…
                  profile: quick
                  expected:
                    status: 200
                    contentType: application/json
                    jsonSchema:
                      type: object
                      required:
                        - orderId
                        - receiptId
                        - fulfilled
                      properties:
                        orderId:
                          type: string
                          pattern: ^ord_[0-9a-f]{24}$
                        receiptId:
                          type: string
                          pattern: ^rcpt_[0-9a-f]{24}$
                        fulfilled:
                          const: true
                  deploymentId: fixed-v2
              minimal:
                summary: Minimal
                value:
                  preflightId: pfl_…
                  profile: quick
      responses:
        '200':
          description: >-
            The release test finished, or this request matched an existing
            finished job (no new payment settled). `verdict` is the gate
            decision.
          headers:
            PAYMENT-RESPONSE:
              description: >-
                Base64-encoded JSON settlement result. Present when this request
                settled the payment; absent when the reply is the stored result
                of an earlier request.
              schema:
                type: string
                contentEncoding: base64
                contentMediaType: application/json
                contentSchema:
                  $ref: '#/components/schemas/X402SettleResponse'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ReleaseTestResponse'
              examples:
                pass:
                  summary: 'PASS: demo merchant fixed-v2 on Mainnet'
                  value:
                    jobId: job_51AZ2WJZ78RF1WZC316Q1K38GG
                    verdict: PASS
                    target: https://demo.useselo.xyz/v1/orders
                    checks:
                      - id: handshake
                        status: PASS
                        code: HANDSHAKE_VALID
                        blocking: true
                        summary: >-
                          Target issued a valid x402 v2 challenge with a
                          supported requirement for network
                          algorand:wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8=
                          and asset 31566704.
                        evidence:
                          - evd_B23GQN052SFAET2QME89MVYX5G
                      - id: paid_delivery
                        status: PASS
                        code: PAID_AND_DELIVERED
                        blocking: true
                        summary: >-
                          Downstream payment settled and target returned 200
                          with transaction
                          QEUF5D33T4YHYP3KKIY2PGCYF55XXSUYKS5GXFBAVAZ4IEZOE3ZQ.
                        evidence:
                          - evd_6K2ASNC2G3JG43Q0XE0MSXJATM
                          - evd_PYNAQNMTD7KQJSJ0ZM6D618AK4
                      - id: response_contract
                        status: PASS
                        code: CONTRACT_MATCHED
                        blocking: true
                        summary: >-
                          Observed response matched the expected status 200 and
                          content type application/json.
                        evidence:
                          - evd_6K2ASNC2G3JG43Q0XE0MSXJATM
                          - evd_EKR9NXGB5HRGPQ4ZBN3WM914FW
                      - id: discovery_contract
                        status: PASS
                        code: DISCOVERY_CONSISTENT
                        blocking: true
                        summary: >-
                          Live challenge and catalog record agree on method,
                          URL, payment terms and carry usable metadata.
                        evidence:
                          - evd_TE2XZJB33D4CXHV2EMW84SS6BG
                      - id: retry_safety
                        status: PASS
                        code: PASS_REPLAY_REJECTED
                        blocking: true
                        summary: >-
                          Replaying the identical signed payment header was
                          rejected with status 402.
                        evidence:
                          - evd_7ZVACZFH5PNMZAQ5T82YZN7Z9R
                    warnings: []
                    inconclusiveReason: null
                    money:
                      seloInboundTxId: EPBEZR22SZ5LYKFM7UJYCSFG5IWNA6QYZ6XHUA6KGTTDDQQ5UIJA
                      downstreamSpendUsdc: '0.01'
                      downstreamTxIds:
                        - QEUF5D33T4YHYP3KKIY2PGCYF55XXSUYKS5GXFBAVAZ4IEZOE3ZQ
                    reportUrl: https://api.useselo.xyz/v1/reports/<report token>
                    attestation:
                      payload:
                        schemaVersion: selo.release.v1
                        issuer: https://api.useselo.xyz
                        jobId: job_51AZ2WJZ78RF1WZC316Q1K38GG
                        testedTarget:
                          url: https://demo.useselo.xyz/v1/orders
                          method: POST
                        deployment:
                          id: fixed-v2
                          verification: asserted_by_client
                        testedAt: '2026-10-26T10:24:02.883Z'
                        completedAt: '2026-10-26T10:24:12.218Z'
                        seloVersion: 71080ad
                        verdict: PASS
                        inconclusiveReason: null
                        checks:
                          - id: handshake
                            status: PASS
                            code: HANDSHAKE_VALID
                            blocking: true
                            evidence:
                              - evd_B23GQN052SFAET2QME89MVYX5G
                          - id: paid_delivery
                            status: PASS
                            code: PAID_AND_DELIVERED
                            blocking: true
                            evidence:
                              - evd_6K2ASNC2G3JG43Q0XE0MSXJATM
                              - evd_PYNAQNMTD7KQJSJ0ZM6D618AK4
                          - id: response_contract
                            status: PASS
                            code: CONTRACT_MATCHED
                            blocking: true
                            evidence:
                              - evd_6K2ASNC2G3JG43Q0XE0MSXJATM
                              - evd_EKR9NXGB5HRGPQ4ZBN3WM914FW
                          - id: discovery_contract
                            status: PASS
                            code: DISCOVERY_CONSISTENT
                            blocking: true
                            evidence:
                              - evd_TE2XZJB33D4CXHV2EMW84SS6BG
                          - id: retry_safety
                            status: PASS
                            code: PASS_REPLAY_REJECTED
                            blocking: true
                            evidence:
                              - evd_7ZVACZFH5PNMZAQ5T82YZN7Z9R
                        expectedContractSha256: >-
                          6e5b149b1088d214be6a1c097e2e06bf1127fe529c544731023abea7b03e7170
                        targetChallengeSha256: >-
                          39793a689d67cb18a7a59fca4a2d6211575e577bc2496accee3c93bd0ca2e7cc
                        payments:
                          network: >-
                            algorand:wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8=
                          asset: '31566704'
                          inboundTxId: EPBEZR22SZ5LYKFM7UJYCSFG5IWNA6QYZ6XHUA6KGTTDDQQ5UIJA
                          inboundAmountUsdc: '1.00'
                          downstream:
                            - status: SETTLED
                              amountUsdc: '0.01'
                              txId: >-
                                QEUF5D33T4YHYP3KKIY2PGCYF55XXSUYKS5GXFBAVAZ4IEZOE3ZQ
                        evidenceSha256: >-
                          49d787f15e7001a408f495aa59f4b16214dca58838e9e99ddd4790f6d6565a96
                        evidence:
                          - id: evd_6K2ASNC2G3JG43Q0XE0MSXJATM
                            kind: paid_response
                            sha256: >-
                              7815cd18d10df182be8f325d6d7a3b67800060da3efeff5c9c01fd409794398b
                          - id: evd_7ZVACZFH5PNMZAQ5T82YZN7Z9R
                            kind: replay_response
                            sha256: >-
                              a72223caf72ed2bd2d24114062b7cb15655f0d0e9c8cd4442a3bac9e791cae85
                          - id: evd_B23GQN052SFAET2QME89MVYX5G
                            kind: target_challenge
                            sha256: >-
                              39793a689d67cb18a7a59fca4a2d6211575e577bc2496accee3c93bd0ca2e7cc
                          - id: evd_EKR9NXGB5HRGPQ4ZBN3WM914FW
                            kind: response_contract
                            sha256: >-
                              f3f8cef64a663df7d7c192ff542878b6653df048b227305e50457bab0fab4bd0
                          - id: evd_MVWEZKMTNT45MWX6RDA8G6HRQ0
                            kind: authorization_snapshot
                            sha256: >-
                              207c2d476ea56e68e5bb88fa34ac5a943912be83251088a30f93f99c515626da
                          - id: evd_PYNAQNMTD7KQJSJ0ZM6D618AK4
                            kind: downstream_payment
                            sha256: >-
                              cca974ffefb3a23f9528745f1c205e58fc5687e8ef18a0bab527f110fedf609e
                          - id: evd_SXXV98XNSNVMPPPF7R7YR2RSC8
                            kind: authorization_snapshot
                            sha256: >-
                              208afc651b36a13f9b93c2decb89cc70558559c709d67ca83606ae65bb2f54e1
                          - id: evd_TE2XZJB33D4CXHV2EMW84SS6BG
                            kind: catalog_record
                            sha256: >-
                              4503636c21768df0807c0dfd88ba1813a9f2fdf9629bdf59fce723ed21e25712
                      signature:
                        algorithm: Ed25519
                        keyId: selo-ed25519-728fcf85650b128f
                        value: >-
                          E3lC29kTmj4Ts8SVoEDFabdAGGAO7ZZs4n9p2P7HUWGePMfi4aO75xQ-v7cynT1T4nT6mB2iQZYAzXe2YIbeBg
                fail:
                  summary: 'FAIL: demo merchant broken-v1 on Mainnet'
                  value:
                    jobId: job_189NZ481HFH77CRGF60AEF22RG
                    verdict: FAIL
                    target: https://demo.useselo.xyz/v1/orders
                    checks:
                      - id: handshake
                        status: PASS
                        code: HANDSHAKE_VALID
                        blocking: true
                        summary: >-
                          Target issued a valid x402 v2 challenge with a
                          supported requirement for network
                          algorand:wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8=
                          and asset 31566704.
                        evidence:
                          - evd_Z9C3SD9CX9WNS38JFYYGT9K60R
                      - id: paid_delivery
                        status: PASS
                        code: PAID_AND_DELIVERED
                        blocking: true
                        summary: >-
                          Downstream payment settled and target returned 200
                          with transaction
                          RZR6XUIA3GTHW6ABAHUIXRDCNYYXIHQVQYGN2T5PAMDO37DDDOLQ.
                        evidence:
                          - evd_C3YHMHPAX7S53BNXWHAG5H2MA4
                          - evd_4J9CPWEE2MZGT9CFWNYY6WR4VR
                      - id: response_contract
                        status: FAIL
                        code: SCHEMA_MISMATCH
                        blocking: true
                        summary: >-
                          Response body did not match the expected schema: must
                          have required property 'receiptId'
                        evidence:
                          - evd_C3YHMHPAX7S53BNXWHAG5H2MA4
                          - evd_SM1RPAAJH2ZZNMTA11SJK431ZW
                      - id: discovery_contract
                        status: INCONCLUSIVE
                        code: CATALOG_UNAVAILABLE
                        blocking: true
                        summary: >-
                          The Bazaar catalog could not be reached to cross-check
                          this resource.
                        evidence:
                          - evd_EJ2B9QAS9RHK6T58EW5ZZKNDMM
                      - id: retry_safety
                        status: PASS
                        code: PASS_REPLAY_REJECTED
                        blocking: true
                        summary: >-
                          Replaying the identical signed payment header was
                          rejected with status 402.
                        evidence:
                          - evd_WVP86FB9SGXNYYK5FGVMD6V1RG
                    warnings: []
                    inconclusiveReason: null
                    money:
                      seloInboundTxId: SVGTX3U6XQLSGJP7L7X7KNPSDLWFBSA3JK7AFNHPWENIWP4YVIEQ
                      downstreamSpendUsdc: '0.01'
                      downstreamTxIds:
                        - RZR6XUIA3GTHW6ABAHUIXRDCNYYXIHQVQYGN2T5PAMDO37DDDOLQ
                    reportUrl: https://api.useselo.xyz/v1/reports/<report token>
                    attestation:
                      payload:
                        schemaVersion: selo.release.v1
                        issuer: https://api.useselo.xyz
                        jobId: job_189NZ481HFH77CRGF60AEF22RG
                        testedTarget:
                          url: https://demo.useselo.xyz/v1/orders
                          method: POST
                        deployment:
                          id: broken-v1
                          verification: asserted_by_client
                        testedAt: '2026-10-26T09:43:01.922Z'
                        completedAt: '2026-10-26T09:43:09.258Z'
                        seloVersion: b769634
                        verdict: FAIL
                        inconclusiveReason: null
                        checks:
                          - id: handshake
                            status: PASS
                            code: HANDSHAKE_VALID
                            blocking: true
                            evidence:
                              - evd_Z9C3SD9CX9WNS38JFYYGT9K60R
                          - id: paid_delivery
                            status: PASS
                            code: PAID_AND_DELIVERED
                            blocking: true
                            evidence:
                              - evd_C3YHMHPAX7S53BNXWHAG5H2MA4
                              - evd_4J9CPWEE2MZGT9CFWNYY6WR4VR
                          - id: response_contract
                            status: FAIL
                            code: SCHEMA_MISMATCH
                            blocking: true
                            evidence:
                              - evd_C3YHMHPAX7S53BNXWHAG5H2MA4
                              - evd_SM1RPAAJH2ZZNMTA11SJK431ZW
                          - id: discovery_contract
                            status: INCONCLUSIVE
                            code: CATALOG_UNAVAILABLE
                            blocking: true
                            evidence:
                              - evd_EJ2B9QAS9RHK6T58EW5ZZKNDMM
                          - id: retry_safety
                            status: PASS
                            code: PASS_REPLAY_REJECTED
                            blocking: true
                            evidence:
                              - evd_WVP86FB9SGXNYYK5FGVMD6V1RG
                        expectedContractSha256: >-
                          6e5b149b1088d214be6a1c097e2e06bf1127fe529c544731023abea7b03e7170
                        targetChallengeSha256: >-
                          5efad9f413c7fd81a8b533887b9f28788322628ea7876796a6bd2b6a16c7fdcc
                        payments:
                          network: >-
                            algorand:wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8=
                          asset: '31566704'
                          inboundTxId: SVGTX3U6XQLSGJP7L7X7KNPSDLWFBSA3JK7AFNHPWENIWP4YVIEQ
                          inboundAmountUsdc: '1.00'
                          downstream:
                            - status: SETTLED
                              amountUsdc: '0.01'
                              txId: >-
                                RZR6XUIA3GTHW6ABAHUIXRDCNYYXIHQVQYGN2T5PAMDO37DDDOLQ
                        evidenceSha256: >-
                          c2a9b8746f89dd557865ad4bd6bc79e9fafdf891c3763eef246aa51fb8a52c83
                        evidence:
                          - id: evd_4J9CPWEE2MZGT9CFWNYY6WR4VR
                            kind: downstream_payment
                            sha256: >-
                              cc6e48537139f26d0e3d2d4cadf666ce89ffc07a0c4830c116e38afb2945f458
                          - id: evd_8HCAE38TGE111B2BXFEY0QMKRW
                            kind: authorization_snapshot
                            sha256: >-
                              02baba31af9c657ac17906f6c6aee56f2a7c25905b42a7cf34f5463d0b08c95d
                          - id: evd_C3YHMHPAX7S53BNXWHAG5H2MA4
                            kind: paid_response
                            sha256: >-
                              e67b250cbb92954c1e098307b6709dbe474e25d9743344233414950f6187a6f7
                          - id: evd_EJ2B9QAS9RHK6T58EW5ZZKNDMM
                            kind: catalog_record
                            sha256: >-
                              77ae5d181bdd2002b067f10f2aae0fc5b0562b39ed1036770fc9c982b37d4b2f
                          - id: evd_MC9R0VM5PTZDZJ62NQNFDBE8CW
                            kind: authorization_snapshot
                            sha256: >-
                              f277c070af27ed5c8642b95d9c354e70613ad12fbb9f4426656c66f4fb36bcb7
                          - id: evd_SM1RPAAJH2ZZNMTA11SJK431ZW
                            kind: response_contract
                            sha256: >-
                              5df97c8b2371600b267d2cc3600ea49964a64e0145f9a3bda4ffb36b5750b89c
                          - id: evd_WVP86FB9SGXNYYK5FGVMD6V1RG
                            kind: replay_response
                            sha256: >-
                              f1ff2bceb92be880f056ca6225fecd177ceee10560d8a74762615defeb00ff22
                          - id: evd_Z9C3SD9CX9WNS38JFYYGT9K60R
                            kind: target_challenge
                            sha256: >-
                              5efad9f413c7fd81a8b533887b9f28788322628ea7876796a6bd2b6a16c7fdcc
                      signature:
                        algorithm: Ed25519
                        keyId: selo-ed25519-728fcf85650b128f
                        value: >-
                          R78F8R3mz7TntBJwiKb7M6Lq4BmBmOYJEWKFRrOqjDqwFCOTrr-11n2d4VHrGqTYMq-HcXd2X0skuR4uyaDpBg
        '400':
          description: >-
            The body, the `Idempotency-Key` header or the `expected` contract is
            invalid.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'
              examples:
                VALIDATION_FAILED:
                  summary: Unknown profile
                  value:
                    error: VALIDATION_FAILED
                    message: |-
                      ✖ Invalid input: expected "quick"
                        → at profile
                IDEMPOTENCY_KEY:
                  summary: Malformed Idempotency-Key
                  value:
                    error: VALIDATION_FAILED
                    message: >-
                      Idempotency-Key must be 8-128 characters of A-Z, a-z, 0-9,
                      _ or -
                CONTRACT:
                  summary: Contract does not compile
                  value:
                    error: VALIDATION_FAILED
                    message: >-
                      expected: status must be an integer between 100 and 599,
                      received 700
        '402':
          description: >-
            Payment required. Returned when no `PAYMENT-SIGNATURE` was sent,
            when the payment was refused at verification, or when settlement
            definitely failed. The body is an empty JSON object.
          headers:
            PAYMENT-REQUIRED:
              description: >-
                Base64-encoded JSON x402 v2 challenge. Present when Selo asks
                for payment: no payment was sent, or the payment was refused at
                verification, in which case `error` says why.
              schema:
                type: string
                contentEncoding: base64
                contentMediaType: application/json
                contentSchema:
                  $ref: '#/components/schemas/X402PaymentRequired'
              example: <base64 JSON challenge>
            PAYMENT-RESPONSE:
              description: >-
                Base64-encoded JSON settlement result with `success: false`.
                Present instead of `PAYMENT-REQUIRED` when settlement definitely
                failed; nothing was charged and no job remains.
              schema:
                type: string
                contentEncoding: base64
                contentMediaType: application/json
                contentSchema:
                  $ref: '#/components/schemas/X402SettleResponse'
            Cache-Control:
              description: '`no-store`.'
              schema:
                type: string
              example: no-store
          content:
            application/json:
              schema:
                type: object
                maxProperties: 0
              example: {}
        '403':
          description: The preflight's authorization is no longer verified and unexpired.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'
              examples:
                AUTHORIZATION_INVALID:
                  summary: Authorization lapsed
                  value:
                    error: AUTHORIZATION_INVALID
                    message: >-
                      Authorization auth_… is not a verified, unexpired
                      authorization for this target
        '404':
          description: No preflight with this ID.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'
              examples:
                PREFLIGHT_NOT_FOUND:
                  summary: Unknown preflight
                  value:
                    error: PREFLIGHT_NOT_FOUND
                    message: Preflight pfl_… does not exist
        '409':
          description: >-
            The preflight expired, or the request matched a job that is still
            running or finished without a report.
          content:
            application/json:
              schema:
                anyOf:
                  - $ref: '#/components/schemas/ApiError'
                  - $ref: '#/components/schemas/JobError'
                  - $ref: '#/components/schemas/JobTerminalError'
              examples:
                PREFLIGHT_EXPIRED:
                  summary: Preflight older than 15 minutes
                  value:
                    error: PREFLIGHT_EXPIRED
                    message: Preflight pfl_… has expired; run a new preflight
                JOB_IN_PROGRESS:
                  summary: Same request still running
                  value:
                    error: JOB_IN_PROGRESS
                    message: >-
                      A release test for this request is already running; retry
                      to fetch its result
                    jobId: job_…
                JOB_IN_PROGRESS_CLAIM:
                  summary: Same key being claimed
                  value:
                    error: JOB_IN_PROGRESS
                    message: >-
                      A request with the same idempotency key is being resolved;
                      retry shortly
                JOB_TERMINAL:
                  summary: Matched job ended without a report
                  value:
                    error: JOB_TERMINAL
                    message: >-
                      This release test already finished without a report; start
                      a new preflight to retest
                    jobId: job_…
                    status: INCONCLUSIVE
                    inconclusiveReason: INTERNAL_ERROR
        '413':
          $ref: '#/components/responses/PayloadTooLarge'
        '422':
          description: >-
            The preflight is not eligible, or the target's price is above the
            per-job spend limit.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'
              examples:
                PREFLIGHT_INELIGIBLE:
                  summary: Ineligible preflight
                  value:
                    error: PREFLIGHT_INELIGIBLE
                    message: Preflight pfl_… is not eligible (NO_PAYMENT_CHALLENGE)
                PRICE_OVER_BUDGET:
                  summary: Price above 0.50 USDC
                  value:
                    error: PRICE_OVER_BUDGET
                    message: >-
                      The target price exceeds the per-job downstream spend
                      limit
        '500':
          description: >-
            Selo failed after your payment settled (`jobId` present, job flagged
            for reconciliation or recorded as INCONCLUSIVE), or failed before
            charging (no `jobId`).
          content:
            application/json:
              schema:
                anyOf:
                  - $ref: '#/components/schemas/JobError'
                  - $ref: '#/components/schemas/ApiError'
              examples:
                INBOUND_RECORD_FAILED:
                  summary: Settled but not recorded
                  value:
                    error: INBOUND_RECORD_FAILED
                    message: >-
                      Your payment settled but Selo could not record it; the job
                      will not run and is flagged for reconciliation
                    jobId: job_…
                INTERNAL_AFTER_SETTLEMENT:
                  summary: Failed after settlement
                  value:
                    error: INTERNAL
                    message: >-
                      Selo failed after settling your payment; the job is
                      recorded as INCONCLUSIVE
                    jobId: job_…
                INTERNAL:
                  summary: Unexpected server error
                  value:
                    error: INTERNAL
                    message: Internal server error
        '502':
          description: >-
            The facilitator could not verify the payment (nothing charged), or
            did not confirm the settlement outcome (job held; do not pay again).
          content:
            application/json:
              schema:
                anyOf:
                  - $ref: '#/components/schemas/ApiError'
                  - $ref: '#/components/schemas/InboundUnresolvedError'
              examples:
                FACILITATOR_UNAVAILABLE:
                  summary: Verification failed
                  value:
                    error: FACILITATOR_UNAVAILABLE
                    message: >-
                      The payment facilitator could not verify the payment;
                      nothing was charged
                INBOUND_SETTLEMENT_UNRESOLVED:
                  summary: Settlement outcome unknown
                  value:
                    error: INBOUND_SETTLEMENT_UNRESOLVED
                    message: >-
                      The facilitator did not confirm the outcome of your
                      payment; the job is held for reconciliation and will not
                      run. Do not pay again for this request.
                    jobId: job_…
                    txId: null
components:
  schemas:
    ReleaseTestRequest:
      type: object
      additionalProperties: false
      required:
        - preflightId
        - profile
      properties:
        preflightId:
          type: string
          minLength: 1
          maxLength: 64
          description: ID of an eligible, unexpired preflight.
        profile:
          $ref: '#/components/schemas/Profile'
        expected:
          $ref: '#/components/schemas/ExpectedContract'
        deploymentId:
          type: string
          pattern: ^[A-Za-z0-9._-]{1,128}$
          description: >-
            Your release identifier, for example a commit SHA. Stored on the job
            and attested as `asserted_by_client`: Selo records it, it does not
            verify it.
    ReleaseTestResponse:
      type: object
      required:
        - jobId
        - verdict
        - target
        - checks
        - warnings
        - inconclusiveReason
        - money
        - reportUrl
      properties:
        jobId:
          type: string
          pattern: ^job_[0-9A-HJKMNP-TV-Z]{26}$
        verdict:
          $ref: '#/components/schemas/Verdict'
        target:
          type: string
          description: The tested URL.
        checks:
          type: array
          items:
            $ref: '#/components/schemas/CheckResult'
          minItems: 5
          maxItems: 5
          description: All five checks, in fixed order.
        warnings:
          type: array
          items:
            type: string
          description: >-
            One line per WARN check (and per non-blocking FAIL or INCONCLUSIVE),
            formatted `<check id>: <summary>`.
        inconclusiveReason:
          oneOf:
            - $ref: '#/components/schemas/InconclusiveReason'
            - type: 'null'
        money:
          type: object
          required:
            - seloInboundTxId
            - downstreamSpendUsdc
            - downstreamTxIds
          properties:
            seloInboundTxId:
              type: string
              description: Algorand transaction ID of your payment to Selo.
            downstreamSpendUsdc:
              type: string
              pattern: ^[0-9]+\.[0-9]{2,6}$
              description: USDC Selo paid the target and saw settle.
            downstreamTxIds:
              type: array
              items:
                type: string
              description: >-
                Algorand transaction IDs of Selo's settled payments to the
                target.
        reportUrl:
          type: string
          description: >-
            Private report link, `https://api.useselo.xyz/v1/reports/<token>`.
            Anyone with it can read the report.
        attestation:
          $ref: '#/components/schemas/ReleaseAttestation'
    ApiError:
      type: object
      description: Error body returned by every failing request.
      required:
        - error
        - message
      properties:
        error:
          type: string
          description: Stable machine-readable error code, for example `VALIDATION_FAILED`.
        message:
          type: string
          description: Human-readable explanation. Do not parse it.
    JobError:
      type: object
      description: Error body for a paid request that already created a job.
      required:
        - error
        - message
        - jobId
      properties:
        error:
          type: string
          description: Error code.
        message:
          type: string
          description: Human-readable explanation.
        jobId:
          type: string
          description: The job this request created or matched, `job_` plus 26 characters.
    JobTerminalError:
      type: object
      description: >-
        The matched job finished without a report. Start a new preflight to
        retest.
      required:
        - error
        - message
        - jobId
        - status
        - inconclusiveReason
      properties:
        error:
          type: string
          const: JOB_TERMINAL
        message:
          type: string
        jobId:
          type: string
        status:
          $ref: '#/components/schemas/JobStatus'
        inconclusiveReason:
          oneOf:
            - $ref: '#/components/schemas/InconclusiveReason'
            - type: 'null'
    InboundUnresolvedError:
      type: object
      description: >-
        The facilitator did not confirm the outcome of your payment. The job is
        held for reconciliation and does not run. Do not pay again for this
        request.
      required:
        - error
        - message
        - jobId
        - txId
      properties:
        error:
          type: string
          const: INBOUND_SETTLEMENT_UNRESOLVED
        message:
          type: string
        jobId:
          type: string
        txId:
          type:
            - string
            - 'null'
          description: >-
            Settlement transaction ID if the facilitator reported one, otherwise
            `null`.
    Profile:
      type: string
      enum:
        - quick
      description: Release test profile. `quick` is the only profile.
    ExpectedContract:
      type: object
      additionalProperties: false
      description: >-
        What the paid response must look like. Omitted fields are not checked,
        except `status`, which defaults to 200.
      properties:
        status:
          type: integer
          minimum: 100
          maximum: 599
          default: 200
          description: Expected HTTP status of the paid response.
        contentType:
          type: string
          maxLength: 200
          pattern: ^[A-Za-z0-9][A-Za-z0-9!#$&^_.+-]*/[A-Za-z0-9][A-Za-z0-9!#$&^_.+-]*$
          description: Expected media type, `type/subtype`, compared case-insensitively.
        jsonSchema:
          type: object
          additionalProperties: true
          description: >-
            JSON Schema the paid response body must satisfy. Compiled with Ajv 8
            in strict mode; at most 16 KiB (16384 bytes) serialized. A schema
            that does not compile is rejected with 400.
    Verdict:
      type: string
      enum:
        - PASS
        - FAIL
        - INCONCLUSIVE
      description: Release verdict computed by the deterministic verdict reducer.
    CheckResult:
      type: object
      required:
        - id
        - status
        - code
        - blocking
        - summary
        - evidence
      properties:
        id:
          $ref: '#/components/schemas/CheckId'
        status:
          $ref: '#/components/schemas/CheckStatus'
        code:
          type: string
          description: >-
            Result code, for example `HANDSHAKE_VALID`, `SCHEMA_MISMATCH` or
            `NOT_RUN`. For `retry_safety` it is a `RetryOutcome`.
        blocking:
          type: boolean
          description: >-
            Whether the check can decide the verdict. Every check in the `quick`
            profile is blocking.
        summary:
          type: string
          description: One sentence describing what Selo observed.
        evidence:
          type: array
          items:
            type: string
            pattern: ^evd_[0-9A-HJKMNP-TV-Z]{26}$
          description: >-
            IDs of the evidence records behind this result. A PASS without
            evidence counts as INCONCLUSIVE.
    InconclusiveReason:
      type: string
      enum:
        - NETWORK_UNAVAILABLE
        - FACILITATOR_UNAVAILABLE
        - TARGET_TIMEOUT
        - BUDGET_EXHAUSTED
        - PAYMENT_UNRESOLVED
        - EVIDENCE_INCOMPLETE
        - AUTHORIZATION_LAPSED
        - INTERNAL_ERROR
      description: Why a job ended INCONCLUSIVE. `null` on PASS and FAIL.
    ReleaseAttestation:
      type: object
      description: >-
        Ed25519-signed statement of the verdict. Present when the Selo
        deployment has a signing key.
      required:
        - payload
        - signature
      properties:
        payload:
          $ref: '#/components/schemas/AttestationPayload'
        signature:
          $ref: '#/components/schemas/AttestationSignature'
    JobStatus:
      type: string
      enum:
        - READY
        - INBOUND_SETTLED
        - QUEUED
        - RUNNING_PREFLIGHT_RECHECK
        - RUNNING
        - ANALYZING
        - PASS
        - FAIL
        - INCONCLUSIVE
        - REPORT_WRITTEN
      description: >-
        Internal state of a release job. A job whose report is available is
        `REPORT_WRITTEN`.
    CheckId:
      type: string
      enum:
        - handshake
        - paid_delivery
        - response_contract
        - discovery_contract
        - retry_safety
      description: The five release checks, always reported in this order.
    CheckStatus:
      type: string
      enum:
        - PASS
        - WARN
        - FAIL
        - INCONCLUSIVE
      description: Outcome of one check.
    AttestationPayload:
      type: object
      description: >-
        The signed statement. Selo signs the canonical JSON of this object (keys
        sorted at every level, no whitespace).
      required:
        - schemaVersion
        - issuer
        - jobId
        - testedTarget
        - deployment
        - testedAt
        - completedAt
        - seloVersion
        - verdict
        - inconclusiveReason
        - checks
        - expectedContractSha256
        - targetChallengeSha256
        - payments
        - evidenceSha256
        - evidence
      properties:
        schemaVersion:
          type: string
          const: selo.release.v1
        issuer:
          type: string
          description: >-
            Origin of the Selo deployment that signed,
            `https://api.useselo.xyz`.
        jobId:
          type: string
          pattern: ^job_[0-9A-HJKMNP-TV-Z]{26}$
        testedTarget:
          type: object
          required:
            - url
            - method
          properties:
            url:
              type: string
            method:
              $ref: '#/components/schemas/HttpMethod'
        deployment:
          type:
            - object
            - 'null'
          required:
            - id
            - verification
          properties:
            id:
              type: string
              pattern: ^[A-Za-z0-9._-]{1,128}$
            verification:
              type: string
              const: asserted_by_client
          description: The `deploymentId` you sent, or `null`.
        testedAt:
          type:
            - string
            - 'null'
          format: date-time
        completedAt:
          type: string
          format: date-time
        seloVersion:
          type: string
          description: Git commit of the Selo build, or `unknown`.
        verdict:
          $ref: '#/components/schemas/Verdict'
        inconclusiveReason:
          oneOf:
            - $ref: '#/components/schemas/InconclusiveReason'
            - type: 'null'
        checks:
          type: array
          items:
            $ref: '#/components/schemas/AttestedCheck'
        expectedContractSha256:
          type:
            - string
            - 'null'
          pattern: ^[0-9a-f]{64}$
          description: >-
            SHA-256 of the canonical JSON of the `expected` contract, or `null`
            when none was sent.
        targetChallengeSha256:
          type:
            - string
            - 'null'
          pattern: ^[0-9a-f]{64}$
          description: SHA-256 of the target's 402 challenge Selo recorded.
        payments:
          type: object
          required:
            - network
            - asset
            - inboundTxId
            - inboundAmountUsdc
            - downstream
          properties:
            network:
              type: string
              description: CAIP-2 network ID.
            asset:
              type: string
              description: USDC asset ID.
            inboundTxId:
              type:
                - string
                - 'null'
            inboundAmountUsdc:
              oneOf:
                - $ref: '#/components/schemas/DecimalAmount'
                - type: 'null'
            downstream:
              type: array
              items:
                $ref: '#/components/schemas/AttestedPayment'
        evidenceSha256:
          type: string
          pattern: ^[0-9a-f]{64}$
          description: SHA-256 of the canonical JSON of `evidence`.
        evidence:
          type: array
          items:
            $ref: '#/components/schemas/AttestedEvidence'
          description: Every evidence record of the job, sorted by ID.
    AttestationSignature:
      type: object
      required:
        - algorithm
        - keyId
        - value
      properties:
        algorithm:
          type: string
          const: Ed25519
        keyId:
          type: string
          pattern: ^selo-ed25519-[0-9a-f]{16}$
          description: Matches a `keyId` in the attestation key set.
        value:
          type: string
          pattern: ^[A-Za-z0-9_-]+$
          description: >-
            Ed25519 signature over the UTF-8 bytes of the payload's canonical
            JSON, base64url without padding.
    HttpMethod:
      type: string
      enum:
        - GET
        - POST
      description: HTTP method Selo uses to call the target.
    AttestedCheck:
      type: object
      required:
        - id
        - status
        - code
        - blocking
        - evidence
      properties:
        id:
          $ref: '#/components/schemas/CheckId'
        status:
          $ref: '#/components/schemas/CheckStatus'
        code:
          type: string
        blocking:
          type: boolean
        evidence:
          type: array
          items:
            type: string
            pattern: ^evd_[0-9A-HJKMNP-TV-Z]{26}$
    DecimalAmount:
      type: string
      pattern: ^[0-9]+\.[0-9]{2,6}$
      description: >-
        USDC amount as a decimal string with 2 to 6 decimals, for example `1.00`
        or `0.01`.
    AttestedPayment:
      type: object
      required:
        - status
        - amountUsdc
        - txId
      properties:
        status:
          $ref: '#/components/schemas/PaymentStatus'
        amountUsdc:
          $ref: '#/components/schemas/DecimalAmount'
        txId:
          type:
            - string
            - 'null'
    AttestedEvidence:
      type: object
      required:
        - id
        - kind
        - sha256
      properties:
        id:
          type: string
          pattern: ^evd_[0-9A-HJKMNP-TV-Z]{26}$
        kind:
          type: string
          description: >-
            Evidence kind: `authorization_snapshot`, `target_challenge`,
            `paid_response`, `downstream_payment`, `response_contract`,
            `catalog_record` or `replay_response`.
        sha256:
          type: string
          pattern: ^[0-9a-f]{64}$
          description: SHA-256 of the sanitized evidence record.
    PaymentStatus:
      type: string
      enum:
        - RESERVED
        - SETTLED
        - RELEASED
        - UNRESOLVED
      description: >-
        State of one downstream payment. `RESERVED`: budget held before signing.
        `SETTLED`: confirmed on chain. `RELEASED`: definitely not paid, budget
        returned. `UNRESOLVED`: outcome unknown, amount held and never released
        by a timeout.
  responses:
    PayloadTooLarge:
      description: The request body is larger than 64 KiB (65536 bytes).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ApiError'
          examples:
            PAYLOAD_TOO_LARGE:
              summary: Body over 64 KiB
              value:
                error: PAYLOAD_TOO_LARGE
                message: Request body exceeds 65536 bytes

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.