# 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. Consul OAuth Linking Flow ### 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. Deposit Instructions 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