Skip to main content
Deploy regulated, fully-backed USD stablecoins on Monad and integrate them into your application with onramps, offramps, and custody.

Overview

Monad’s high-performance EVM, with 10,000+ tx/s and sub-second time-to-finality, makes it well-suited for stablecoin-powered applications. Brale lets businesses create their own stablecoins and then access them programmatically through a single API for money movements.

What You’ll Build

By the end of this guide, you’ll have:
  • A custom stablecoin launched on Monad
  • API credentials for programmatic control
  • Working integrations for onramps, transfers, swaps, and offramps

Why Custom Stablecoins?

Use CaseBenefit
Branded experienceYour token, your identity—build user trust
Direct fiat railsConnect to ACH, wire, and RTP through a single orchestration layer
Cross-chain liquidityMove value across supported chains through a unified transfers model
Compliance built-inBrale handles the regulated issuance and orchestration layer

Prerequisites

Before starting, ensure you have:

Choose Your Environment

Use Brale testnet while developing and switch to mainnet for production. In testnet, use monad_testnet as the transfer_type and in mainnet, use monad. Brale’s testnet supports testnet networks only and skips the real fiat leg on mint and redemption flows.

Part 1: Launch Your Stablecoin in Brale

Stablecoin issuance is a dashboard workflow. API integration begins after your stablecoin has been launched. Once issued, the stablecoin is available via the API.

Step 1: Complete Business Verification

  1. Log in to the Brale Dashboard
  2. Complete KYB (Know Your Business) verification:
    • Business name and EIN
    • Beneficial ownership information

Step 2: Launch Your Stablecoin

In the Brale Dashboard, create your stablecoin and configure:
  • Name: Your stablecoin’s display name (for example, Acme Dollar)
  • Symbol: Token ticker (for example, ACME)
  • Chains: Select Monad
  • Branding: Add your token logo
Then fund it via Wire, ACH, or USDC. Once your stablecoin is live, continue with the API setup below.

Part 2: Integrate with the Brale API

Brale’s API uses OAuth 2.0 client credentials, environment-specific applications, and address_id values as the source/destination primitive for transfers.

Step 1: Create API Credentials

  1. Sign in to your Brale account
  2. Navigate to Settings → API
  3. Click Create Application
  4. Give the application a name and choose Mainnet or Testnet
  5. Copy the generated CLIENT_ID and CLIENT_SECRET
Brale API applications are environment-specific, and the same application credentials can be used for direct API access and the hosted Brale API MCP server.

Step 2: Authenticate

Exchange your credentials for a bearer token:
export CLIENT_ID="your_client_id"
export CLIENT_SECRET="your_client_secret"

CLIENT_CREDENTIALS=$(
  printf "%s:%s" "${CLIENT_ID}" "${CLIENT_SECRET}" | base64 | tr -d '\n'
)

curl --request POST \
  --url "https://auth.brale.xyz/oauth2/token" \
  --header "Authorization: Basic ${CLIENT_CREDENTIALS}" \
  --header "Content-Type: application/x-www-form-urlencoded" \
  --data grant_type=client_credentials
Response
{
  "access_token": "eyJ...",
  "token_type": "Bearer",
  "expires_in": 3600
}
Use the returned token in subsequent requests:
export AUTH_TOKEN="your_access_token"

Step 3: Set Your Working Variables

export ACCOUNT_ID="your_account_id"
export TOKEN_SYMBOL="ACME"

# Use testnet while developing
export MONAD_TRANSFER_TYPE="monad_testnet"

# Switch to mainnet for production
# export MONAD_TRANSFER_TYPE="monad"

Step 4: Retrieve Your account_id

List your accounts and identify the account you want to operate on:
curl --request GET \
  --url "https://api.brale.xyz/accounts" \
  --header "Authorization: Bearer ${AUTH_TOKEN}"

Step 5: Get Your Internal Monad address_id

Brale automatically generates internal custodial addresses on supported chains for onboarded accounts. Retrieve the internal Monad address you want to use for custodial flows:
curl --request GET \
  --url "https://api.brale.xyz/accounts/${ACCOUNT_ID}/addresses?type=internal&transfer_type=${MONAD_TRANSFER_TYPE}" \
  --header "Authorization: Bearer ${AUTH_TOKEN}"
Store the returned internal Monad address_id:
export MONAD_ADDRESS_ID="your_internal_monad_address_id"

Step 6: Register an External Monad Wallet (Optional)

If you want to send to or receive from a self-custody Monad wallet, register it as an external address first. In Brale, addresses can be type=internal or type=external, and both are represented by address_id values used in transfers.
curl --request POST \
  --url "https://api.brale.xyz/accounts/${ACCOUNT_ID}/addresses/external" \
  --header "Authorization: Bearer ${AUTH_TOKEN}" \
  --header "Content-Type: application/json" \
  --header "Idempotency-Key: $(uuidgen)" \
  --data '{
    "name": "User Monad Wallet",
    "address": "0x1234567890abcdef1234567890abcdef12345678",
    "transfer_types": ["'"${MONAD_TRANSFER_TYPE}"'"]
  }'
