x402 facilitator (generic)
Add AgentTrust decisions to any x402 facilitator without coupling to one vendor.
If you already accept x402 payment requests and want AgentTrust decisions before settlement, this is the guide. Generic shape — facilitator-specific quirks live in the adapter, not in the route layer.
If you're using Pay.sh, start there. If you're adding a new facilitator from scratch, start with the adapter contract.
Integration points
| Step | Facilitator action | AgentTrust call |
|---|---|---|
| 1 | receive x402 request or retry proof | FacilitatorAdapter.parseRequest |
| 2 | translate into payer, payee, mint, amount, policy ID | VerifyContext |
| 3 | check policy | gate_payment simulation or gatePayment() |
| 4 | return x402 denial or validation need | adapter.formatChallenge |
| 5 | settle payment | one signed transaction (gate + transfer + feedback) |
| 6 | emit feedback | adapter.emitFeedback |
Minimal Express mount
The published SDK is the easy path:
import express from "express";
import { Keypair } from "@solana/web3.js";
import { mountTrustGate } from "@agenttrust-sdk/trustgate/express";
const app = express();
app.use(express.json());
await mountTrustGate(app, {
rpcUrl: process.env.SOLANA_RPC_URL!,
facilitatorKeypair: Keypair.fromSecretKey(/* facilitator key */),
defaultPolicyId: 1,
network: "solana-devnet",
atomicityEnforced: true,
});Routes added: POST /verify, POST /settle, POST /dispute, GET /receipt/:hash. Full reference: SDK → mountTrustGate.
Verify request shape
POST /verify
Content-Type: application/json
{
"payerAgentAsset": "<base58 pubkey>",
"payeeAgentAsset": "<base58 pubkey>",
"amount": "1000000",
"mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
"policyId": 1
}For Pay.sh, the adapter reads this context from paymentRequirements.extra.agentTrust, verifies serviceSignature, derives paymentIdHash from extra.memo, and rejects mismatched payTo / payeeRecipient.
Denial response
HTTP/1.1 402 Payment Required
X-Agent-Trust-Decision: Deny
X-Payment-Required: denied
X-Payment-Reason-Code: 6
X-Payment-Reason-Name: CounterpartyTierBelowMin
X-Payment-Network: solana-devnetReason codes are stable: 1..15. Full table: Reference → DenyReason codes.
Validation response
HTTP/1.1 402 Payment Required
X-Agent-Trust-Decision: RequireValidation
X-Payment-Required: validation
X-Capability-Required: 366c075140aa69746625d4b733b55e267fc5c28387fd6d1c24901976ee3ddc42
X-Payment-Network: solana-devnetThe X-Capability-Required value is the 32-byte SHA-256 hash of the capability namespace name (e.g., kyc.tier-1.v1). Full lifecycle: Capability namespaces.
Settlement rule
The facilitator must keep the policy gate, token transfer, and feedback emission in one signed Solana transaction. The SDK enforces this via the atomicityEnforced: true literal-type guard. Splitting them silently corrupts VelocityLedger when a Token-2022 mint's TransferHook extension reverts the transfer. Full proof: Verification → Atomic-tx invariant.
Sources
| File | Purpose |
|---|---|
trustgate/server/src/x402.ts | x402 wire envelope helpers |
trustgate/sdk/src/express.ts | Published Express middleware |
trustgate/server/src/facilitators/ | Per-facilitator adapter implementations |
Read next
Dexter adapter
In-flight worked example — the second AgentTrust facilitator adapter, used to prove portability of the FacilitatorAdapter contract.
Facilitator adapters
Add a new x402 facilitator without rewriting AgentTrust routes or policy. Five-method contract, registry-based dispatch, conformance-tested.