---
title: API Webhooks | Crewkerne Timebank
description: Subscribe to events with HTTPS callback URLs. Each delivery includes an X-Partner-Signature header — an HMAC-SHA256 of the raw body, keyed by the subscription secret returned at creation time.
canonical: https://app.project-nexus.ie/crewkerne-timebank/developers/webhooks
generated: 2026-05-20T15:34:48.127Z
---## Available events

- wallet.credited — fires when a partner credits time hours via /wallet/credit

## Create a subscription

POST /api/partner/v1/webhooks/subscriptions with event_types (array) and target_url (https only). The response includes a one-time secret — store it.

## Verifying signatures

Compute HMAC-SHA256 of the raw request body using your subscription secret. Compare with the X-Partner-Signature header in constant time.

Node.js PHP

```
// Verify an inbound NEXUS webhook (Node 18+)
import crypto from 'crypto';

function verifySignature(rawBody, signatureHeader, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(rawBody)
    .digest('hex');
  // Constant-time comparison
  const a = Buffer.from(expected);
  const b = Buffer.from(signatureHeader);
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

// Express handler
app.post('/webhooks/nexus', express.raw({ type: 'application/json' }), (req, res) => {
  const sig = req.header('X-Partner-Signature') || '';
  if (!verifySignature(req.body, sig, process.env.NEXUS_WEBHOOK_SECRET)) {
    return res.status(401).send('invalid signature');
  }
  const event = JSON.parse(req.body.toString());
  // event.event_type === 'wallet.credited'
  // event.data === { transaction_id, user_id, hours, reference }
  res.status(200).send('ok');
});
```
