Reference
Webhooks
Receive HTTP callbacks when things happen in Finora Business. Signed, retried, and scoped per event type.
Webhooks let your integration react to events in Finora Business without polling. Register an endpoint, subscribe to the events you care about, and we'll POST a signed JSON payload when each event fires.
Supported events
| Event | Fires when |
|---|---|
invoice.created | New invoice is created (any source). |
invoice.paid | Invoice transitions to paid (full payment recorded). |
invoice.voided | Invoice is voided, reversing its GL entry. |
receipt.created | New receipt is recorded. |
expense.created | New expense is posted. |
bill.created | New supplier bill is recorded. |
bill.paid | Bill is fully paid. |
payment.received | A payment webhook from Paystack (or other gateway) lands successfully. |
customer.created | New customer is created. |
customer.updated | Customer record is updated. |
report.generated | A scheduled report PDF/Excel is generated. |
loan.disbursed | A loan disbursement is recorded (enterprise). |
loan.repayment | A loan repayment is recorded (enterprise). |
health_score.changed | Business health score crosses a threshold (enterprise). |
Register a webhook
/v1/webhooksCreates a webhook. The response includes a signing secret — save it, it's shown once.
Body
| Field | Type | Required | Notes |
|---|---|---|---|
url | string | yes | Your receiver endpoint. Must be HTTPS. |
events | array | yes | One or more event types. At least one required. |
description | string | no | Shown in the dashboard. |
Response — 201
{
"success": true,
"data": {
"id": "wh_abc",
"url": "https://your-app.com/finora-webhook",
"events": ["invoice.paid", "receipt.created"],
"secret": "whsec_a1b2c3d4e5f6...",
"isActive": true,
"createdAt": "2026-04-17T10:30:00.000Z"
}
}List webhooks
/v1/webhooksReturns your registered webhooks (secrets are masked).
Update a webhook
/v1/webhooks/{id}Edit URL, events, or active status. The secret stays the same.
Delete a webhook
/v1/webhooks/{id}Permanently removes the webhook. In-flight events are abandoned (no retries).
Verifying signatures
Every webhook POST includes X-Finora-Signature: sha256=<hex> computed as
HMAC-SHA256 of the raw request body with your webhook secret as the key.
Node.js example
import crypto from 'node:crypto'
function verify(rawBody: string, signatureHeader: string, secret: string) {
const expected = crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex')
const provided = signatureHeader.replace(/^sha256=/, '')
return crypto.timingSafeEqual(
Buffer.from(expected, 'hex'),
Buffer.from(provided, 'hex'),
)
}Reject any request that fails verification — the webhook did not come from Finora Business.
Delivery semantics
- Webhooks are delivered with at-least-once semantics. Your handler must be idempotent (we include an event ID in every payload).
- Finora Business retries on 5xx responses with exponential backoff: 1m, 5m, 30m, 2h, 12h.
- A 2xx response (any of 200–299) is considered acknowledged.
- 4xx responses are not retried — fix the handler and update the webhook.
Payload shape
{
"eventId": "evt_01HZXA...",
"event": "invoice.paid",
"businessId": "biz_001",
"occurredAt": "2026-04-17T10:30:00.000Z",
"data": {
"invoiceId": "inv_abc",
"invoiceNumber": "INV-2026-001",
"totalKobo": 53750000,
"amountPaidKobo": 53750000
}
}Limits
- Maximum 20 webhooks per account.
- URL must be HTTPS.
- Each event is delivered within a few seconds under normal load.
Related endpoints
Reference index
Back to all modules