The DiscoFi API
One API for the whole payout: create it, let DiscoFi pick the fastest rail the recipient’s bank accepts, follow live status, and read a double-entry ledger that reconciles itself.
Authentication
Requests authenticate with a secret key in the Authorization header. Keys are managed in the dashboard (Settings → API keys); test keys exercise the sandbox ledger.
Authorization: Bearer sk_live_xxxxxxxxxxxx
Create a payout
One call creates the payout, runs the transaction-monitoring gate, selects the rail, and writes both ledger legs. Rail selection is automatic; pass rail only to constrain it to rtp or fednow.
Request
curl https://api.trydiscofi.com/api/payouts \
-H "Authorization: Bearer sk_live_xxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"recipientId": "rcp_7f2d",
"amountCents": 240000,
"memo": "Race day crew",
"rail": "rtp"
}'Response
{
"transferId": "tr_91c4",
"chainId": "ch_2210",
"status": "processing",
"fee": {
"amountCents": 120,
"basisPoints": 5
},
"activation": {
"wasFirst": false,
"secondsSinceApproval": null
}
}Recipients
A recipient is created once and paid many times. Creating one triggers the verification invite; the record carries its own status so you can see who is ready to pay.
Request
curl https://api.trydiscofi.com/api/recipients \
-H "Authorization: Bearer sk_live_xxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"firstName": "Jordan",
"lastName": "Avery",
"email": "jordan@example.com",
"type": "individual"
}'Response
{
"recipientId": "rcp_7f2d",
"status": "pending_kyc"
}status moves to verified when the recipient completes verification; businesses pass type: "business" with a company name.
Fund your balance
Balances fund over ACH from a linked bank, or by requesting the money over RTP with a Request for Payment when it needs to arrive before a batch goes out.
Request
curl https://api.trydiscofi.com/api/funding/ach \
-H "Authorization: Bearer sk_live_xxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"externalBankId": "bnk_31aa",
"amountCents": 5000000,
"memo": "Settlement float"
}'Response
{
"transferId": "tr_44b0",
"chainId": "ch_5921",
"status": "processing"
}POST /api/funding/rfp takes the same shape plus a subledgerId and returns { requestId, expiresAt }: the response is the receipt, and the funds move when the payer’s bank approves, announced by the funding.completed webhook.
Payout lifecycle
Every transfer moves through a small, explicit status set. Ledger postings happen only on settlement, so the books never show money that has not actually moved.
| Status | Meaning |
|---|---|
| processing | Accepted, monitoring passed, rail submission in flight. |
| completed | Settled on the rail; ledger legs posted. |
| pending | Queued (for example, awaiting approval or a retry window). |
| failed | Declined by the rail or the monitoring gate; nothing posted. |
Webhooks
Add an endpoint in Settings, under Webhooks, and choose its events. DiscoFi POSTs each event to it as JSON, signed with the endpoint’s secret, which is shown once when you add it. Endpoints must be public https addresses.
POST /your/webhook/path
Content-Type: application/json
DiscoFi-Signature: t=1758117600,v1=5f2b9c...
{
"id": "evt_8c1f04d2a9b35e71",
"type": "payout.status_changed",
"createdIso": "2026-09-17T14:00:00.000Z",
"data": {
"reference": "8FK2QX",
"recipient": "Sarah Chen",
"amount": "750.00",
"currency": "USD",
"rail": "FedNow",
"status": "settled",
"return_code": null
}
}To check a delivery, compute an HMAC-SHA256 of the timestamp, a dot and the raw request body (t.body) with the signing secret, and compare it with v1. Answer with any 2xx within 5 seconds. A delivery that fails is tried again. An endpoint that has been failing for 3 days is paused, and events that happen while it is paused are not kept; turn it back on in Settings.
| Event | Fires when |
|---|---|
| payment_link.received | A recipient receives the money from a payment link. data carries the rail and received_at. |
| payout.status_changed | A payment link payout settles or is returned. status is settled or returned, with the return_code when returned. |
| funding.completed | A deposit lands in your balance, over ACH or a paid Request for Payment. advance_repaid is the part that repaid an advance. |
| webhook.test | You send a test event to the endpoint from Settings. |
Errors & declines
Validation problems return standard HTTP errors. A payment the transaction-monitoring gate declines is its own case: the API answers 422 with the limit that declined it and why, so your system can show the reason instead of a shrug.
HTTP/1.1 422 Unprocessable Entity
{
"approved": false,
"declinedBy": {
"limitId": "txn_cap",
"reason": "Exceeds per-transaction limit"
}
}The gate runs inside the payment call, before any transfer is created, so a declined payment never touches the ledger.
Pay by link
For a recipient you will pay once, generate a secure payment link instead of collecting bank details up front. The recipient opens it and connects their own bank to receive the money; you get the payment_link.received event when they do.
Request
curl https://api.trydiscofi.com/api/links \
-H "Authorization: Bearer sk_live_xxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"amountCents": 75000,
"note": "Local crew, night of Aug 30",
"recipientHint": "Sam R."
}'Response
{
"token": "8FK2QX",
"url": "https://trydiscofi.com/receive/8FK2QX",
"amountCents": 75000
}Links pass the same monitoring gate as payouts when created, expire if the money is not received, and a link that has not been received can be cancelled with POST /api/links/:token/cancel.
Activity & balance
Two reads cover most operational questions. Activity is filterable by date range, status and free text; balance splits available, ledger and held funds.
GET /api/customers/me/activity?from=2026-08-01&status=completed GET /api/customers/me/balance
{
"available": 1084723000,
"ledger": 1084723000,
"hold": 0,
"currency": "USD"
}Rails & timing
| Rail | Availability |
|---|---|
| RTP | Typically 60 seconds or less* See Instant Pay disclosure |
| FedNow | Typically 60 seconds or less* See Instant Pay disclosure |
| Visa Direct | Typically 60 seconds or less* See Instant Pay disclosure |
| ACH | Standard ACH timelines* See Instant Pay disclosure |