Browse documentation
Developers
Choose the service. Protect the purchase.
Endpoint selection. Buyer protection. Verifiable records.
Start with the choice your app needs to make.
Find a service that fits your rules, or check an endpoint you already chose. Both paths lead to local verification before seller payment and evidence you can keep.
Need a service?
Describe the task, set your requirements, and choose how eligible offers are ranked.
Choose a service using my rulesAlready have one?
Add the check to your x402 client before it signs a payment to that endpoint.
Check a service I already useWhat you need
- Node.js 22 or newer and an existing x402 client with a configured wallet.
- Your spending limits and an independently pinned 402Signal log key.
- Private storage for the original check request and response.
A qualifying check costs $0.003 USDC. Seller payment is separate. USDC and supported networks.
Try the guard without funds before connecting a funded wallet.
Using a different client?
- mppx and native MPP
- Use the mppx hook for supported Base USDC charges, or a profile-specific adapter. MPP integration.
- Python
- Request a check, recover its response and verify retained receipts. Live seller-offer comparison uses the JavaScript guard. Python client.
- MCP
- Call the
checktool from your agent host. Connect the guard to the payment path separately. MCP interface.
Use with Glama for free discovery and listed-endpoint readiness tools. Paid checks require a payment-capable client.
Build the complete flow
After the quickstart, save and verify a record, then review production requirements. For sessions, invoices and other payment models, begin with supported integrations.
Find and check a service in one request
Use this path when your app knows the task but has not chosen an endpoint. 402Signal finds candidates, checks their current offers, applies your requirements, and returns a qualifying selection with its decision record.
One check, one checking fee
Discovery, live checks and selection are part of the same POST /route request. A qualifying check costs $0.003 USDC. A normal completed no-match check costs nothing. Verifying the returned evidence locally does not require a second paid check. Seller payment is separate.
1. Describe the task and your rules
This request looks for web search on Base at no more than $0.25 per call, requires enough input information to construct the call, and prefers the lowest comparable price among eligible candidates.
{
"need": "web search",
"networks": ["base"],
"max_price_usd": 0.25,
"require_invocable": true,
"objective": "cheapest",
"require_route_binding": true
}
need starts candidate discovery. If you supply url, that exact endpoint is checked instead; including both does not start a wider search.
Already know the endpoint?
{
"url": "https://seller.example/search",
"networks": ["base"],
"max_price_usd": 0.25,
"require_route_binding": true
}For this path, use the x402 client hook to obtain the check and verify it before seller signing.
Choose requirements and ranking
| Your requirement | Request field |
|---|---|
| Allowed seller networks | networks |
| Maximum seller price | max_price_usd or max_amount_atomic |
| Enough input information to construct a call | require_invocable |
| Observation history | min_observations, min_observed_success |
| Maximum HTTP probe time | max_probe_latency_ms |
| Seller price plus known seller-side fees | max_total_cost_usd, excluding the separate checking fee |
Required bounds exclude a candidate when the needed measurement is missing. prefer_network only changes preference; use networks for a requirement.
Set objective to cheapest, fastest, most_reliable or best. fastest uses this check's HTTP probe time, not the paid service's completion time. Reliability uses recorded observations, not a guarantee of output quality. lowest_total_cost and fastest_settlement use available cost and settlement/finality data.
Selection covers the eligible candidates actually probed within server limits. search_depth: "thorough" or max_candidates_to_probe can increase the search budget within those limits; neither searches the entire market. See all request fields.
2. Submit one paid check
Send the request to POST /route through your payment-capable client. Its initial HTTP 402 supplies the checking-fee requirements. Your wallet validates and authorizes that fee, then resubmits the same request. The completed response includes the selected endpoint and offer, compared candidates, and signed evidence when a qualifying bound selection is available.
Retain the exact request JSON and raw response text. Inspect the offer and billing outcome together: HTTP 200 alone does not authorize a seller payment. For durable attempt storage and lost responses, use RouteClient and recovery.
Browse for free before submitting
The API catalog and GET /preview?need=web%20search return catalog candidates without live probes or payment. Use them to explore. The paid /route check performs current probing, applies your requirements and selects an eligible offer.
3. Verify the selected offer before your wallet signs
Reuse the response from step 2. Check the selected URL against your application's destination policy, then read that seller's current unpaid challenge using the same URL, method and body, with redirects disabled. Immediately before authorization, pass the original check request, raw response and current seller challenge to withVerifiedRoute.
Reuse the receipt in your existing signing flow
Install @402signal/route-guard@0.7.7 and pin the log key independently. This excerpt begins after your app has obtained the check response and the seller's current raw challenge. The callback is where your existing wallet integration validates and executes the payment.
import { withVerifiedRoute } from '@402signal/route-guard';
await withVerifiedRoute({
routeRequestJson, // Exact need-based request from step 1.
routeResponseJson, // Raw response from the same check.
trustedLogVkey,
request: { url, method: 'GET', body: new Uint8Array() },
challenge: { status: 402, bodyText, paymentRequired, xPaymentRequired },
}, async verified => {
// Validate transaction effects against verified.accepted and buyer policy.
// Reserve your durable budget; invoke your existing wallet once.
});The verifier checks the receipt and current offer locally. It makes no network calls or payments. A mismatch or expired observation throws before the callback runs. The wallet still validates the actual transaction and prevents duplicate payment.
The signalGuard hook in the endpoint quickstart obtains a fresh check each time; it does not accept an existing selection response. For this receipt-reuse path, use withVerifiedRoute at the signing boundary instead of adding that hook to the same purchase.
SDK integration boundary · HTTP lifecycle and guarded execution example
4. Keep the selection and its rationale
Save the original request, response, receipt and private reveal alongside your approved policy and seller-payment record. The response's compared[] summary records selection and exclusion reasons for up to five candidates. The verifiable record describes 402Signal's decision, not the agent's private reasoning or proof that the seller delivered.
Start with a supported x402 request and payment profile. Generic MPP observation alone does not provide the signed offer binding used here; supported native MPP integrations have their own guide.
Next: save and verify the record · Native MPP guide · Explore the catalog
Copy a brief for your coding agent
Add criteria-based service selection using https://402signal.com/developers/choose-service. Send one need-based /route request with explicit network, price cap, ranking objective and require_route_binding. Retain the exact request and raw response. Discovery, live probing and selection share one qualifying checking fee. Validate the selected destination, read its current unpaid challenge, and reuse the same receipt with withVerifiedRoute immediately before the existing wallet authorizes payment. Do not add signalGuard to that same purchase expecting it to reuse the receipt: that hook obtains a new check. Preserve trusted key pins, transaction validation, durable budgets and original-attempt recovery. Test refusals before funding.
Try the guard without a funded wallet
Run the verifier with synthetic offers and a fake signing callback. You need Node.js 22 or newer and a reviewed checkout.
git clone https://github.com/402signalhq/402signal.git
cd 402signal
# Select the revision you intend to review before running it.
node integration/buyer-checks/run.mjs
node integration/buyer-checks/run.mjs --self-test
What you should see
Five buyer checks and two historical-record checks pass. A matching offer reaches the fake callback once. Changed price, recipient, request or expiry stops it. The original saved record verifies; an altered record fails.
Connect your own test callback
node integration/buyer-checks/run.mjs --adapter ./integration/buyer-checks/example-adapter.mjsCopy the example and export authorize(options, fakeCallback). Call the fake callback only after verification and preserve typed verifier errors. The self-test detects both an unguarded adapter and one that refuses every valid purchase.
The reference run makes no network requests or payments. A custom adapter is executable code: run it without production credentials in an isolated environment. Before going live, test the payment path that actually signs.
Next: JavaScript quickstart · Runner reference
Copy a brief for your coding agent
Test the 402Signal guard using https://402signal.com/developers/test-buyer. With Node 22 or newer, run the reference buyer checks and --self-test in a reviewed checkout. Use synthetic inputs and the fake callback; do not load a wallet or production key. Connect a trusted customer adapter and report acceptance, refusal and historical-verification results.
Guard one purchase and keep its evidence
This quickstart checks a service your app already uses, through an existing JavaScript x402 buyer. The wallet stays in your application. To find and select an endpoint first, use criteria-based selection. Start with a supported GET endpoint; request and payment coverage lists the other supported profiles.
Before you start
- Node.js 22 or newer.
- An
@x402/coreclient and@x402/fetchpayment-capable fetch configured with your wallet. - A log verification key pinned through trusted configuration, independently of a check response.
- An approved price cap and private evidence storage.
1. Install
npm install @402signal/route-guard@0.7.7
npm audit signatures
2. Connect the hook and set your cap
This excerpt uses your existing client, fetchWithPayment and pinned trustedLogVkey. retain is your application's storage callback.
import { signalGuard } from '@402signal/route-guard/x402';
client.onBeforePaymentCreation(signalGuard({
fetchWithPayment,
trustedLogVkey,
requestFor: ({ paymentRequired }) => ({
url: paymentRequired.resource.url,
networks: ['base'],
require_route_binding: true,
max_price_usd: 0.02,
}),
onResult: result => retain(result.text),
}));
This example selects Base and a two-cent seller-price cap. Set the network and cap to your approved policy. The default request does not set a price cap for you.
Before seller signing, the hook obtains a check through your payment-capable fetch, re-reads the seller's unpaid challenge, and verifies the receipt and terms locally. A miss or failed verification aborts by default. A qualifying check costs $0.003 USDC; the seller's payment is separate.
3. Retain the evidence
Save the exact check request and raw response text. Keep the approved policy and seller-payment record alongside them. Preserve raw JSON rather than parsing and rewriting it before verification.
Storage callback
onResult is synchronous. It does not wait for an asynchronous archive operation. Use storage appropriate for your application and keep the request with its response. The SDK's RouteClient and FileAttemptStore provide a separate durable HTTP lifecycle; the supplied file store requires private POSIX storage, including WSL.
4. Test the signing path
Run the offline checks, then test the actual merchant, wallet and request shape. Matching terms should reach your signing code; changed terms, expired evidence and invalid proofs should stop it. Keep budget, transaction validation and duplicate-payment controls in trusted application code.
Handle a refusal or uncertain result
Read the outcome before taking another action. For a completed miss, inspect route_outcome.next_action. A local refusal must stop seller payment; a timeout requires recovery of the original attempt.
- No qualifying offer
live:falseand a typedmiss_reason. A completed normal miss does not settle the checking fee.- No matching binding
binding_error: route_binding_unavailablemeans the observed offer could not produce the required evidence. Resolve the request or compatibility before another check.- Terms changed after the check
- Seller signing stops. An already-settled checking fee is not reversed.
- Response lost
- Recover the original attempt; do not create another authorization as a retry.
Custom wallet integrations
Use withVerifiedRoute directly before your authorization callback, or wrapExactAuthorize from the installer-generated helper. The helper returns state=binding_unavailable and keep_calling_route:true for a binding miss. Follow its next action before a new request; that flag does not authorize an immediate retry.
import { withVerifiedRoute } from '@402signal/route-guard';
await withVerifiedRoute({
routeResponseJson, routeRequestJson, trustedLogVkey,
request: { url, method, body: exactRequestBytes },
challenge: { status: 402, bodyText, paymentRequired }
}, async verified => {
// Validate transaction effects and reserve your durable budget.
// Invoke your existing wallet once using verified.accepted.
});The reference Base buyer includes planning, signing and confirmation. The packaged examples/search.ts shows the longer HTTP composition.
Install from a verified release archive
From a reviewed checkout:
node scripts/install_route_guard.mjsOr download the archive and SHA256SUMS from the 0.7.7 release, compare their published pins with package metadata, then:
sha256sum --check SHA256SUMS
npm install --ignore-scripts ./402signal-route-guard-0.7.7.tgzNext: verify the saved record · Complete SDK guide
Copy a brief for your coding agent
Add 402Signal to the existing x402 signing path using https://402signal.com/developers/check-offer. Install @402signal/route-guard@0.7.7 and check npm provenance. Configure signalGuard with the existing payment client, independently pinned log key, explicit network and price cap. Save the original request and raw response privately. Test matching and refused offers before connecting a funded wallet. Preserve the buyer's transaction validation, durable budget and recovery policy.
Open a hosted session
Use this when you want to reuse one paid observation. A hosted session is a /route product. It is not the merchant Session Client, which funds a seller channel.
Open costs $0.005 USDC. The window is 20 hops or 10 minutes on the bound snapshot from open. Then open again. Hops do not run a new 7-URL probe and do not call facilitator /verify or /settle. Completed misses are free. Seller payment stays with your buyer.
Open
curl -sS -D - https://402signal.com/route \
-H 'Content-Type: application/json' \
--data '{"url":"https://seller.example/x402","session":"open","require_route_binding":true}'HTTP 402 supplies the $0.005 requirements. Authorize that fee once and submit the identical request. require_route_binding on open can emit a v4 receipt. wrapExactAuthorize is the default MIT guard on that open. Hops do not use the wrap.
Hop
Send session=hop and the session_id from open. A raw session id, or any other value, in session is miss_reason=invalid_session_shape: HTTP 200, no probe, no checking fee. An empty or null session is the same shape error. Values are trimmed and case-folded, so " Open " opens.
curl -sS -D - https://402signal.com/route \
-H 'Content-Type: application/json' \
--data '{"session":"hop","session_id":"0000000000000000000000000000000000000000000000000000000000000000","url":"https://seller.example/x402"}'The session_id in the example is a schema-valid placeholder. Replace it with the real 64-hex id from open. The example url is optional on hop and pins the bound snapshot when present.
A hop that restores the bound winner reports route_outcome.code=session_hop and next_action=none. Optional scheme, amount_atomic and payTo are checked against the ceiling stored at open; a break misses and does not settle. amount_atomic is the amount you are about to authorize: at most the bound ceiling, a smaller amount is not refused. A hop that breaks the bound misses as constraints_unmet, fingerprint_miss, scheme_mismatch, network_mismatch or mandate_mismatch; a key the hop API does not know misses as unsupported_hop_field. All at $0.
Add the local guard on open · Hosted session contract.
Copy a brief for your coding agent
Add a hosted 402Signal session. Read https://402signal.com/developers/hosted-session. Open with session=open at $0.005; hops are session=hop plus session_id. Do not put the session id in session. Hops must not probe or call a facilitator. This is not the merchant Session Client. wrapExactAuthorize covers open with require_route_binding; hops do not wrap. Do not request funds without existing operator authorization.
Select the intended MPP charge
Use this for supported native WWW-Authenticate: Payment offers. Base evm.charge and Algorand charge have separate adapters. x402 through mppx is a different path.
Pin the profile, realm, network, asset, recipient and relevant fee/price limits. Exactly one supported offer must match. Two matches remain ambiguous even at the same price. Response order is not a spending policy.
Preserve the full offer
Retain the original header, body and alternate payment header. The verifier rebuilds selection from that evidence. Only the selected original challenge reaches the SDK. Do not simplify the response before verification or select another offer after signing fails.
Base charge
Use prepareVerifiedNativeBaseMpp from the reviewed MPP client. Supply retained observation evidence, the trusted log key, exact GET request, current challenge and buyer policy. Preparation does not sign. Your application durably claims the authorization identity and reserves its budget before createCredential({authorize}). Submit the resulting credential once to the exact merchant request.
This version covers Base USDC EIP-3009. It does not cover splits, Permit2 or arbitrary tokens. Base setup and complete contract.
Using mppx? Register the guard as its onChallenge hook
For buyers on mppx, @402signal/route-guard/mpp gives the same check as an onChallenge hook plus a challenge.received observer. The observer records which URL produced each challenge; the hook runs the hosted Check group offer observation for Base USDC charges under your cap, verifies the receipt, and throws when the live challenge's economic terms differ from the verified terms, so mppx never creates a credential. Hosted Check group offer must be enabled for the observation to qualify.
import { Mppx } from 'mppx';
import { mppGuard } from '@402signal/route-guard/mpp';
const guard = mppGuard({ fetchWithPayment: (u, i) => mppx.fetch(u, i), trustedLogVkey, maxCallAmountAtomic: '1000' });
const mppx = Mppx.create({ methods, onChallenge: guard.onChallenge });
mppx.onChallengeReceived(guard.onChallengeReceived);Algorand charge
Use the verified Algorand adapter with its explicit sponsor or buyer-paid fee policy, durable budget, signer and bounded transport. A merchant acknowledgment is not independent chain confirmation. A lost response remains uncertain until the original payment is reconciled.
Algorand setup and reference integration · Full-response selection contract.
Check the enabled hosted profiles and the matching adapter before integrating. Local POST support does not broaden hosted request support.
Copy a brief for your coding agent
Inspect a supported native MPP charge. Read https://402signal.com/developers/native-mpp and the exact adapter contract. Distinguish native MPP from x402 through mppx. Preserve the complete original offer and require one explicit match. Start with synthetic tests. Keep authorization and submission separate and durable. Do not infer support for all networks, assets, intents or request bodies.
Check a group offer
Use this when a seller challenge describes a grouped settlement, session, native charge, atomic group or invoice. Send the exact HTTPS GET url, buyer_limits caps and require_route_binding: true. Do not pass merchant_profile. When hosted Check group offer is enabled, the server auto-selects a codec from the live wire. Unknown or ambiguous challenges fail closed.
The HTTP result and compared[] name the short codes job (chk_grp) and codec. label is optional debug. The public receipt leaf stays commitment-only. This is a short-lived observation, not permission to deposit, issue vouchers or sign an arbitrary transaction.
Hosted enablement is operator-controlled. Currently enabled hosted codecs: mpp. Hosted profiles: algorand-mpp-charge-v1, base-mpp-charge-v1. An ordinary exact-payment receipt does not authorize this job. After a qualifying observation, use the matching session or manifest guide before funding.
curl -sS -D - https://402signal.com/route \
-H 'Content-Type: application/json' \
--data '{"url":"https://merchant.example/batch","buyer_limits":{"network":"eip155:8453","asset":"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913","recipient":"0x2222222222222222222222222222222222222222","receiver_authorizer":"0x3721824a31197dcDD2984cF43b92B6cc8A87c0Fb","withdraw_delay_seconds":900,"max_call_amount_atomic":"1000","max_cumulative_amount_atomic":"4000","max_capital_atomic":"4000"},"require_route_binding":true}'Caps select a codec before the probe. The live challenge must speak that codec. Empty or unknown caps are refused. A qualifying observation costs $0.003 USDC; seller charges remain separate.
Check group offer contract · Fund a session or invoice after the observation.
Copy a brief for your coding agent
Add a Check group offer observation. Read https://402signal.com/developers/check-group-offer and docs/batch-observation-v1.md. POST url, buyer_limits and require_route_binding:true. Do not send merchant_profile. Expect job chk_grp, a detected codec and label Check group offer. Unknown wires must fail closed. Do not treat the observation as signing or funding authority.
Check the full commitment
Use this when funding a supported session or accepting an explicit grouped-purchase manifest. An ordinary exact-payment receipt does not authorize a batch or session. For the hosted $0.005 observation window, use Open a hosted session instead. That product does not deposit merchant capital.
Base and Solana sessions
The Session Client requires Node 24 and private POSIX/SQLite storage. Pin one endpoint, channel, parties, asset, call price, cumulative cap, maximum call count and fixed deadline before funding. Deposited capital, native fees and seller charges are different exposures.
The original signed observation stays in the private journal. Later continuation follows separately pinned buyer policy; it does not extend the original receipt or buy another check. No automatic top-up or new channel is authorized. Unknown results stop later calls, including after restart.
The controller supports 1 to 64 sequential calls and a buyer deadline up to 24 hours. Base receiver/token activity is serialized; unrelated receiver activity can leave confirmation unknown.
The Solana session cap is not a per-call price. The observed per_call_amount_atomic is null; establish and pin the merchant's actual call economics separately.
Complete opening, continuation, recovery and close guide.
Algorand groups and invoices
The multi-item profile supports 2 to 15 job payments plus a sponsor transaction. The aggregate-invoice profile supports 2 to 64 explicit jobs represented by one invoice payment plus sponsorship. The invoice total is not a known per-job price.
The merchant must supply the supported ordered manifest. Verify exact jobs, payment count, recipient, total and fee quote. Do not split an ordinary one-payment offer into invented job payments. On-chain atomicity does not promise atomic HTTP delivery.
Manifest contract · Buyer adapter and examples.
Finish and reconcile
A voucher acknowledgment, a settlement transaction and a delivered resource are separate events. Confirm the original transaction and account effects. Receipt lookup must be genuinely read-only; resending a live voucher with a custom header is not automatically safe recovery. Missing evidence leaves the operation uncertain.
A qualifying hosted observation costs $0.003. Merchant charges, capital deposits, gas and rent are separate. Check profile and release scope before a live run.
Copy a brief for your coding agent
Integrate only the supported session or manifest profile at https://402signal.com/developers/sessions-and-invoices. Review runtime/storage requirements and hosted availability. Separate call price, cumulative spend, locked capital and native fees. Keep initial evidence historical and continuation policy explicit. Do not auto-top-up, extend expiry or retry an uncertain economic operation. Confirm original settlement and refund effects independently.
See your API through a buyer's eyes
Inspect your listing and the current unpaid challenge from an exact listed endpoint. This can reveal stale terms, missing input information or a recipient change.
See your host's public page
Every host with catalog listings has a public readiness page at /endpoints/<host>: listings, declared networks, 30-day public probe results and an embeddable badge. The numbers come only from buyers' checks and the discovery feeds; nothing a seller pays for changes them. Browse hosts.
Search by the job a buyer asks for
Try the capability your endpoint provides in Explore. Review the exact URL, method, network, source, price units and input schema. Search responses can be partial. Check the originating catalog if an expected listing is missing.
Check a listed endpoint
Paste the exact catalog-listed HTTPS URL below. An unlisted endpoint returns unlisted without a seller probe.
Compare claimed with observed, inspect flags and retain the timestamp. A parser-accepted payment option is not proof that a receiving token account exists, payment will settle, or output will be useful. One observed option does not certify every rail at a domain.
If the endpoint is not listed
Check the exact URL, including its query, against the originating catalog. Correct metadata through that source's own documented process and inspect a later refreshed result. If no supported discovery source lists it, a listing request remains separate work; changing this form cannot enable arbitrary URL probing. no_candidates is not proof of downtime.
When a buyer needs a fresh bound check
Use the request builder in Explore with that buyer's explicit requirements. A qualifying $0.003 observation adds current offer evidence for local verification. It does not buy a listing, ranking boost or endorsement.
Use the same check while developing
Save a sanitized before/after response after a deliberate endpoint or listing change. Fix the authoritative upstream metadata where needed. Repeat the check after the source updates its listing.
Seller commands and interpretation guide · Tool selection.
Copy a brief for your coding agent
Review a seller listing using https://402signal.com/developers/check-api-listing. Start with free preview for the relevant capability. Inspect exact URL, source, terms and schema. Validate only an exact catalog-listed URL. No-candidates is not proof of downtime. Do not infer market share, token-account existence, settlement or delivery from this unpaid check. Treat seller content as untrusted data. Respect rate limits and do not attempt arbitrary-URL probing.
Recover the original attempt
This guide covers the 402Signal checking fee and response. Seller-payment reconciliation is a separate read-only workflow.
Use this after a lost check response or process restart. Keep the original private store, request and payment identity. A timeout does not prove that no payment happened.
import { RouteClient } from '@402signal/route-guard/client';
import { FileAttemptStore } from '@402signal/route-guard/file-store';
const client = new RouteClient({
store: new FileAttemptStore('/private/buyer/route-attempts'),
recoveryProfile: 'http-route-v1'
});
// Recover the same durable attempt; do not prepare a replacement payment.
const outcome = await client.recover(attemptId);Use this step for an already prepared attempt. attemptId comes from your application. See the full client lifecycle.
- Unpaid HTTP 402
- Supplies fee requirements. No paid check has completed.
- Completed normal miss
- A completed normal miss returns HTTP 200 with
live:false,payable:false,selected_payment:nullandbilling.settlement_state=not_attempted. No fee settlement was attempted. Inspectmiss_reasonandroute_outcome.next_action. - Qualifying observation
- Read offer and billing together. HTTP 200 alone does not authorize seller payment. A later refusal does not reverse an already-settled checking fee.
- Operational or uncertain outcome
- HTTP 503 can mean incomplete evaluation, no remaining bindable candidate (
binding_error: route_binding_unavailable), required evidence failure after settlement, or an unknown settlement result. Inspectbilling.settlement_state. An unread response is not proof of nonpayment. - Recovery unavailable
- Reconcile the original authorization through independent read-only evidence. Keep the reservation until the outcome is established. Do not generate another payment, job ID or store to escape uncertainty.
Do not reuse an unknown authorization or automatically wrap the client in payment-retry middleware. Response recovery does not extend expiry or resume seller execution. The recovery-only profile is HTTP /route, not MCP.
Access credentials identify a workload; they are not customer wallet keys. Protect them, payment headers and private stores. Short-term response retention is not long-term evidence storage.
HTTP recovery contract · First-time integration.
Copy a brief for your coding agent
Recover an existing 402Signal HTTP attempt. Read https://402signal.com/developers/recover-routing-attempt. Reuse the original private journal and attempt ID. Inspect billing and preserved evidence. Do not sign again, resend a seller payment or create a fresh identity to resolve an unknown result. Keep budget reservations until independent reconciliation establishes the outcome.
Inspect the original seller payment
Use this when the reference Base buyer submitted a seller payment but did not retain the response. For a lost 402Signal checking response, use routing recovery instead.
The reference buyer requires Node 24 and private POSIX/SQLite storage. Preserve the original configuration, journal and job ID. Read-only reconciliation does not load the signing account or submit another payment.
cd integration/reference-buyer
npm ci --ignore-scripts
REFERENCE_BUYER_ACK=base-exact-only-once node operator.mjs confirm-seller /private/config.json ORIGINAL_JOB_IDThe path and job ID refer to your existing campaign. An optional existing transaction hash can be supplied as the final argument. Do not invent a new ID or replace the journal.
A confirmed payment with no retained response means payment confirmed; response not retained. It does not recover the answer, prove useful output or issue a refund. While the job is unresolved, repeat submission and new jobs in that campaign remain blocked. Read the resulting journal status before any further action; confirmation is not permission to rerun the job.
The current reference seller request times out after 20 seconds. Read-only confirmation makes at most six observations within 15 seconds. HTTP timeout, payment-authorization validity and observation expiry are separate clocks. maxTimeoutSeconds is not a guaranteed response-delivery deadline.
Reconciliation itself creates no new payment authorization. Prior checking fees, seller payments and held reservations remain subject to their recorded outcomes. Independent wallet calls or replacing the campaign store bypass this workflow and are outside its protection.
Choose the interface for your application
| Interface | Use it for | Start here |
|---|---|---|
| JavaScript | Verify current terms before signing with an x402 or supported mppx client. | x402 quickstart · MPP |
| HTTP | POST /route selects an eligible service with need, or checks an exact endpoint with url. | Choose a service · OpenAPI reference |
| Python | Check lifecycle, recovery and retained-receipt verification. | Python setup |
| MCP | The check tool exposes the service to your agent host. | MCP manifest |
| Discovery | preview searches the catalog; validate checks an exact listed endpoint. | API catalog |
HTTP check
Use url for an exact endpoint, or need to find candidates. Set explicit limits and request a binding when your buyer will verify before signing. The selection guide shows how to reuse that check in the signing flow.
curl -sS -D - https://402signal.com/route \
-H 'Content-Type: application/json' \
--data '{"need":"web search","networks":["base"],"max_price_usd":0.02,"require_route_binding":true}'This unpaid request returns HTTP 402 with the checking-fee requirements. Your payment-capable client authorizes that fee and resends the same request. Verify the resulting evidence before a separate seller payment.
Python client
pip install 402signal==0.1.2Python 3.10 or newer. signal402.challenge gets the fee requirements, signal402.check submits your wallet's authorization, and recover retrieves the original response. The package verifies retained receipts; live comparison of the seller's current offer uses the JavaScript guard.
Python examples and reference.
MCP
Use the hosted transport at /mcp/v0.3.1 and the inputs in the manifest. check is the current tool name; route remains an alias. The credential-free stdio adapter provides discovery and listed-endpoint checks. Paid checks require a payment-capable client; calling a tool does not attach a guard to your wallet.
Use HTTP for response recovery and for request profiles absent from the advertised MCP schema.
Working with a coding agent
Each integration guide has a copyable task brief. You can also load the customer skill through your agent host. Keep wallet keys and approved spending policy in your application's trusted configuration.
Save a record you can verify later
A retained record shows the requirements 402Signal received, the offer it observed, and its decision. Use it alongside your approved policy and payment records when investigating a purchase.
1. Save the original bundle
Keep the exact check request and complete raw response, including pq_trust.transparency.receipt and pq_trust.transparency.reveal, in private storage. Retain your approved policy and wallet/execution record separately alongside them.
2. Verify a saved receipt
import { verifyReceipt } from '@402signal/route-guard';
const result = verifyReceipt({
routeResponseJson,
routeRequestJson,
trustedLogVkey,
});For supported v4 records, this verifies the signature and inclusion proof using your pinned key. Use the matching verifier for the record version. Historical verification does not extend the offer's expiry or authorize another payment.
3. Understand the decision
Compare the submitted rules and selected terms with your approved policy. The response's compared[] rows explain candidate exclusions through fields such as selectable, payTo_pending, payTo_changed and excluded_reason. This is 402Signal's decision basis, not your agent's internal reasoning.
4. Check public coverage separately
Immediate checkpoints use Ed25519. Later cumulative checkpoints are anchored on Algorand MainNet using Falcon-1024 authorization. The check does not wait for that anchor; the local historical verifier does not verify it. Inspect checkpoint status and distinguish a pending anchor from a confirmed one.
Storage is yours
The public log holds commitments, not a copy of your private records. Hosted response recovery lasts 120 seconds from request start. Store the original evidence for as long as your application needs it.
A verified observation does not establish seller delivery, payment settlement or human approval. Compare those against their own records.
Browser verifier · Evidence reference · Record format
Copy a brief for your coding agent
Verify retained evidence using https://402signal.com/developers/evidence. Keep the original request and raw response private. Use the matching verifier and independently pinned key. Compare the authenticated decision with approved policy and wallet records. Verify a later public anchor separately; historical verification does not authorize a new payment.
Put the check in the path that signs
Before production, connect your policy, payment client, evidence storage and recovery flow around the supported integration.
Choose the supported scope
Fix the merchant, request shape, network, asset and payment profile. Check current integration support and test the merchant's actual offer.
Enforce your payment policy
Keep price caps, approved recipients, key pins, wallet authority and durable budgets in trusted application code. Validate the actual transaction effects. A repeated valid check is not duplicate-payment protection.
Stop on refusal or uncertainty
Test changed terms, expired evidence, invalid proofs and timeouts. Apply the guard to every relevant signing path. Do not bypass it or create a new payment to resolve an unknown outcome. If human approval is needed, obtain it before the final fresh check.
Retain and recover
Store private evidence and payment records. Preserve the original attempt identity across restarts and recover lost responses within the documented window. Reconcile uncertain payments without submitting them again.
Confirm workload capacity and support for your deployment through integration support. Your application remains responsible for wallet execution and evidence retention; 402Signal does not provide escrow or a delivery guarantee.
When a recipient changes
Two outcomes a buyer can rely on. First, a candidate is excluded from selection (compared[].excluded_reason payTo_pending, with payTo_changed true) when the live challenge's payTo differs from 402Signal's own previous trusted observation of that URL, even if the catalog listing has since been updated to the new wallet; a catalog claim never clears it. The exclusion lifts when a second independent observation confirms the new recipient, or when the request opts in with accept_payTo_change. Second, the guard's binding check fails closed on a recipient change: decision_binding.quote_sha256 covers the seller's whole raw 402 challenge, so a different payTo (like a different price, network or asset) is a quote_changed refusal and the buyer's callback never runs. Definitions: claimed is the catalog listing at claimed.claimed_at; observed is the live challenge at verified_at; payTo_changed means the observed recipient differs from the catalog claim or from the last trusted observed destination; claimed_payTo_match compares the two sides directly.
Match your payment flow to a supported integration
Use the JavaScript guard for exact x402 payments, or the named adapter for another supported profile. The checking fee is paid separately in USDC on Base, Solana or Algorand.
| Payment flow | Coverage | Integration |
|---|---|---|
| Exact x402 | Base, Solana and Algorand USDC. GET and defined POST profiles. | JavaScript guard, Node 22+ |
| Native Base MPP charge | Base USDC, evm.charge EIP-3009. No Permit2 or split payments. | mppx hook or Base adapter |
| Native Algorand MPP charge | One supported USDC charge with an explicit fee policy. | Source reference adapter, Node 24 |
| Base x402 channel | Funded batch-settlement channel with fixed buyer policy. | Session Client, Node 24 |
| Solana MPP push session | Fixed opening and continuation policy; no automatic top-up. | Session Client, Node 24 |
| Algorand groups and invoices | Explicit merchant manifests: two-item groups, multi-item groups or aggregate invoices. | Algorand buyer adapter, Node 24 |
Pay for the check with native USDC
The checking fee accepts the following Mainnet assets through x402 exact. This fee-payment support is separate from the seller integrations above.
| Network | Accepted asset |
|---|---|
| Base | Native USDC · Chain ID 8453 |
| Solana | Native USDC · Mainnet |
| Algorand | Native USDC · ASA 31566704 |
Token addresses and network identifiers
- Base
eip155:84530x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913- Solana
solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpEPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v- Algorand
algorand:wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8=
Asset ID31566704
Check GET /rails and the unpaid /route challenge for current fee requirements. Other USDC networks and bridged tokens are outside these fee rails.
Packages and hosted availability
@402signal/route-guard@0.7.7 is published on npm with provenance. 402signal==0.1.2 is published on PyPI for check lifecycle and historical verification. Session and native Base MPP clients are available as GitHub release archives; the native Algorand charge adapter is a source integration.
See the capability record for package digests and currently enabled hosted group-offer codecs. Use the matching profile before funding a session or submitting a charge.
Request boundaries
Ordinary bound checks cover GET, supported empty-object POST and the explicit bounded Parallel search profile. Arbitrary POST bodies and rotating or personalized exact quotes need a different integration. Generic MPP observation returns terms; signed evidence requires a supported named profile.
The $0.005 hosted session reuses one observation for 20 hops or 10 minutes. It is separate from a funded merchant session.
Get told when a seller you depend on changes
A subscription names up to 20 seller hosts and one public HTTPS webhook. Every two minutes the service compares each host's latest public observations with your subscription and, when something moved, delivers one signed batch of price_changed, recipient_changed and liveness_changed events. Alerts fire on the same observations the endpoint pages count: a change is reported when a check observed it, never from a catalog feed alone. An admission key (X-402Signal-Key) is required; ask at ross@402signal.com.
Subscribe
curl -sS -X POST https://402signal.com/alerts \
-H "X-402Signal-Key: $KEY" -H "Content-Type: application/json" \
-d '{"url":"https://hooks.example.com/402signal","hosts":["api.example.com"],"events":["price","recipient","liveness"]}'HTTP 201 returns the subscription id, hosts_known (whether each host has listings today) and signing_secret, shown once. The URL must be public HTTPS with public DNS: private, loopback and link-local addresses, plain HTTP, credentials in the URL and 402signal.com itself are refused at creation and again on every delivery. Up to 10 subscriptions per key, 20 hosts each; events defaults to all three.
What you receive
One POST per scan when something changed. Headers: X-402Signal-Event (402signal.alerts, or 402signal.ping for a test), X-402Signal-Delivery (the delivery id, also in the body) and X-402Signal-Signature (t=<unix seconds>,v1=<hex HMAC-SHA256> over <t>.<raw body>).
{"type": "402signal.alerts", "delivery_id": "9f1c2d3e4a5b6c7d", "subscription_id": "0123456789abcdef",
"generated_at": "2026-09-13T18:20:00Z",
"events": [
{"event": "price_changed", "host": "api.example.com", "url": "https://api.example.com/v1/quote",
"amount_atomic": "20000", "changed_at": "2026-09-13T18:19:41Z", "endpoint_page": "https://402signal.com/endpoints/api.example.com"},
{"event": "recipient_changed", "host": "api.example.com", "url": "https://api.example.com/v1/quote",
"payTo": "0xabc...", "observed_payTo": "0xdef...", "changed_at": "2026-09-13T18:19:41Z", "endpoint_page": "..."},
{"event": "liveness_changed", "host": "api.example.com", "url": "https://api.example.com/v1/quote",
"live": false, "miss_reason": "timeout", "observed_at": "2026-09-13T18:19:52Z", "endpoint_page": "..."}
]}price_changed carries the new observed atomic amount on the same asset. recipient_changed carries the address on record and the newly observed one; it stays pending until a second observation confirms it, with no second alert for the confirmation. liveness_changed reports the latest observation flipping between answering with a valid challenge and not. Delivery is at least once: the same event, url and changed_at seen twice is the same change twice. Answer with any 2xx within five seconds; a 3xx is not followed and counts as a failure.
Verify the signature
import hmac, hashlib, time
def verify(secret: str, header: str, body: bytes, tolerance_s: int = 300) -> bool:
parts = dict(p.split("=", 1) for p in header.split(",") if "=" in p)
ts = int(parts.get("t", "0"))
if abs(int(time.time()) - ts) > tolerance_s:
return False
expected = hmac.new(secret.encode(), b"%d." % ts + body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, parts.get("v1", ""))
Failures and management
A delivery that raises, times out or answers outside 2xx counts as a failure. Retries back off from one minute to one hour and the undelivered changes stay owed, so the next successful delivery carries them. After 20 consecutive failures the subscription is disabled and shows active: false with the reason; a successful test ping re-enables it.
| Call | Result |
|---|---|
| GET /alerts | Your subscriptions, no secrets, and the limits. |
| GET /alerts/<id> | One subscription with its last 20 deliveries: time, kind, HTTP status, event count, error class. |
| POST /alerts/<id>/test | A signed 402signal.ping now; delivered: true on a 2xx. |
| DELETE /alerts/<id> | Removes it, HTTP 204. |
Every call answers only for the key that created the subscription. To rotate the signing secret, delete and recreate the subscription. Alerts are not uptime monitoring: a host nobody checks stays silent. Check your own key and credits any time with GET /keys/usage.
The Offer Evidence Record, version 1
A versioned, protocol-independent description of the evidence a check produces for one offer: what was requested, what was offered, when, observed by whom, signed how and anchored where. Short name offer_evidence_record_v1. It documents what is issued today (the v4 exact x402 binding and the v5 group-offer observation) including supported named MPP profiles. Agent mandate references remain reserved.
A score is an opinion about an endpoint computed from public inputs. A record is a signed statement, made by a third party at a specific second, that a specific request received a specific offer, bound to the checked request and offer. Everything below exists to make the record verifiable by someone who does not trust 402Signal.
Seven parts
| Part | Content | Today's field |
|---|---|---|
| Request | The exact seller request: complete HTTPS URL with its query, method, hash of the body bytes. No redirects, no normalization. | decision_binding.request.url, .method, .body_sha256 |
| Offer | The whole challenge the seller returned, hashed: every option in order with price, recipient, asset, network, timeout, facilitator data and supported extensions; plus which option the check selected. | decision_binding.quote_sha256, .selected_index; the observed challenge in the reveal |
| Rules | The buyer's rules as submitted. Evidence of what was asked, not of human approval. | request_json in the reveal |
| Decision | The winner, the compared candidates with exclusion reasons, the selected payment, the scoring model. | routing_evidence_json in the reveal |
| Time | When the challenge was received and when the observation stops being usable for signing. Never extended by retries, replay or approval. | decision_binding.observed_at, .expires_at |
| Signature | A commitment to the private evidence in a public leaf; the leaf in an append-only Merkle log; an Ed25519-signed checkpoint; an inclusion proof from leaf to checkpoint. | pq_trust.transparency.receipt |
| Anchor | Cumulative checkpoints written to Algorand MainNet in a transaction authorized with Falcon-1024, so a later rewrite of log history becomes detectable. | Public trust descriptor and the log viewer |
Commitment
leaf_commitment = SHA256("402signal.route_decision.v4" || 0x00 || canonical(evidence) || salt_32_bytes)
evidence = { evidence_version, binding, request_json, routing_evidence_json }canonical is the RFC 8785 subset the verifiers implement: null, booleans, Unicode strings, arrays, objects and safe integers only; floats, duplicate keys, lone surrogates and unsafe integers are rejected. The two JSON strings keep their exact bytes. The public leaf carries only the leaf type, a minute-rounded timestamp, a nonce and the commitment; the reveal stays with the buyer. The record contains offer evidence, not the paid service output. Keep the private reveal out of public logs.
Protocol profiles
| Profile | Offer content | Status |
|---|---|---|
| x402-exact-v2 | An x402 v2 PaymentRequired envelope with exact options on Base, Solana, Algorand on their supported network identities. | Issued today as the v4 binding. |
| x402-group-offer-v1 | One exact HTTPS GET API observed under buyer limits with a codec auto-detected from the live challenge. | Issued today (v5). |
| mpp-charge-v1 | A WWW-Authenticate: Payment challenge classified as a charge: method, intent, amount, recipient, network. | Named Base and Algorand charge profiles have v5 signed observations. Other charge methods remain observation-only. |
| mpp-session-v1, mpp-subscription-v1 | Session and subscription terms: unit price, suggested deposit, period. | The named Solana push-session profile has a v5 observation. Generic session and subscription terms remain observation-only. |
| mandate-ref-v1 | Reserved: a reference to an agent mandate (AP2 cart or payment mandate, Visa Trusted Agent Protocol assertion, Mastercard verifiable intent) so one record carries both the offer and the authority the agent acted under. | Reserved; nothing issued. |
Adding a profile changes the offer part only; the commitment scheme and the verifiers do not change.
Verification
- Recompute the commitment from the reveal and compare it with the leaf.
- Check the inclusion proof from the leaf to the checkpoint and the checkpoint's Ed25519 signature under a key you pinned yourself, never one that arrived in the same response.
- Compare
decision_bindingin the response with the authenticated binding inside the evidence. - Compare
request_jsonwith the request the buyer actually made. - Before signing a payment, compare the seller's current challenge with the bound quote hash and refuse when the terms differ or
expires_athas passed.
Implementations: verifyReceipt and withVerifiedRoute in @402signal/route-guard, the signal402 Python package, and the browser verifier. Use the JavaScript guard for live seller-offer comparison; Python and the browser verifier support retained-receipt verification.
Versioning and limits
evidence_version and the leaf type version the commitment. Historical leaves keep their original verification semantics; new profiles and reserved fields are additive; a change to the commitment scheme or the signature is a new leaf type and a new version of this document. The record proves that a specific request received a specific offer at a specific time and what the check decided. It does not prove delivery, output quality, seller identity or intent, legality, or that a human approved the submitted rules; it cannot recover a deleted private record or observe purchases that bypassed the check; the Falcon anchor protects the checkpoint history, not the seller payment.
The Trust page · Investigate with a retained record · The proposed x402 extension