# Get Consul Balance
Source: https://docs.onconsul.com/api-reference/accounts/get-consul-balance
get /balance
Returns the current USDC balance of your Consul account.
The balance reflects the settled funds immediately
available for payouts or withdrawals.
Funds are held as USDC on Base.
# Authorization
Source: https://docs.onconsul.com/api-reference/authorization
Every Consul API request is authenticated by sending a credential in
the `Authorization` header using the `Bearer` scheme. Consul determines
the credential type from its prefix — there is no separate auth scheme
per credential.
```shell theme={"system"}
curl https://api.onconsul.com/v1/balance \
-H "Authorization: Bearer $CONSUL_CREDENTIAL"
```
### API key
A long-lived API key issued from the Consul dashboard. Cleartexts are
prefixed with `csl_live_…`. API keys are scoped at creation time to a
set of resource-scoped permissions (e.g. `balance:read`,
`payouts:write`). Requests that exceed the key's scope are rejected
with `403 forbidden`.
You can provision and revoke API keys from the **Developers** section
of the [dashboard](https://dashboard.onconsul.com). The cleartext key
is shown exactly once at creation time — Consul stores only an
HMAC-SHA256 hash and cannot recover the original value if you lose it.
```shell theme={"system"}
curl https://api.onconsul.com/v1/balance \
-H "Authorization: Bearer $CONSUL_API_KEY"
```
### OAuth access token
For third-party apps acting on behalf of a Consul user, authenticate
with a token obtained through the WorkOS Connect authorization code
flow at `auth.onconsul.com`.
```shell theme={"system"}
curl https://api.onconsul.com/v1/balance \
-H "Authorization: Bearer $OAUTH_ACCESS_TOKEN"
```
OAuth tokens carry the same kind of resource-scoped permissions as
API keys — but the user picks them on the consent screen rather than
the developer picking them at key-creation time.
See the [OAuth Connections](/guides/oauth) guide for the full flow,
including obtaining and refreshing access tokens.
# Archive a Bank Account
Source: https://docs.onconsul.com/api-reference/bank-accounts/archive-a-bank-account
post /bank_accounts/{id}/archive
Archive a bank account so it can no longer be used for
new transfers. Any in-flight transfers will still
complete normally.
# Create a Bank Account
Source: https://docs.onconsul.com/api-reference/bank-accounts/create-a-bank-account
post /bank_accounts
Register a new fiat bank account. You must specify the
currency, payment rail (e.g. `usd_ach`, `brl_pix`,
`mxn_spei`), and the corresponding rail-specific details
such as routing/account numbers for USD ACH, a PIX key
for BRL, or a CLABE for MXN SPEI.
# Get a Bank Account
Source: https://docs.onconsul.com/api-reference/bank-accounts/get-a-bank-account
get /bank_accounts/{id}
Retrieve a single bank account by its ID, including its
status and rail-specific details.
# List Bank Accounts
Source: https://docs.onconsul.com/api-reference/bank-accounts/list-bank-accounts
get /bank_accounts
Returns a paginated list of bank accounts registered under
your app. Bank accounts are fiat accounts used for deposits
(onramps) and withdrawals, each tied to a specific currency
and payment rail.
# Update a Bank Account
Source: https://docs.onconsul.com/api-reference/bank-accounts/update-a-bank-account
patch /bank_accounts/{id}
Update metadata on a bank account. Currently only the
display name can be changed.
# Create a Deposit Quote
Source: https://docs.onconsul.com/api-reference/deposits/create-a-deposit-quote
post /deposits/quote
Request a quote to convert fiat currency into USDC.
The currency and payment rail are determined by the
linked bank account.
The response includes the exchange rate, fees,
and estimated settlement time.
# Execute a Deposit
Source: https://docs.onconsul.com/api-reference/deposits/execute-a-deposit
post /deposits/quote/{id}/execute
Lock in a previously created quote and initiate the
fiat-to-USDC transfer. The response includes transfer
instructions (e.g. wire details or PIX code) needed to
complete the deposit.
# Get a Deposit
Source: https://docs.onconsul.com/api-reference/deposits/get-a-deposit
get /deposits/{id}
Retrieve the current status and details of a deposit
transfer by ID.
# Errors
Source: https://docs.onconsul.com/api-reference/errors
The API returns standard HTTP status codes. Error responses conform to [RFC 9457](https://www.rfc-editor.org/rfc/rfc9457.html) and always include:
| Field | Type | Description |
| -------- | ------- | ------------------------------------------------------------------------------------------- |
| `type` | string | A URI identifying the error type. |
| `title` | string | A short, human-readable summary. |
| `status` | integer | The HTTP status code. |
| `detail` | string | A human-readable explanation specific to this occurrence. |
| `errors` | array | Optional list of per-field validation errors, each with `location`, `message`, and `value`. |
# Archive an External Wallet
Source: https://docs.onconsul.com/api-reference/external-wallets/archive-an-external-wallet
post /external_wallets/{id}/archive
Archive an external wallet so it can no longer be used
for payouts.
# Create an External Wallet
Source: https://docs.onconsul.com/api-reference/external-wallets/create-an-external-wallet
post /external_wallets
Register a new external crypto wallet scoped to a chain.
Once created, the wallet can be used as a destination for payouts.
The same wallet address can be added multiple times for use on
different chains.
# Get an External Wallet
Source: https://docs.onconsul.com/api-reference/external-wallets/get-an-external-wallet
get /external_wallets/{id}
Retrieve a single external wallet by its ID.
# List External Wallets
Source: https://docs.onconsul.com/api-reference/external-wallets/list-external-wallets
get /external_wallets
Returns a paginated list of external crypto wallets.
# Get the current caller
Source: https://docs.onconsul.com/api-reference/me/get-me
get /me
Returns the authenticated caller's self-view: the credential type,
the owning entity, and the scopes granted to the request. Useful as
a smoke test that your `Authorization` header is wired correctly
and that the scopes attached to your API key cover the endpoints
you intend to call.
# Overview
Source: https://docs.onconsul.com/api-reference/overview
The Consul API is organized around REST principles and returns responses in JSON format. All endpoints live under the `/v1` base path.
**Base URLs:**
| Environment | URL |
| ----------- | --------------------------------- |
| Production | `https://api.onconsul.com/v1` |
| Sandbox | `https://sandbox.onconsul.com/v1` |
All requests must be authenticated. See [Authorization](/api-reference/authorization) for details.
# Pagination
Source: https://docs.onconsul.com/api-reference/pagination
All list endpoints return a paginated response with two fields:
* `data`: an array of results.
* `next_cursor`: an opaque cursor for fetching the next page. `null` when there are no more results.
Pass `page_size` to control the number of items per page (default and max: 100). To fetch the next page, pass the returned `next_cursor` as the `cursor` query parameter.
### Example
```shell theme={"system"}
# First page
curl "https://api.onconsul.com/v1/recipients?page_size=25" \
-H "Authorization: Basic base64($CONSUL_API_KEY)"
# Next page
curl "https://api.onconsul.com/v1/recipients?page_size=25&cursor=eyJpZCI6..." \
-H "Authorization: Basic base64($CONSUL_API_KEY)"
```
# Cancel a Payout
Source: https://docs.onconsul.com/api-reference/payouts/cancel-a-payout
post /payouts/{id}/cancel
Cancel a payout that has not yet completed. Items in
`processing` or `completed` state are unaffected;
items still `queued` or `pending_claim` are cancelled
and their funds returned to your Consul wallet.
# Cancel a Payout Item
Source: https://docs.onconsul.com/api-reference/payouts/cancel-a-payout-item
post /payouts/{id}/items/{item_id}/cancel
Cancel a single item within a payout. The item must be in
`pending_claim`; items in any other status return an
`InvalidOperation` error. For cancelling everything still
cancellable on a payout in one call, use [Cancel a Payout](/api-reference/payouts/cancel-a-payout).
Returns the updated `PayoutItem` snapshot.
# Create a Payout
Source: https://docs.onconsul.com/api-reference/payouts/create-a-payout
post /payouts
Send USDC from your Consul wallet to one or more
recipients. Each item in the payout specifies an amount
and a target: either an email address or a `recipient_id`.
Supports an `Idempotency-Key` header to safely retry
without creating duplicate payouts.
# Get a Payout
Source: https://docs.onconsul.com/api-reference/payouts/get-a-payout
get /payouts/{id}
Retrieve a payout by ID, including its overall status,
total amount, and item count.
# List Payout Items
Source: https://docs.onconsul.com/api-reference/payouts/list-payout-items
get /payouts/{id}/items
Returns a paginated list of individual items within a
payout, showing each recipient's name, amount, and
current status.
# Create a Recipient
Source: https://docs.onconsul.com/api-reference/recipients/create-a-recipient
post /recipients
Create a new recipient by providing their email address
and an optional nickname. When you later send a payout
to this recipient, they receive a Consul wallet where
they can save, spend, or withdraw their funds.
# Delete a Recipient
Source: https://docs.onconsul.com/api-reference/recipients/delete-a-recipient
delete /recipients/{id}
Permanently delete a recipient. This does not affect
any past payouts sent to them.
# Get a Recipient
Source: https://docs.onconsul.com/api-reference/recipients/get-a-recipient
get /recipients/{id}
Retrieve a recipient by ID, including their display name,
linked entity info, and total payout history.
# List Recipients
Source: https://docs.onconsul.com/api-reference/recipients/list-recipients
get /recipients
Returns a paginated list of recipients. Recipients are
the people or businesses you send payouts to, each
identified by an email address.
# Update a Recipient
Source: https://docs.onconsul.com/api-reference/recipients/update-a-recipient
patch /recipients/{id}
Update a recipient's nickname. The nickname is a
display label visible only to your app.
# Get a Transaction
Source: https://docs.onconsul.com/api-reference/transactions/get-a-transaction
get /transactions/{id}
Retrieve full details of a single transaction, including
fee breakdown, fiat or crypto transfer instructions, and
on-chain transaction hashes.
# List Transactions
Source: https://docs.onconsul.com/api-reference/transactions/list-transactions
get /transactions
Returns a paginated list of all money movement through
your Consul account. Each transaction has a type
(`inbound_crypto`, `outbound_crypto`, `onramp`,
`offramp`, or `bulk_payout`) and tracks status from
creation through completion.
You can filter by transaction type and date range.
# Create a Withdrawal Quote
Source: https://docs.onconsul.com/api-reference/withdrawals/create-a-withdrawal-quote
post /withdrawals/quote
Request a quote to convert USDC into fiat currency.
The destination currency and payment rail are determined
by the linked bank account.
The response includes the exchange rate, fees, and
estimated settlement time.
# Execute a Withdrawal
Source: https://docs.onconsul.com/api-reference/withdrawals/execute-a-withdrawal
post /withdrawals/quote/{id}/execute
Lock in a previously created quote and initiate the
USDC-to-fiat transfer. The withdrawal is debited from
your Consul wallet immediately and arrives at the
destination bank account once the underlying rail
settles.
# Get a Withdrawal
Source: https://docs.onconsul.com/api-reference/withdrawals/get-a-withdrawal
get /withdrawals/{id}
Retrieve the current status and details of a withdrawal
transfer by ID.
# Changelog
Source: https://docs.onconsul.com/changelog
Product updates and announcements
## Launch
Initial release of the Consul documentation site.
# Bank Accounts
Source: https://docs.onconsul.com/guides/bank-accounts
Link fiat bank accounts for deposits and withdrawals
Bank accounts are used to deposit fiat into your Consul balance and to receive withdrawals. Each
bank account is tied to a specific currency and payment rail. Use the
[Create a Bank Account](/api-reference/bank-accounts/create-a-bank-account) endpoint to link one.
The required fields depend on the currency and rail. Select your currency below for the full
schema and a working example.
**Payment rail:** `usd_fedwire`
| Field | Required | Description |
| -------------------------- | ------------- | ---------------------------- |
| `account_number` | Yes | Bank account number |
| `routing_number` | Yes | ABA routing number |
| `bank_account_type` | Yes | `checking` or `savings` |
| `bank_account_holder_type` | Yes | `business` or `individual` |
| `owner_address` | Yes | Account holder's address |
| `owner_first_name` | If individual | Account holder's first name |
| `owner_last_name` | If individual | Account holder's last name |
| `owner_business_name` | If business | Business name on the account |
```bash theme={"system"}
curl -X POST https://api.onconsul.com/v1/bank_accounts \
-H "Authorization: Basic base64($CONSUL_API_KEY)" \
-H "Content-Type: application/json" \
-d '{
"name": "Acme Corp Operating",
"currency": "USD",
"payment_rail": "usd_fedwire",
"usd_domestic_wire": {
"account_number": "123456789",
"routing_number": "021000021",
"bank_account_type": "checking",
"bank_account_holder_type": "business",
"owner_business_name": "Acme Corp",
"owner_address": {
"line1": "123 Main St",
"city": "New York",
"state": "NY",
"postal_code": "10001",
"country": "US"
}
}
}'
```
**Payment rail:** `usd_ach`
Same fields as Wire. Uses the `usd_ach` field instead of `usd_domestic_wire`:
```bash theme={"system"}
curl -X POST https://api.onconsul.com/v1/bank_accounts \
-H "Authorization: Basic base64($CONSUL_API_KEY)" \
-H "Content-Type: application/json" \
-d '{
"name": "Acme Corp ACH",
"currency": "USD",
"payment_rail": "usd_ach",
"usd_ach": {
"account_number": "123456789",
"routing_number": "021000021",
"bank_account_type": "checking",
"bank_account_holder_type": "business",
"owner_business_name": "Acme Corp",
"owner_address": {
"line1": "123 Main St",
"city": "New York",
"state": "NY",
"postal_code": "10001",
"country": "US"
}
}
}'
```
**Payment rail:** `brl_pix`
| Field | Required | Description |
| --------- | -------- | ------------------------------------------------ |
| `pix_key` | Yes | PIX key (CPF, CNPJ, email, phone, or random key) |
```bash theme={"system"}
curl -X POST https://api.onconsul.com/v1/bank_accounts \
-H "Authorization: Basic base64($CONSUL_API_KEY)" \
-H "Content-Type: application/json" \
-d '{
"name": "BRL PIX Account",
"currency": "BRL",
"payment_rail": "brl_pix",
"brl_pix": {
"pix_key": "12345678901"
}
}'
```
**Payment rail:** `ars_transfers_3_0`
| Field | Required | Description |
| ------------------ | -------- | ---------------------------- |
| `beneficiary_name` | Yes | Name of the account holder |
| `account_type` | Yes | `cvu`, `cbu`, or `alias` |
| `account_value` | Yes | The CVU, CBU, or alias value |
```bash theme={"system"}
curl -X POST https://api.onconsul.com/v1/bank_accounts \
-H "Authorization: Basic base64($CONSUL_API_KEY)" \
-H "Content-Type: application/json" \
-d '{
"name": "ARS Account",
"currency": "ARS",
"payment_rail": "ars_transfers_3_0",
"ars_transfers": {
"beneficiary_name": "Juan Pérez",
"account_type": "cvu",
"account_value": "0000003100099632680016"
}
}'
```
**Payment rail:** `mxn_spei`
| Field | Required | Description |
| ------------------ | -------- | -------------------------- |
| `beneficiary_name` | Yes | Name of the account holder |
| `spei_clabe` | Yes | 18-digit CLABE number |
```bash theme={"system"}
curl -X POST https://api.onconsul.com/v1/bank_accounts \
-H "Authorization: Basic base64($CONSUL_API_KEY)" \
-H "Content-Type: application/json" \
-d '{
"name": "MXN SPEI Account",
"currency": "MXN",
"payment_rail": "mxn_spei",
"mxn_spei": {
"beneficiary_name": "María García",
"spei_clabe": "012345678901234567"
}
}'
```
**Payment rail:** `cop_pse`
| Field | Required | Description |
| ------------------------ | -------- | ----------------------------------- |
| `beneficiary_first_name` | Yes | First name |
| `beneficiary_last_name` | Yes | Last name |
| `document_type` | Yes | `cc`, `ce`, `nit`, `pass`, or `pep` |
| `document_id` | Yes | Document number |
| `email` | Yes | Contact email |
| `bank_code` | Yes | Colombian bank code |
| `bank_account` | Yes | Bank account number |
| `account_type` | Yes | `checking` or `savings` |
```bash theme={"system"}
curl -X POST https://api.onconsul.com/v1/bank_accounts \
-H "Authorization: Basic base64($CONSUL_API_KEY)" \
-H "Content-Type: application/json" \
-d '{
"name": "COP PSE Account",
"currency": "COP",
"payment_rail": "cop_pse",
"cop_ach": {
"beneficiary_first_name": "Carlos",
"beneficiary_last_name": "Rodríguez",
"document_type": "cc",
"document_id": "1234567890",
"email": "carlos@example.com",
"bank_code": "007",
"bank_account": "12345678901234",
"account_type": "checking"
}
}'
```
# Coverage
Source: https://docs.onconsul.com/guides/coverage
Consul is able to onboard users in 100+ countries
and has opt-in support for payment rails spanning
50+ currencies.
For a full up-to-date list of supported countries and currencies,
please reach out to support.
# Deposits
Source: https://docs.onconsul.com/guides/deposits
How to deposit funds into your Consul balance
Before you can issue payouts, your Consul balance must be funded. There are two ways to move
money into your balance: stablecoin deposits and fiat deposits.
## Stablecoin Deposits
If you already hold USDC or USDT in a wallet or exchange, you can transfer it to your Consul
balance directly. This is instant and requires no conversion. You will receive an
`inbound_transfer.created` webhook event once funds arrive.
By default, funds must be sent as **USDC or USDT on Base**. If you need support for other
tokens or chains, contact support to enable.
To find your Consul wallet address, go to
[Dashboard](https://app.onconsul.com) **→ Add Funds** and copy the displayed
wallet address.
## Fiat Deposits
Consul converts fiat deposits into USDC and credits your balance. Use the
[Deposits API](/api-reference/deposits/create-a-deposit-quote) to initiate a fiat deposit
programmatically.
| Method | Currency | Speed | Notes |
| ----------------------- | -------- | ----------------- | ------------------------------------------------------------------------- |
| Domestic Wire (FedWire) | USD | 0-1 business days | Fastest for large USD deposits |
| ACH Push | USD | 1-2 business days | You initiate the transfer from your bank |
| ACH Debit | USD | 1-2 business days | Consul pulls funds from your linked bank account. **Enabled on request.** |
| PIX | BRL | A few minutes | Instant Brazilian payments |
| Transfers 3.0 | ARS | A few minutes | Argentine instant transfers |
| SWIFT (coming soon) | USD | 2-5 business days | SWIFT international wire transfers |
*PSE in Colombia and SPEI in Mexico can be enabled on request.*
### Example
#### 1. Link a Bank Account
Before creating a deposit, you need a linked bank account. The bank account determines the
currency and payment rail. See the [Bank Accounts guide](/guides/bank-accounts) for the full
schema and examples for each supported currency.
#### 2. Create a Deposit Quote
Request a quote with the [Create a Deposit Quote](/api-reference/deposits/create-a-deposit-quote)
endpoint. The currency and payment rail are determined by the linked bank account:
```bash theme={"system"}
curl -X POST https://api.onconsul.com/v1/deposits/quote \
-H "Authorization: Basic base64($CONSUL_API_KEY)" \
-H "Content-Type: application/json" \
-d '{
"source_bank_account_id": "ba_abc123",
"amount": "10000"
}'
```
The response includes the exchange rate, fees, and a quote `id`.
#### 3. Execute the Deposit
Execute the quote with the [Execute a Deposit](/api-reference/deposits/execute-a-deposit)
endpoint to lock in the rate and receive transfer instructions:
```bash theme={"system"}
curl -X POST https://api.onconsul.com/v1/deposits/quote/qt_abc123/execute \
-H "Authorization: Basic base64($CONSUL_API_KEY)" \
```
The response is a `Transfer` object containing `fiat_transfer_instructions` - the details
needed to complete the transfer from your bank:
```json theme={"system"}
{
"id": "tr_8xKp2mNvQw",
"transfer_type": "deposit",
"status": "awaiting_funds",
"source": {
"currency": "USD",
"amount": "10000.00"
},
"destination": {
"currency": "USDC",
"amount": "9995.00"
},
"market_rate": "0.9995",
"fiat_transfer_instructions": {
"payment_method": "usd_fedwire",
"usd_fedwire_transfer_instructions": {
"routing_number": "021000089",
"account_number": "9876543210",
"bank_name": "Lead Bank",
"beneficiary_name": "Consul Inc.",
"beneficiary_address": "123 Main St, Kansas City, MO 64108",
"transfer_memo": "AVK2H1VVP1Z"
}
},
"created_at": "2025-03-24T14:30:00Z"
}
```
For USD wires and ACH pushes, use the `transfer_memo` when initiating the wire or transfer from your bank.
Each payment rail has a different set of transfer instructions. See the schema in
[Execute a Deposit](/api-reference/deposits/execute-a-deposit) for the full details or contact support.
### Deposit Lifecycle
A deposit can be in one of the following states:
| State | Description |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `awaiting_transfer` | The deposit is awaiting transfer to your Consul balance. |
| `processing` | Funds have been received and the deposit is being processed. |
| `completed` | The deposit has been completed successfully. Funds have been credited to your Consul balance. |
| `failed` | The deposit has failed. A failure reason is provided. |
| `cancelled` | The deposit has been canceled. Funds have not been credited. |
| `expired` | The deposit has expired while waiting for funds to be received. If funds are received after expiry, they are still credited to your Consul balance but may require manual processing. |
The following state diagram shows the lifecycle of a deposit.
```mermaid theme={"system"}
stateDiagram-v2
[*] --> awaiting_transfer: Quote executed
awaiting_transfer --> processing: Funds received
awaiting_transfer --> expired: Timed out
awaiting_transfer --> cancelled: Cancelled
processing --> completed: Funds credited
processing --> failed: Error
expired --> processing: Late funds received
```
# Pre-Funding Flows
Source: https://docs.onconsul.com/guides/funding-flows
Workflows for pre-funding your Consul balance before issuing payouts
Before you can issue payouts, your Consul balance must be funded.
See [Deposits](/guides/deposits) for the full list of deposit methods.
For most payout platforms and marketplaces, Domestic Wire and ACH Debit are the most common, covered below.
| Method | Initiation | Settlement | Best For |
| ------------- | --------------------- | -------------------------- | -------------------------------- |
| Domestic Wire | Manual from your bank | Same day (often \< 1 hour) | Urgent top-ups |
| ACH Debit | API (pull-based) | 2 business days | Automated, recurring pre-funding |
## Domestic Wire
Wires generally settle same day (often within the hour) if initiated before 3 PM EST. However, this requires
manually sending a wire from your business bank to Consul. Best used for top-ups when funds are needed ASAP.
You can initiate a wire via the [Create a Deposit Quote](/api-reference/deposits/create-a-deposit-quote) endpoint
or directly from the [dashboard](/guides/quickstart).
Wire cutoff times depend on the sending and receiving bank. During setup, we
can provide the exact cutoff times for your bank.
## ACH Debit
ACH debits are pull-based - Consul initiates the debit directly from your linked bank account via the API. No manual
wire required. Budget **2 business days** for ACH funds to settle.
There are a few common strategies for managing your balance with ACH debits, depending on how quickly you need
payouts to reach recipients.
### Debit as needed
The simplest approach. Initiate ACH debits as payout requests come in - batch them at end of day or on the
cadence that works for your platform. Present payouts as "1-2 business days" to your end users, since funds need to
settle before the payout is sent.
No pre-funding or balance management required.
### Threshold-based top-ups
For instant payouts, maintain a pre-funded balance and top it up automatically when it drops below a minimum
threshold. You can detect balance changes by listening for
[`deposit.updated` and `payout.updated` webhooks](/guides/webhooks), or by polling your balance via the API.
When the balance falls below your threshold, trigger an ACH debit to replenish. This keeps payouts instant from
the recipient's perspective without needing to forecast payout volume.
### Projection-based pre-funding
For instant payouts, pre-fund on a rolling daily basis by projecting forward the payout balance you'll need in 2 business days.
This works well when you have predictable or forecastable payout volume.
Keep the following in mind for projections:
* Funds debited **Wednesday** settle **Friday** - should cover weekend payouts.
* Funds debited **Thursday** settle **Monday** - should cover Monday payouts.
ACH does not settle on weekends or bank holidays. Plan your pre-funding schedule accordingly.
To enable ACH debits or get help with any of these strategies, reach out to support.
# OAuth Connections
Source: https://docs.onconsul.com/guides/oauth
Authorize Consul API actions on behalf of your users
OAuth 2.0 (Open Authorization) is the standard protocol for delegated
access. Rather than asking users to paste an API key, OAuth lets a
third-party application redirect the user to Consul, where they
explicitly consent to a set of permissions. The application then
receives an access token scoped to that user's account.
Consul's OAuth flow is hosted at **`auth.onconsul.com`** and powered
by [WorkOS Connect](https://workos.com/connect). For a thorough
primer on the protocol itself, see
[The Complete Guide to OAuth 2.0](https://workos.com/guide/the-complete-guide-to-oauth)
by WorkOS.
### What this enables
* **Embedded digital wallets.** Surface a user's Consul balance and
transaction history directly in your UI.
* **Checkout and payment acceptance.** Send and reconcile funds from
your users' Consul balances natively in your app.
* **Fiat deposits from your platform.** Trigger deposits into a user's
Consul balance from your platform without them leaving your app.
### Prerequisites
To act on behalf of Consul users, you need a Consul **Connect App**.
Register one from the **Developers → Connect** section of the
[dashboard](https://dashboard.onconsul.com). The dashboard will show
you:
| Name | What it is |
| --------------- | ------------------------------------------------------------------------------------------------ |
| `client_id` | Identifies your Connect App to Consul during the OAuth flow. |
| `client_secret` | Secret used to exchange authorization codes for tokens. Never expose this client-side. |
| `redirect_uri` | The URL Consul redirects users to after they grant access. You set this during app registration. |
API keys and OAuth client credentials serve different purposes. An
**API key** (`csl_live_…` / `csl_test_…`) authenticates requests to
your own Consul account. A **client\_id + client\_secret** authenticates
your *app* during the OAuth flow so it can act on behalf of *other*
users' accounts. When making API requests on behalf of a connected
user, use the OAuth access token — not your API key.
### Authorization code flow
Consul uses the standard OAuth 2.0 authorization code flow. All
OAuth endpoints are served by `auth.onconsul.com`; the Consul v1 API
at `api.onconsul.com` only consumes the resulting access tokens.
```mermaid theme={"system"}
sequenceDiagram
participant User
participant App as Your App
participant Auth as auth.onconsul.com
participant API as api.onconsul.com
App->>Auth: Redirect user to /authorize
Auth->>User: Show consent screen
User->>Auth: Grant access
Auth->>App: Redirect back with authorization code
App->>Auth: POST /token (exchange code)
Auth->>App: Access token + refresh token
App->>API: v1 API requests with access token
```
**1. Redirect the user to Consul's authorization endpoint:**
```
https://auth.onconsul.com/authorize
?response_type=code
&client_id=YOUR_CLIENT_ID
&redirect_uri=https://yourapp.com/callback
&scope=balance:read payouts:write
&state=RANDOM_STATE_VALUE
```
The `state` parameter should be a random, unguessable string tied to
the user's session. Verify it when Consul redirects back to prevent
CSRF attacks.
**2. Exchange the authorization code for tokens:**
```bash theme={"system"}
curl -X POST https://auth.onconsul.com/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=authorization_code&\
code=AUTH_CODE_FROM_REDIRECT&\
redirect_uri=https://yourapp.com/callback&\
client_id=YOUR_CLIENT_ID&\
client_secret=YOUR_CLIENT_SECRET"
```
The response includes an `access_token`, `refresh_token`, `expires_in`,
and the granted `scope`.
**3. Make v1 API requests on behalf of the user:**
```bash theme={"system"}
curl https://api.onconsul.com/v1/balance \
-H "Authorization: Bearer OAUTH_ACCESS_TOKEN"
```
### Refreshing tokens
Access tokens expire. Use the refresh token to get a new one without
requiring the user to re-authorize:
```bash theme={"system"}
curl -X POST https://auth.onconsul.com/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=refresh_token&\
refresh_token=REFRESH_TOKEN&\
client_id=YOUR_CLIENT_ID&\
client_secret=YOUR_CLIENT_SECRET"
```
### Revoking access
To disconnect a user's account, revoke their token:
```bash theme={"system"}
curl -X POST https://auth.onconsul.com/revoke \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "token=ACCESS_OR_REFRESH_TOKEN&\
client_id=YOUR_CLIENT_ID&\
client_secret=YOUR_CLIENT_SECRET"
```
### Scopes
Consul scopes are **resource-scoped slugs** of the form
`resource:action`. Request them as a space-separated string in the
`scope` query parameter during authorization. Users see the requested
permissions on the consent screen before granting access.
| Scope | Description |
| ---------------------------------- | -------------------------------------------------------- |
| `balance:read` | Read the user's Consul wallet balance. |
| `bank_accounts:read` / `:write` | Read or manage the user's linked bank accounts. |
| `external_wallets:read` / `:write` | Read or manage the user's external self-custody wallets. |
| `recipients:read` / `:write` | Read or manage the user's payout recipients. |
| `transactions:read` | Read the user's transaction history. |
| `deposits:read` / `:write` | Read or initiate fiat-to-USDC deposits. |
| `withdrawals:read` / `:write` | Read or initiate USDC-to-fiat withdrawals. |
| `payouts:read` / `:write` | Read or initiate outbound payouts. |
Request the minimum scopes your application needs. Webhook
subscriptions are not exposed via the API at all (neither API keys
nor OAuth tokens can manage them); they are configured exclusively
through the Consul dashboard.
# Payouts
Source: https://docs.onconsul.com/guides/payouts
Send USDC payouts to recipients via email
Once your Consul balance is funded, you can send payouts to one or more recipients in a single
API call. Payouts are on-chain USDC transfers on Base that settle in seconds.
### How Payouts Work
Each payout is a batch of items. Each item specifies an amount and a target - either an
**email address** or a **recipient ID**. Consul handles the rest:
* **Existing users** receive USDC directly in their Consul wallet.
* **New users** receive an email invite to claim their funds. Consul creates a wallet behind the
scenes and guides them through onboarding.
Payouts are **atomic**. If any item in a batch fails validation (e.g. insufficient balance for
the total), the entire payout is rejected. No partial transfers occur.
### Sending a Payout
Use the [Create a Payout](/api-reference/payouts/create-a-payout) endpoint. You can target
recipients by email or by `recipient_id` if they've already been created via the
[Recipients API](/api-reference/recipients/create-a-recipient):
```bash theme={"system"}
curl -X POST https://api.onconsul.com/v1/payouts \
-H "Authorization: Basic base64($CONSUL_API_KEY)" \
-H "Idempotency-Key: $UNIQUE_KEY" \
-H "Content-Type: application/json" \
-d '{
"items": [
{"amount": "500", "email": "alice@example.com"},
{"amount": "250", "recipient_id": "recipient_abc123"},
{"amount": "1000", "email": "new-contractor@example.com"}
]
}'
```
The response returns a payout in `processing` status. Individual items settle almost instantly
(\< 3s p99). Listen for the `transfer.updated` webhook to confirm each item has completed.
### Idempotency
Include an `Idempotency-Key` header to safely retry payout requests without creating
duplicates. If a request with the same key has already been processed, Consul returns the
original response.
### Payout Lifecycle
Each payout item follows its own lifecycle:
| Status | Description |
| --------------- | ------------------------------------------------------------------------------ |
| `processing` | Transfer initiated on-chain |
| `completed` | USDC delivered to recipient's wallet |
| `pending_claim` | Recipient isn't on the platform yet - funds are held securely until they claim |
| `cancelled` | Payout was cancelled; funds returned to your balance |
| `expired` | Payout expired; funds returned to your balance |
| `failed` | Transfer failed; a failure reason is provided |
```mermaid theme={"system"}
stateDiagram-v2
[*] --> processing: Payout created
processing --> completed: Recipient on platform
processing --> pending_claim: Recipient not on platform
processing --> failed: Error
pending_claim --> completed: Recipient claims
pending_claim --> cancelled: Cancelled
pending_claim --> expired: Expired
```
### Cancelling a Payout
You can cancel a payout that hasn't settled yet by calling
[POST /payouts//cancel](/api-reference/payouts/cancel-a-payout). This is only possible while the
payout is still in `processing` or `pending_claim` status. Once a payout item has `completed`, it cannot
be reversed.
```bash theme={"system"}
curl -X POST https://api.onconsul.com/v1/payouts/payout_abc123/cancel \
-u $CONSUL_API_KEY
```
The response returns the updated payout object with a `cancelled` status. Funds are returned to your
Consul balance.
### Tracking Payouts
* **Get a Payout**: [GET /payouts/](/api-reference/payouts/get-a-payout) returns the
overall status, total amount, and item count.
* **List Payout Items**: [GET /payouts//items](/api-reference/payouts/list-payout-items)
returns each item's recipient, amount, and status.
# Quickstart
Source: https://docs.onconsul.com/guides/quickstart
Send your first payout in minutes
Whether you're building a buyer-seller marketplace, a payments platform, or just
looking to automate contractor payments, the high-level flow is to (1) deposit funds into your Consul balance
and then (2) issue a payout.
### Get API Keys
Complete your entity onboarding in the [Consul dashboard](https://dashboard.onconsul.com), then provision your API keys in the Developer section.
### Deposit Funds
To send payouts, you need USDC in your Consul wallet. The quickest way is to link a bank account and initiate a deposit from the dashboard via **Add Funds** or the **Move Money** dropdown. You'll receive deposit instructions once complete.
To deposit via the API, use the [Create a Deposit Quote](/api-reference/deposits/create-a-deposit-quote) endpoint. See the [Deposits](/guides/deposits) guide for more details.
### Wait for Deposit Webhook
Once the deposit completes, you'll receive an email notification. For automated flows, listen for the `deposit.updated` webhook event. The funds are now available for payouts.
### Send a Payout
To issue a payout from the dashboard, create a new payout and select the recipients you want to pay.
Alternatively, use the [Create a Payout](/api-reference/payouts/create-a-payout) endpoint to send USDC to one or more email addresses:
```bash theme={"system"}
curl -X POST https://api.onconsul.com/v1/payouts \
-H "Authorization: Basic base64($CONSUL_API_KEY)" \
-H "Content-Type: application/json" \
-d '{
"items": [
{"amount": "100", "email": "alice@example.com"},
{"amount": "200", "email": "bob@example.com"}
]
}'
```
Payouts settle almost instantly (less than 3s p99), but the response returns a payout in `processing` status.
Listen for the `payout.updated` webhook to confirm completion.
### Automating Pre-Funding
The flow above requires a manual deposit. See [Pre-Funding Flows](/guides/funding-flows) for automated pre-funding options.
### What's Next
Learn more about sending payouts
Receive real-time notifications for payout lifecycle events
Explore the full API
Manage pre-funding flows for your payout balance
# Stablecoin Basics
Source: https://docs.onconsul.com/guides/stablecoin-basics
The basics of stablecoin payouts in 5 minutes
Stablecoins (e.g. USDC, USDT) are cryptocurrencies pegged to a fiat currency, typically the US dollar
but sometimes other currencies (e.g. EURC is pegged to the Euro). They maintain this peg by holding
a reserve of that fiat currency. By combining the stability of fiat with the speed and programmability
of crypto, stablecoins have become an important tool in global payments.
#### USDC
USDC is a stablecoin pegged to the US dollar. [Circle](https://circle.com/) is the issuer and maintains
a reserve surplus of all USDC in circulation. These reserves are held by BlackRock and
[independently audited by Deloitte](https://www.circle.com/transparency).
#### USDT
USDT is the most widely used stablecoin by transaction volume. [Tether](https://tether.to/) is the
issuer. USDT volume is primarily on Tron, a separate L1 blockchain from Ethereum.
### Why Stablecoins for Payouts
Traditional cross-border payments route through multiple correspondent banks, take 2-5 business days,
and charge fees at each hop. Stablecoin transfers settle in seconds, cost fractions of a cent, and
work 24/7 - including weekends and holidays.
USDC and USDT also have deep liquidity across global currency pairs, making conversion to
local currency straightforward (see [Off-Ramping](#off-ramping)).
### L1s and L2s
Ethereum is the original layer-1 (L1) blockchain that USDC launched on. Over time, high gas fees and
congestion led to layer-2 (L2) networks - blockchains built on top of Ethereum that batch transactions
for lower cost. The primary L2 that Consul uses is **Base, Coinbase's L2**. All USDC funds on Consul
are held on Base.
A stablecoin can exist on multiple chains. USDC exists on both Ethereum and Base.
USDT volume is primarily on Tron.
### Gas Fees
Every blockchain transaction requires a small fee called **gas**, paid to the network to process the
transaction. On Ethereum L1, gas can cost several dollars per transfer during peak congestion. On Base,
gas fees are typically fractions of a cent - making it practical for high-volume payouts.
**Consul abstracts away gas fees for you, so you don't need to worry about them.**
### On-Ramping
On-ramping is the process of converting fiat currency into stablecoins. When you deposit USD or
another fiat currency into your Consul balance, Consul converts those funds into USDC on Base.
This happens automatically - you send fiat and your payout balance is credited in USDC. See
[Flow of Funds](/guides/flow-of-funds) for deposit methods and timing.
### Off-Ramping
Once a recipient receives USDC, they can hold it, send it to others, or convert it to local currency.
Conversion to fiat - called **off-ramping** - is available through exchanges, local payment providers,
or directly through Consul. Recipients in any supported country can turn stablecoin payouts into
spendable local currency.
## Next Steps
Send your first payout in minutes
Jump into the V1 API
Understand how money moves through Consul
# Adding Stablecoin Payouts to a Marketplace
Source: https://docs.onconsul.com/guides/tutorials/existing-marketplace
Add Consul as a payout method to pay sellers in your marketplace
This guide walks through adding Consul as a payout method to an existing marketplace. Your platform manages a single
Consul treasury, pre-funds it, and pays sellers by email when transactions complete. Here, your backend is the only party
interacting with the Consul API.
## Prerequisites
| What | Why | Where to get it |
| --------------------- | ------------------------------------------- | ------------------------------------------- |
| Consul API key | Authenticate API requests from your backend | [Dashboard](https://dashboard.onconsul.com) |
| Funded Consul balance | Payouts draw from your balance | [Pre-Funding Flows](/guides/funding-flows) |
| Webhook endpoint | Receive real-time payout status updates | [Events and Webhooks](/guides/webhooks) |
## How It Works
When a sale completes on your platform, your backend calls the [Create a Payout](/api-reference/payouts/create-a-payout)
endpoint with the seller's email and amount. Consul handles the rest:
* If the seller already has a Consul account, USDC lands in their wallet in seconds.
* If they're new, Consul sends an email invite. A wallet is created behind the scenes and funds are held securely
until the seller claims their account.
You can batch multiple sellers into a single payout request. See the [Payouts](/guides/payouts) guide for details
on batching, idempotency, and the payout lifecycle.
## Tracking Payouts
Register a [webhook endpoint](/guides/webhooks) and subscribe to `payout.updated` events. This tells you when each
payout item settles or fails. You can also poll [GET /payouts/](/api-reference/payouts/get-a-payout) if you prefer.
A payout status of `pending_claim` means the seller hasn't created their Consul account yet. Funds are held
securely. The seller can claim the funds before the payout expires or is cancelled.
If cancelled via [Cancel a Payout](/api-reference/payouts/cancel-a-payout), funds are refunded to your Consul balance.
## Keeping Your Balance Funded
Your Consul balance needs to cover every payout you issue. The [Pre-Funding Flows](/guides/funding-flows) guide
covers several strategies depending on your needs, from simple end-of-day batching to pre-funded balances that
support instant payouts. Domestic wires are also available for same-day top-ups.
# Building a New Marketplace with Consul
Source: https://docs.onconsul.com/guides/tutorials/new-marketplace
Build a marketplace with buyer Consul accounts, escrow, and seller payouts
This guide covers a more advanced marketplace integration where buyers have their own Consul accounts connected via
OAuth. Funds flow from the buyer's account into your platform's central Consul account (escrow), and you release
them to sellers when conditions are met. If you don't need escrow or user-facing Consul accounts, see the simpler
[Adding Payouts to a Marketplace](/guides/tutorials/marketplace) tutorial.
## Prerequisites
| What | Why | Where to get it |
| ---------------------------------------------------------------- | -------------------------------------------------------- | -------------------------------------------- |
| Consul API key | Authenticate requests from your central (escrow) account | [Dashboard](https://dashboard.onconsul.com) |
| OAuth credentials (`client_id`, `client_secret`, `redirect_uri`) | Connect buyer Consul accounts to your app | Provided during app registration with Consul |
| Webhook endpoint | Track payout and deposit status updates | [Events and Webhooks](/guides/webhooks) |
## Architecture
Two sets of credentials are in play:
* **OAuth access tokens** (per buyer) — used to move funds *from* a buyer's Consul balance during checkout.
* **API key** (your central account) — used to release funds *to* sellers on completion.
```mermaid theme={"system"}
sequenceDiagram
participant Buyer
participant YourApp as Your App
participant Escrow as "Consul (Your Account)"
participant BuyerWallet as "Consul (Buyer Account)"
Buyer->>YourApp: Link Consul account
YourApp->>BuyerWallet: OAuth authorization flow
BuyerWallet-->>YourApp: Access token
Buyer->>YourApp: Checkout
YourApp->>BuyerWallet: Payout to your account (buyer's OAuth token)
BuyerWallet-->>Escrow: USDC transfer
Note over YourApp,Escrow: Funds held in escrow
YourApp->>Escrow: Payout to seller's email (API key)
```
## Connect Buyer Accounts
Use the [OAuth authorization code flow](/guides/oauth) to let buyers link their Consul accounts. When a buyer
connects, you receive an access token scoped to their account. Store it (along with the refresh token) per user.
The buyer sees a Consul consent screen and grants your app permission to initiate transfers on their behalf. See
the [OAuth Connections](/guides/oauth) guide for the full implementation, including token refresh and revocation.
## Checkout: Collect Funds into Escrow
When a buyer checks out, use their OAuth access token to call [Create a Payout](/api-reference/payouts/create-a-payout)
on their behalf. The payout recipient is your platform's own Consul account (by email or recipient ID). This moves
USDC from the buyer's balance into yours - effectively placing funds in escrow.
Before initiating the payout, check the buyer's balance via [GET /balance](/api-reference/accounts/get-consul-balance)
(using their OAuth token). If the balance is insufficient, you can request deposit instructions via the
[Deposits API](/api-reference/deposits/create-a-deposit-quote) on their behalf so they can fund their account.
Listen for the `deposit.updated` [webhook](/guides/webhooks) to know when funds arrive, then notify the buyer
that their balance is ready and they can complete checkout.
## Release: Payout to Seller
When your platform's conditions are met - delivery confirmed, milestone completed, dispute resolved - release funds
to the seller by calling [Create a Payout](/api-reference/payouts/create-a-payout) with your API key. Target the
seller by email. If the seller doesn't have a Consul account yet, they'll receive an email invite to claim their
funds.
Track settlement via `payout.updated` [webhooks](/guides/webhooks) or by polling the
[Payouts API](/api-reference/payouts/get-a-payout).
## Creating a Wallet UI
With OAuth, you can surface each buyer's Consul account details directly in your UI - effectively white-labelling Consul as a
wallet in your platform. Use the buyer's OAuth token to:
* **Show their balance** via [GET /balance](/api-reference/accounts/get-consul-balance)
* **List their transactions and statuses** via [GET /transactions](/api-reference/transactions/list-transactions)
* **Create deposits** via [Create a Deposit](/api-reference/deposits/create-a-deposit)
This lets buyers view their funds and transaction history without ever leaving your app.
## Refunds
If a dispute is resolved in the buyer's favor, issue a payout from your central account back to the buyer's email
using the same [Create a Payout](/api-reference/payouts/create-a-payout) endpoint.
# Events and Webhooks
Source: https://docs.onconsul.com/guides/webhooks
Receive real-time notifications for object lifecycle events
To receive real-time notifications about events in the Consul API, register a webhook endpoint
from the **Developers, Webhooks** section of the
[dashboard](https://dashboard.onconsul.com). Consul sends HTTP POST requests to your URL when
subscribed events occur.
Event notifications include an `event_type` header and a JSON body that names the event and
points to the affected resource. Categories are intentionally simple: they only indicate whether
an object was `created` or `updated`. To get the full object state, fetch it from the relevant
API endpoint. This avoids issues with concurrent or out-of-order notifications.
Webhook delivery today is best-effort. Automatic retries, durable queueing, and request signing
are on the roadmap but not yet shipped. Until they ship: design your handler to be idempotent,
treat your webhook URL as a shared secret (serve it on a hard-to-guess path), and fall back to
polling the relevant `GET` endpoint when you cannot afford to miss an event.
### Webhook Delivery
Each subscribed event triggers a single `POST` to your endpoint with a JSON body. Your handler
should return a `2xx` quickly, ideally by enqueueing the event for an async worker.
Non-`2xx` responses, network errors, and timeouts (10 seconds) are logged and dropped; there is
no automatic retry yet. If your endpoint is temporarily unavailable, expect to lose events for
the duration of the outage.
### Headers
Each delivery includes the following headers:
| Header | Description |
| ------------------- | --------------------------------------------- |
| `Content-Type` | Always `application/json`. |
| `User-Agent` | Identifies the request as coming from Consul. |
| `Consul-Event-Type` | The event type, e.g. `payout.updated`. |
Request signing is not currently emitted. When signing ships, deliveries will gain a
`Consul-Signature` header (HMAC-SHA256 over the raw body) and a `Consul-Timestamp` header for
replay protection; we'll publish a verification snippet alongside that release.
### Managing Subscriptions
Webhook subscriptions are managed exclusively from the dashboard. There is no API surface for
creating, listing, or deleting them, and OAuth-authenticated callers cannot manage subscriptions
on a user's behalf. To stop receiving events from an endpoint, delete the subscription from the
dashboard.
### Event Types
| Event | Description |
| -------------------------- | ------------------------------------------------------ |
| `deposit.created` | A fiat deposit has been initiated |
| `deposit.updated` | A fiat deposit status changed (e.g. completed, failed) |
| `inbound_transfer.created` | A stablecoin deposit was received on-chain |
| `payout.created` | An on-chain payout transfer was initiated |
| `payout.updated` | A payout transfer status changed |
| `withdrawal.created` | A fiat withdrawal (off-ramp) was initiated |
| `withdrawal.updated` | A fiat withdrawal status changed |
| `recipient.created` | A new recipient was created |
| `recipient.updated` | A recipient was updated |
# Welcome to Consul
Source: https://docs.onconsul.com/guides/welcome
Everything you need to set up global payouts
#### Getting Started
Consul is designed for agencies and marketplaces that want to pay creators, sellers,
and freelancers in stablecoins across the globe without forcing them to become crypto
experts. Instead of managing wallets, private keys, and compliance flows yourself,
Consul lets your platform send USDC/USDT to more than 180 countries using just an email
address.
Stablecoin payouts settle instantly, cost a fraction of traditional cross‑border
transfers, and are available 24/7. This guide introduces the core concepts and walks you
through setting up your first payout.
#### At a high level, Consul gives you:
* **Global reach.** USDC/USDT transfers can be sent to over 180 countries.
* **Low‑cost, instant settlement.** Sending stablecoins is free, and transfers clear in seconds rather than days.
* **Security and trust.** USDC/USDT transactions are cryptographically secure. The underlying stablecoins are fully backed by cash and U.S. Treasuries, and issuers must publish monthly attestations in line with U.S. law.
* **Email‑based delivery.** Your agency or marketplace only needs a recipient’s email address. Consul creates a secure wallet behind the scenes, handles private keys, and guides the recipient through claiming funds.
* **Compliance built‑in.** Consul offers two customer types - individuals and businesses - and performs Know‑Your‑Customer (KYC) / Know‑Your‑Business (KYB) on each customer.
* **Programmable payouts.** Stablecoins are fully programmable and can be used to build a variety of payout workflows. For example, you can hold funds in escrow to protect buyers and sellers.
When you release, the recipient instantly receives their funds. This is ideal for platforms with milestone‑based payouts or dispute resolution.
## Next Steps
Send your first payout in minutes
Jump into the V1 API
Learn how to fund your Consul balance