Webhooks

Webhooks let API Express notify your application in real time when async operations complete — bulk verifications, recharge status changes, background jobs. Instead of polling our APIs, your server receives events as they happen.

Overview

Some API Express operations are asynchronous — meaning they don't return a final result immediately. Bulk verifications, background job processing, and settlement completions happen on our side over seconds to minutes.

Webhooks flip that model: instead of your app constantly asking "is it done yet?", we notify your app the moment the operation completes. This is faster, more efficient, and more reliable than polling.

💡
When to use webhooks: Any workflow with an async operation — bulk verifications, recharge confirmations that depend on operator responses, background fraud scoring, or settlement events.

How it works

  1. You register a webhook URL in your API Express dashboard
  2. You select which events you want to subscribe to
  3. When an event occurs, we send an HTTP POST to your URL with the event data
  4. Your server responds with 2xx to acknowledge receipt
  5. If your server returns a non-2xx response, we retry with exponential backoff

Setting Up Webhooks

Webhook configuration is done through your API Express dashboard. You can also manage webhooks programmatically via our API.

Via dashboard

  1. Log in to your API Express dashboard
  2. Navigate to Settings → Webhooks
  3. Click Add Webhook
  4. Enter your endpoint URL (must be HTTPS)
  5. Select the events you want to receive
  6. Copy the webhook secret for signature verification
  7. Click Save

Via API

curl -X POST https://api.apiexpress.in/v1/webhooks \
  -H "Authorization: Bearer sk_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://yourapp.com/webhooks/apiexpress",
    "events": ["recharge.completed", "verification.bulk.completed"],
    "secret": "whsec_you_generate_this"
  }'
