ElebneElebneDocs
Getting Started

Webhooks

Receive real-time event notifications from Elebne. Set up endpoints, verify signatures, and handle retries.

Webhooks

Webhooks let your server receive real-time notifications when events occur in your Elebne account — payments completing, orders being placed, refunds processing, and more. Instead of polling the API, register an endpoint and Elebne will push events to you.

Setting up an endpoint

Create a webhook endpoint by specifying a URL and the events you want to receive:

curl -X POST https://api.elebne.ai/api/v1/dev/webhooks/endpoints \
  -H "Authorization: Bearer sk_test_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -H "X-Idempotency-Key: webhook-setup-$(date +%s)" \
  -d '{
    "url": "https://your-server.com/webhooks/elebne",
    "events": ["payment.confirmed", "order.new"]
  }'
const response = await fetch('https://api.elebne.ai/api/v1/dev/webhooks/endpoints', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer sk_test_YOUR_KEY',
    'Content-Type': 'application/json',
    'X-Idempotency-Key': `webhook-setup-${Date.now()}`,
  },
  body: JSON.stringify({
    url: 'https://your-server.com/webhooks/elebne',
    events: ['payment.confirmed', 'order.new'],
  }),
});

const data = await response.json();
console.log(data);
import requests
import time

response = requests.post(
    'https://api.elebne.ai/api/v1/dev/webhooks/endpoints',
    headers={
        'Authorization': 'Bearer sk_test_YOUR_KEY',
        'Content-Type': 'application/json',
        'X-Idempotency-Key': f'webhook-setup-{int(time.time())}',
    },
    json={
        'url': 'https://your-server.com/webhooks/elebne',
        'events': ['payment.confirmed', 'order.new'],
    },
)

print(response.json())

The response includes a secret — a 64-character hex string used to verify webhook signatures. Store it securely.

{
  "success": true,
  "data": {
    "id": "wh_endpoint_abc123",
    "url": "https://your-server.com/webhooks/elebne",
    "events": ["payment.confirmed", "order.new"],
    "secret": "a1b2c3d4e5f6...64_hex_chars",
    "active": true
  }
}

Endpoint limit

Each business can register up to 5 webhook endpoints. Each endpoint can subscribe to different events.

Webhook payload

When an event occurs, Elebne sends a POST request to your endpoint with these headers and body:

Headers:

Content-Type: application/json
X-Elebne-Signature: sha256=<hex_digest>
X-Elebne-Timestamp: 1712234400
X-Elebne-Event: payment.confirmed

Body:

{
  "event": "payment.confirmed",
  "timestamp": "2026-04-04T10:30:00.000Z",
  "sandbox": true,
  "data": {
    "referenceNumber": "PI-3XXXXXXXXXXXXXX",
    "amount": 50000,
    "status": "PAID"
  }
}

Signature verification

Every webhook includes an HMAC-SHA256 signature so you can verify it came from Elebne. The signature is computed over {timestamp}.{raw_body} using your endpoint's secret.

const crypto = require('crypto');

function verifyWebhook(payload, signature, timestamp, secret) {
  const message = `${timestamp}.${payload}`;
  const expected = crypto
    .createHmac('sha256', secret)
    .update(message)
    .digest('hex');
  const received = signature.replace('sha256=', '');

  // Timing-safe comparison to prevent timing attacks
  if (expected.length !== received.length) return false;
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(received)
  );
}

// Express.js example
app.post('/webhooks/elebne', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.headers['x-elebne-signature'];
  const timestamp = req.headers['x-elebne-timestamp'];

  if (!verifyWebhook(req.body.toString(), signature, timestamp, WEBHOOK_SECRET)) {
    return res.status(401).send('Invalid signature');
  }

  const event = JSON.parse(req.body);
  // Process the event...

  res.status(200).send('OK');
});
import hmac
import hashlib
from flask import Flask, request

app = Flask(__name__)

def verify_webhook(payload, signature, timestamp, secret):
    message = f"{timestamp}.{payload}"
    expected = hmac.new(
        secret.encode(), message.encode(), hashlib.sha256
    ).hexdigest()
    received = signature.replace("sha256=", "")
    return hmac.compare_digest(expected, received)

@app.route('/webhooks/elebne', methods=['POST'])
def handle_webhook():
    signature = request.headers.get('X-Elebne-Signature')
    timestamp = request.headers.get('X-Elebne-Timestamp')

    if not verify_webhook(request.data.decode(), signature, timestamp, WEBHOOK_SECRET):
        return 'Invalid signature', 401

    event = request.get_json()
    # Process the event...

    return 'OK', 200
function verifyWebhook(string $payload, string $signature, string $timestamp, string $secret): bool
{
    $message = "{$timestamp}.{$payload}";
    $expected = hash_hmac('sha256', $message, $secret);
    $received = str_replace('sha256=', '', $signature);

    return hash_equals($expected, $received);
}

// Usage
$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_ELEBNE_SIGNATURE'] ?? '';
$timestamp = $_SERVER['HTTP_X_ELEBNE_TIMESTAMP'] ?? '';

if (!verifyWebhook($payload, $signature, $timestamp, $webhookSecret)) {
    http_response_code(401);
    exit('Invalid signature');
}

$event = json_decode($payload, true);
// Process the event...

http_response_code(200);
echo 'OK';

Retry schedule

If your endpoint returns a non-2xx response or times out (30 seconds), Elebne retries delivery with exponential backoff:

AttemptDelayTotal elapsed
1Immediate0s
230 seconds30s
32 minutes2.5 min
415 minutes17.5 min
51 hour1h 17min
64 hours5h 17min
712 hours17h 17min
824 hours41h 17min

After 8 failed attempts, the event delivery is marked as failed. You can manually retry failed deliveries through the API.

Local testing with ngrok

During development, use ngrok to expose your local server to the internet:

Start your local server

Run your webhook handler on a local port (e.g., http://localhost:4000/webhooks/elebne).

Start ngrok

ngrok http 4000

ngrok gives you a public URL like https://abc123.ngrok-free.app.

Register the ngrok URL as a webhook endpoint

curl -X POST https://api.elebne.ai/api/v1/dev/webhooks/endpoints \
  -H "Authorization: Bearer sk_test_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -H "X-Idempotency-Key: ngrok-$(date +%s)" \
  -d '{
    "url": "https://abc123.ngrok-free.app/webhooks/elebne",
    "events": ["payment.confirmed"]
  }'

Trigger an event

Create a payment intent and simulate payment. Watch the webhook arrive in your local server logs and in the ngrok dashboard.

Best practices

  • Always verify signatures. Never trust webhook payloads without verifying the HMAC signature.
  • Respond 2xx quickly. Return a 200 status immediately, then process the event asynchronously. Elebne times out after 30 seconds.
  • Handle retries idempotently. The same event may be delivered more than once. Use the event ID to deduplicate.
  • Log all deliveries. Store raw payloads for debugging and auditing.
  • Monitor failed deliveries. Use GET /dev/webhooks/events to check for delivery failures and retry them.

Next steps

  • Common Patterns — Response formats, headers, and pagination
  • Sandbox — Test webhooks in sandbox mode
  • Pay API — Full list of payment events

Was this page helpful?

On this page