Webhooks

Comprehensive webhook system for real-time event notifications in BigLedger. Get instant notifications when business events occur, enabling seamless integration and automation.

ℹ️
Event-Driven Architecture: BigLedger’s webhooks follow industry best practices with HMAC-SHA256 signature verification, automatic retries, and comprehensive event filtering.

Overview

BigLedger webhooks provide real-time notifications for important business events. Instead of polling our APIs, you can receive instant notifications when:

  • Invoices are created, sent, or paid
  • Payments are received or recorded
  • Customers are created or updated
  • Inventory levels change or reach thresholds
  • E-invoices are submitted or accepted
  • Sales orders are placed or fulfilled

Webhook Events

Invoice Events

EventDescriptionWhen Triggered
invoice.createdNew invoice createdInvoice is saved in BigLedger
invoice.updatedInvoice modifiedInvoice data is changed
invoice.sentInvoice sent to customerEmail sent or status manually updated
invoice.viewedCustomer viewed invoiceCustomer opens invoice link
invoice.paidInvoice fully paidPayment recorded that covers full amount
invoice.partially_paidPartial payment receivedPayment recorded for partial amount
invoice.overdueInvoice becomes overdueDue date passes without full payment
invoice.cancelledInvoice cancelledInvoice status changed to cancelled

Payment Events

EventDescriptionWhen Triggered
payment.receivedPayment recordedAny payment is recorded in system
payment.matchedPayment matched to invoiceAutomatic or manual payment matching
payment.unmatchedPayment couldn’t be matchedPayment received but no matching invoice
payment.refundedPayment refundedRefund processed for a payment

Customer Events

EventDescriptionWhen Triggered
customer.createdNew customer addedCustomer record created
customer.updatedCustomer information changedCustomer data modified
customer.credit_limit_exceededCredit limit exceededCustomer balance exceeds credit limit
customer.status_changedCustomer status changedActive/inactive status change

Inventory Events

EventDescriptionWhen Triggered
inventory.low_stockStock below minimum levelItem quantity falls below threshold
inventory.out_of_stockItem out of stockItem quantity reaches zero
inventory.adjustmentStock adjustment madeManual stock level adjustment
inventory.transferStock transferredItems moved between locations

E-Invoice Events

EventDescriptionWhen Triggered
einvoice.createdE-invoice generatedE-invoice document created
einvoice.validatedE-invoice validation passedDocument passes validation checks
einvoice.submittedE-invoice submitted to portalSubmitted to government portal
einvoice.acceptedE-invoice acceptedGovernment portal accepts e-invoice
einvoice.rejectedE-invoice rejectedGovernment portal rejects e-invoice
einvoice.cancelledE-invoice cancelledE-invoice cancellation processed

Sales Order Events

EventDescriptionWhen Triggered
sales_order.createdNew sales orderSales order created
sales_order.confirmedOrder confirmedOrder status changed to confirmed
sales_order.shippedOrder shippedShipping information added
sales_order.deliveredOrder deliveredDelivery confirmed
sales_order.cancelledOrder cancelledOrder status changed to cancelled

Purchase Order Events

EventDescriptionWhen Triggered
purchase_order.createdNew purchase orderPurchase order created
purchase_order.sentPO sent to supplierPurchase order emailed or transmitted
purchase_order.confirmedSupplier confirmed POSupplier acceptance received
purchase_order.receivedGoods receivedStock received against PO
purchase_order.invoicedSupplier bill receivedBill created from PO

Bill & Expense Events

EventDescriptionWhen Triggered
bill.createdNew supplier billBill record created
bill.approvedBill approved for paymentApproval workflow completed
bill.paidBill payment madePayment applied to bill
expense.createdNew expense recordedExpense entry created
expense.approvedExpense approvedExpense approval completed

User & Access Events

EventDescriptionWhen Triggered
user.loginUser logged inSuccessful authentication
user.logoutUser logged outSession terminated
user.password_changedPassword updatedUser changed password
user.createdNew user addedUser account created
user.deactivatedUser deactivatedUser access revoked

System Events

EventDescriptionWhen Triggered
backup.completedData backup finishedAutomated backup completed
report.generatedReport processing doneScheduled report ready
integration.sync_completedIntegration sync finishedThird-party sync completed
integration.sync_failedIntegration sync failedThird-party sync error

Setting Up Webhooks

Create Webhook Subscription

