POST request to that endpoint every time a matching event occurs.
Managing webhooks — in the app or via the API — requires an admin user. Non-admin API keys receive a
403 from the webhook endpoints.Setting up a webhook
You can create a webhook in the Campfire app or via the API.In the app
1
Go to the Webhooks page
Go to Settings > Developer > Webhooks in your Campfire instance.
2
Create the webhook
Click New Webhook, enter the HTTPS URL of your endpoint, check Active, and select the topics you want to subscribe to. Topics are grouped by object (Invoices, Bills, Journal Entries, etc.) — you can select a whole group or individual topics.
3
Copy your signing secret
After saving, the webhook card shows an HMAC-SHA256 Secret. Click Show to reveal it and store it in your application’s secret store. You’ll use it to verify signatures on incoming deliveries.
Via the API
Create a webhook with aPOST to the webhook endpoint. See our authentication guide for how to get an API key.
token) and the webhook’s uuid:
The signing secret is generated by Campfire and cannot be set or changed. To rotate it, delete the webhook and create a new one.
Topics
A webhook only receives events for the topics it is subscribed to. Topic names are exact strings — subscribe using the values below.
Payment topics have special semantics for invoices and bills:
Invoice.payment/Bill.paymentfires whenever the amount paid increases — including partial payments.Invoice.paid/Bill.paidfires when the document transitions to fully paid. When that happens, both the.paidand.paymenttopics fire (as two separate deliveries), and no.updatedevent is sent for that change.
Payload
Every delivery is aPOST with a JSON body containing four fields:
Example delivery for
Invoice.paid:
Request headers
Verifying the signature
Every delivery is signed with your webhook’s secret so you can verify it genuinely came from Campfire. The signed message is the delivery timestamp and the raw request body, joined by a single: character:
timestamp is the value of the Campfire-Webhook-Request-Timestamp header. Campfire computes an HMAC-SHA256 digest of this message using your signing secret as the key, hex-encodes it, and sends it in the Campfire-Webhook-Signature-v1 header.
To verify a delivery:
- Read the
Campfire-Webhook-Request-TimestampandCampfire-Webhook-Signature-v1headers and the raw request body. - Reject the delivery if the timestamp is too old — we recommend a tolerance of 5 minutes. This protects against replay attacks. (Retried deliveries are re-signed with a fresh timestamp, so a tolerance check will not reject legitimate retries.)
- Compute
HMAC-SHA256over"{timestamp}:{raw body}"using your signing secret, hex-encode it, and compare it to the signature header using a constant-time comparison.
In Express, use
express.raw({ type: "application/json" }) (or capture req.rawBody) on your webhook route so the body is available unparsed. In Django/Flask/FastAPI, use request.body / request.get_data() / await request.body() respectively.Delivery and retries
- Deliveries are
POSTrequests. Respond with a 2xx status code to acknowledge receipt. Any 4xx or 5xx response is treated as a failure. - Failed deliveries are retried up to 5 times, roughly one minute apart. A
404response is not retried. - Each retry is re-signed with a fresh timestamp and signature.
- Respond quickly — acknowledge the delivery first and process the event asynchronously if your handling is slow.
- Deliveries are not guaranteed to arrive in order, and retries mean your endpoint may occasionally receive the same event more than once. Make your handler idempotent — for example, treat the payload’s
dataas the latest state of the object rather than applyingchangesincrementally.
Delivery logs
Recent delivery attempts appear under Recent Events on the Webhooks settings page, where you can inspect each request and response. The same data is available via the API (most recent 25 events):status (PENDING, SUCCESS, or FAILURE), the topic, and an http object with the full request and response — URL, headers, body, and response status code.
Managing webhooks via the API
Notes:
{id}is the integeridfield from the webhook object, not itsuuid.urlandactiveare required on create and update.topicsis a list of{"name": "<topic>"}objects. Topic names must match the values in the topics table exactly.- Updating
topicsreplaces the full set — include every topic the webhook should be subscribed to, not just new ones. token(the signing secret) is read-only and returned on every read. To rotate it, delete the webhook and create a new one.- To pause deliveries without deleting the webhook, set
"active": false.
Need help? Contact your Campfire support team and we’re happy to help you build your best Campfire workflow!