Payments

A session states an obligation. Everything after it is measured against that statement rather than inferred from chain traffic.

The payment surface is served on both api.stonereason.com and pay.stonereason.com. The OpenAPI 3.1 description is at GET /v1/openapi.json.

Open a session

curl -X POST https://api.stonereason.com/v1/payments/sessions \
  -H "authorization: Bearer $XRPC_KEY" \
  -H 'content-type: application/json' \
  -d '{
    "merchant_reference": "INV-2026-0912",
    "network": "base",
    "asset": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
    "asset_decimals": 6,
    "expected_amount": "12.50",
    "destination": "0x…your address…"
  }'

Required fields

FieldNotes
merchant_reference Your own reference. XRPC never parses it. Max 200 characters.
network One of the networks this deployment follows.
asset A contract address, a mint, or native. Never a tickerUSDT names a different contract on every network and more than one on some.
expected_amount As a person writes it: "12.50". More decimal places than the asset has is refused rather than rounded.
destination Where the money goes — your address. On networks where an address alone does not identify the payee, routing is also required.

Optional, and worth knowing

  • asset_decimals — required for an asset whose scale XRPC has not established. Without it the session is refused with DECIMALS_UNKNOWN, because an amount whose scale is unknown cannot be read.
  • amount_match_policyExact or AtLeast. AnyPositive is refused: a session that cannot be underpaid is not an obligation.
  • idempotency_key — the same key with the same request returns the same session; with a materially different request it is a 409.
  • expires_in_seconds — how long your offer stands. This is not how long XRPC watches.
  • metadata — up to 1 KB, opaque, echoed on every delivery.

The creation floor

The response carries the chain head read before the session existed:

"creation": {
  "chain_head":     51184176,
  "creation_floor": 51184176
}

A payment at or below the floor was on the chain before your offer existed and cannot pay for it. The floor is the maximum of what has been delivered, what the ingest has read, and the chain head — three positions that are never conflated. A delivery or webhook cursor is never used as a chain head.

If a network cannot establish a chain head safely, session creation on it is refused rather than given an invented floor.

Reconciliation: four standings

"reconciliation": {
  "amount":      { "standing": "NOTHING_SEEN" },
  "finality":    { "standing": "NOTHING_TO_SETTLE" },
  "observation": { "standing": "ESTABLISHED", "read_to_height": 51184180 },
  "timing":      { "standing": "NO_DEADLINE" }
}
  • amountNOTHING_SEEN, UNDERPAID, EXPECTED_AMOUNT_SEEN, OVERPAID.
  • finality — settlement in the chain's own vocabulary.
  • observation — whether XRPC is watching and to what height. If this is not ESTABLISHED, silence means nothing.
  • timing — whether your offer still stands.

may_release_goods combines all four. It is the only field that answers the merchant's real question, and it is false whenever part of the picture is missing.

Reading a session

EndpointReturns
GET /v1/payments/sessions Your sessions, scoped to the calling project.
GET /v1/payments/sessions/{id} The session, its reconciliation and may_release_goods.
GET /v1/payments/sessions/{id}/events Every step of every payment for this session.
GET /v1/payments/sessions/{id}/receipt The evidence receipt.
POST /v1/payments/sessions/{id}/cancel Withdraws the offer. Lifecycle becomes CANCELLED.

The evidence receipt

A receipt records what XRPC read, each entry named in the chain's own identity. It also states, in its own body, what it is not:

"basis": {
  "standing": "XRPC_OBSERVATION",
  "why_not_a_proof": "this is XRPC's record of what it read from the chain,
    not a proof from the chain. It carries no validator signature and no
    inclusion proof, and it cannot be checked by anybody who does not trust
    XRPC. To verify independently, take the native identity of each entry to
    any node or explorer for that network"
}

That sentence is the point of the receipt. It gives you everything needed to check the claim somewhere else.

Webhooks

  • Register destinations with POST /v1/webhooks. Up to 10 per project.
  • Deliveries are signed HMAC-SHA256. Verify the signature before trusting the body.
  • Delivery ids do not repeat, and resume survives a restart — watches are re-registered from their own creation height.
  • A webhook is a notification, not a source of truth. Re-read the session; the delivered position is not a chain position.

Free Beta limits

Reported by GET /v1/payments/beta: 1,000 watches per project, 200 active, 10 webhook destinations, light-path pass every 12 seconds with up to 200 sessions per pass.

Not offered: fiat settlement, custody, refunds, chargebacks, swaps and bridges. XRPC watches chains it cannot write to.