Custom Payment Flow Webhooks

View as Markdown

Real-time webhook notifications sent when payments are received via custom payment flow. CryptAPI sends two types of webhooks: pending (payment detected in mempool) and confirmed (payment verified by blockchain).

Method: You choose when creating the payment:

  • GET (default) - Webhook data sent as URL query parameters
  • POST (set post=1) - Webhook data sent in request body

Content-Type: You choose when creating the payment:

  • application/x-www-form-urlencoded (default) - Standard form encoding
  • application/json (set json=1) - JSON format in request body

Tracking Payments with Custom Parameters


Webhook Types

Pending Webhook

Sent when a payment is detected in the blockchain mempool but not yet confirmed. Particularly useful for slower blockchains (Bitcoin, Litecoin, Bitcoin Cash) to provide immediate user feedback.

Confirmed Webhook

Sent when a payment receives the specified number of confirmations. The transaction is considered final and funds are forwarded to your address.


Webhook Fields

Common Fields (Both Pending & Confirmed)

These fields are included in both pending and confirmed webhooks:

uuidstringRequired

Unique identifier for each payment transaction. Use this to prevent processing duplicate webhooks.

Important: Always store and check this UUID in your database before processing any payment to avoid duplicates.

address_instringRequired

CryptAPI-generated payment address where your customer sent the payment.

address_outstringRequired

Your destination address(es) where CryptAPI forwards the payment.

Format:

  • Single address: 1H6ZZpRmMnrw8ytepV3BYwMjYYnEkWDqVP
  • Multiple addresses: {1H6ZZpRmMnrw8ytepV3BYwMjYYnEkWDqVP: 0.70, 1PE5U4temq1rFzseHHGE2L8smwHCyRbkx3: 0.30}

txid_instringRequired

Transaction hash of your customer's payment on the blockchain.

coinstringRequired

Cryptocurrency ticker used for the payment.

Format:

  • Native coins: btc, eth, ltc, bch, trx
  • ERC-20 tokens: erc20_usdt, erc20_usdc
  • TRC-20 tokens: trc20_usdt, trc20_usdc
  • BEP-20 tokens: bep20_usdt, bep20_usdc
  • Polygon tokens: polygon_usdt, polygon_usdc

pricenumberRequired

Cryptocurrency price in USD at the time the webhook was sent.

pendingintegerRequired

Indicates webhook type:

  • 1 = Pending webhook (payment detected, not confirmed)
  • 0 = Confirmed webhook (payment verified and forwarded)

Confirmed Webhook Only Fields

These additional fields are only included in confirmed webhooks (pending=0):

txid_outstringRequired

Transaction hash of CryptAPI's forwarding transaction to your address(es).

confirmationsintegerRequired

Number of blockchain confirmations the transaction has received.

value_coinnumberRequired

Payment amount sent by your customer before any fees are deducted.

Note: For the amount after fees, use value_forwarded_coin

value_coin_convertstring

JSON-encoded object with FIAT currency conversions of value_coin.

Availability: Only when convert=1 was set during payment creation

Format: {"USD": "3.20", "EUR": "3.05", "GBP": "2.62", "CAD": "4.16", ...}

value_forwarded_coinnumberRequired

Amount forwarded to your address after CryptAPI fees are deducted.

value_forwarded_coin_convertstring

JSON-encoded object with FIAT currency conversions of value_forwarded_coin.

Note: Only present when convert=1 was set during payment creation

Format: {"USD": "3.17", "EUR": "3.01", "GBP": "2.59", "CAD": "4.12", ...}

fee_coinnumberRequired

CryptAPI service fee deducted from the value_coin amount.


Security: Verify Webhook Signatures


Reliability and Best Practices

For important information on how CryptAPI handles webhook delivery, retries, and timeouts, along with essential best practices for building a reliable webhook handler, please see our main guide.

➡️ Read the How Webhooks Work Guide


Implementation Examples

// Express.js webhook handler
app.post('/webhook', express.json(), (req, res) => {
  // 1. Verify the webhook signature
  if (!verifyWebhookSignature(req)) {
    return res.status(401).send('Unauthorized');
  }

  // 2. Extract data from the payload
  const { uuid, pending, value_coin, coin, order_id, user_id } = req.body;
  
  // Note: order_id and user_id come from custom parameters you added to notify_url
  
  // 3. Check for duplicates using UUID
  if (isWebhookAlreadyProcessed(uuid)) {
    return res.status(200).send('*ok*');
  }
  
  // 4. Handle based on webhook type
  if (pending === 1) {
    // Handle pending payment
    notifyUser(user_id, 'Payment detected, awaiting confirmation...');
  } else if (pending === 0) {
    // Handle confirmed payment  
    processSuccessfulPayment({
      uuid,
      value_coin,
      coin,
      orderId: order_id,
      userId: user_id
    });
  }
  
  // 5. Mark this webhook as processed using the UUID
  markWebhookAsProcessed(uuid);
  
  // Always respond to stop retries
  res.status(200).send('*ok*');
});