Skip to content

Webhooks

Christian KASSE edited this page Apr 2, 2026 · 1 revision

Webhooks

Overview

Essabu sends webhook events to notify your application of changes in real-time. The SDK provides utilities to parse and verify webhook payloads.

Supported Events

Module Event Types
HR employee.created, employee.updated, employee.terminated, leave.approved, leave.rejected, payroll.approved, contract.created
Accounting invoice.created, invoice.finalized, invoice.paid, payment.received, quote.accepted, journal.posted
Identity user.created, user.updated, user.deactivated, role.changed, tenant.created
Trade order.created, order.fulfilled, customer.created, opportunity.won, opportunity.lost, stock.low
Payment payment_intent.confirmed, payment_intent.failed, subscription.created, subscription.cancelled, refund.processed, payout.completed
E-Invoice einvoice.submitted, einvoice.accepted, einvoice.rejected, einvoice.cancelled
Project project.created, task.completed, milestone.reached
Asset asset.created, maintenance.due, maintenance.completed

Receiving Webhooks

Spring Boot Controller

@RestController
@RequestMapping("/webhooks")
public class WebhookController {

    @PostMapping("/essabu")
    public ResponseEntity<Void> handleWebhook(
            @RequestBody String payload,
            @RequestHeader("X-Essabu-Signature") String signature) {

        // Verify the webhook signature
        boolean valid = EssabuWebhook.verify(payload, signature, "your-webhook-secret");
        if (!valid) {
            return ResponseEntity.status(401).build();
        }

        // Parse the event
        Map<String, Object> event = EssabuWebhook.parse(payload);
        String eventType = (String) event.get("type");
        Map<String, Object> data = (Map<String, Object>) event.get("data");

        switch (eventType) {
            case "invoice.paid" -> handleInvoicePaid(data);
            case "employee.created" -> handleEmployeeCreated(data);
            case "payment_intent.confirmed" -> handlePaymentConfirmed(data);
            default -> log.info("Unhandled event: {}", eventType);
        }

        return ResponseEntity.ok().build();
    }
}

Webhook Payload Structure

{
  "id": "evt_xxxxxxxxxxxx",
  "type": "invoice.paid",
  "createdAt": "2026-03-26T10:00:00Z",
  "tenantId": "your-tenant-uuid",
  "data": {
    "id": "inv_xxxxxxxxxxxx",
    "amount": 1500.00,
    "currency": "USD",
    "customerId": "cust_xxxxxxxxxxxx"
  }
}

Signature Verification

Every webhook includes an X-Essabu-Signature header containing an HMAC-SHA256 signature. Always verify this signature before processing the event.

boolean valid = EssabuWebhook.verify(rawBody, signatureHeader, webhookSecret);

Best Practices

  1. Always verify signatures to prevent spoofed events.
  2. Respond with 200 quickly -- process events asynchronously if needed.
  3. Handle duplicate events -- webhooks may be retried. Use the event id for idempotency.
  4. Log unhandled event types for future implementation.

Clone this wiki locally