ReinkeyDocsConsole

Facilitator API

The facilitator is a plain HTTP service. The live, always-current reference is its OpenAPI document: openapi.json. Agents can start from llms.txt. This page is a map.

The wire format itself — payment requirements, the payload, the voucher bytes, the verification rules — is written down separately in the open channel scheme specification, so another implementation can interoperate without reading this code.

Amounts are decimal strings in base units. Errors use the shape described in Reason codes: error is a stable identifier you may branch on, message is informational text you must not parse.

Service

GET /health{ ok, chainMode, latestLedger, db, demoControls } plus the price source's state. Never rate-limited, so it is safe as a liveness probe.
GET /demo/infoThe only source of contract identifiers. Network and passphrase, rpcUrl, explorerTxBase, channelContract, usdc, xlm, dexRouter, dexFactory, dexPair, accountWasm (the hash of the deployed account code, which the console deploys for you), seller, the demo account, and the demo seller's prices. Read this at startup instead of hard-coding ids.

x402 facilitator

GET /supportedSchemes and networks this facilitator handles.
POST /verifyVerify a payment. For channel, a valid voucher is accepted: the channel's highest accepted cumulative advances. Returns { isValid, receipt } or { isValid: false, invalidReason }.
POST /settleQueue an accepted voucher for settlement.
JSON
{
  "paymentPayload": { "x402Version": 2, "accepted": { "scheme": "channel" }, "payload": { "channelId": "7", "cumulative": "145000", "signature": "…" } },
  "paymentRequirements": { "scheme": "channel", "payTo": "G…", "amount": "5000", "unit": "request", "resource": "https://…" }
}

Channels

GET /channelsKnown channels, newest first.
GET /channels/:idDeposit, claimed, last accepted, remaining, expiry.
POST /channels/:id/voucherNext voucher for a running stream (streamId, cumulative, signature).
POST /channels/:id/claimSettle now. Callable by anyone; pays only the channel's payee.

Streams (external sellers)

The facilitator holds stream sessions so a seller can wait for the next voucher where vouchers are verified. @reinkey/meter's rk.stream() wraps these.

POST /streamsOpen a session after the first slice was verified: { channelId, payTo, resource, unit, sliceCost, initialCharge } → { streamId, requiredCumulative, voucherUrl }. At most 4 open sessions per channel.
POST /streams/:id/waitLong-poll for the next voucher (timeoutMs ≤ 30000). Returns { kind: "paid", receipt, requiredCumulative }, or kind = timeout / exhausted / frozen / aborted.
DELETE /streams/:idEnd the session: { reason, units } → { charged, vouchers }; emits stream.ended. Idle sessions are closed after 90 s.

Accounts and audit

GET /accounts/:addressowner (the G… key that may change the policy), the policy — agentKey, asset, perTxCap, dailyCap, payees, channel, expiresLedger, dexRouter, dexFactory, pairs as readable names, pairIds as the contract addresses behind them, and controller — plus spentToday, day, frozen, the USDC and XLM balances and the open channels.
GET /accounts/:address/ledgerThe account's events, newest first, cursor-paginated (?cursor=, ?limit= up to 200).
GET /eventsServer-sent events: the last 200 events, then live. Filter with ?account= or ?channelId=.
GET /statsTotals: vouchers, on-chain transactions, volume, median verification latency.
POST /v1/reportReport an on-chain transaction; verified against the chain before it is published. An unverifiable report is REPORT_NOT_VERIFIED.

pairIds exists because pairs is for reading and pairIds is for writing: rewriting a policy needs the contract addresses, not "USDC→XLM".

Receipts

A receipt is the proof of what a payment bought. It is signed by the facilitator's key and commits to the request hash and, once the seller attests it, the response hash. Verification needs only the signer's public key — the same check runs in the browser or offline.

