Developer ToolsWebhooks

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.manage permission. Owners and Admins have it by default.
  • You need a public HTTPS URL that can receive POST requests. Plain http, 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

1

Open Settings > Integrations

Go to the webhooks section and choose to add an endpoint.

2

Enter the URL

Paste your HTTPS URL, for example https://example.com/webhooks/fia.

3

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.

4

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.

5

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.

6

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
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:

FieldMeaning
urlYour HTTPS URL
eventsThe event types to send
business_idOptional. Only events of this business; leave out (or null) for all businesses
statusactive or disabled

The signing secret is write-only after creation: no later response contains it, only its last four characters.

Manage endpoints

TaskEndpointCLI
List endpointsList webhook endpointswebhook list
Create or change oneCreate or update a webhook endpointwebhook upsert
Delete oneDelete a webhook endpointwebhook delete
Send a test eventTest a webhook endpointwebhook test
See the delivery logList webhook deliverieswebhook deliveries
Redeliver one deliveryRedeliver a webhookwebhook 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:

  1. Your server is reachable over HTTPS from the internet.
  2. Your signature check accepts our signature with your secret.
  3. You return a 2xx status quickly.
Send a test
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.

StatusMeaning
pendingWaiting for its first or next attempt
succeededYour endpoint answered with success
failedThe last attempt failed; another attempt is scheduled
deadAll 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:

Redeliver
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

GoalEvents
Mark an order paid in your systemdocument.paid, document.partially_paid
Keep a CRM up to dateclient.created, client.updated, document.sent
Alert your team about bank transfer proofspayment_proof.submitted
Start work when a client accepts a quoteestimate.accepted
Track AI agent resultsagent_run.completed, agent_run.failed
Chase late payers in your own toolsdocument.overdue