selo CLI is a convenience. Selo’s paid route is an ordinary x402 v2 resource on Algorand, so any x402 client that can pay an exact USDC requirement can run a release test. This page walks through the raw HTTP flow and shows a client built on the same x402 packages Selo uses.
Prerequisites
- An x402 v2 client that can sign an
exactpayment on Algorand Mainnet in USDC (ASA31566704). - A payer wallet opted in to USDC that holds at least 1.00 USDC per test.
- Your endpoint’s URL and method, and a way to serve a file on its origin.
Steps
1
Authorize the target
Terminal
expectedContent line at the returned verificationUrl, then verify:Terminal
"status": "VERIFIED". See Authorize your endpoint for every field and error.2
Run a preflight
Terminal
preflightId from the 201 answer. You have 15 minutes to pay. See Run a preflight.3
Build the release test body
release-test.json
string
required
The eligible preflight, at most 64 characters.
string
required
quick, the only profile.object
The release contract: optional
status, contentType and jsonSchema. It is compiled before you are asked to pay. Without it, the paid response is only checked for status 200. See Write a release contract.string
The release you are testing, such as a Git SHA: 1 to 128 characters of
A-Z, a-z, 0-9, ., _ or -. It is signed into the attestation as asserted_by_client.4
Read Selo's 402
Send the body without a payment. Selo validates it, checks the preflight and its authorization, and only then answers This is Selo’s live challenge as captured from the public API, without its
402 with the challenge in the PAYMENT-REQUIRED header, base64-encoded JSON:Terminal
extensions (bazaar, with the input and output schema of the route, and x402-merchant, with Selo’s name and categories):PAYMENT-REQUIRED (decoded)
If the body, the preflight or the authorization is wrong, you get that error instead of a
402, and nothing is charged. See Errors.5
Pay and send the request again
Sign the requirement with your x402 client and send the same body again with two extra headers:
Selo verifies the payment, claims the job under your key, settles the payment and records it, and only then pays your endpoint. The answer is the report, with Selo’s settlement in the
PAYMENT-RESPONSE header.A client on Selo’s own x402 packages
Selo pays targets, and its CLI pays Selo, with the official x402 packages:@x402/core for the client and @x402/avm for the Algorand exact scheme, at version 2.27.0, with algosdk 3.8.0 to load the wallet. This script uses the same calls. It refuses to sign anything but the payment you approved.
Terminal
pay-selo.mts
Terminal
The response
A finished test answers200. This is the fixed-v2 result from the demo merchant’s Mainnet run on 26 October 2026, rebuilt from that run’s published summary and attestation, with the report token replaced:
200 OK
attestation, the signed envelope described in Verify a signed verdict.
string
The release job.
string
PASS, FAIL or INCONCLUSIVE.string
The tested URL.
object[]
One result per check:
id, status (PASS, WARN, FAIL or INCONCLUSIVE), code, blocking, summary and the evidence ids. See The five release checks.string[]
Concerns that do not block, such as a WARN.
string | null
Why the verdict is INCONCLUSIVE, otherwise
null.object
seloInboundTxId, the transaction in which you paid Selo; downstreamSpendUsdc, what Selo spent on the target; downstreamTxIds, the transactions in which Selo paid it.string
The private report link. Anyone who has it can read the report, so keep it secret.
object
The Ed25519-signed attestation, when Selo has a signing key.
checks before you trust it: any blocking FAIL with evidence is FAIL; otherwise any blocking INCONCLUSIVE, missing check or check without evidence is INCONCLUSIVE; otherwise PASS. That is what the CLI does. See Verdicts and the gate.
Retries
Send every retry of the same logical request with the sameIdempotency-Key and the same preflightId. Selo’s 402 still asks for a payment on a retry, so your client signs one, but Selo uses the key to find the existing job and never settles the new payment:
Retries work while the preflight is valid. After 15 minutes the route answers
409 PREFLIGHT_EXPIRED before it looks for the job. If you send no Idempotency-Key, Selo derives the key from the payment payload itself, so only the identical signed payment is recognized as the same request, and a newly signed retry would be a new paid test.
Errors
The route checks in this order, and everything before the settlement is free:
See Held payments and reconciliation for what Selo does with a held settlement, and Errors and reason codes for every code.
Next
API overview
Every route, request and response.
Run a release test
The CLI does all of this, plus recovery and exit codes.