POST /api/v1/webhooks/subscribe

Request Body:

{
  "url": "https://your-app.com/webhooks/bigledger",
  "events": [
    "invoice.created",
    "invoice.paid",
    "payment.received"
  ],
  "secret": "your-webhook-secret-key",
  "description": "Main webhook for invoice and payment events",
  "active": true,
  "retryPolicy": {
    "maxRetries": 3,
    "retryDelay": 5,
    "backoffMultiplier": 2
  }
}

Required Fields:

  • url: HTTPS endpoint to receive webhook notifications
  • events: Array of event types to subscribe to

Example Request:

curl -X POST "https://api.bigledger.com/v1/webhooks/subscribe" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "X-Company-Id: YOUR_COMPANY_ID" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-app.com/webhooks/bigledger",
    "events": ["invoice.created", "invoice.paid"],
    "secret": "your-secret-key"
  }'

Response:

{
  "success": true,
  "data": {
    "id": "webhook_123456789",
    "url": "https://your-app.com/webhooks/bigledger",
    "events": ["invoice.created", "invoice.paid"],
    "secret": "your-secret-key",
    "description": "Main webhook for invoice and payment events",
    "active": true,
    "createdAt": "2024-01-15T10:30:00Z",
    "statistics": {
      "totalDelivered": 0,
      "totalFailed": 0,
      "lastDelivery": null
    }
  }
}

List Webhook Subscriptions

GET /api/v1/webhooks

Example Request:

curl -X GET "https://api.bigledger.com/v1/webhooks" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "X-Company-Id: YOUR_COMPANY_ID"

Update Webhook Subscription

PUT /api/v1/webhooks/{webhookId}

Request Body:

{
  "events": [
    "invoice.created",
    "invoice.paid",
    "customer.created"
  ],
  "active": true
}

Delete Webhook Subscription

DELETE /api/v1/webhooks/{webhookId}

Webhook Payload Format

All webhook events follow a consistent payload structure:

{
  "id": "evt_987654321",
  "event": "invoice.paid",
  "timestamp": "2024-01-15T10:30:00Z",
  "livemode": true,
  "api_version": "v1",
  "data": {
    "object": {
      "id": "inv_123456789",
      "invoiceNumber": "INV-2024-0001",
      "customerId": "cust_456789012",
      "total": 2567.50,
      "amountPaid": 2567.50,
      "status": "paid",
      "paidAt": "2024-01-15T10:30:00Z"
    },
    "previous_attributes": {
      "amountPaid": 0.00,
      "status": "sent"
    }
  },
  "context": {
    "companyId": "company_abc123",
    "userId": "user_789",
    "source": "api",
    "ip": "203.0.113.1"
  }
}

Payload Fields

FieldTypeDescription
idstringUnique event identifier
eventstringEvent type (e.g., “invoice.paid”)
timestampstringISO 8601 timestamp when event occurred
livemodebooleanTrue for production, false for sandbox
api_versionstringAPI version used
data.objectobjectCurrent state of the affected object
data.previous_attributesobjectPrevious values of changed fields
contextobjectAdditional context about the event

Event Examples

Invoice Created

{
  "id": "evt_001",
  "event": "invoice.created",
  "timestamp": "2024-01-15T10:30:00Z",
  "data": {
    "object": {
      "id": "inv_123456789",
      "invoiceNumber": "INV-2024-0001",
      "customerId": "cust_456789012",
      "customer": {
        "id": "cust_456789012",
        "name": "Acme Corporation",
        "email": "billing@acme.com"
      },
      "invoiceDate": "2024-01-15",
      "dueDate": "2024-02-14",
      "status": "draft",
      "currency": "MYR",
      "total": 2567.50,
      "amountDue": 2567.50,
      "items": [
        {
          "description": "Professional Services",
          "quantity": 10,
          "unitPrice": 230.00,
          "total": 2300.00
        }
      ],
      "createdAt": "2024-01-15T10:30:00Z"
    }
  }
}

Payment Received

{
  "id": "evt_002",
  "event": "payment.received",
  "timestamp": "2024-01-20T14:45:00Z",
  "data": {
    "object": {
      "id": "pay_789012345",
      "amount": 2567.50,
      "currency": "MYR",
      "paymentDate": "2024-01-20",
      "paymentMethod": "bank_transfer",
      "reference": "TXN-20240120-001",
      "customerId": "cust_456789012",
      "invoiceId": "inv_123456789",
      "status": "completed",
      "bankAccount": {
        "name": "Main Business Account",
        "accountNumber": "***7890"
      },
      "createdAt": "2024-01-20T14:45:00Z"
    }
  }
}

