Proof Rail ← Back to home Customer sign in

Developer API

Issue your first proof in minutes.

API version v1 · JSON over HTTPS

01

Get sandbox credentials

Sign in to an approved customer account, open Developer access and create a key with environment “Sandbox”. The secret begins with rp_sandbox_ and is shown once. Sandbox records are excluded from billing and the public directory.

02

Create a record

POST /api/v1/records with Authorization: Bearer rp_sandbox_…, Content-Type: application/json and a unique Idempotency-Key.

{"subject":"Example Ltd","claim_type":"company_status","claim":"The company states that it is operational.","evidence_level":"participant_attested","visibility":"public"}

An agent authorization records that a program was allowed to act for a principal, within a limit, for a session. Send principal, agent_id and act; scope, session_id, valid_until and mandate_hash are optional. Never send keys, secrets or wallet material, send a hash of the instruction instead.

Register the public key your mandates are signed with before your agent acts: POST /api/v1/mandate-keys with key_id and a base64url public_key. The principal is taken from your API key, never from the body. Send only the public half: Proof Rail must never hold the key you sign with. Read one back at GET /keys/:key_id, which is public so a relying party can repeat the signature check itself.

Omit evidence_level and the record issues as participant_attested: the principal told us what its own agent was allowed to do, and nobody checked. A mandate is not public, so a source name and URL cannot raise that: above participant_attested an authorization must carry the signed mandate itself, as mandate_hash, mandate_signature and principal_key_id. Anything else would print "checked" about something only you can see.

{"subject":"Alpine Gate payout bot","claim_type":"agent_authorization","claim":"This agent was authorized to send a USDC payout for Alpine Gate, up to 125000 USDC, for this session.","evidence_level":"participant_attested","visibility":"public","principal":"Alpine Gate","agent_id":"ag-payout-bot-v1","act":"usdc_payout","scope":{"asset":"USDC","max_amount":"125000","network":"ethereum"},"session_id":"sess_example_001","mandate_hash":"sha256:example"}

02b

Prove something against a source

Send "evidence_level":"source_verified" and Proof Rail checks the claim itself where a connector exists; the certificate then names the source, the URL and the time. Claim types with a connector today:

Company identity · company_identity with "evidence":{"lei":"HWUPKR0MPOU8FGXBT394"}, checked against the GLEIF LEI register.

On-chain transaction · evm_transaction with "evidence":{"network":"ethereum","tx_hash":"0x…"}, optionally expected_from and expected_to. Networks: ethereum, base, polygon, arbitrum, optimism, bsc. The receipt must be confirmed and successful.

Domain ownership · first call POST /api/v1/domain-challenges with {"domain":"example.com"}. It returns a token and the DNS TXT record to publish at _rail-proof-challenge.example.com. Once it resolves, send the domain_ownership record it hands back. Keep the TXT record published; it is what anyone can re-check.

Wallet ownership · first call POST /api/v1/wallet-challenges with {"address":"…","network":"ethereum"}. It returns a message to sign; sign it with that wallet and put the signature into the wallet_ownership record it hands back. Proof Rail confirms the signature was made by the key behind that address, proof of key control, not just a past transaction. Networks: EVM (ethereum, base, polygon, arbitrum, optimism, bsc, one signature proves the address on every EVM chain; personal_sign), bitcoin (your wallet's "Sign message", Electrum, Sparrow, Bitcoin Core, Ledger; legacy 1…, 3… and bc1q… addresses; taproot bc1p… is not supported yet) and solana (signMessage, Phantom, Solflare, Backpack). Bitcoin and Solana signatures are checked entirely inside Proof Rail.

Verified listing · verified_listing binds a rental or service listing to a licensed operator, a verified website and a verified payee, so a customer can check all three at /check before paying. Evidence: {"operator":"Desert Camp LLC","listing_url":"https://desertcamp.ae/chalet-12","domain":"desertcamp.ae","licence":{"authority":"DCT Abu Dhabi","number":"HH-12345"},"payee":{"type":"iban","value":"AE07…","account_name":"Desert Camp LLC","bank_name":"FAB"},"licence_record":"RP-…","domain_record":"RP-…"}. It is a composite: licence_record must be an active company-identity or licence record for the same operator, domain_record an active domain_ownership record for that domain, and a wallet payee ({"type":"wallet","network":"ethereum","value":"0x…"}) must cite a payee_record, an active wallet_ownership record for that address. When every part is on record the listing issues at once as source_verified; a bank-account payee, or a licence given only as a number, goes to a reviewer (202 pending_review) and issues as evidence_verified once confirmed from a bank letter or the authority's register. The public lookup matches exactly on IBAN, wallet, website, listing link or licence number, and only against active records.

Any other claim type with a source_name, source_url and checked_at is accepted as 202 pending_review and issued after a human reviewer confirms it. A connector that cannot reach its source does the same rather than failing you.

03

Issue a validated batch

POST /api/v1/records/batch accepts {"records":[{...},{...}]} with the same authorization headers and one batch-level Idempotency-Key. Up to 250 records are accepted per request.

Every record is validated before issuance begins. Invalid batches return row-level errors and issue nothing. Successful responses report issued, existing, pending-review and failed totals with a result for every row. Replaying the same batch key does not create duplicates. The customer dashboard also provides a CSV template, validation preview and explicit confirmation step.

04

Bybit Verified Trade: live & test pilot

Live · claim type bybit_verified_trade with environment: mainnet and a read-only Bybit mainnet key reconciles one closed trade against the exchange and issues a real, source-verified certificate. Live verification is off until enabled for your account; the test pilot below needs no enablement.

Test pilot · use a sandbox Proof Rail key and POST /api/v1/records with claim type bybit_verified_trade_test. The customer dashboard provides an internal synthetic simulator. Direct API evidence can use environment: testnet with a permitted read-only Bybit Testnet key and secret, derivatives category, closed order ID, and a lookup window no longer than seven days.

Proof Rail checks the key through Bybit, rejects write-enabled or withdrawal-capable credentials, retrieves the closed-P&L record and executions, removes the credentials before persistence, and forces the resulting certificate into the sandbox. This pilot verifies one trade only, not complete performance. Never send production exchange credentials to the test pilot.

05

Response and verification

A successful request returns the verification ID, public certificate URL, QR URL, signature-verification URL, lifecycle and integrity object. Basic creation returns HTTP 201; an idempotent replay returns HTTP 200.

GET /api/v1/records/:id retrieves a record. GET /api/v1/records/:id/signature verifies its Ed25519 signature. POST /api/v1/records/:id/lifecycle suspends, revokes, expires or supersedes a record with a reason.

06

Webhooks, errors and limits

POST /api/v1/webhooks registers an HTTPS destination. Events are signed, retried with backoff and disabled after repeated failures. API requests are rate limited; clients should retry HTTP 429 and transient 5xx responses with exponential backoff.

Common responses: 401 invalid key, 402 plan inactive or limit reached, 422 invalid claim or missing evidence, 429 rate limit exceeded. Never place API keys in browser code, URLs or public repositories.

Evidence methodologyOpen customer dashboard ↗