Rails API
Invoice in dollars, settle in stablecoin — over a clean REST API. Create an invoice, collect by ACH, and receive net USDT/USDC to your wallet, with screening and reconciliation built in.
Quickstart
Every request is JSON over HTTPS and authenticated with your tenant API key. Create an invoice, and Rails issues the ACH collection and drives settlement — you receive webhook events as state changes.
# 1 · create an invoice curl -X POST https://rails-api.infinihash.com/api/v1/invoices \ -H "Authorization: Bearer $IHB_KEY" \ -H "Content-Type: application/json" \ -d '{ "number": "INV-2001", "customer": "Northgate Wholesale", "terms": "Net 14", "lineItems": [ { "description": "Wholesale order", "quantity": 1, "unitCents": 204120 } ] }'
Authentication
Pass your tenant API key as a bearer token on every request. Keys are per-tenant and issued by Infinihash once your business is verified; if you do not have one yet, apply for access (a person reviews every application). Treat them as secrets and rotate them if exposed.
Authorization: Bearer ihb_live_xxxxxxxxxxxxxxxxxxxxxxxx
Sandbox
The sandbox is a separate instance at https://rails-sandbox.infinihash.com, running the same application code as production with a mock settlement adapter and mock screening. Payments, screening, settlement and webhooks all execute end-to-end against those mocks; no real money moves and no email is sent. It has its own base URL, its own database and its own keys — a sandbox key will not authenticate against production, and vice versa. Sandbox keys are not self-serve: they are issued on request after a person reviews your onboarding application (a form, not an email thread; we aim to reply within two business days). Until you have a key you can read this documentation, the sandbox’s public reference and /api/v1/capabilities, and try the live demo. Moving to production afterwards is a change of base URL and key. One deliberate difference from production: the sandbox screens through a mock KYT adapter, not the live vendor — /health reports kyt.mock: true there and false in production. It is deterministic rather than permissive: a destination address containing BLOCKED, SANCTION or OFAC is declined, so you can exercise a real fail-closed decline before you go live. Settlement in the sandbox goes through the mock adapter, not the Bridge rail (/health reports the adapter in use); invoicing, the API surface, webhooks and reserves run the production application code.
GET /api/v1/capabilities on whichever instance you are pointed at. It reports, from that deployment's running settlement adapter, which operations it can actually serve and the exact error code for those it cannot.GET /api/v1/capabilities — it reports, from the running settlement adapter, which operations that deployment can actually serve, and the exact error code for those it cannot.Invoices
Invoices are the entry point. Create one with line items; Rails computes totals, generates a branded PDF, and issues the ACH collection link. Statuses flow draft → sent → partially_paid → paid.
# list invoices curl https://rails-api.infinihash.com/api/v1/invoices \ -H "Authorization: Bearer $IHB_KEY"
Payments & settlement
When an invoice is paid, Rails runs the settlement pipeline: screen the destination wallet (KYT + sanctions), deduct the platform fee, convert dollars to stablecoin, and settle net USDT/USDC to your wallet. The whole thing is fail-closed — if screening doesn't pass, funds don't move.
# settlement result (via webhook or GET) { "status": "completed", "fiatInCents": 204120, "feeCents": 8165, "usdtOut": "1959.55", "destChain": "tron", "screeningRef": "kyt_…", "txHash": "…" }
Onboarding (KYB)
Bring a new business onto your platform with a white-label verification flow. Create an invite from the admin API to get a magic link; the customer completes a branded KYB wizard whose sensitive data streams straight to the regulated partner. Onboarding invites are created and tracked by Infinihash with an internal operator token (these routes are not part of the public OpenAPI spec). KYB does not currently emit webhooks.
# create an onboarding invite (internal token) curl -X POST https://rails-api.infinihash.com/admin/kyb/invites \ -H "Authorization: Bearer $INTERNAL_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "legalName": "Northgate Wholesale LLC", "contactEmail": "ap@northgate.example" }' # → { "magicLink": "https://onboard.infinihash.com/onboard/…" }
Endpoint reference
Generated from the live OpenAPI spec (/docs/openapi.json); if this page and the spec ever disagree, the spec is right. Tenant-facing routes are below; anything under /admin is operated by Infinihash with an internal token and is listed separately.
| Method | Path | Description |
|---|---|---|
| POST | /api/v1/invoices | Create an invoice |
| GET | /api/v1/invoices | List invoices |
| GET | /api/v1/invoices/{id} | Get an invoice |
| POST | /api/v1/invoices/{id}/send | Send an invoice (emits invoice.sent) |
| GET | /api/v1/invoices/{id}/pdf | Invoice PDF |
| POST | /api/v1/invoices/{id}/pay | Collect payment and run the settlement spine |
| GET | /api/v1/capabilities | What this deployment can actually serve (public, no key) |
| GET | /health | Liveness and adapter in use (public, no key) |
| POST | /onboard/signup | Request access (public) |
/api/v1/escrow…, /api/v1/disputes…). Managed escrow and refunds are not available on any current deployment; call GET /api/v1/capabilities for what your deployment serves before building against a route.Internal operator routes (not tenant-facing)
| Method | Path | Description |
|---|---|---|
| POST | /admin/tenants/{id}/webhooks | Register a webhook endpoint for a tenant (internal token; Infinihash registers yours on request) |
| POST | /admin/kyb/invites | Create a KYB onboarding invite (internal token; live route, not in the OpenAPI spec) |
| GET | /admin/kyb/invites | List onboarding applications and status (internal token; not in the spec) |
Webhooks
Once Infinihash has registered your endpoint (there is no tenant self-serve webhook route today), Rails posts a signed event for every state change. Each delivery is HMAC-SHA256 signed with your endpoint secret so you can verify authenticity, and failed deliveries are retried with exponential backoff (60s, 5m, 15m, 1h, 6h; six attempts).
This is the complete list of events Rails emits. An event name that is not on it is not sent — subscribing to one receives nothing.
| Group | Events |
|---|---|
| Invoicing | invoice.sent |
| Settlement | settlement.completed, settlement.settling, settlement.declined, settlement.failed, settlement.amount_mismatch, settlement.rail_unknown, settlement.stalled |
| Reserves | reserve.hold_blocked, reserve.release_ready |
| Disputes | alert.created, alert.matched, alert.unmatched, alert.notify_iso, alert.routed_to_review, alert.auto_refunded (not a confirmation that Rails refunded anyone: refunds are unsupported, and this event only mirrors a refund the card network already made), case.created, case.status_changed, ratio.threshold_breached, program.credit_suspended, program.credit_reinstated |
A successful settlement emits settlement.completed. settlement.settling is the ordinary in-flight case rather than an error: the rail has accepted the disbursement but has not yet returned an on-chain hash, so a settlement.completed (or settlement.stalled) follows once proof arrives. There is no payment-received event — collection outcomes reach you through the settlement events above — and KYB emits no webhooks at all.
# example event { "event": "settlement.completed", "data": { "invoice": "INV-2001", "usdtOut": "1959.55", "chain": "tron" } }
GET /api/v1/capabilities, and the full event list is published in the API reference (raw spec: /docs/openapi.json). Both are generated from the running code.X-Infinihash-Signature header against the raw request body using your endpoint's signing secret before trusting an event.