# How Webhooks Work Webhooks (also called callbacks or IPNs) are how BlockBee notifies your application of events in real time — payment received, payment confirmed, subscription due, and others. Your application registers a URL; BlockBee POSTs to it as events occur. No polling required. ## How Webhooks Work ```mermaid sequenceDiagram participant App as "Your App" participant BlockBee as "BlockBee" participant Customer App->>BlockBee: Request payment with webhook URL BlockBee-->>App: Return payment address Customer->>BlockBee: Makes payment BlockBee->>App: Send webhook notification App-->>BlockBee: Respond "*ok*" ``` ## 1. Create a Webhook Endpoint Create an endpoint in your application to receive BlockBee's notifications. The endpoint must: - Be publicly accessible over HTTPS. - Accept `POST` requests. - Respond with `200` and the body `*ok*`. Any other response is treated as a failure, and we retry. ## 2. Provide the Endpoint URL to BlockBee Pass your endpoint URL when creating a payment, deposit, or subscription. The parameter is `callback` for Custom Payment Flow, `notify_url` for Checkout. ```bash # Custom Payment Flow curl "https://api.blockbee.io/btc/create/?apikey=YOUR_API_KEY&callback=https://yoursite.com/webhook" ``` > **TIP** >**URL-encode your webhook URL** if it contains its own query parameters. For example, `https://yoursite.com/webhook?order_id=123` becomes `https%3A//yoursite.com/webhook%3Forder_id%3D123`. ```javascript const originalUrl = 'https://yoursite.com/webhook?order_id=123'; const encodedUrl = encodeURIComponent(originalUrl); // encodedUrl: "https%3A%2F%2Fyoursite.com%2Fwebhook%3Forder_id%3D123" ``` ```php ``` ```python import urllib.parse original_url = 'https://yoursite.com/webhook?order_id=123' encoded_url = urllib.parse.quote(original_url, safe='') # encoded_url is "https%3A%2F%2Fyoursite.com%2Fwebhook%3Forder_id%3D123" ``` ```ruby require 'uri' original_url = 'https://yoursite.com/webhook?order_id=123' encoded_url = URI.encode_www_form_component(original_url) # encoded_url is "https%3A%2F%2Fyoursite.com%2Fwebhook%3Forder_id%3D123" ``` ```cs using System.Net; string originalUrl = "https://yoursite.com/webhook?order_id=123"; string encodedUrl = WebUtility.UrlEncode(originalUrl); // encodedUrl is "https%3A%2F%2Fyoursite.com%2Fwebhook%3Forder_id%3D123" ``` ```java import java.net.URLEncoder; import java.nio.charset.StandardCharsets; String originalUrl = "https://yoursite.com/webhook?order_id=123"; String encodedUrl = URLEncoder.encode(originalUrl, StandardCharsets.UTF_8.toString()); // encodedUrl is "https%3A%2F%2Fyoursite.com%2Fwebhook%3Forder_id%3D123" ``` ```go import "net/url" originalUrl := "https://yoursite.com/webhook?order_id=123" encodedUrl := url.QueryEscape(originalUrl) // encodedUrl is "https%3A%2F%2Fyoursite.com%2Fwebhook%3Forder_id%3D123" ``` ```curl # The final API call with the URL-encoded callback parameter curl "https://api.blockbee.io/btc/create/?apikey=YOUR_API_KEY&callback=https%3A%2F%2Fyoursite.com%2Fwebhook%3Forder_id%3D123" ``` ## 3. Secure Your Endpoint > **WARNING** >**Verify webhook signatures.** Signature verification confirms the request came from BlockBee and was not modified in transit. Skipping this step exposes your integration to spoofed events. > > **[Read the Verify Webhook Signatures guide](/webhooks/verify-webhook-signature) →** ## Webhook Payloads Webhook payloads vary by service and event type. A new deposit webhook contains different fields than a completed checkout payment. See the relevant guide for the full payload schema: - **[Custom Payment Flow Webhooks](/webhooks/custom-payment-flow-webhooks)** - **[Checkout Webhooks](/webhooks/checkout-payments-webhook)** - **[Deposit Webhooks](/webhooks/checkout-deposits-webhook)** - **[Subscription Webhooks](/webhooks/subscriptions-webhook)** ## Best Practices & Troubleshooting - **Respond within 30 seconds.** Your endpoint must acknowledge with `200` and `*ok*` within 30 seconds, or the connection times out. For any processing longer than that, queue it asynchronously after responding. - **Make your handler idempotent.** BlockBee retries failed deliveries, so your endpoint will receive the same webhook more than once. Track the `uuid` of each processed webhook and skip duplicates. - **Allowlist BlockBee's IPs.** If your firewall blocks unknown sources, allow `51.77.105.132` and `135.125.112.47`. - **Use `ngrok` for local testing.** Expose your local development server to the public internet so you can test webhooks without deploying. ### Retry schedule If your endpoint doesn't respond with `*ok*`, BlockBee retries with exponential backoff: - **1st retry:** 6 minutes - **2nd retry:** 12 minutes - **3rd retry:** 24 minutes - …doubling each time until the transaction is 3 days old, after which retries stop. ### Troubleshooting - **Webhooks not received.** Check that your endpoint is publicly accessible, that your firewall or CDN (e.g. Cloudflare) doesn't block BlockBee's IPs, and that the customer sent the same coin and chain the address was generated for. - **Duplicate processing.** Your handler is not idempotent. Track the `uuid` of each webhook and skip duplicates.