Documentation Index
Fetch the complete documentation index at: https://docs.invoica.ai/llms.txt
Use this file to discover all available pages before exploring further.
@invoica/webhooks-receiver
A small TypeScript package that handles Invoica webhook delivery for you: HMAC signature verification, discriminated-union event types for every Invoica event, and an optional Express middleware. Apache-2.0.
Install
npm install @invoica/webhooks-receiver
ESM and CJS both supported. Strict TypeScript types. Express is an optional peer — the verifier works standalone with any framework.
The 10-line example
import express from 'express';
import { invoicaWebhookHandler } from '@invoica/webhooks-receiver';
const app = express();
app.post(
'/webhooks/invoica',
express.raw({ type: 'application/json' }), // keep the bytes Invoica signed
invoicaWebhookHandler(process.env.INVOICA_WEBHOOK_SECRET!, {
'mandate.signed_by_both': async (event) => {
console.log('PACT mandate fully signed:', event.data.id, event.data.scope);
},
'invoice.settled': async (event) => {
console.log('Invoice paid:', event.data.id, event.data.amount, event.data.currency);
},
}),
);
app.listen(3000);
That’s it. HMAC verified, event typed, handler dispatched.
Use express.raw, not express.json. Invoica signs the raw request bytes. If you parse JSON first, the signature won’t verify — the SDK warns loudly when this happens, but use express.raw as the default.
Standalone verifier (no Express)
If you’re on Fastify, Hono, Cloudflare Workers, or anything else:
import { verifyWebhook } from '@invoica/webhooks-receiver';
const result = verifyWebhook(
rawBodyBytes, // string | Buffer | Uint8Array
signatureHeader, // value of x-invoica-signature
process.env.INVOICA_WEBHOOK_SECRET!,
);
if (!result.valid) {
return new Response(result.error, { status: 401 });
}
// result.event is fully typed by event_type
switch (result.event.type) {
case 'mandate.signed_by_both':
// result.event.data is MandatePayload, narrowed
break;
case 'invoice.settled':
// result.event.data is InvoicePayload, narrowed
break;
}
verifyWebhook never throws. It returns { valid: true, event } or { valid: false, error }. Explicit failure modes for explicit error handling.
Event types supported
Discriminated by event.type. All payloads strictly typed.
Mandate lifecycle
mandate.proposed
mandate.signed_by_proposer
mandate.signed_by_both
mandate.completed
mandate.disputed
Invoice lifecycle
invoice.created
invoice.settled
Event types not yet emitted
The following are registered in /v1/webhooks/events but Invoica’s backend does not currently fire them — partner code subscribing to these waits forever. Tracked as a bug, fix in flight:
mandate.in_progress
mandate.expired
invoice.cancelled
invoice.refunded
agent.reputation_changed
settlement.confirmed
The SDK does not type these in v0.1 because they don’t ship payloads we can promise. Types will land in v0.2 once the backend emits them.
Signature canonicalization
For partners not using the SDK, the signing input is JSON.stringify(event) — the whole envelope, not just event.data. Header: x-invoica-signature: sha256=<hex>. HMAC-SHA256 with the secret you received during onboarding.
Re-serializing the body (e.g., parsing then re-stringifying) will produce a different byte sequence and the signature will not verify. Always sign and verify against the raw bytes Invoica sent.
Repo
github.com/skingem/Invoica/sdk/webhooks-receiver
License
Apache-2.0.