Inventory Low Stock

{
  "id": "evt_003",
  "event": "inventory.low_stock",
  "timestamp": "2024-01-15T16:20:00Z",
  "data": {
    "object": {
      "id": "item_widget001",
      "itemCode": "WDG-001",
      "description": "Premium Widget",
      "currentStock": 15,
      "minimumStock": 20,
      "location": {
        "id": "loc_warehouse1",
        "name": "Main Warehouse"
      },
      "unitCost": 18.50,
      "stockValue": 277.50,
      "lastRestocked": "2024-01-10T09:00:00Z"
    }
  }
}

E-Invoice Accepted

{
  "id": "evt_004",
  "event": "einvoice.accepted",
  "timestamp": "2024-01-15T10:33:00Z",
  "data": {
    "object": {
      "id": "einv_123456789",
      "invoiceId": "inv_123456789",
      "format": "PEPPOL_UBL",
      "status": "accepted",
      "uuid": "01234567-89ab-cdef-0123-456789abcdef",
      "government": {
        "portal": "myinvois",
        "submissionId": "MY-INV-2024-000001",
        "longId": "EI20240115103000001234567890",
        "acceptedAt": "2024-01-15T10:33:00Z"
      },
      "qrCode": "data:image/png;base64,iVBOR...",
      "pdfUrl": "https://files.bigledger.com/einvoice/einv_123456789.pdf"
    }
  }
}

Security

Signature Verification

All webhook requests include a signature in the X-Signature header. Verify the signature to ensure the request is from BigLedger:

// Node.js example
const crypto = require('crypto');

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

// Express middleware example
app.use('/webhooks/bigledger', express.raw({type: 'application/json'}));

app.post('/webhooks/bigledger', (req, res) => {
  const signature = req.headers['x-signature'];
  const payload = req.body;
  
  if (!verifyWebhookSignature(payload, signature, WEBHOOK_SECRET)) {
    return res.status(401).send('Invalid signature');
  }
  
  // Process webhook...
  const event = JSON.parse(payload);
  console.log('Received event:', event.event);
  
  res.status(200).send('OK');
});

Python Example

import hmac
import hashlib
from flask import Flask, request, abort

app = Flask(__name__)

def verify_signature(payload, signature, secret):
    expected_signature = hmac.new(
        secret.encode('utf-8'),
        payload,
        hashlib.sha256
    ).hexdigest()
    
    received_signature = signature.replace('sha256=', '')
    
    return hmac.compare_digest(expected_signature, received_signature)

@app.route('/webhooks/bigledger', methods=['POST'])
def handle_webhook():
    signature = request.headers.get('X-Signature')
    payload = request.get_data()
    
    if not verify_signature(payload, signature, WEBHOOK_SECRET):
        abort(401)
    
    event = request.get_json()
    
    # Process webhook event
    if event['event'] == 'invoice.paid':
        handle_invoice_paid(event['data']['object'])
    elif event['event'] == 'payment.received':
        handle_payment_received(event['data']['object'])
    
    return 'OK', 200

PHP Example

<?php
function verifySignature($payload, $signature, $secret) {
    $expectedSignature = hash_hmac('sha256', $payload, $secret);
    $receivedSignature = str_replace('sha256=', '', $signature);
    
    return hash_equals($expectedSignature, $receivedSignature);
}

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

if (!verifySignature($payload, $signature, $webhookSecret)) {
    http_response_code(401);
    exit('Invalid signature');
}

$event = json_decode($payload, true);

switch ($event['event']) {
    case 'invoice.paid':
        handleInvoicePaid($event['data']['object']);
        break;
    case 'payment.received':
        handlePaymentReceived($event['data']['object']);
        break;
}

http_response_code(200);
echo 'OK';
?>

Handling Webhook Events

Best Practices

  1. Respond Quickly: Return a 200 status code within 10 seconds
  2. Verify Signatures: Always verify the webhook signature
  3. Handle Duplicates: Use the event ID to detect and handle duplicate events
  4. Use HTTPS: Webhook URLs must use HTTPS
  5. Implement Idempotency: Handle the same event multiple times safely