Response
{
  "id": "2VcUIIsgARwVbEGlIYbhg6fGG57"
}
Store the returned id as the wallet’s Brale address_id:
export EXTERNAL_MONAD_ADDRESS_ID="your_external_monad_address_id"

Alternative: Brale API MCP Server

Brale offers a hosted API MCP server for live API access from MCP-compatible clients. The remote server URL is https://mcp.brale.xyz/.

Setup for Claude Desktop

  1. Open Settings → Connectors
  2. Click Add custom connector
  3. Enter the remote MCP server URL: https://mcp.brale.xyz/
  4. Open Advanced settings and enter your OAuth Client ID and Client Secret
  5. Save the connector
Once added, enable it from the Search and Tools menu in chat.
Custom connectors are available on Pro, Max, Team, and Enterprise plans.

Setup for Cursor

Add a server entry in Settings → MCP:
{
  "name": "Brale API MCP",
  "url": "https://mcp.brale.xyz/",
  "env": {
    "CLIENT_ID": "your_client_id",
    "CLIENT_SECRET": "your_client_secret"
  }
}

Setup for Windsurf

Edit ~/.codeium/windsurf/mcp_config.json:
{
  "mcpServers": {
    "Brale API MCP": {
      "url": "https://mcp.brale.xyz/",
      "env": {
        "CLIENT_ID": "your_client_id",
        "CLIENT_SECRET": "your_client_secret"
      }
    }
  }
}
Restart Windsurf after saving.

Move Value with Transfers

All stablecoin movement is handled through the Transfers API. Every transfer is scoped to an account_id, references address_id values where applicable, and uses transfer_type plus value_type to define the rail/chain and asset being moved.

Base URL

https://api.brale.xyz/accounts/${ACCOUNT_ID}/transfers

Required Headers

Authorization: Bearer ${AUTH_TOKEN}
Content-Type: application/json
Idempotency-Key: $(uuidgen)
Always include a fresh Idempotency-Key on create requests.

Onramps: Fiat to Stablecoin

Convert USD to your stablecoin on Monad.

Option A: Wire Onramp

curl --request POST \
  --url "https://api.brale.xyz/accounts/${ACCOUNT_ID}/transfers" \
  --header "Authorization: Bearer ${AUTH_TOKEN}" \
  --header "Content-Type: application/json" \
  --header "Idempotency-Key: $(uuidgen)" \
  --data '{
    "amount": {
      "value": "1000.00",
      "currency": "USD"
    },
    "source": {
      "value_type": "usd",
      "transfer_type": "wire"
    },
    "destination": {
      "address_id": "'"${MONAD_ADDRESS_ID}"'",
      "value_type": "'"${TOKEN_SYMBOL}"'",
      "transfer_type": "'"${MONAD_TRANSFER_TYPE}"'"
    }
  }'

Option B: ACH Onramp

If you are funding from a linked bank account, use an ACH funding address and mint to your Monad destination address:
export FUNDING_ADDRESS_ID="your_ach_funding_address_id"

curl --request POST \
  --url "https://api.brale.xyz/accounts/${ACCOUNT_ID}/transfers" \
  --header "Authorization: Bearer ${AUTH_TOKEN}" \
  --header "Content-Type: application/json" \
  --header "Idempotency-Key: $(uuidgen)" \
  --data '{
    "amount": {
      "value": "1000.00",
      "currency": "USD"
    },
    "source": {
      "address_id": "'"${FUNDING_ADDRESS_ID}"'",
      "value_type": "usd",
      "transfer_type": "ach_debit"
    },
    "destination": {
      "address_id": "'"${MONAD_ADDRESS_ID}"'",
      "value_type": "'"${TOKEN_SYMBOL}"'",
      "transfer_type": "'"${MONAD_TRANSFER_TYPE}"'"
    }
  }'

Option C: Automated Onramps

For recurring flows, create an Automation that mints stablecoins automatically when USD arrives:
  1. In the Dashboard, navigate to Automations
  2. Create an automation that listens for inbound ACH or wire funding
  3. Set the destination to your Monad address
  4. Save the automation

Transfers: Move Stablecoins

Transfer your stablecoin between addresses on Monad or across chains.

On-Chain Transfer (Monad)

export SOURCE_ADDRESS_ID="your_source_monad_address_id"
export DESTINATION_ADDRESS_ID="your_destination_monad_address_id"