⚠️
Your endpoint must use HTTPS. We reject HTTP URLs for security reasons. Self-signed certificates are not accepted — use a valid TLS certificate (Let's Encrypt works fine).

Event Types

API Express sends the following event types. Subscribe to only the events your application needs.

Recharge events

EventFires when
recharge.completedA recharge transaction succeeds
recharge.failedA recharge transaction fails
recharge.pendingRecharge is pending operator confirmation
recharge.refundedRecharge was reversed and refunded

Verification events

EventFires when
verification.completedSingle verification completes
verification.bulk.completedBulk verification batch completes
verification.failedVerification returns a definitive failure

Account & billing events

EventFires when
quota.warningYou reach 80% of your monthly quota
quota.exceededYou hit 100% of your monthly quota
payment.succeededA payment for your account succeeds
payment.failedA payment for your account fails

Payload Format

Every webhook event has the same envelope structure. The data field varies by event type.

Event envelope

{
  "id": "evt_1a2b3c4d5e6f",
  "type": "recharge.completed",
  "created": 1728139842,
  "api_version": "2026-01-01",
  "data": {
    // Event-specific data
  }
}

Example: recharge.completed

{
  "id": "evt_1a2b3c4d5e6f",
  "type": "recharge.completed",
  "created": 1728139842,
  "api_version": "2026-01-01",
  "data": {
    "transaction_id": "TXN928374",
    "mobile": "9876543210",
    "operator": "Airtel",
    "circle": "Maharashtra",
    "amount": 199,
    "type": "prepaid",
    "status": "success",
    "ref_id": "YOUR_REF_001",
    "completed_at": "2026-10-05T10:30:42Z"
  }
}

Example: verification.bulk.completed

{
  "id": "evt_9x8y7z6w5v4u",
  "type": "verification.bulk.completed",
  "created": 1728139842,
  "api_version": "2026-01-01",
  "data": {
    "batch_id": "batch_a1b2c3d4",
    "total": 1000,
    "successful": 987,
    "failed": 13,
    "results_url": "https://api.apiexpress.in/v1/verification/batch/batch_a1b2c3d4/results",
    "expires_at": "2026-10-12T10:30:42Z"
  }
}

Signature Verification

Every webhook request includes an X-APIX-Signature header containing an HMAC-SHA256 signature. Always verify this signature before processing the event — it proves the request came from API Express and hasn't been tampered with.

Signature format

The header looks like this:

X-APIX-Signature: t=1728139842,v1=5d41402abc4b2a76b9719d911017c592...

Where:

  • t — Unix timestamp of when the signature was generated
  • v1 — HMAC-SHA256 hex digest

Verifying in Node.js

const crypto = require('crypto');

function verifyWebhook(rawBody, signatureHeader, secret) {
  // Parse signature header
  const parts = signatureHeader.split(',');
  const timestamp = parts[0].split('=')[1];
  const signature = parts[1].split('=')[1];

  // Reconstruct the signed payload
  const signedPayload = `${timestamp}.${rawBody}`;

  // Compute expected signature
  const expected = crypto
    .createHmac('sha256', secret)
    .update(signedPayload, 'utf8')
    .digest('hex');

  // Compare securely
  return crypto.timingSafeEqual(
    Buffer.from(signature, 'hex'),
    Buffer.from(expected, 'hex')
  );
}

Verifying in Python

import hmac
import hashlib

def verify_webhook(raw_body, signature_header, secret):
    parts = signature_header.split(',')
    timestamp = parts[0].split('=')[1]
    signature = parts[1].split('=')[1]

    signed_payload = f"{timestamp}.{raw_body}"

    expected = hmac.new(
        secret.encode('utf-8'),
        signed_payload.encode('utf-8'),
        hashlib.sha256
    ).hexdigest()

    return hmac.compare_digest(signature, expected)
⚠️
Verify on the raw body. Parse the request body as a raw string, not as JSON — JSON parsing can reorder fields and break the signature. In Express.js use express.raw() middleware for webhook routes.

Timestamp tolerance

Reject webhooks where the timestamp is more than 5 minutes old. This prevents replay attacks.

Retry Logic

If your endpoint returns a non-2xx status or times out, we retry the delivery with exponential backoff.

Retry schedule

AttemptDelay after previousTotal elapsed
1 (initial)—0
210 seconds10s
330 seconds40s
42 minutes2m 40s
510 minutes12m 40s
61 hour~1h
76 hours~7h
8 (final)24 hours~31h

After 8 failed attempts, the event is marked as failed and no further retries occur. You can view failed deliveries in your dashboard and manually retry them.

Best practices for handling retries

  • Respond fast. Acknowledge with 200 OK within 5 seconds. Process the event asynchronously in the background — don't do heavy work in the request handler.
  • Handle duplicates. If your server takes longer than 5 seconds, we may retry while your original request is still processing. Use the event id to deduplicate.
  • Return the right status. Use 200 for success, 4xx for permanent errors (no retry), 5xx for temporary errors (retry).
  • Log everything. Log incoming webhooks with their IDs and event types so you can trace issues.

Idempotency — handling duplicate events

Retries can cause the same event to be delivered more than once. Guard against this by tracking processed event IDs.

async function handleWebhook(event) {
  // Check if we've already processed this event
  const exists = await db.webhooks.findOne({ id: event.id });
  if (exists) {
    return; // Already processed — skip
  }

  // Process the event
  await processEvent(event);

  // Mark as processed
  await db.webhooks.insert({ id: event.id, processed_at: new Date() });
}

Best Practices

Always verify signatures

Without signature verification, anyone who knows your webhook URL can send fake events to your server. Always verify the X-APIX-Signature header before processing events.

Use HTTPS only

Webhook URLs must use HTTPS. This protects event payloads in transit. Use a valid TLS certificate (Let's Encrypt, AWS ACM, Cloudflare, or a CA-signed cert).

Acknowledge quickly

Return 200 OK within 5 seconds. If your processing takes longer, queue the event for async processing and return immediately.

Handle out-of-order events

In rare cases, events may arrive out of chronological order. Use the created timestamp and any state fields in the payload to reconcile the correct order.

Monitor your webhook endpoint

Set up monitoring for your webhook endpoint — alert on:

  • Response time above 5 seconds
  • Error rate above 1%
  • Signature verification failures (could indicate an attack)
  • Duplicate event IDs (indicates a bug in your deduplication logic)

Don't rely on webhooks alone

Webhooks are best-effort delivery. If your endpoint is down for more than 24 hours, events will fail permanently. For critical workflows, also poll the relevant API periodically as a fallback.

✅
Testing webhooks? Use webhook.site or ngrok during development to inspect webhook payloads without setting up a real endpoint.

Related Documentation

Ready to build?

Get Real-Time Notifications on Every Event

Set up your first webhook in minutes. Sign up free, add your endpoint, and start receiving events. No credit card required.

No credit card required 1,000 free API calls HTTPS endpoints Signature verification