AgentNOMOS
Live on XRPL x402

Governance preflights for AI agents before consequential actions.

Two paid endpoints on XRPL Mainnet. Your agent asks before it acts, pays 0.001 XRP for the answer with x402, and gets back a decision it can keep — no account, no subscription, no key ever leaving your side.

Governed XRPL Payment Preflight

0.001 XRP per call · 1000 drops

Your agent is about to send an XRPL payment. Describe it first — destination, amount, asset, network — and get back a governed decision: ALLOW, REVIEW or BLOCK, together with the policy and the authority that produced it, and a receipt bound to the exact request. Then you decide whether to sign.

Real response · abridged · our mainnet canary
{
  "decision": "ALLOW",
  "reason_codes": [],
  "policy_result": {
    "policy_id": "nxp-policy-v0.1",
    "outcome": "ALLOW"
  },
  "authority_result": {
    "authorized": true,
    "human_gate_required": false
  },
  "evidence_id": "ev_d07284d95f0e5371",
  "receipt": {
    "receipt_id": "pfr_7e8125e7df9a1798",
    "request_digest": "sha256:563465d9…",
    "fee_settlement": {
      "transaction": "C3BAE6D2…F29D532"
    },
    "receipt_hash": "sha256:d4507b21…"
  }
}

Cross-Border Compliance Preflight

0.001 XRP per call · 1000 drops

Your agent is about to move data across a border. Describe the transfer — source regime, target regime, data category — and get back an advisory classification: the verdict, whether standard contractual clauses are indicated, whether a human has to look at it, and an evidence reference you can keep.

Real response · abridged · our mainnet canary
{
  "preflight_summary": {
    "verdict": "REVIEW",
    "adequacy_status": "unknown",
    "risk_level": "low",
    "sccs_required": true,
    "requires_human_review": true,
    "note": "Jurisdiction pair not in
              matrix. Manual review required.",
    "evidence_hash": "sha256:49836a13…"
  },
  "receipt_hash": "sha256:a8e481b3…",
  "disclaimer": "Preflight classification only.
     Not legal advice. …"
}
Not legal advice. Preflight classification only — not a legal compliance certificate. Engage qualified counsel before an actual data transfer.
How one call works

Ask, pay, get an answer you can keep

x402 turns HTTP 402 into a real payment step. The first request is free and returns the price; the second carries the proof of payment.

01 · free

POST your intent

No payment header yet. You get 402 with an accepts[0] challenge: where to pay, how much, and a fresh invoiceId.

02 · 1000 drops

Pay and sign yourself

A tiny XRPL payment bound to that invoice. You sign it with your own wallet — we never see a key or a seed.

03 · answer

Repeat with the proof

Same request plus an X-PAYMENT header. You get 200, the decision and a receipt. Replaying the header returns the same result rather than charging twice.

# 1 — ask for the price. Free, and it costs you nothing to try right now.
curl -sS -i -X POST https://agentnomos.com/xrpl-agentic-payments/api/x402/preflight \
  -H 'content-type: application/json' \
  -d '{"intent":{"network":"xrpl:0",
        "destination":"rhteihAJz1KsY6GpWPEc9Jo1W9qrqg1z1i",
        "amount":"5000",
        "invoice_id":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
        "source_tag":1}}'

# -> HTTP/1.1 402 Payment Required
#   accepts[0] = { "scheme":"exact", "network":"xrpl:0", "asset":"XRP",
#                  "amount":"1000", "payTo":"rhtei…z1i", "maxTimeoutSeconds":600,
#                  "extra":{ "invoiceId":"…", "sourceTag":804681468 } }
#
# The intent above is the payment you are ASKING ABOUT — destination, amount and
# source_tag are yours and are echoed back untouched. The 1000 drops in accepts[]
# are the fee for the check itself. Two different payments; never conflate them.

# 2 — pay those 1000 drops on XRPL and build the payload (see Python / TypeScript)

# 3 — same request, now with the proof
curl -sS -i -X POST https://agentnomos.com/xrpl-agentic-payments/api/x402/preflight \
  -H 'content-type: application/json' \
  -H "X-PAYMENT: $PAYMENT" \
  -d "$INTENT"

