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.

● Base URL · https://rails-api.infinihash.com

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
Each tenant is fully isolated — a key only ever sees its own invoices, customers, and settlements.

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.

Call 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.
Before you build against either, call 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.

MethodPathDescription
POST/api/v1/invoicesCreate an invoice
GET/api/v1/invoicesList invoices
GET/api/v1/invoices/{id}Get an invoice
POST/api/v1/invoices/{id}/sendSend an invoice (emits invoice.sent)
GET/api/v1/invoices/{id}/pdfInvoice PDF
POST/api/v1/invoices/{id}/payCollect payment and run the settlement spine
GET/api/v1/capabilitiesWhat this deployment can actually serve (public, no key)
GET/healthLiveness and adapter in use (public, no key)
POST/onboard/signupRequest access (public)
The spec also describes escrow and dispute-management routes (/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)

MethodPathDescription
POST/admin/tenants/{id}/webhooksRegister a webhook endpoint for a tenant (internal token; Infinihash registers yours on request)
POST/admin/kyb/invitesCreate a KYB onboarding invite (internal token; live route, not in the OpenAPI spec)
GET/admin/kyb/invitesList 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.

GroupEvents
Invoicinginvoice.sent
Settlementsettlement.completed, settlement.settling, settlement.declined, settlement.failed, settlement.amount_mismatch, settlement.rail_unknown, settlement.stalled
Reservesreserve.hold_blocked, reserve.release_ready
Disputesalert.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" }
}
The authoritative, per-deployment list of what this API can do lives at 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.
Verify the X-Infinihash-Signature header against the raw request body using your endpoint's signing secret before trusting an event.