Error Handling

// Robust webhook handler with error handling
app.post('/webhooks/bigledger', async (req, res) => {
  try {
    const signature = req.headers['x-signature'];
    const payload = req.body;
    
    // Verify signature
    if (!verifyWebhookSignature(payload, signature, WEBHOOK_SECRET)) {
      return res.status(401).send('Invalid signature');
    }
    
    const event = JSON.parse(payload);
    
    // Check for duplicate events
    const existingEvent = await getEventFromDatabase(event.id);
    if (existingEvent) {
      console.log('Duplicate event received:', event.id);
      return res.status(200).send('Duplicate event processed');
    }
    
    // Store event for duplicate detection
    await storeEventInDatabase(event);
    
    // Process event
    await processWebhookEvent(event);
    
    res.status(200).send('Event processed successfully');
    
  } catch (error) {
    console.error('Webhook processing error:', error);
    
    // Return 200 to prevent retries for application errors
    // Return 500+ for temporary issues that should be retried
    if (error.type === 'temporary') {
      res.status(503).send('Temporary error, please retry');
    } else {
      res.status(200).send('Event processing failed');
    }
  }
});

async function processWebhookEvent(event) {
  switch (event.event) {
    case 'invoice.created':
      await handleInvoiceCreated(event.data.object);
      break;
    case 'invoice.paid':
      await handleInvoicePaid(event.data.object);
      break;
    case 'payment.received':
      await handlePaymentReceived(event.data.object);
      break;
    case 'inventory.low_stock':
      await handleLowStock(event.data.object);
      break;
    default:
      console.log('Unhandled event type:', event.event);
  }
}

Retry Policy

BigLedger automatically retries failed webhook deliveries:

  • Initial Retry: 5 seconds after failure
  • Subsequent Retries: Exponential backoff (10s, 20s, 40s)
  • Maximum Retries: 5 attempts over 24 hours
  • Failure Conditions: HTTP status codes 4xx or 5xx
  • Success Condition: HTTP status code 200-299

Webhook Statistics

Monitor webhook delivery statistics:

GET /api/v1/webhooks/{webhookId}/statistics

Response:

{
  "success": true,
  "data": {
    "totalDelivered": 1234,
    "totalFailed": 12,
    "successRate": 99.03,
    "lastDelivery": "2024-01-15T10:30:00Z",
    "averageResponseTime": 245,
    "recentDeliveries": [
      {
        "eventId": "evt_123",
        "event": "invoice.paid",
        "deliveredAt": "2024-01-15T10:30:00Z",
        "responseCode": 200,
        "responseTime": 156
      }
    ]
  }
}

Testing Webhooks

Using ngrok for Local Development

# Install ngrok
npm install -g ngrok

# Start your local server
node server.js

# In another terminal, create tunnel
ngrok http 3000

# Use the HTTPS URL for webhook subscription
curl -X POST "https://api.bigledger.com/v1/webhooks/subscribe" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "X-Company-Id: YOUR_COMPANY_ID" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://abc123.ngrok.io/webhooks/bigledger",
    "events": ["invoice.created"]
  }'

Webhook Testing Endpoint

Use our webhook testing endpoint to send test events:

POST /api/v1/webhooks/{webhookId}/test

Request Body:

{
  "event": "invoice.created",
  "testData": {
    "invoiceId": "inv_test_123",
    "customerId": "cust_test_456"
  }
}

Event Filtering

Create targeted webhook subscriptions for specific conditions:

Filter by Customer

{
  "url": "https://your-app.com/webhooks/vip-customers",
  "events": ["invoice.created", "payment.received"],
  "filters": {
    "customer.tags": ["vip", "enterprise"],
    "invoice.total": {"min": 1000.00}
  }
}

Filter by Amount

{
  "url": "https://your-app.com/webhooks/large-payments",
  "events": ["payment.received"],
  "filters": {
    "payment.amount": {"min": 5000.00}
  }
}

Filter by Product

{
  "url": "https://your-app.com/webhooks/software-sales",
  "events": ["invoice.created"],
  "filters": {
    "invoice.items.category": ["software", "licenses"]
  }
}

Webhook Logs

View detailed logs of webhook deliveries:

GET /api/v1/webhooks/{webhookId}/logs

Query Parameters:

ParameterTypeDescription
eventstringFilter by event type
statusstringFilter by delivery status (success, failed, pending)
dateFromstringStart date for logs
dateTostringEnd date for logs
limitintegerNumber of log entries

Response:

{
  "success": true,
  "data": [
    {
      "id": "log_123456789",
      "eventId": "evt_987654321",
      "event": "invoice.paid",
      "deliveredAt": "2024-01-15T10:30:00Z",
      "responseCode": 200,
      "responseTime": 234,
      "responseBody": "Event processed successfully",
      "retryCount": 0,
      "status": "success"
    },
    {
      "id": "log_123456790",
      "eventId": "evt_987654322",
      "event": "payment.received",
      "deliveredAt": "2024-01-15T10:32:00Z",
      "responseCode": 500,
      "responseTime": 5000,
      "responseBody": "Internal server error",
      "retryCount": 2,
      "status": "failed",
      "nextRetryAt": "2024-01-15T10:42:00Z"
    }
  ]
}

Advanced Webhook Features

Webhook Event Batching

For high-volume environments, enable event batching to receive multiple events in a single request:

{
  "url": "https://your-app.com/webhooks/batch",
  "events": ["invoice.created", "payment.received"],
  "batchSettings": {
    "enabled": true,
    "maxEvents": 100,
    "maxWaitTime": 30,
    "flushOnEvent": ["invoice.paid"]
  }
}

Batch Payload:

{
  "batch": true,
  "events": [
    {
      "id": "evt_001",
      "event": "invoice.created",
      "timestamp": "2024-01-15T10:30:00Z",
      "data": {...}
    },
    {
      "id": "evt_002", 
      "event": "payment.received",
      "timestamp": "2024-01-15T10:31:00Z",
      "data": {...}
    }
  ],
  "batchId": "batch_123456789",
  "batchTimestamp": "2024-01-15T10:31:30Z"
}

Webhook Transformations

Customize webhook payloads to match your application’s data format:

{
  "url": "https://your-app.com/webhooks/custom",
  "events": ["invoice.created"],
  "transform": {
    "template": {
      "id": "{{data.object.id}}",
      "invoice_number": "{{data.object.invoiceNumber}}",
      "customer_name": "{{data.object.customer.name}}",
      "amount": "{{data.object.total}}",
      "currency": "{{data.object.currency}}",
      "custom_field": "Invoice Created"
    },
    "contentType": "application/json"
  }
}

Conditional Webhooks

Create webhooks that only fire when specific conditions are met:

{
  "url": "https://your-app.com/webhooks/conditional",
  "events": ["invoice.created"],
  "conditions": [
    {
      "field": "data.object.total",
      "operator": "gt",
      "value": 1000.00
    },
    {
      "field": "data.object.customer.tags",
      "operator": "contains",
      "value": "vip"
    }
  ],
  "conditionLogic": "AND"
}

Integration Examples

Complete E-commerce Integration

// Handle order fulfillment workflow
async function handleOrderWebhooks(event) {
  switch (event.event) {
    case 'sales_order.created':
      await notifyWarehouse(event.data.object);
      await updateInventorySystem(event.data.object.items);
      break;
      
    case 'sales_order.shipped':
      await sendShippingNotification(event.data.object);
      await updateTrackingInfo(event.data.object);
      break;
      
    case 'invoice.paid':
      await releaseShipment(event.data.object.salesOrderId);
      await updateCRMStatus(event.data.object.customerId, 'paid');
      break;
      
    case 'inventory.low_stock':
      await createPurchaseOrder(event.data.object);
      await notifyBuyer(event.data.object);
      break;
  }
}

Financial Reporting Automation

// Automate financial reporting
async function handleFinancialWebhooks(event) {
  const { event: eventType, data } = event;
  
  if (eventType === 'invoice.paid') {
    // Update cash flow forecasting
    await updateCashFlowForecast(data.object);
    
    // Check for milestone achievements
    await checkRevenueTargets(data.object);
  }
  
  if (eventType === 'bill.paid') {
    // Update expense tracking
    await updateExpenseAnalysis(data.object);
    
    // Check budget compliance
    await validateBudgetCompliance(data.object);
  }
  
  if (eventType === 'report.generated') {
    // Automatically distribute reports
    await distributeFinancialReports(data.object);
  }
}

Webhooks provide the foundation for building real-time, event-driven integrations with BigLedger, enabling sophisticated automation and seamless data synchronization across your entire business ecosystem.