Skip to Content
PekoPay API Integration Docs

Webhooks

Webhooks are managed from the PekoPay portal Webhook Settings page.

Configure:

  • Destination URL
  • Signing secret
  • Event subscriptions

Payload envelope

{
  "type": "customer:created",
  "businessId": "<tenantId>",
  "payload": {
    "customer_id": "..."
  }
}

Fields:

  • type: event name
  • businessId: tenant/business identifier
  • payload: event-specific data

Event types

Customer lifecycle:

  • customer:created
  • customer:updated
  • customer:deleted

Product lifecycle:

  • product:created
  • product:updated
  • product:deleted

Subscription lifecycle:

  • subscription:created
  • subscription:updated
  • subscription:deleted

Subscription charge lifecycle:

  • subscription:charge:success
  • subscription:charge:failed

Invoice lifecycle:

  • invoice:created
  • invoice:updated
  • invoice:deleted

API as source of truth

Treat webhook payloads as signals, not authoritative state snapshots.

Recommended pattern:

  1. Verify signature.
  2. Persist raw event and metadata.
  3. Acknowledge quickly (2xx).
  4. Process asynchronously.
  5. Re-fetch the latest state from API before updates.
  6. Apply idempotent writes in your system.

Resource re-fetch mapping:

  • customer:* -> GET /tenants/:tenantId/customers/:id
  • product:* -> GET /tenants/:tenantId/products/:id
  • subscription:* and subscription:charge:* -> GET /tenants/:tenantId/subscriptions/:id
  • invoice:* -> GET /tenants/:tenantId/transactions

Signature verification

Header:

  • X-Webhook-Signature = hex(HMAC_SHA256(rawBody, secret))
import crypto from 'crypto';
import express from 'express';
 
const app = express();
const webhookSecret = process.env.WEBHOOK_SECRET!;
 
app.post('/webhooks/pekopay', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.header('X-Webhook-Signature') || '';
  const rawBody = req.body as Buffer;
 
  const expected = crypto.createHmac('sha256', webhookSecret).update(rawBody).digest('hex');
 
  if (signature !== expected) {
    return res.status(401).send('Invalid signature');
  }
 
  const payload = JSON.parse(rawBody.toString('utf8'));
  return res.status(200).send('OK');
});
Last updated on