The flow
Before any of this, the owner of the endpoint authorizes the exact origin, route and method by serving a one-time line at/.well-known/selo-verification.txt. Every request that can pay or cause a side effect checks that authorization again.
Two payment legs
Every release test has two payments, both in USDC (ASA31566704) on Algorand Mainnet. Both appear in the report and in the signed attestation.
You pay only the inbound leg. Selo pays the downstream leg from its own wallet. When Selo signs the downstream payment, the requirement must match what it reserved: network, asset, amount and payee. It signs exactly the requirement your
402 offered and nothing more. See Payments and spend limits.
Why Selo settles before it runs
Selo never spends before it has been paid. The paid route composes the steps explicitly, in this order: validate the request, answer402, verify the payment, claim the job by idempotency key, settle, record the inbound transaction on the job, then run.
This order matters for two reasons:
- Selo cannot be made to spend for free. If Selo paid your endpoint before its own payment settled, a payment that never lands would still cost Selo the downstream price. The runner refuses any job whose inbound settlement is not recorded, and a test (
A12) checks the database timestamps and the call order. - The stock middleware settles too late. The stock x402 middleware for Hono settles after the handler runs. That order cannot support a service that pays someone else inside the handler, so Selo does not use it on the paid route.
502 INBOUND_SETTLEMENT_UNRESOLVED, tells the client not to pay again, never runs the job, and keeps the transaction id for reconciliation. A duplicate request with the same idempotency key is answered from the existing job and never settles again. See Held payments and reconciliation.
What your endpoint sees
Selo calls only the exact HTTPS URL and method you authorized. Every preflight and test request to your route carries anIdempotency-Key header with a stable operation id.
selo test runs its own preflight on every run, so one selo test sends two unpaid requests, one paid request and one replay. Selo also reads your payee’s listing from the Bazaar catalog, which is a request to the facilitator, not to your endpoint.
The five checks
The checks run in this order. If the handshake does not pass, Selo stops there and pays nothing downstream.
Each check ends in
PASS, WARN, FAIL or INCONCLUSIVE with a code and the ids of the evidence that backs it. The response contract is evaluated from the paid response, so it sends no request of its own. Every code is listed in The five release checks.
The verdict
The verdict is a pure function of the check results, inpackages/core/src/verdict.ts. No model is involved.
WARN with evidence is listed under warnings and does not change the verdict. See Verdicts and the gate.
The signed attestation
When the run ends, Selo signs an attestation of the result with its Ed25519 key and returns it with the report. The payload (selo.release.v1) records the job, the tested URL and method, the release id you asserted, the verdict, every check with its evidence ids, both payment legs, a SHA-256 of your release contract and of your 402, and a manifest of the evidence with its hashes. It carries no secret, request body or report token.
Anyone can verify it offline against the keys at https://api.useselo.xyz/.well-known/selo-attestation-keys.json. See Signed attestations.
The report
The response toPOST /v1/release-test carries the job id, the verdict, every check, any warnings, the inconclusive reason, both transaction ids, the downstream spend and a private report link. The full report at that link adds the observed response (status, content type, body hash and size), every payment with its status, and each scenario with its operation id, attempts and duration. The link is an unguessable token, served with noindex, no-store and no-referrer.
Where the gate decision happens
Selo computes the verdict on the server. The decision to ship is made on your side:- The
seloCLI does not trust the verdict label. It recomputes the verdict from the report’s own checks with the same reducer Selo uses. It then requires a result for all five checks, an inbound transaction, a downstream transaction and a report link. Only then does it printgate ALLOWand exit0. A disagreement or a gap blocks with exit2. - In GitHub Actions, the paid job runs the CLI and the promote job runs only when the gate is
ALLOW. Before promoting, it verifies the signed attestation against a key you pinned, for the exact commit, URL and method. A PASS for one commit cannot unlock another.
Next steps
Run your first release test
Authorize, preflight and pay, step by step.
Security model
Authorization, SSRF rules, spend limits and secret handling.