Record a settled x402 payment
You already settled. Fiscal402 observes the evidence and returns a signed receipt. It does not generate HTTP 402, authorize the transfer, or hold funds. A transaction hash alone is not enough.
- SETTLED x402 exact
- ↓
- POST /settlements
- ↓
- EU VAT + UBL + fiscal402.receipt
- ↓
- verify locally
1. Configure the key
Server only. Header X-Fiscal402-Key. Environment variable FISCAL402_MERCHANT_KEY. Merchant keys use the f402m_ prefix. Never ship the key to a browser. Seller establishment (Acme BV, NL) lives on the merchant account, not in this body.
.env
FISCAL402_MERCHANT_KEY=f402m_your_key_here
FISCAL402_ORIGIN=https://api.fiscal402.com2. POST the settlement
Example: Acme BV sells digital inference to Buyer GmbH in DE. Amounts are decimal strings — never 0.1 as a float. consumerCountry is attested buyer location.
record.ts
const origin = process.env.FISCAL402_ORIGIN ?? "https://api.fiscal402.com";
const response = await fetch(`${origin}/settlements`, {
method: "POST",
headers: {
"content-type": "application/json",
"X-Fiscal402-Key": process.env.FISCAL402_MERCHANT_KEY!,
"Idempotency-Key": "acme-nl-de-1",
},
body: JSON.stringify({
amountUsdc: "1.00",
txHash: "0xYOUR_SETTLED_TX",
timestamp: "2026-09-10T12:00:00.000Z",
payerWallet: "0xPAYER",
receiverWallet: "0xPAY_TO",
network: "eip155:1",
asset: "USDC",
scheme: "exact",
consumerCountry: "DE",
consumerName: "Buyer GmbH",
}),
});
if (!response.ok) {
const body = await response.json();
throw body.error ?? new Error(`Fiscal402 ${response.status}`);
}
const event = await response.json();
// event.id → GET /v1/receipts/:id
shell
export FISCAL402_ORIGIN="https://api.fiscal402.com"
export FISCAL402_MERCHANT_KEY="f402m_your_key_here"
curl -sS -X POST "$FISCAL402_ORIGIN/settlements" \
-H "Content-Type: application/json" \
-H "X-Fiscal402-Key: $FISCAL402_MERCHANT_KEY" \
-H "Idempotency-Key: acme-nl-de-1" \
-d '{
"amountUsdc": "1.00",
"txHash": "0xYOUR_SETTLED_TX",
"timestamp": "2026-09-10T12:00:00.000Z",
"payerWallet": "0xPAYER",
"receiverWallet": "0xPAY_TO",
"network": "eip155:1",
"asset": "USDC",
"scheme": "exact",
"consumerCountry": "DE",
"consumerName": "Buyer GmbH"
}'3. Read the result
201 means the event was recorded. custody is always none. PENDING_MANUAL_REVIEW is a result — Fiscal402 did not invent a VAT rate.
201.json
{
"id": "x402-4f9c1670",
"custody": "none",
"reviewStatus": "PROCESSED",
"classification": {
"regime": "OSS_B2C",
"taxCategoryCode": "S",
"ratePercent": 19,
"taxingCountry": "DE"
},
"fx": {
"pair": "USDC/EUR",
"netEur": "0.86",
"vatEur": "0.16"
}
}4. Fetch and verify the receipt
http
GET https://api.fiscal402.com/v1/receipts/rcpt_x402-4f9c1670
GET https://api.fiscal402.com/settlements/x402-4f9c1670/ubl
GET https://api.fiscal402.com/.well-known/jwks.jsonverify.ts
import { verifyReceipt } from "./verify"; // source only — not on npm
const report = await verifyReceipt({
receipt,
jwks,
ubl,
});
if (report.result !== "VERIFIED") {
throw new Error(report.code);
}
// VERIFIED = cryptographic integrity. Not tax-authority approval.
VERIFIED means cryptographic integrity under Fiscal402 rules. It does not mean tax-authority approval, a VAT return, or legal advice.
What just happened
x402 moved USDC. Fiscal402 stored settlement evidence, classified an EU OSS B2C pattern for a DE consumer, bound UBL 2.1 bytes, and signed fiscal402.receipt/1.0.0 with Ed25519. You can verify that object later with JWKS and the exact invoice bytes — without this API being up.
OSS is the EU One-Stop-Shop: destination VAT for intra-EU B2C digital services. Reverse charge is intra-EU B2B when a VAT ID is accepted. VIES is the EU VAT-number check. Place of supply is the tax jurisdiction, not the chain.
Idempotency
Send Idempotency-Key per settlement. The same key plus the same body returns the same fiscal event. The same key plus a different body is IDEMPOTENCY_CONFLICT (409). Do not retry 4xx validation blindly.
B2B with a VAT number
b2b.json
{
"amountUsdc": "1.00",
"txHash": "0xYOUR_SETTLED_TX",
"timestamp": "2026-09-10T12:00:00.000Z",
"payerWallet": "0xPAYER",
"receiverWallet": "0xPAY_TO",
"network": "eip155:1",
"asset": "USDC",
"scheme": "exact",
"consumerCountry": "DE",
"consumerVatNumber": "DE123456789",
"consumerName": "Buyer GmbH"
}
// Reverse charge only when the VAT ID is accepted. Unconfirmed IDs → PENDING_MANUAL_REVIEW.review.ts
if (event.reviewStatus === "PENDING_MANUAL_REVIEW") {
// No rate was invented. Complete attested buyer country / VAT ID and retry.
}
if (!response.ok && body.error?.code === "UNSUPPORTED_JURISDICTION") {
// Corridor is not a production engine. Do not treat as 0% VAT.
}Source client (not on npm)
Do not npm install @fiscal402/node. When you copy the source client, the typed surface is recordSettlement over POST /settlements.
source.ts
import { Fiscal402 } from "@fiscal402/node"; // source only — not on npm
const fiscal = new Fiscal402({
apiKey: process.env.FISCAL402_MERCHANT_KEY!,
origin: process.env.FISCAL402_ORIGIN ?? "https://api.fiscal402.com",
});
const result = await fiscal.recordSettlement({
amountUsdc: "1.00",
txHash: "0xYOUR_SETTLED_TX",
timestamp: "2026-09-10T12:00:00.000Z",
payerWallet: "0xPAYER",
receiverWallet: "0xPAY_TO",
network: "eip155:1",
buyer: { country: "DE", legalName: "Buyer GmbH" },
supply: { kind: "digital", description: "Inference units" },
idempotencyKey: "acme-nl-de-1",
});
console.log(result.id, result.reviewStatus);
Optional sandbox classify
POST /v1/compliance/evaluate classifies without writing a production settlement. Seller country may be anywhere; the buyer must be an attested EU customer for this EU VAT preview. Currency is USDC.
evaluate.http
POST https://api.fiscal402.com/v1/compliance/evaluate
Content-Type: application/json
{
"seller_country": "NL",
"buyer_country": "DE",
"buyer_type": "B2C",
"service_type": "DIGITAL_SERVICE",
"amount": "1.00",
"currency": "USDC",
"chain": "eip155:1",
"settled_at": "2026-09-10T12:00:00.000Z"
}