Skip to main content

Documentation Index

Fetch the complete documentation index at: https://developer.quickei.io/llms.txt

Use this file to discover all available pages before exploring further.

Receive real-time notifications when payout events occur.

How Webhooks Work

Configure a webhook URL in your Merchant Dashboard under Developer API > Webhooks. Quickei sends an HTTP POST request to your URL whenever a payout is completed.

Payload Format

The webhook payload is a flat JSON object (not wrapped in a data envelope):
{
  "event": "payout.completed",
  "payout_id": "POAB1C2D3E",
  "reference": "PAYROLL-2026-03-001",
  "amount": 100.00,
  "currency": "EUR",
  "fee": 2.50,
  "total_deducted": 102.50,
  "exchange_rate": 655.957,
  "receiver_amount": 65595.70,
  "receiver_currency": "XAF",
  "status": "completed",
  "processed_at": "2026-03-29T14:01:15+00:00",
  "timestamp": "2026-03-29T14:01:16+00:00"
}
event
string
The event type. Currently always payout.completed.
payout_id
string
Unique payout identifier matching the one returned from the Payout endpoint.
reference
string
Your internal reference for this payout.
amount
number
The amount sent (before fees).
currency
string
The sender currency code.
fee
number
Total fee charged.
total_deducted
number
Total debited from your merchant wallet (amount + fee).
exchange_rate
number
Applied exchange rate.
receiver_amount
number
Amount credited to the recipient.
receiver_currency
string
The recipient’s currency code.
status
string
Payout status (e.g. completed).
processed_at
string
ISO 8601 timestamp when the payout was processed.
timestamp
string
ISO 8601 timestamp when the webhook was sent.

Headers

Each webhook request includes these headers:
HeaderDescription
Content-Typeapplication/json
X-Quickei-SignatureHMAC-SHA256 signature of the raw request body
X-Webhook-Eventpayout.completed

Signature Verification

The X-Quickei-Signature header contains an HMAC-SHA256 hex digest of the raw request body, signed with your webhook secret (configured in the Merchant Dashboard).
X-Quickei-Signature: <HMAC-SHA256 hex digest>
Always verify the signature before processing webhook events. Reject any request with an invalid or missing signature.

Verification Examples

$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_QUICKEI_SIGNATURE'] ?? '';

$expected = hash_hmac('sha256', $payload, $webhook_secret);

if (!hash_equals($expected, $signature)) {
    http_response_code(403);
    exit('Invalid signature');
}

$event = json_decode($payload, true);

if ($event['event'] === 'payout.completed') {
    // Mark payout as successful in your system
}

http_response_code(200);
const crypto = require('crypto');

app.post('/webhooks/quickei', (req, res) => {
  const payload = req.rawBody; // ensure raw body is available
  const signature = req.headers['x-quickei-signature'] || '';

  const expected = crypto
    .createHmac('sha256', WEBHOOK_SECRET)
    .update(payload)
    .digest('hex');

  if (!crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signature)
  )) {
    return res.status(403).send('Invalid signature');
  }

  const event = JSON.parse(payload);

  if (event.event === 'payout.completed') {
    // Mark payout as successful
  }

  res.status(200).send('OK');
});
import hmac
import hashlib

@app.route('/webhooks/quickei', methods=['POST'])
def handle_webhook():
    payload = request.get_data(as_text=True)
    signature = request.headers.get('X-Quickei-Signature', '')

    expected = hmac.new(
        WEBHOOK_SECRET.encode(),
        payload.encode(),
        hashlib.sha256
    ).hexdigest()

    if not hmac.compare_digest(expected, signature):
        return 'Invalid signature', 403

    event = request.get_json()

    if event['event'] == 'payout.completed':
        # Mark payout as successful
        pass

    return 'OK', 200

Retry Policy

If your endpoint does not return a 2xx status code, Quickei retries the webhook delivery:
AttemptDelay
1st retry10 seconds
2nd retry1 minute
3rd retry5 minutes
After 3 failed attempts, the webhook is marked as failed. You can view failed deliveries and trigger manual retries from the Merchant Dashboard under Developer API > Webhooks > Delivery Log.
Return a 200 status code as quickly as possible. Perform heavy processing (database writes, notifications) asynchronously after acknowledging the webhook.

Best Practices

  • Verify signatures on every webhook before processing
  • Respond quickly with a 200 and process asynchronously
  • Handle duplicates — you may receive the same event more than once during retries. Use the payout_id to deduplicate.
  • Use the status endpoint as a fallback. If you miss a webhook, poll GET /payout/{id} to get the current status.