Reading configuration
Route value from an X identity into any supported asset.
These endpoints are the ones this deployment serves. Where an endpoint needs a key it says which one and answers 503 until it is set.
Record deposit
POST/api/v1/deposits
Request
{
"tx": "0x085d251ede4d29ea14887bcd00bd850fc3148411037377fc05b24ee6a7f70fcd"
}
Response
{
"ok": true,
"duplicate": false,
"deposit": {
"tx": "0x085d251ede4d29ea14887bcd00bd850fc3148411037377fc05b24ee6a7f70fcd",
"x_handle": "relayuser",
"eth_amount": "0.05",
"amount": "119.83",
"eth_usd": 2396.5,
"round_id": "18446744073709562891",
"price_stale": false,
"block": 64162143
},
"claim": { "id": "clm_6f1c2a0b9d4e7a1c83", "source": "deposit", "status": "available" }
}
No key. The only field read from the body is
tx. The recipient, the amount and the handle are read from chain: the transfer has to be addressed to RELAY_WALLET, the receipt has to be a success, and the calldata has to decode to the UTF-8 bytes of xrelay:@handle. A refusal names itself in reason: unknown-tx, not-mined, reverted, wrong-recipient, no-memo, bad-prefix, bad-handle, zero-value. Posting the same hash again returns the first claim with duplicate: true.Create claim
POST/api/v1/claims
Request
{
"x_account_id": "1450000000000000000",
"x_handle": "relayuser",
"amount": "250.00",
"currency": "USD",
"source": "partner_payout",
"memo": "March creator pool"
}
Response
{
"claim": {
"id": "clm_6f1c2a0b9d4e7a1c83",
"x_account_id": "1450000000000000000",
"amount": "250.00",
"currency": "USD",
"status": "available",
"created_at": "2026-09-16T02:14:51.201Z",
"settled_tx": null
}
}
Send the relay key in the
x-api-key header. Without RELAY_API_KEY set the endpoint answers 503 and says so.Resolve identity
GET/api/identity
Request
No body. The session cookie carries the identity.
Response
{
"signedIn": true,
"configured": true,
"resolvedBy": "session",
"identity": { "id": "1450000000000000000", "handle": "relayuser" },
"totals": { "available": 250, "claimed": 0, "pending": 0 },
"claimCount": 1
}
Without a session the response carries
signedIn: false and the reason.Get supported assets
GET/api/assets?category=stocks&q=nvda
Request
No body.
Response
{
"counts": { "all": 205, "stocks": 188, "commodities": 3, "rwas": 3, "crypto": 11 },
"feedCount": 52,
"ethUsd": 2396.5,
"assets": [{
"symbol": "NVDA",
"name": "NVIDIA",
"address": "0xd0601CE157Db5bdC3162BbaC2a2C8aF5320D9EEC",
"decimals": 18,
"category": "stocks",
"kind": "stock-token",
"price": 211.92,
"priceSource": "chainlink",
"issuer": "Robinhood Assets (Jersey) Limited"
}]
}
The registry is cached for 60 seconds per function instance.
Get quote
GET/api/quote?mode=wallet&from=ETH&to=NVDA&amount=0.01&wallet=0x
Request
No body.
Response
{
"ok": true,
"mode": "wallet",
"venue": "uniswap-v3",
"feeTier": 500,
"pool": "0x62ab521f71431f78ac374cdbadc6cda3c8916b6c",
"amountIn": 0.01,
"amountOut": 0.113078,
"minReceived": 0.110817,
"steps": [{ "kind": "send", "to": "0xcaf681...", "data": "0x04e45aaf...", "value": "0x2386f26fc10000" }]
}
A pair with no pool answers
ok: false with the reason no pool yet.Execute swap
POST/api/execute
Request
{
"mode": "wallet",
"wallet": "0x0000000000000000000000000000000000000000",
"from": "ETH",
"to": "NVDA",
"amount": "0.01"
}
Response
{
"ok": true,
"signer": "wallet",
"chainId": 4663,
"steps": [{ "kind": "send", "to": "0xcaf681...", "data": "0x04e45aaf...", "value": "0x2386f26fc10000" }]
}
Wallet mode returns the unsigned steps and the browser signs them. Claim mode needs RELAY_WALLET_KEY and answers 503 until it is set.
Settle claim
POST/api/execute
Request
{
"mode": "claim",
"claim_id": "clm_6f1c2a0b9d4e7a1c83",
"to": "NVDA",
"destination_wallet": "0x0000000000000000000000000000000000000000"
}
Response
{
"ok": true,
"signer": "relay-wallet",
"claim_id": "clm_6f1c2a0b9d4e7a1c83",
"status": "claimed",
"settled_tx": "0x...",
"settled_asset": "NVDA",
"settled_amount": "0.565390",
"explorer": "https://robinhoodchain.blockscout.com/tx/0x..."
}
Needs a signed X session that owns the claim, plus RELAY_WALLET_KEY on the deployment. The destination wallet is the only user-typed value that reaches a transaction; the destination asset is looked up in the registry by symbol. A refusal names itself:
no-session, bad-destination, relay-wallet-required, unknown-claim, claim-not-available, claim-busy, tx-cap, day-cap, unknown-asset, stale-feed, no-pool, thin-pool, no-curve-quote, no-code, insufficient-funds, simulation-reverted, gas-estimate-failed, send-failed, reverted. WETH as a destination is delivered as ETH. The caps are RELAY_MAX_TX_USD and RELAY_MAX_DAY_USD, enforced in the send path and published by /api/status.Reconcile settling claims
GET/api/v1/claims/reconcile?key=
Request
No body. Send the relay key in x-api-key.
Response
{
"checked": 2,
"resolved": [
{ "claim_id": "clm_6f1c2a0b9d4e7a1c83", "tx": "0x...", "status": "claimed" },
{ "claim_id": "clm_9b2d41e7ca0f8813", "tx": "0x...", "status": "available", "reason": "receipt status 0" }
]
}
A settlement writes
settling with its transaction hash before the send, so a crash between the send and the ledger write is recoverable. This endpoint re-checks every settling claim against chain and moves it to claimed or back to available.Read the audit log
GET/api/audit?date=2026-09-16
Request
No body. Send the relay key in x-api-key.
Response
{
"date": "2026-09-16",
"count": 2,
"lines": [
{ "at": "2026-09-16T02:14:51.201Z", "event": "refused", "code": "tx-cap", "claim_id": "clm_...", "amount": "900.00" },
{ "at": "2026-09-16T02:19:02.884Z", "event": "settle.sent", "claim_id": "clm_...", "tx": "0x...", "fee_eth": "0.000104" }
]
}
Append-only JSONL, one file per day. Every decision is written, including refusals. The relay key is never written and neither is the session cookie.
Get transaction
GET/api/transaction?hash=0x
Request
No body.
Response
{
"hash": "0x085d251ede4d29ea14887bcd00bd850fc3148411037377fc05b24ee6a7f70fcd",
"status": "confirmed",
"from": "0x...",
"to": "0x...",
"value": 0.02,
"block": 64162143,
"explorer": "https://robinhoodchain.blockscout.com/tx/0x085d25..."
}
Read straight from the chain with eth_getTransactionReceipt.
Webhook status
POST/api/webhook
Request
{
"type": "claim.settled",
"id": "clm_6f1c2a0b9d4e7a1c83",
"tx": "0x..."
}
Response
{
"received": true,
"type": "claim.settled",
"at": "2026-09-16T02:20:04.117Z"
}
Signed with an HMAC SHA-256 of the raw body in
x-relay-signature. Needs RELAY_WEBHOOK_SECRET and answers 503 until it is set.Environment
X_CLIENT_ID, X_CLIENT_SECRET, X_REDIRECT_URI, SESSION_SECRETSign in with X
BLOB_READ_WRITE_TOKENClaim ledger
RELAY_API_KEYClaim creation
RELAY_WALLETDeposit address, relays scope
RELAY_WALLET_KEYClaim settlement
RELAY_MAX_TX_USDPer-transaction cap, default 500
RELAY_MAX_DAY_USDPer-day cap, default 5000
RELAY_WEBHOOK_SECRETWebhook delivery