# -> HTTP/1.1 200 OK
#   PAYMENT-RESPONSE: base64({"network","payer","success","transaction"})
#   body: { "decision": "ALLOW" | "REVIEW" | "BLOCK", …, "receipt": { … } }

# The cross-border endpoint speaks the same protocol, different body:
curl -sS -i -X POST https://agentnomos.com/api/x402/cross-border-preflight \
  -H 'content-type: application/json' \
  -d '{"source_regime":"EU","target_regime":"US",
        "data_category":"personal_data","max_results":1}'
# pip install xrpl-py requests
import base64, hashlib, json, math, requests
from xrpl.clients import JsonRpcClient
from xrpl.wallet import Wallet
from xrpl.models.transactions import Payment, Memo
from xrpl.transaction import autofill_and_sign
from xrpl.core.binarycodec import encode
from xrpl.models.requests import Ledger

ENDPOINT = "https://agentnomos.com/xrpl-agentic-payments/api/x402/preflight"
INTENT = {"intent": {"network": "xrpl:0",
                     "destination": "rhteihAJz1KsY6GpWPEc9Jo1W9qrqg1z1i",
                     "amount": "5000",
                     "invoice_id": "A" * 64,
                     "source_tag": 1}}

# -- 1. free request -> the 402 challenge ------------------------------
r = requests.post(ENDPOINT, json=INTENT, timeout=30)
assert r.status_code == 402, r.status_code
acc = r.json()["accepts"][0]
invoice_id = acc["extra"]["invoiceId"]

# -- 2. bind an XRPL payment to that invoice, sign it locally ---------
#      InvoiceID = sha256(invoiceId) ; memo = hex(invoiceId). Both are required.
client = JsonRpcClient("https://xrplcluster.com")
wallet = Wallet.from_seed("sEd…")          # your seed. It stays here.
latest = client.request(Ledger(ledger_index="validated")).result["ledger_index"]

tx = Payment(
    account=wallet.classic_address,
    destination=acc["payTo"],
    amount=acc["amount"],                                # "1000" drops, a string
    source_tag=acc["extra"]["sourceTag"],                 # the facilitator's, not yours
    invoice_id=hashlib.sha256(invoice_id.encode()).hexdigest().upper(),
    memos=[Memo(memo_data=invoice_id.encode().hex().upper())],
    last_ledger_sequence=latest + math.ceil(acc["maxTimeoutSeconds"] / 5) + 2,
)
signed = autofill_and_sign(tx, client, wallet)
blob = encode(signed.to_xrpl())

# -- 3. same request, now with the proof ------------------------------
payload = {"x402Version": 2,
           "accepted": {k: acc[k] for k in ("scheme", "network", "amount", "asset",
                                            "payTo", "maxTimeoutSeconds", "extra")},
           "payload": {"invoiceId": invoice_id, "signedTxBlob": blob}}
header = base64.b64encode(
    json.dumps(payload, sort_keys=True, separators=(",", ":")).encode()).decode()

r = requests.post(ENDPOINT, json=INTENT, headers={"X-PAYMENT": header}, timeout=90)
out = r.json()

# -- 4. the only line that matters ------------------------------------
if out.get("decision") != "ALLOW":
    raise SystemExit(f"not signing: {out.get('decision')} {out.get('reason_codes')}")
# … only now build and sign the payment you actually intended to make.
// npm i xrpl
import { Client, Wallet, encode } from "xrpl";
import { createHash } from "node:crypto";

const ENDPOINT = "https://agentnomos.com/xrpl-agentic-payments/api/x402/preflight";
const INTENT = { intent: { network: "xrpl:0",
                          destination: "rhteihAJz1KsY6GpWPEc9Jo1W9qrqg1z1i",
                          amount: "5000",
                          invoice_id: "A".repeat(64),
                          source_tag: 1 } };
const post = (h = {}) => fetch(ENDPOINT, { method: "POST",
  headers: { "content-type": "application/json", ...h },
  body: JSON.stringify(INTENT) });

// -- 1. free request -> the 402 challenge ------------------------------
const challenge = await post();
if (challenge.status !== 402) throw new Error(`expected 402, got ${challenge.status}`);
const acc = (await challenge.json()).accepts[0];
const invoiceId: string = acc.extra.invoiceId;

