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.confirmedBody:
{
"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', 200function 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:
| Attempt | Delay | Total elapsed |
|---|---|---|
| 1 | Immediate | 0s |
| 2 | 30 seconds | 30s |
| 3 | 2 minutes | 2.5 min |
| 4 | 15 minutes | 17.5 min |
| 5 | 1 hour | 1h 17min |
| 6 | 4 hours | 5h 17min |
| 7 | 12 hours | 17h 17min |
| 8 | 24 hours | 41h 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).
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/eventsto 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?