Webhooks

Receive real-time notifications when events occur in your Venshack account.

Overview

Webhooks allow your application to receive automated HTTP POST requests when certain events happen. Instead of polling the API to check for updates, webhooks push data to your server in real-time.

Setting Up Webhooks

1. Configure Your Endpoint

Create an endpoint on your server that can receive POST requests. The endpoint must:

2. Register Your Webhook

Register your webhook endpoint via the API or dashboard:

POST /v1/webhooks
Content-Type: application/json
Authorization: Bearer your_api_key

{
  "url": "https://your-app.com/webhooks/venshack",
  "events": ["vend.completed", "vend.failed", "payment.received"],
  "secret": "whsec_your_webhook_secret"
}

Response

{
  "success": true,
  "data": {
    "id": "whk_abc123",
    "url": "https://your-app.com/webhooks/venshack",
    "events": ["vend.completed", "vend.failed", "payment.received"],
    "status": "ACTIVE",
    "createdAt": "2024-01-15T10:30:00Z"
  }
}

Webhook Events

Subscribe to the events relevant to your integration:

Vending Events

Event Description
vend.initiated A vend request has been submitted and is being processed
vend.completed Electricity token has been successfully generated
vend.failed Vend request failed (with error details)
vend.refunded Failed vend has been refunded to the wallet

Payment Events

Event Description
payment.received Payment has been received and credited to wallet
payment.failed Payment attempt failed
wallet.credited Wallet balance has been credited
wallet.debited Wallet balance has been debited

Access Code Events

Event Description
access_code.used An access code has been used for gate entry
access_code.expired An access code has expired

Webhook Payload

All webhook payloads follow this structure:

{
  "id": "evt_xyz789",
  "type": "vend.completed",
  "timestamp": "2024-01-15T14:30:00Z",
  "data": {
    "vendId": "vnd_abc123",
    "meterNumber": "12345678901",
    "amount": 5000,
    "units": 45.5,
    "token": "1234-5678-9012-3456-7890"
  }
}

Payload Fields

Field Type Description
id string Unique event identifier for idempotency
type string The event type (e.g., vend.completed)
timestamp string ISO 8601 timestamp when the event occurred
data object Event-specific data payload

Verifying Webhooks

Always verify webhook signatures to ensure requests are from Venshack and haven't been tampered with.

Signature Header

Each webhook includes a X-Venshack-Signature header containing an HMAC-SHA256 signature.

Verification Example (Node.js)

const crypto = require('crypto');

function verifyWebhook(payload, signature, secret) {
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(payload, 'utf8')
    .digest('hex');
  
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expectedSignature)
  );
}

// In your webhook handler
app.post('/webhooks/venshack', (req, res) => {
  const signature = req.headers['x-venshack-signature'];
  const payload = JSON.stringify(req.body);
  
  if (!verifyWebhook(payload, signature, process.env.WEBHOOK_SECRET)) {
    return res.status(401).json({ error: 'Invalid signature' });
  }
  
  // Process the webhook
  const { type, data } = req.body;
  
  switch (type) {
    case 'vend.completed':
      // Handle successful vend
      await notifyResident(data.token);
      break;
    case 'vend.failed':
      // Handle failed vend
      await alertAdmin(data.error);
      break;
  }
  
  res.status(200).json({ received: true });
});
Security Best Practice
Always use constant-time comparison (like crypto.timingSafeEqual) when comparing signatures to prevent timing attacks.

Handling Webhooks

Respond Quickly

Your endpoint must respond within 30 seconds. If processing takes longer, acknowledge the webhook immediately and process asynchronously:

app.post('/webhooks/venshack', async (req, res) => {
  // Verify signature first
  if (!verifyWebhook(req)) {
    return res.status(401).end();
  }
  
  // Acknowledge immediately
  res.status(200).json({ received: true });
  
  // Process asynchronously
  await processWebhookAsync(req.body);
});

Handle Retries

If your endpoint returns a non-2xx response or times out, Venshack will retry with exponential backoff:

Idempotency
Use the id field to ensure you don't process the same event twice. Store processed event IDs and check before processing.

Idempotency Example

const processedEvents = new Set(); // Use Redis in production

app.post('/webhooks/venshack', async (req, res) => {
  const { id, type, data } = req.body;
  
  // Check if already processed
  if (processedEvents.has(id)) {
    return res.status(200).json({ received: true });
  }
  
  // Mark as processed
  processedEvents.add(id);
  
  // Process the event
  await handleEvent(type, data);
  
  res.status(200).json({ received: true });
});

Testing Webhooks

Sandbox Mode

In sandbox mode, you can trigger test webhook events from the dashboard to verify your integration.

Local Development

Use a tool like ngrok to expose your local server for testing:

# Install ngrok and start tunnel
ngrok http 3000

# Use the HTTPS URL as your webhook endpoint
# https://abc123.ngrok.io/webhooks/venshack

Managing Webhooks

List Webhooks

GET /v1/webhooks
Authorization: Bearer your_api_key

Update Webhook

PATCH /v1/webhooks/whk_abc123
Content-Type: application/json
Authorization: Bearer your_api_key

{
  "events": ["vend.completed", "vend.failed"],
  "status": "ACTIVE"
}

Delete Webhook

DELETE /v1/webhooks/whk_abc123
Authorization: Bearer your_api_key