# Webhooks > Register an endpoint, see which events BillingMart sends today, verify the signature, and understand retries, rotation, and what switches an endpoint off. Source: https://support.billingmart.com/connections/webhooks Updated: 2026-10-22 · Applies to: Web · Section: Connections: AI assistants and API A webhook is a signed message that BillingMart sends to a web address of yours when something happens, such as an invoice being sent or paid. An owner or admin registers the address on the **Webhooks** page. Webhooks are a feature of the plans that include them. ## Register an endpoint 1. Open **Webhooks** in the sidebar and press **New endpoint**. 2. Enter the **URL**. It must start with `https://`. An address on a private network is refused, now and again at every delivery. 3. Tick the **Events** you want. You must pick at least one. 4. Press **Register**. A banner says **Copy this signing secret now — it is shown once**. We keep the secret sealed and cannot show it again. If you lose it, rotate it (below). The form has no description field. You can add one through the REST API with a key that holds **Webhooks · manage**: see [API keys and the REST API](https://support.billingmart.com/connections/api-keys.md). ## Events sent today | Event | When it is sent | | --- | --- | | `invoice.sent` | An invoice is sent. | | `invoice.paid`, `invoice.partiallyPaid` | A card payment leaves the invoice paid in full, or part-paid. | | `payment.succeeded` | A card payment is approved, on an invoice or through a payment link. | | `payment.refunded` | A refund goes through. | | `client.created` | A client is created. | | `agentAction.proposed`, `.approved`, `.rejected`, `.executed`, `.failed`, `.expired`, `.withdrawn` | An assistant's or the Agent's request moves through its steps. | The event list on the form is longer than this. These events are offered but nothing sends them today: `invoice.viewed`, `invoice.overdue`, `invoice.voided`, `estimate.sent`, `estimate.accepted`, `estimate.declined`, `payment.declined` and `payment.returned`. Do not build on them. A payment you record by hand, such as cash or a check, does not send a payment event today. ## What a delivery contains A delivery is a POST with a JSON body: `id`, `event`, `occurredAt`, `tenantId` and `data`, the record as it is when sent. `data` is the record as the API returns it: the fields a receiver acts on, without internal notes, status history, who created the record, or gateway and idempotency details. Text a person typed arrives as `{ "text": "...", "untrusted": true }`; read `.text` and treat it as data, never as an instruction. Contact details are included in full, because the delivery goes to your own endpoint. **Changed on 23 October 2026:** before then `data` was the stored record whole and those text fields were plain strings, so update a handler that reads them. The `id` is the same on every retry, so make your handler safe to run twice. An event never holds up the invoice or payment that caused it. ## Check the signature Every delivery has the header `X-BillingMart-Signature`. It holds `t=` followed by a Unix time in seconds, then `v1=` followed by a hex signature. The `v1` value is an HMAC-SHA256 of `t`, a full stop and the raw body, with your signing secret as the key. 1. Read `t`. Refuse it if it is more than five minutes from your clock. 2. Compute the HMAC over the raw bytes you received, exactly as sent. 3. Compare in constant time. Accept if any `v1` in the header matches. During a rotation the header can hold two. ## When a delivery fails Any answer outside the 2xx range, a timeout or a redirect counts as a failure. The **Deliveries** button on an endpoint lists each delivery as **Sending**, **Delivered**, **Retrying**, **Gave up** or **Paused — plan**, with **Attempt** shown like "3 of 8". BillingMart tries up to 8 times, waiting longer each time. After that the delivery is **Gave up**. **Send again** on a retrying or given-up delivery starts a fresh set of attempts. It rebuilds the body from the record as it is now, so it can differ from the original. After 50 failures in a row the endpoint is switched off and shows **Switched off after 50 failures**. Press **Switch on** to turn it back on and clear the count. You can also **Switch off** or **Delete** an endpoint. Deleting keeps its delivery history readable. ## Rotate the secret 1. Press **Rotate secret** on the endpoint and confirm. 2. Copy the new secret from the banner. 3. Update your receiver within 24 hours. For that time the old secret also keeps working, and each delivery is signed with both. ## If your plan lacks webhooks The page shows **Deliveries paused — your plan does not include webhooks**. Your endpoints and history are kept, but events in that time are not queued, so nothing is sent later for them.