Skip to content

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.

PreviewThis is the contract the platform is being built against. The API is not yet generally available; talk to us about early access with your integration.

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 payment.rfp.response_received event.

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.

StatusMeaning
processingAccepted, monitoring passed, rail submission in flight.
completedSettled on the rail; ledger legs posted.
pendingQueued (for example, awaiting approval or a retry window).
failedDeclined by the rail or the monitoring gate; nothing posted.

Live status

Status changes stream over server-sent events, so a dashboard or your own system sees each payout clear without polling.

event: payout.status_changed
data: { "transferId": "tr_91c4", "status": "completed" }
EventFires when
payout.status_changedA transfer changes status; refresh the activity row.
payment.rfp.response_receivedThe payer’s bank answers a Request for Payment.
link.claimedA pay-by-link recipient collects the money.
customer.phase_changedYour account’s onboarding phase changes.

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.

For a recipient you will pay once, generate a secure claim link instead of collecting bank details up front. The recipient opens it and connects their own bank to receive the money; you get the link.claimed 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 unclaimed, and an unclaimed link can be revoked 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

RailAvailability
RTPTypically 60 seconds or less* See Instant Pay disclosure
FedNowTypically 60 seconds or less* See Instant Pay disclosure
Visa DirectTypically 60 seconds or less* See Instant Pay disclosure
ACHStandard ACH timelines* See Instant Pay disclosure

Payout infrastructure for sports, music & entertainment.

hello@trydiscofi.com
Skyward Labs, Inc. © 2026