Skip to main content

Overview

This guide shows how to use Onchain Data Webhooks from a Server Wallet workflow to create webhook subscriptions via our REST endpoints and receive events at your target destination.
Enhanced webhooks for Server Wallets and Embedded Wallets are coming soon. Today, this guide uses Onchain Data webhooks with wallet address filters.
This is a Server Wallet-focused adaptation of the Onchain Data Webhooks flow, tailored for use cases such as tracking USDC transfers into and out of wallet addresses.

Prerequisites

Before setting up your webhook subscription, make sure you have the following:
If you don’t already have one, follow the Server Wallet v2 Quickstart.After completing it, you should have:
  1. CDP_API_KEY_ID
  2. CDP_API_KEY_SECRET
  3. An EVM wallet address to use as WALLET_ADDRESS
Funding is not required to create a webhook subscription. Funding is required only if you want to trigger and validate live transfer events.
You’ll need an HTTPS URL to receive webhook events.
  1. Go to webhook.site.
  2. Copy your unique HTTPS URL.
  3. Use that URL as WEBHOOK_URL in Local environment variables configured.
Install cdpcurl to make authenticated requests to CDP APIs:
  1. Install with Homebrew or Go:
# With Homebrew
brew tap coinbase/cdpcurl && brew install cdpcurl

# Or with Go
go install github.com/coinbase/cdpcurl@latest
Set these once in your terminal so you can run the commands in this guide as-is. This guide defaults to Base Sepolia for testnet validation.
export CDP_API_KEY_ID="YOUR_API_KEY_ID" # auth cdpcurl requests
export CDP_API_KEY_SECRET="YOUR_API_KEY_SECRET" # auth cdpcurl requests
export WEBHOOK_URL="https://webhook.site/YOUR_UNIQUE_ID" # i.e. webhook.site
export WALLET_ADDRESS="0xYourServerWalletAddress" # your CDP wallet address
export NETWORK="base-sepolia" # testnet before going live
export USDC_CONTRACT_ADDRESS="0x036CbD53842c5426634e7929541eC2318f3dCF7e" # USDC on Base Sepolia
USDC_CONTRACT_ADDRESS tells the webhook which token contract to watch. Without this filter, your subscription may match transfers from other tokens too. The value shown above is the USDC contract on Base Sepolia (testnet).
If you downloaded your Secret API key file (cdp_api_key.json) from CDP Portal, you can load credentials directly:
export CDP_API_KEY_ID="$(jq -r '.id // .name' "$HOME/Downloads/cdp_api_key.json")"
export CDP_API_KEY_SECRET="$(jq -r '.privateKey' "$HOME/Downloads/cdp_api_key.json")"
brew install jq

1. Prepare subscription payload

In this example, you will track USDC transfers in or out of your CDP Server Wallet. Each subscription listens for onchain.activity.detected events on the USDC contract, filtered by your wallet address using the params.from and params.to labels.
Run this to create outgoing-usdc.json using your exported env vars:
cat > outgoing-usdc.json << EOF
{
  "description": "USDC Transfers",
  "eventTypes": ["onchain.activity.detected"],
  "target": {
    "url": "$WEBHOOK_URL",
    "method": "POST"
  },
  "labels": {
    "network": "$NETWORK",
    "contract_address": "$USDC_CONTRACT_ADDRESS",
    "event_name": "Transfer",
    "params.from": "$WALLET_ADDRESS"
  },
  "isEnabled": true
}
EOF
For all available fields and advanced options, see REST API Reference.
You can filter on any ERC-20 event parameter using params.[any_param] in labels (e.g. params.from, params.to, params.value). See the configuration fields table in the Onchain Data Webhooks Quickstart for the full list of supported label filters.
onchain.activity.detected covers ERC-20 token transfers only. Native ETH transactions are a different event stream and are not currently supported. Do not expect ETH transfer notifications from this subscription type.
If you want to monitor all ERC-20 token transfer activity on your wallet address across any token (without specifying a contract), use wallet.activity.detected instead. This is a simpler approach and is what CDP Portal uses by default.
wallet.activity.detected covers ERC-20 token transfers only. Native ETH transfers are not currently supported.
Use this when:
  • You want to track all ERC-20 tokens, not just USDC
  • You don’t need contract-level filtering
  • You prefer fewer labels and simpler setup
The same two-subscription pattern applies and you should use one per direction. Because labels use AND logic, params.from and params.to cannot be combined in a single subscription.
cat > outgoing-wallet.json << EOF
{
  "description": "Outgoing transfers",
  "eventTypes": ["wallet.activity.detected"],
  "target": {
    "url": "$WEBHOOK_URL"
  },
  "labels": {
    "params.from": "$WALLET_ADDRESS"
  },
  "isEnabled": true
}
EOF

cat > incoming-wallet.json << EOF
{
  "description": "Incoming transfers",
  "eventTypes": ["wallet.activity.detected"],
  "target": {
    "url": "$WEBHOOK_URL"
  },
  "labels": {
    "params.to": "$WALLET_ADDRESS"
  },
  "isEnabled": true
}
EOF
Then create both subscriptions using the same cdpcurl commands from step 2, replacing outgoing-usdc.json / incoming-usdc.json with outgoing-wallet.json / incoming-wallet.json.

2. Subscribe to transfer events

Using the configurations you created in the previous step, create webhook subscriptions using cdpcurl.
cdpcurl -X POST \
  -i "$CDP_API_KEY_ID" \
  -s "$CDP_API_KEY_SECRET" \
  "https://api.cdp.coinbase.com/platform/v2/data/webhooks/subscriptions" \
  -d "$(cat outgoing-usdc.json)"
You should see a response similar to the following, indicating the subscription was created:
response.json
201 Created
{
  "createdAt": "2025-10-08T13:58:38.681893Z",
  "description": "USDC Transfers",
  "eventTypes": [
    "onchain.activity.detected"
  ],
  "isEnabled": true,
  "labels": {
    "project": "<YOUR_CDP_PROJECT_ID>",
    "network": "base-sepolia",
    "contract_address": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
    "event_name": "Transfer",
    "params.to": "0xYourWalletAddress"
  },
  "metadata": {
    "secret": "<SECRET_FOR_WEBHOOK_VERIFICATION>"
  },
  "subscriptionId": "<YOUR_SUBSCRIPTION_ID>",
  "target": {
    "url": "https://your-webhook-url.com"
  }
}

3. Verify webhook event capture

Use the CDP Faucet to send 1 USDC to your wallet and confirm the webhook fires.
1

Send USDC via CDP Faucet

Open the CDP Faucet, select Base Sepolia + USDC, paste your WALLET_ADDRESS, and click Send.
2

Check your webhook URL

Open your WEBHOOK_URL and wait for an incoming POST request. You should receive an onchain.activity.detected payload similar to:
{
  "block_number": 12345678,
  "contract_address": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
  "event_name": "Transfer",
  "event_signature": "Transfer(address,address,uint256)",
  "network": "base-sepolia",
  "parameters": {
    "from": "0xFaucetAddress",
    "to": "0xYourWalletAddress",
    "value": "1000000"
  },
  "timestamp": "2026-03-04T22:13:42Z",
  "transaction_hash": "0xYourTransactionHash"
}
3

Confirm the event matches your filter

Check that parameters.to matches your WALLET_ADDRESS you exported in the prerequisites. This confirms your subscription to incoming transfer events is working successfully.To test outgoing, send USDC from your wallet and confirm parameters.from matches. See Transferring tokens from a CDP Wallet.
For managing subscriptions after creation (list, view, update, delete), see the REST API Reference.