pfl_… id that stays valid for 15 minutes; the paid release test must name it.
What preflight checks
Selo runs these checks in order and stops at the first one that fails.1
The target URL
An absolute
https URL on a public hostname, on the default port, with no username or password.2
The authorization
The authorization exists, is
VERIFIED, has not expired, and covers the exact origin, path and method. Nothing is sent to your endpoint if this fails. See Authorize your endpoint.3
One unpaid request
Selo sends your method and, for
POST, your JSON body with content-type: application/json and an Idempotency-Key: preflight-<preflight id> header. It resolves DNS, refuses the request if any answer is not a public address, pins the connection to the vetted address, waits at most 10 seconds and does not follow redirects.4
The 402 challenge
The answer must be a
402 whose PAYMENT-REQUIRED header decodes to an x402 version 2 challenge. It must offer an exact requirement on Algorand Mainnet in USDC (ASA 31566704) with a positive integer amount and a valid Algorand payTo address.5
The price
The price of that requirement must be at most 0.50 USDC, the most Selo spends on a target in one job. Exactly 0.50 is accepted.
6
A Bazaar snapshot
Selo looks your route up in the Bazaar catalog among the listings of the
payTo in your 402 and stores what it finds, for the discovery check later. A missing listing or an unreachable catalog never makes a preflight ineligible.Your endpoint receives exactly one unpaid request per preflight. Nothing is signed and nothing is paid.
Prerequisites
- A verified authorization for the exact URL and method. See Authorize your endpoint.
- For
POST, the JSON body Selo should send. The same body is used for the paid request and for the replay in the release test.
Run a preflight with the CLI
1
Write the request body (POST only)
body.json
null, at most 16 KiB once serialized. A GET takes no body.2
Run it
Terminal
3
Read the terms
Output
The command exits
0 when the target is eligible.The API call
Terminal
string
required
The verified
auth_… id, at most 64 characters.string
required
The
https URL of your paid route, at most 2048 characters.string
required
GET or POST.any JSON value
POST only. Any JSON value except a top-level null, at most 16 KiB once serialized. Omit it to send no body.400 VALIDATION_FAILED, and the whole request may be at most 64 KiB.
Eligible
An eligible preflight answers201 Created:
201 Created
boolean
true.string
The id the paid release test must name.
object
string
The most Selo will spend on your endpoint in this job. A
quick test pays once; the replay re-sends the same signed payment.string
What the release test costs you.
string
When the preflight stops being payable.
Ineligible
An ineligible preflight answers with the HTTP status of its reason and this body:422 Unprocessable Entity
preflightId is null when Selo refused the request before it recorded a preflight: for a URL that breaks the URL rules (invalid, not https, or a blocked hostname or port), or for an unknown authorization id. Every other rejection is recorded and carries its id.
GET /v1/preflight/<pfl id> returns the stored eligible or ineligible view of any preflight.
Rejection reasons
Other errors use the standard error body, with an
error code and a message:
Exit codes
Validity
A preflight is valid for 15 minutes. After that,POST /v1/release-test answers 409 PREFLIGHT_EXPIRED before any payment is taken, and you run a new preflight. The paid route also re-checks the preflight before it asks for payment: an unknown id is 404 PREFLIGHT_NOT_FOUND, an ineligible one is 422 PREFLIGHT_INELIGIBLE, an authorization that is no longer verified is 403 AUTHORIZATION_INVALID, and a price above the job budget is 422 PRICE_OVER_BUDGET. See Pay with any x402 client.
Troubleshooting
My endpoint answers 200 to the unpaid request
My endpoint answers 200 to the unpaid request
Selo expects a paid route to answer an unpaid request with
402. A 200 means the route is not protected, and the preflight is NO_PAYMENT_CHALLENGE. Check that your x402 middleware covers the exact path and method you are testing.My endpoint redirects, for example from http to https or to a trailing slash
My endpoint redirects, for example from http to https or to a trailing slash
Preflight does not follow redirects, so the redirect itself is the answer and the preflight is
NO_PAYMENT_CHALLENGE. Authorize and test the final URL.My 402 offers several networks
My 402 offers several networks
That is fine. Selo picks the
exact requirement on Algorand Mainnet in USDC and ignores the others.My POST route rejects the unpaid request because of the body
My POST route rejects the unpaid request because of the body
The probe carries the body you give with
--body or requestBody. Send the same body your clients send, so the route reaches its payment check.Next
Write a release contract
Declare what a paid response must look like.
Run a release test
Pay once and gate on the verdict.