// -- 2. bind an XRPL payment to that invoice, sign it locally ---------
const client = new Client("wss://xrplcluster.com");
await client.connect();
const wallet = Wallet.fromSeed("sEd…");            // your seed. It stays here.
const latest = await client.getLedgerIndex();

const prepared = await client.autofill({
  TransactionType: "Payment",
  Account: wallet.classicAddress,
  Destination: acc.payTo,
  Amount: acc.amount,                                // "1000" drops, a string
  SourceTag: acc.extra.sourceTag,                    // the facilitator's, not yours
  InvoiceID: createHash("sha256").update(invoiceId).digest("hex").toUpperCase(),
  Memos: [{ Memo: { MemoData: Buffer.from(invoiceId).toString("hex").toUpperCase() } }],
  LastLedgerSequence: latest + Math.ceil(acc.maxTimeoutSeconds / 5) + 2,
});
const { tx_blob } = wallet.sign(prepared);
await client.disconnect();

// -- 3. same request, now with the proof.
//      Keys sorted, no spaces — the header is canonical JSON.
const payload = { accepted: Object.fromEntries(
    ["scheme", "network", "amount", "asset", "payTo", "maxTimeoutSeconds", "extra"]
      .map(k => [k, acc[k]])),
  payload: { invoiceId, signedTxBlob: tx_blob },
  x402Version: 2 };
const canon = (v: any): string => Array.isArray(v) ? `[${v.map(canon).join(",")}]`
  : v && typeof v === "object"
    ? `{${Object.keys(v).sort().map(k => JSON.stringify(k) + ":" + canon(v[k])).join(",")}}`
    : JSON.stringify(v);
const header = Buffer.from(canon(payload)).toString("base64");

const out = await (await post({ "X-PAYMENT": header })).json();

// -- 4. the only line that matters ------------------------------------
if (out.decision !== "ALLOW") {
  throw new Error(`not signing: ${out.decision} ${out.reason_codes}`);
}
// … only now build and sign the payment you actually intended to make.
Use case · check before signing

The preflight is only worth it if it can stop you

A governance call that you ignore when the answer is inconvenient is decoration. The whole value is in one branch of one if.

  agent decides to pay
          |
          v
  +-----------------------+
  | 1. POST the intent    |   no X-PAYMENT header yet
  +-----------+-----------+
              |
              v
          HTTP 402  -->  payTo . amount . invoiceId . sourceTag
              |
              v
  +-----------------------+
  | 2. pay 1000 drops     |   you sign it; your key never leaves your side
  +-----------+-----------+
              |
              v
  +-----------------------+
  | 3. repeat with proof  |   X-PAYMENT: base64(canonical payload)
  +-----------+-----------+
              |
              v
          HTTP 200  -->  decision + receipt
              |
      +-------+--------+
      |                |
      v                v
   ALLOW            anything else
      |                |
      v                v
  sign the real    do NOT sign - escalate to a human,
  payment          keep the receipt as the reason

Only decision == "ALLOW" may lead to a signature. Treat REVIEW, BLOCK, a timeout, a transport error and a missing decision field alike: do not sign. A preflight that fails open is not a preflight.

Full agent flow & invoice binding

Evidence

A receipt someone else can check

Every paid call returns a receipt. It carries the digest of the exact request, the decision, the policy and authority behind it, and the on-ledger hash of the fee you paid — so a third party can verify the payment happened without taking our word for it.

Our own mainnet canary · 2026-08-03
schema
nomos.xrpl.preflight.receipt.v0.1
decision
ALLOW
fee settled
1000 drops (0.001 XRP)
settlement tx
C3BAE6D20DA99058598F91EA7E0CFF8A0B8CC0B5E3683674F24FF7731F29D532
ledger
106039516 · validated · tesSUCCESS

Those are facts about our own first paid call, not about yours. The fee payment sits on the public ledger — check it yourself rather than trust this page. The receipt that call produced is internal evidence and is not published.

The file below is a synthetic example of the receipt shape: placeholders throughout, no real call, no on-ledger execution. Its receipt_hash is computed from exactly that file with the production formula, and the file ships the one-line command to recompute it.