curl --request POST \
  --url "https://api.brale.xyz/accounts/${ACCOUNT_ID}/transfers" \
  --header "Authorization: Bearer ${AUTH_TOKEN}" \
  --header "Content-Type: application/json" \
  --header "Idempotency-Key: $(uuidgen)" \
  --data '{
    "amount": {
      "value": "500.00",
      "currency": "USD"
    },
    "source": {
      "address_id": "'"${SOURCE_ADDRESS_ID}"'",
      "value_type": "'"${TOKEN_SYMBOL}"'",
      "transfer_type": "'"${MONAD_TRANSFER_TYPE}"'"
    },
    "destination": {
      "address_id": "'"${DESTINATION_ADDRESS_ID}"'",
      "value_type": "'"${TOKEN_SYMBOL}"'",
      "transfer_type": "'"${MONAD_TRANSFER_TYPE}"'"
    }
  }'

Cross-Chain Transfer

Move your stablecoin from Monad to another supported chain:
export BASE_ADDRESS_ID="your_base_address_id"

curl --request POST \
  --url "https://api.brale.xyz/accounts/${ACCOUNT_ID}/transfers" \
  --header "Authorization: Bearer ${AUTH_TOKEN}" \
  --header "Content-Type: application/json" \
  --header "Idempotency-Key: $(uuidgen)" \
  --data '{
    "amount": {
      "value": "500.00",
      "currency": "USD"
    },
    "source": {
      "address_id": "'"${MONAD_ADDRESS_ID}"'",
      "value_type": "'"${TOKEN_SYMBOL}"'",
      "transfer_type": "'"${MONAD_TRANSFER_TYPE}"'"
    },
    "destination": {
      "address_id": "'"${BASE_ADDRESS_ID}"'",
      "value_type": "'"${TOKEN_SYMBOL}"'",
      "transfer_type": "base"
    }
  }'

Swaps: Exchange Stablecoins

Swap between supported stablecoins at 1:1 with no slippage.
Confirm the specific value_type and transfer_type combination you plan to use before production.

Example: Swap Your Token for USDC

export USDC_DESTINATION_ADDRESS_ID="your_usdc_destination_address_id"

curl --request POST \
  --url "https://api.brale.xyz/accounts/${ACCOUNT_ID}/transfers" \
  --header "Authorization: Bearer ${AUTH_TOKEN}" \
  --header "Content-Type: application/json" \
  --header "Idempotency-Key: $(uuidgen)" \
  --data '{
    "amount": {
      "value": "250.00",
      "currency": "USD"
    },
    "source": {
      "address_id": "'"${MONAD_ADDRESS_ID}"'",
      "value_type": "'"${TOKEN_SYMBOL}"'",
      "transfer_type": "'"${MONAD_TRANSFER_TYPE}"'"
    },
    "destination": {
      "address_id": "'"${USDC_DESTINATION_ADDRESS_ID}"'",
      "value_type": "USDC",
      "transfer_type": "base"
    }
  }'

Offramps: Stablecoin to Fiat

Convert your stablecoin back to USD via wire or ACH.

Offramp to Wire

export USD_DESTINATION_ADDRESS_ID="your_wire_destination_address_id"

curl --request POST \
  --url "https://api.brale.xyz/accounts/${ACCOUNT_ID}/transfers" \
  --header "Authorization: Bearer ${AUTH_TOKEN}" \
  --header "Content-Type: application/json" \
  --header "Idempotency-Key: $(uuidgen)" \
  --data '{
    "amount": {
      "value": "1000.00",
      "currency": "USD"
    },
    "source": {
      "address_id": "'"${MONAD_ADDRESS_ID}"'",
      "value_type": "'"${TOKEN_SYMBOL}"'",
      "transfer_type": "'"${MONAD_TRANSFER_TYPE}"'"
    },
    "destination": {
      "address_id": "'"${USD_DESTINATION_ADDRESS_ID}"'",
      "value_type": "usd",
      "transfer_type": "wire"
    }
  }'

Offramp to ACH

For ACH payouts, use ach_credit or same_day_ach_credit on the destination address instead of wire.

Monitoring Transfers

Check transfer status by querying the transfer ID:
export TRANSFER_ID="your_transfer_id"

curl --request GET \
  --url "https://api.brale.xyz/accounts/${ACCOUNT_ID}/transfers/${TRANSFER_ID}" \
  --header "Authorization: Bearer ${AUTH_TOKEN}"

Transfer States

StatusDescription
pendingTransfer submitted but not yet in progress
processingTransfer is in progress
completeTransfer finalized and funds have arrived at the destination
canceledTransfer has been canceled
failedTransfer could not be completed

Best Practices

Security

  • Store API credentials in environment variables or a secrets manager
  • Use a fresh idempotency key for each new transfer request
  • Refresh bearer tokens when they expire

Testing

  • Use testnet credentials during development
  • Use monad_testnet while testing and monad in production
  • Do not mix testnet credentials/resources with mainnet flows

Addressing

  • Use internal addresses for Brale-custodied wallets
  • Register self-custody Monad wallets as external addresses before using them in transfers
  • Store returned address_id values and reuse them across workflows

Next Steps