Developer Tools
Webhooks
Set up webhook endpoints in the app or through the API, choose events, send a test, read the delivery log and redeliver failed events.
Webhooks push events to your own systems the moment something happens in your account: an invoice is sent or paid, a client submits a payment proof, an estimate is accepted. You add an HTTPS URL, choose the events you care about, and we POST a signed JSON event to it every time one of them happens. This page shows how to set up and manage endpoints. The payload, signature scheme and full event list are in Webhook events.
Webhooks are available on every plan from launch.
Before you start
- You need the
integrations.managepermission. Owners and Admins have it by default. - You need a public HTTPS URL that can receive
POSTrequests. Plainhttp, private network addresses and internal hostnames are refused (proposed: port 443 only, and redirects are not followed). - Your endpoint should verify the signature on every request. The Node.js sample does it in a few lines.
Add an endpoint in the app
Open Settings > Integrations
Go to the webhooks section and choose to add an endpoint.
Enter the URL
Paste your HTTPS URL, for example https://example.com/webhooks/fia.
Choose events
Tick the event types this endpoint should receive, such as document.paid, payment.succeeded and credit_note.created. Only ticked events are sent. See the event list.
Limit to one business (optional)
If your account has several businesses, you can limit the endpoint to one. Leave it on all businesses to receive events for every business.
Save and copy the signing secret
The endpoint gets its own signing secret. Copy it into your server's secret store. Afterwards the app only shows its last four characters.
Send a test
Send a test delivery from the endpoint to get a sample event, and check that your endpoint returns 200 and verifies the signature.
Add an endpoint through the API
Use Create or update a webhook endpoint. The same call updates an endpoint when you pass its id; PATCH /v1/webhook-endpoints/{id} also works.
curl -X POST "https://api.freeinvoiceautomation.com/v1/webhook-endpoints" \
-H "Authorization: Bearer $FIA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: webhook-endpoint-billing-server" \
-d '{
"url": "https://example.com/webhooks/fia",
"events": ["document.sent", "document.paid", "payment.succeeded", "credit_note.created"],
"business_id": "0193f29e-11aa-7c3b-8d2e-5a6b7c8d9e0f"
}'The endpoint fields are:
| Field | Meaning |
|---|---|
url | Your HTTPS URL |
events | The event types to send |
business_id | Optional. Only events of this business; leave out (or null) for all businesses |
status | active or disabled |
The signing secret is write-only after creation: no later response contains it, only its last four characters.
Manage endpoints
| Task | Endpoint | CLI |
|---|---|---|
| List endpoints | List webhook endpoints | webhook list |
| Create or change one | Create or update a webhook endpoint | webhook upsert |
| Delete one | Delete a webhook endpoint | webhook delete |
| Send a test event | Test a webhook endpoint | webhook test |
| See the delivery log | List webhook deliveries | webhook deliveries |
| Redeliver one delivery | Redeliver a webhook | webhook redeliver |
All of them need integrations.manage.
Test your endpoint
The test sends a sample signed event to your URL, so you can check three things before real events arrive:
- Your server is reachable over HTTPS from the internet.
- Your signature check accepts our signature with your secret.
- You return a
2xxstatus quickly.
curl -X POST "https://api.freeinvoiceautomation.com/v1/webhook-endpoints/0193f3b0-2c3d-7e4f-8a9b-0c1d2e3f4a5b/test" \
-H "Authorization: Bearer $FIA_API_KEY" \
-H "Idempotency-Key: webhook-test-1"While developing, use a tunnelling tool that gives your local server a public HTTPS URL, and point a test endpoint at it. Use a separate endpoint (with its own secret) for production.
The delivery log
Every delivery is recorded per endpoint, with its event id, event type, status, number of attempts, the last HTTP status your server returned, the next attempt time and when it was delivered.
| Status | Meaning |
|---|---|
pending | Waiting for its first or next attempt |
succeeded | Your endpoint answered with success |
failed | The last attempt failed; another attempt is scheduled |
dead | All attempts failed; it will not be retried automatically |
Failed deliveries are retried with exponential backoff, about 8 attempts over roughly 24 hours (proposed). The event keeps the same id on every retry, so your endpoint can ignore repeats.
Redeliver an event
After you fix a problem on your side, send dead or failed deliveries again from the delivery log, or with Redeliver a webhook:
curl -X POST "https://api.freeinvoiceautomation.com/v1/webhook-deliveries/0193f3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b/redeliver" \
-H "Authorization: Bearer $FIA_API_KEY" \
-H "Idempotency-Key: redeliver-0193f3c4"A redelivered event has the same id as the original, so if you already processed it, your deduplication skips it safely.
Endpoints that keep failing
An endpoint whose deliveries fail again and again can be disabled automatically (proposed: after 50 failed deliveries in a row). Its status becomes disabled and nothing more is sent until you fix the problem and set it back to active. Events that happened while it was disabled can be caught up through the API using the list endpoints with updated_after. See Pagination and filters.
Rotate the signing secret
If a secret may have leaked, rotate it. For a grace period we sign each delivery with both the old and the new secret, so your server keeps accepting deliveries while you deploy the new one. See Rotating the secret.
Writing a reliable receiver
Respond fast
Verify the signature, store the event, return 200, and do the real work in a background job. Slow responses time out and are retried.
Deduplicate on the event id
Keep the ids you have processed. The same event can arrive more than once, after a retry or a redelivery.
Do not rely on order
Events can arrive out of order. Use created_at, or fetch the current state from the API, for decisions.
Confirm important state with the API
Before you ship goods because an invoice is paid, fetch the document with Get a document and check status and balance_minor.
Accept new event types and fields
New events and fields are added as features ship. Ignore what you do not recognise.
Common uses
| Goal | Events |
|---|---|
| Mark an order paid in your system | document.paid, document.partially_paid |
| Keep a CRM up to date | client.created, client.updated, document.sent |
| Alert your team about bank transfer proofs | payment_proof.submitted |
| Start work when a client accepts a quote | estimate.accepted |
| Track AI agent results | agent_run.completed, agent_run.failed |
| Chase late payers in your own tools | document.overdue |