GET /receiptsReceipts, newest first. ?payee=, ?channelId=, ?limit= (default 50, max 200).
GET /receipts/:idOne receipt with its signature.
GET /receipts/:id/verify{ valid, signer, receipt }. A convenience: you never have to take the server's word for its own signature.
POST /receipts/:id/attestThe seller commits to the hash of the response it delivered: { "responseHash": "<64 hex>" }. The facilitator never sees the response; it only records the hash and refuses to let it change.

Exchange

We run no liquidity pool and never hold funds. Trades settle in the liquidity that already exists on Stellar (Soroswap); what this layer adds is the route, the slippage floor the contract insists on, and — the part nobody else can give an agent — whether its own account will accept the trade.

GET /dex/quoteside (USDC_XLM | XLM_USDC), amountIn in base units, optional account and slippageBps (default 100 = 1%). Returns the output from the pool's constant-product curve, the execution price against the pool mid, the price impact in basis points, a suggested minOut, the pool reserves, and the exact router call the agent would sign.

With account, the response carries a policy verdict: the same rules the account contract applies in __check_auth (check_swap plus the caps), evaluated before anything is signed.

JSON
"policy": {
  "allowed": false,
  "code": "PER_TX_CAP_EXCEEDED",
  "message": "Above the per-transaction cap (10000000 base units)",
  "caps": { "perTxCap": "10000000", "dailyCap": "50000000", "spentToday": "20000000",
            "remainingToday": "30000000", "counted": "20000000" }
}

Branch on code, never on message. Codes are stable identifiers; the message beside them is informational, may be reworded at any time, and is currently written in Turkish by the hosted facilitator. The same rule applies to every error / message pair on this page.

The verdict is a preview; the chain is what decides. It exists so an agent can find out it would be rejected without paying a fee to learn it. Prices are reported from two sources: the Soroswap pool mid, and the conservative price the credit pool uses (the lower of the Reflector oracle and the pool), so a quote and a loan valuation can never disagree silently.

Seller finance (Meter)

Derived from the audit ledger, so the report and the ledger can't disagree. Three numbers are kept apart: earned (accepted vouchers), settled (claimed on-chain), receivable (the difference).

GET /sellers/:payTo/revenueTotals (earned, settled, receivable, payments, settlements, payments per settlement, buyers), revenue by resource, a time series (?bucket=hour|day, ?days= 1–365), receivables aging per channel, and the settlement list.
GET /sellers/:payTo/settlements.csvAccounting export: one row per on-chain settlement (time, tx, channel, payer, amount, payments covered, explorer link).

Float (credit pool)

The HTTP API is read-only: these endpoints read the contract by simulation and never submit a transaction. Depositing and withdrawing are wallet-signed contract calls in the console. See Float.

GET /floatPool summary (size, share price, utilization), known credit lines with health, investor positions, share-price history.
GET /float/lines/:accountOne account's line: debt, value, healthBps, liquidatable.
GET /float/positions/:addressAn investor's shares and their USDC value.

Discovery

GET /discovery/resourcesx402 Bazaar catalog. Every resource that has received at least one verified payment through this facilitator, with its 402 terms (accepts) and extensions.bazaar input/output metadata. Query: payTo, limit, offset. Response: { x402Version, items[], pagination }. There is no registration endpoint: a verified payment is the listing.
GET /openapi.jsonOpenAPI 3 document.
GET /llms.txtPlain-text guide for language models.
POST /mcpModel Context Protocol endpoint, read-only: reinkey_list_resources (the catalog), reinkey_price_list, reinkey_get_channel, reinkey_get_account, reinkey_stats, reinkey_supported. For tools that actually pay, run @reinkey/mcp beside your assistant.

Events

Each event has id, type, source and ts. Types: channel.opened, channel.topped_up, voucher.accepted, voucher.rejected, channel.claimed, channel.closed, payment.exact, chain.rejected, stream.started, stream.ended, dex.swapped, account.frozen.

Reinkey runs on Stellar testnet. The contracts are unaudited. Don't send mainnet funds.