Platform

Webhooks

Receive real-time event notifications instead of polling the API.

How webhooks work

When an event happens in Xeboki (subscription renewed, invoice paid, etc.), we immediately send an HTTP POST request to your registered endpoint. Your server responds with a 2xx status to acknowledge receipt.

Register

Add your HTTPS endpoint in Connected Apps

Receive

We POST signed JSON to your URL within seconds

Verify

Check the X-Xeboki-Signature header to confirm authenticity

Event catalog

Subscription
subscription.createdA new subscription was activated
subscription.updatedSubscription plan or status changed
subscription.canceledSubscription was canceled
subscription.renewedSubscription auto-renewed successfully
subscription.pausedSubscription billing was paused
subscription.resumedPaused subscription was resumed
Billing
invoice.createdNew invoice was generated
invoice.paidInvoice payment succeeded
invoice.failedInvoice payment failed
payment.succeededPayment processed successfully
payment.refundedPayment was refunded
Account
user.updatedProfile information changed
user.deletedAccount was permanently deleted

Verifying signatures

Every webhook delivery includes an X-Xeboki-Signature header. Use it to verify the payload came from Xeboki and was not tampered with.

Node.js
const crypto = require('crypto')

function verifySignature(secret, rawBody, signatureHeader) {
  const expected = 'sha256=' + crypto
    .createHmac('sha256', secret)
    .update(rawBody)
    .digest('hex')

  // Use timingSafeEqual to prevent timing attacks
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signatureHeader)
  )
}

// In your Express handler:
app.post('/xeboki-events', express.raw({ type: 'application/json' }), (req, res) => {
  const sig = req.headers['x-xeboki-signature']
  if (!verifySignature(process.env.XEBOKI_WEBHOOK_SECRET, req.body, sig)) {
    return res.status(400).send('Invalid signature')
  }
  const event = JSON.parse(req.body)
  console.log('Event:', event.event, event.data)
  res.json({ received: true })
})
Python
import hmac, hashlib

def verify_signature(secret: str, raw_body: bytes, signature_header: str) -> bool:
    expected = 'sha256=' + hmac.new(
        secret.encode(), raw_body, hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, signature_header)

# In your Flask handler:
@app.route('/xeboki-events', methods=['POST'])
def handle_webhook():
    sig = request.headers.get('X-Xeboki-Signature', '')
    if not verify_signature(os.environ['XEBOKI_WEBHOOK_SECRET'], request.data, sig):
        return 'Invalid signature', 400
    event = request.get_json(force=True)
    print(f"Event: {event['event']}", event['data'])
    return {'received': True}

Example payload

{
  "id": "wh_a1b2c3d4e5f6",
  "event": "invoice.paid",
  "created_at": "2026-03-30T12:00:00Z",
  "test": false,
  "data": {
    "invoice_id": "inv_xyz789",
    "amount_cents": 2900,
    "currency": "usd"
  }
}

Retry policy

If your endpoint returns a non-2xx response or times out (10s), we retry with exponential backoff:

15 seconds
230 seconds
32 minutes
410 minutes
51 hour

After 5 consecutive failures the endpoint is automatically disabled. Re-enable it in Connected Apps.

Ready to add a webhook endpoint?

Register your HTTPS endpoint in Connected Apps to start receiving events.

Add endpoint