Skip to main content

Overview

CDP offers two types of webhook subscriptions for monitoring wallet activity:
  • Transaction Webhooks — Track the full lifecycle of transactions sent from your wallets using wallet.transaction.* events (created, broadcast, confirmed, failed, and more).
  • Onchain Data Webhooks — Monitor onchain token transfers (ERC-20) into and out of any address, even for non-CDP wallets, using onchain.activity.detected or wallet.activity.detected events.
Transaction webhooks work the same way for both Server Wallets and Embedded Wallets.

Transaction webhooks

Transaction webhooks let you subscribe to real-time events that track the full lifecycle of transactions sent from your CDP wallets. Events fire as your transaction moves through each stage — from creation to confirmation or failure. Use transaction webhooks when you need to:
  • Monitor whether a transaction has been signed, broadcast, or confirmed
  • Detect stuck or pending transactions and take action (e.g., submit a replacement)
  • Build reliable transaction status tracking without polling

Event types

The following event types cover the full transaction lifecycle:
EventDescription
wallet.transaction.createdTransaction is created
wallet.transaction.signedTransaction is signed
wallet.transaction.broadcastTransaction is broadcast to the network
wallet.transaction.pendingTransaction stuck in mempool for more than 30 seconds
wallet.transaction.replacedTransaction replaced an existing pending transaction
wallet.transaction.confirmedTransaction confirmed onchain
wallet.transaction.failedTransaction failed onchain or was replaced

Creating a subscription

Use the SDK to create a webhook subscription for transaction lifecycle events:
import { CdpClient } from "@coinbase/cdp-sdk";

const cdp = new CdpClient();

const subscription = await cdp.webhooks.createSubscription({
  description: "Monitor wallet transactions",
  eventTypes: [
    "wallet.transaction.created",
    "wallet.transaction.broadcast",
    "wallet.transaction.pending",
    "wallet.transaction.confirmed",
    "wallet.transaction.failed",
    "wallet.transaction.replaced",
    "wallet.transaction.signed",
  ],
  targetUrl: "https://example.com/webhook",
  isEnabled: true,
});

EVM

Supported events

All transaction lifecycle events are supported on EVM:
EventDescription
wallet.transaction.createdTransaction is created
wallet.transaction.signedTransaction is signed (signEvmTransaction)
wallet.transaction.broadcastTransaction is broadcast to the network
wallet.transaction.pendingTransaction stuck in mempool for more than 30 seconds
wallet.transaction.replacedTransaction replaced an existing pending transaction
wallet.transaction.confirmedTransaction confirmed onchain
wallet.transaction.failedTransaction failed onchain or was replaced

Webhook payload

EVM payloads include the transaction_hash and the address of the account that sent the transaction. Confirmed event:
{
  "transaction_hash": "0x123...",
  "address": "0xabc...",
  "network": "base-sepolia",
  "confirmed_at": "2024-01-15T10:30:00Z"
}
Pending event: The pending event includes gas parameters so you can evaluate whether to submit a replacement transaction with higher gas.
{
  "transaction_hash": "0x123...",
  "address": "0xabc...",
  "network": "base-sepolia",
  "pending_since": "2024-01-15T10:30:00Z",
  "max_fee_per_gas": "1000000000",
  "max_priority_fee_per_gas": "100000000"
}

Solana

Supported events

Solana supports a subset of transaction lifecycle events. The pending and replaced events are not supported because Solana does not have a mempool or transaction replacement mechanism.
EventDescription
wallet.transaction.createdTransaction is created
wallet.transaction.signedTransaction is signed (signSolanaTransaction)
wallet.transaction.broadcastTransaction is broadcast to the network
wallet.transaction.confirmedTransaction confirmed onchain
wallet.transaction.failedTransaction failed onchain

Webhook payload

Solana payloads use transaction_signature instead of transaction_hash and do not include an address field. Confirmed event:
{
  "transaction_signature": "5xyz...",
  "network": "solana-devnet",
  "confirmed_at": "2024-01-15T10:30:00Z"
}

Transaction lifecycle scenarios

The events you receive depend on how the transaction progresses through the network.

Transaction confirmed successfully

The most common path: a transaction is created, broadcast, and confirmed onchain.
created → broadcast → confirmed
No action required.

Transaction failed onchain

The transaction was broadcast but failed during execution (e.g., reverted).
created → broadcast → failed
Handle the failure in your application logic.

Transaction replaced

When a stuck transaction is replaced with a new one (e.g., with higher gas), the original and replacement each emit separate events.
TransactionEvents
Original (stuck)failed
Replacementbroadcastreplacedconfirmed or failed
The original transaction fires a failed event because it will never land onchain. Handle the old transaction failure accordingly.

Transaction stuck > 30 seconds (EVM only)

If a transaction remains pending in the mempool for more than 30 seconds, a pending event fires with gas parameters. You can use this to decide whether to submit a replacement transaction or continue waiting.
created → broadcast → pending

Transaction monitoring expires (EVM only)

If a transaction is still pending when the monitoring window expires, no further events are fired.
created → broadcast → pending → (no further events)
The pending event does not indicate failure. It means the transaction has been pending for more than 30 seconds. In the rare case that the monitoring window (3 minutes) expires without a confirmed or failed event, you can query the network to check whether the transaction is still pending, has since confirmed, or has failed.

Best practices

Always verify the signature on incoming webhook payloads using the secret returned when you created the subscription. This ensures the payload is genuinely from CDP and has not been tampered with. See Verify webhook signatures for implementation details.
CDP guarantees at-least-once delivery for all webhook events fired within the monitoring window. Most transactions confirm well within this period. In the rare case that a transaction is still pending when the monitoring window expires (3 minutes for EVM, 2 minutes for Solana), no further events are fired. You can query the network at that point to check whether the transaction is still pending, has since confirmed, or has failed.
The pending event means a transaction has been in the mempool for more than 30 seconds — it does not mean the transaction failed. When you receive a pending event, you can:
  • Submit a replacement transaction with higher gas (using the gas parameters in the payload)
  • Continue waiting for a confirmed or failed event
In the rare case that the 3-minute monitoring window expires without a terminal event, you can query the network to check the transaction’s current status.

Onchain data webhooks

This section 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.
Both onchain.activity.detected and wallet.activity.detected cover 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 these subscription types.
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:
Sign up at portal.cdp.coinbase.com, then navigate to API Keys and select Create API key under the Secret API Keys tab.
  1. Enter an API key nickname (restrictions are optional)
  2. Click Create
  3. Secure your API Key ID and Secret in a safe location
You’ll need an HTTPS URL to receive webhook events. For quick testing, webhook.site gives free temporary URLs instantly.For production, use your own HTTPS endpoint.
Install cdpcurl to make authenticated requests to CDP APIs:
# With Homebrew
brew tap coinbase/cdpcurl && brew install cdpcurl

# Or with Go
go install github.com/coinbase/cdpcurl@latest
If you don’t already have one, follow the Server Wallet v2 Quickstart.After completing it, you should have 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.
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.
wallet.activity.detected covers ERC-20 token transfers only. Native ETH transfers are a different event stream and are not currently supported. Do not expect ETH transfer notifications from this subscription type.
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
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.

Create webhook subscription

Full reference for supported event types, label filters (params.*, transaction_from, transaction_to), and advanced options

Onchain Data Webhooks Overview

Learn more about webhook capabilities, delivery guarantees, and supported use cases

Verify Webhook Signatures

Validate that webhook payloads are genuinely from CDP

Sending Transactions

Learn about the Sign and Send APIs for EVM transactions