Base URL (use this for every request)
https://pay.thrt.ai
All endpoints in this page are relative to this. Example: GET /protected = https://pay.thrt.ai/protected
NLx402 API key
Overview: request, verify, unlock
In plain English:
NLx402 lets you put a nonce-locked, tamper-checked paywall in front of any resource. Your user signs in with Phantom, gets an anonymized API key tied only to their wallet, you issue a quote, the client proves they didn’t touch it, the user pays on-chain, and NLx402 unlocks the paywalled data.
Accounts are pseudonymous: NLx402 only cares about a wallet_id + mint, and issues an API key you can safely use in your backend.
Step 0 — Phantom login
Your user connects their Phantom wallet and picks a token. NLx402 creates an API key tied to that wallet + mint, so their “username” is just their wallet address.
Step 1 — Issue a quote
From your backend, call
GET /protected
with X-API-Key.
NLx402 returns a quote (amount, mint, nonce, expiry, recipient). You send that quote JSON to your client.
Step 2 — Verify / lock the quote
The client sends the quote back to you. You POST it to
POST /verify
as payment_data + nonce.
NLx402 rebuilds and rehashes the quote server-side. If anything was changed, verification fails.
Step 3 — Pay & unlock
After the user pays the quote on-chain, your backend calls
GET /protected again with
X-API-Key and an
X-Payment header
({"tx":"<TX>","nonce":"<NONCE>"}).
NLx402 checks that the nonce was successfully verified and that the tx matches the quote, then returns your paywalled data.
The /verify step exists
only to enforce tamper-resistance. The client cannot safely change
amount,
mint,
recipient,
nonce or
expires_at without being caught.
If X-Payment is sent for a nonce that was never verified,
NLx402 will reject it and tell you the quote needs to be verified first.
The header X-Total-Price is still optional on the initial
/protected request; if you don’t send it, NLx402 uses the default.
Whatever price is used is baked into the quote, re-derived at
/verify, and enforced again when
X-Payment is checked.
Official SDKs
Use the NLx402 client SDKs to talk to
https://pay.thrt.ai
without hand-rolling HTTP calls. All SDKs are open source and live on GitHub.
All SDKs are MIT licensed and track the same /api/auth/me, /api/metadata, /protected and /verify flow described above.
Endpoints
Base URL for everything on this page:
https://pay.thrt.ai
Get current API identity
GET /api/auth/me
Introspect the API key you are using. This returns the wallet and selected mint currently bound to your
X-API-Key, plus the creation timestamp.
Headers
X-API-Key(required) – your NLx402 API key.
Example request
curl -H "x-api-key: <YOUR_API_KEY>" \
https://pay.thrt.ai/api/auth/me
Example response
{
"created_at": 1763503560.0582392,
"ok": true,
"selected_mint": "YOUR MINT",
"wallet_id": "YOUR WALLET ADDRESS"
}
Get NLx402 metadata & supported mints
GET /api/metadata
Public metadata about the facilitator: network, supported chains, current NLx402 version, and the list of supported token mints for this deployment.
Headers
- None – this endpoint is public.
Example request
curl https://pay.thrt.ai/api/metadata
Example response
{
"metadata": {
"network": "https://api.mainnet-beta.solana.com",
"supported_chains": [
"SOL"
],
"version": "NLx402.thrt-v0.1"
},
"ok": true,
"supported_mints": [
"CA_1",
"CA_2"
]
}
Step 1 — Get a quote
GET /protected
Ask the server: “How much should this request cost?” You get back a quote with amount, mint, nonce, and expiry. No payment is required yet.
Headers
X-API-Key(required) – your NLx402 API key.X-Total-Price(optional) – logical total price for this request (e.g.0.002). Default is0.1.
Example request
curl -H "x-api-key: <YOUR_API_KEY>" \
-H "x-total-price: 0.002" \
https://pay.thrt.ai/protected
Example response
{
"amount": "1.3957",
"chain": "solana",
"decimals": 6,
"expires_at": 1763498359.7547407,
"mint": "YOUR REGISTERED MINT ADDRESS",
"network": "mainnet-beta",
"nonce": "YOUR NONCE",
"recipient": "YOUR WALLET ID",
"version": "NLx402.thrt-v0.1"
}
Step 2 — Lock / verify the quote
POST /verify
Send the quote back. The server rebuilds the quote on its side and confirms it matches what it originally sent. If anything has changed, verification fails.
Headers
X-API-Key(required)Content-Type: application/x-www-form-urlencoded
Form fields
payment_data– JSON string containing the quote returned from/protected.nonce– the same nonce value from the quote.
Example request
curl -H "x-api-key: <YOUR_API_KEY>" \
-XPOST \
-H "content-type: application/x-www-form-urlencoded" \
--data-urlencode \
'payment_data={"amount":"1.3957","chain":"solana","decimals":6,"expires_at":1763498359.7547407,"mint":"...",\
"network":"mainnet-beta","nonce":"YOUR NONCE","recipient":"...","version":"NLx402.thrt-v0.1"}' \
--data-urlencode 'nonce=YOUR NONCE' \
https://pay.thrt.ai/verify
Example response
{
"ok": true
}
Step 3 — Access after payment
GET /protected (with X-Payment)
After the user pays the quote on-chain, call GET /protected again.
This time you include the Solana transaction hash and nonce in the
X-Payment header.
Headers
X-API-Key(required)-
X-Payment(required) – JSON string:{"tx":"<TX_SIGNATURE>","nonce":"<QUOTE_NONCE>"}
Example request (before verify)
curl -H 'x-payment: \
{"tx":"YOUR TX",\
"nonce":"YOUR NONCE"}' \
-H "x-api-key: <YOUR_API_KEY>" \
https://pay.thrt.ai/protected
If you skip the /verify step, you’ll see:
{
"error": "X-PAYMENT has not been verified"
}
Example request (after verify)
curl -H 'x-payment: \
{"tx":"YOUR TX",\
"nonce":"YOUR NONCE"}' \
-H "x-api-key: <YOUR_API_KEY>" \
https://pay.thrt.ai/protected
Example response (paid)
{
"ok": true,
"x402": {
"amount": "1.3957",
"decimals": 6,
"mint": "3dQugtW8ne2PHcjoGFek2j8Ht7o87jKqaSPNWuC2pump",
"nonce": "YOUR NONCE",
"status": "paid",
"tx": "YOUR TX",
"version": "NLx402.thrt-v0.1"
}
}