Base URL (use this for every request)

https://pay.thrt.ai

All endpoints in this page are relative to this. Example: GET /protectedhttps://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 is 0.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"
  }
}