LOOMAL
SDK / API ReferencePayments

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

FieldTypeRequiredDescription
paymentHeaderstringYesThe buyer's X-Payment header (base64-encoded JSON)
resourcestringYesFull URL of the paid request — query strings are stripped before matching endpoints
amountstringYesMust match the amount used in the original /challenge
networkstringYesMust match the network used in the original /challenge
descriptionstringNoOptional, 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.

FieldDescription
txHashOn-chain settle transaction. View on BaseScan.
payerBuyer's EOA address (the signer of the EIP-3009 authorization).
paymentInIdInternal ID of the dashboard row. Lookup with GET /v0/payments/:id.
signedReceiptEd25519 receipt signed by your Identity's DID key — the seller-issued proof of payment.
recordingFailedtrue 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

StatuserrorCause
400bad_requestInvalid body, malformed paymentHeader, or payTo mismatch
401unauthorizedMissing or invalid Authorization header
403forbiddenAPI key lacks payments:accept scope
409no_walletIdentity has no wallet provisioned
503feature_disabledPayments not enabled on this Loomal instance

What happens server-side

  1. Loomal validates the paymentHeader against the x402 spec.
  2. The buyer's signed authorization is checked against the project's wallet and the expected amount. Mismatches return { ok: false, stage: "verify" }.
  3. On valid input, Loomal verifies the signature and settles the USDC transfer on-chain. Failures here return { ok: false, stage: "settle" }.
  4. 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.

On this page