Skip to main content

Webhook Event Types

Invoice AI fires a webhook event every time a significant action occurs on an invoice. Your registered webhook endpoints receive a signed HTTP POST with a JSON payload describing what happened and which invoice was affected.

All event types

There is no invoice.overdue event. Overdue status is computed from due_date at read time — it is never stored — so there is no discrete moment when it transitions. To find overdue invoices, poll GET /api/v1/invoices?status=overdue.

Payload structure

Every event payload follows the same envelope:
string
The event type string, e.g. "invoice.paid". Always present.
object
Container for the event data.

Event payload examples

invoice.paid

Fired when an invoice is marked paid. The paid_at timestamp is set and status changes to "paid".

invoice.email_failed

Fired when an invoice email bounced or was rejected. The meta field on the underlying event row may contain error details from the mail provider. Use this event to detect delivery failures and notify the customer through an alternative channel.
When you receive invoice.email_failed:
  1. Retrieve the customer’s contact details with GET /api/v1/customers/:id.
  2. Verify or correct the email address, then re-send through the dashboard or the send endpoint.
  3. Consider notifying your team via a support queue so a human can follow up.

invoice.finalized

Fired when a draft is finalized. An invoice number is assigned and status moves to "open".

invoice.voided

Fired when an invoice is voided. status becomes "voided", voided_at is set, and amount_due drops to 0.

Signature verification

All deliveries are signed using the Standard Webhooks HMAC-SHA256 scheme. Three headers travel with every request: The signed string is constructed as:
The HMAC key is derived from your whsec_… secret by stripping the whsec_ prefix and base64url-decoding the remainder.

Node.js verification example

Express.js handler example

You must use timingSafeEqual (or an equivalent constant-time comparison) when comparing signatures. A standard === check is vulnerable to timing side-channel attacks.
Use the webhook-id as an idempotency key in your handler. If your server crashes after processing but before responding 200, Invoice AI will retry with the same webhook-id. Deduplicate on this value to avoid processing the same event twice.

Secret rotation

To rotate your webhook signing secret:
  1. Delete the existing endpoint (DELETE /api/v1/webhook-endpoints/:id) and immediately create a new one with the same URL (POST /api/v1/webhook-endpoints). The creation response includes your new whsec_… secret.
  2. Update the secret value in your server’s configuration before any new deliveries arrive on the new endpoint.
  3. Any in-flight deliveries against the old endpoint will exhaust their retry schedule and stop; the new endpoint starts with a clean slate.
Because the webhook-signature header may carry multiple space-separated signatures, you can verify against both the old and new secret during your deployment window to avoid dropping events mid-rotation. Pass both secrets through your verification logic and accept the delivery if either matches.