Redeem
Verify and settle a buyer-signed x402 payment
Scope: payments:accept
Verifies an EIP-3009 signature from the X-Payment header, settles the USDC transfer on-chain, records the payment to your dashboard, signs an Ed25519 receipt, and dispatches your webhook (if registered).
Most sellers don't call this directly — the requirePayment middleware does it for you. See Accept payments.
Request
import { Loomal } from "@loomal/sdk";
const loomal = new Loomal({ apiKey: process.env.LOOMAL_API_KEY! });
const result = await loomal.payments.redeem({
paymentHeader: req.header("x-payment")!,
resource: "https://your-api.com/search",
amount: "0.05",
network: "base",
});import os
from loomal import Loomal
loomal = Loomal(api_key=os.environ["LOOMAL_API_KEY"])
result = loomal.payments.redeem(
payment_header=request.headers["x-payment"],
resource="https://your-api.com/search",
amount="0.05",
network="base",
)curl -X POST https://api.loomal.ai/v0/payments/redeem \
-H "Authorization: Bearer loid-your-api-key" \
-H "Content-Type: application/json" \
-d '{
"paymentHeader": "<base64 X-Payment header value from buyer>",
"resource": "https://your-api.com/search",
"amount": "0.05",
"network": "base"
}'Body
| Field | Type | Required | Description |
|---|---|---|---|
paymentHeader | string | Yes | The buyer's X-Payment header (base64-encoded JSON) |
resource | string | Yes | Full URL of the paid request — query strings are stripped before matching endpoints |
amount | string | Yes | Must match the amount used in the original /challenge |
network | string | Yes | Must match the network used in the original /challenge |
description | string | No | Optional, must match the description used in /challenge |
The amount, network, resource, and description are used to reconstruct the exact PaymentRequirements the buyer must have signed against. If they don't match, verify fails.
Response — 200, settled
{
"ok": true,
"paymentResponse": "<base64 X-Payment-Response header value>",
"txHash": "0x<onchain-settle-tx-hash>",
"payer": "0x<buyer-eoa-address>",
"paymentInId": "cmoh…",
"signedReceipt": {
"body": { /* canonical payment data */ },
"signature": "<base64 Ed25519 signature>",
"publicKey": "<multibase z6Mk… DID public key>"
},
"recordingFailed": false
}Set paymentResponse as the X-Payment-Response header on your 200 response so the agent can verify settlement.
| Field | Description |
|---|---|
txHash | On-chain settle transaction. View on BaseScan. |
payer | Buyer's EOA address (the signer of the EIP-3009 authorization). |
paymentInId | Internal ID of the dashboard row. Lookup with GET /v0/payments/:id. |
signedReceipt | Ed25519 receipt signed by your Identity's DID key — the seller-issued proof of payment. |
recordingFailed | true if the on-chain settle succeeded but Loomal couldn't record it server-side. The transfer still stands; check your activity log for any settle anomalies to reconcile. |
Response — 200, rejected
{
"ok": false,
"stage": "verify",
"reason": "invalid_signature",
"requirement": { "x402Version": 1, "accepts": [ /* … */ ] }
}stage tells you where it failed:
"verify"— signature,payTo, amount, or auth-payload check failed before the on-chain submit"settle"— the on-chain submit was rejected (most often: replay of an already-used nonce)
Send the requirement body back to the buyer with HTTP 402. They can sign a fresh authorization and retry.
Errors
| Status | error | Cause |
|---|---|---|
| 400 | bad_request | Invalid body, malformed paymentHeader, or payTo mismatch |
| 401 | unauthorized | Missing or invalid Authorization header |
| 403 | forbidden | API key lacks payments:accept scope |
| 409 | no_wallet | Identity has no wallet provisioned |
| 503 | feature_disabled | Payments not enabled on this Loomal instance |
What happens server-side
- Loomal validates the
paymentHeaderagainst the x402 spec. - The buyer's signed authorization is checked against the project's wallet and the expected amount. Mismatches return
{ ok: false, stage: "verify" }. - On valid input, Loomal verifies the signature and settles the USDC transfer on-chain. Failures here return
{ ok: false, stage: "settle" }. - On success, Loomal signs an Ed25519 receipt and returns it. The endpoint's webhook (if configured) gets the same receipt; the project owner gets a plain-text email if mail is configured; the payment shows up in the project's activity log.
The receipt, webhook dispatch, log entry, and email are best-effort — if any fail, the payment is still settled on-chain and you still get back ok: true.