---
title: "A new webhook contract: visit events and delivery IDs"
description: "Webhooks now name each delivery after what the visitor did (visit.entered, visit.interacted, visit.routed, visit.exited), carry a signed delivery ID that stays the same on retries, and wrap the visit in a versioned envelope."
canonical: "https://linkbreakers.com/help/article/a-new-webhook-contract-visit-events-and-delivery-ids"
last-updated: 2026-11-03
---

# A new webhook contract: visit events and delivery IDs

> Webhooks now name each delivery after what the visitor did (visit.entered, visit.interacted, visit.routed, visit.exited), carry a signed delivery ID that stays the same on retries, and wrap the visit in a versioned envelope.

## Short answer

There is now one webhook delivery per step completion, named after what the visitor did: `visit.entered`, `visit.interacted`, `visit.routed` or `visit.exited`. Two headers come with it, `X-Linkbreakers-Event` and `X-Linkbreakers-Delivery-ID`, and both are covered by the request signature. The body is an envelope (`id`, `type`, `version`, `createdAt`, `attempt`, `webhook`, `data`) with the visit and the step under `data`. The contract version is `2026-11-04`.

## What changed

**Event names.** A delivery is named after the kind of step that triggered it:

| Event | Step kind | When |
|---|---|---|
| `visit.entered` | `ENTRY` | The visitor scanned the QR code or opened the link |
| `visit.interacted` | `INTERACTION` | The visitor submitted a form, entered a password, picked a link or completed another interactive step |
| `visit.routed` | `CONDITION` | A condition routed the visitor by country, device, language, time or visitor data |
| `visit.exited` | `EXIT` | The visitor left the workflow through a redirect, a contact card or another exit |

They replace `event.completed` and `step.completed`. Your webhook's triggers still pick which step kinds it receives.

**Headers.** Two headers of ours, both signed:

- `X-Linkbreakers-Event`: the event name, the same as the body's `type`
- `X-Linkbreakers-Delivery-ID`: one ID per delivery, that is per webhook and step completion, the same as the body's `id`. A step completed twice in a visit, such as a form submitted again, sends two deliveries with two IDs

`X-Linkbreakers-Event-Type`, `X-Linkbreakers-Event-ID` and `X-Linkbreakers-Webhook-ID` are gone.

**Body.** The visit moved under `data`:

```json
{
  "id": "693b65e8-0262-57ac-b62b-e5c0169adc2d",
  "type": "visit.interacted",
  "version": "2026-11-04",
  "createdAt": "2026-11-04T09:12:25Z",
  "attempt": 1,
  "webhook": { "id": "42806ec9-1085-404a-b353-75b156cb71b3", "name": "New leads" },
  "data": {
    "event": { "visitor": {}, "device": {}, "link": {}, "traces": [], "journey": {} },
    "step": { "eventAction": "FORM_SUBMIT", "kind": "INTERACTION" },
    "lbid": "2VqKhjxtKwC7pBbg"
  }
}
```

`createdAt` is when the visitor completed the step, not when the request left. `webhook` now holds only the webhook's `id` and `name`. `data.event` carries everything it did before: the visitor with their attributes and visit counts, the device with its in-app browser, the link with its metadata and tags, every step so far in `traces`, and the journey. `data.step` is the whole step, as it was when the visitor completed it.

## Deduplicate on the delivery ID

A delivery that fails is retried up to three times. Every retry is the same delivery: the same `X-Linkbreakers-Delivery-ID`, the same step, and `attempt` one higher. If your endpoint processed a delivery but answered too late, the retry reaches you with an ID you have already seen. Store the IDs you handled and answer a repeat with a 2xx without acting on it again, as you would with a GitHub or Shopify delivery ID.

## What to update

- Route on `X-Linkbreakers-Event` or `type`, with the new names
- Read the visit from `data.event` and the step from `data.step` instead of `event` and `workflowStep`
- Read the LBID from `data.lbid`
- If you verify signatures, add `x-linkbreakers-event` and `x-linkbreakers-delivery-id` to the values you rebuild the signature base from, or build the base from the component list in `Signature-Input`

Zapier, Make and n8n scenarios that map fields from a Linkbreakers webhook need their mappings pointed at `data.event` and `data.step`.

The full reference is in [How do webhooks work in Linkbreakers?](https://linkbreakers.com/help/article/how-do-webhooks-work-in-linkbreakers), and signature checks with code for Node.js and Python are in [Verify Linkbreakers webhook signatures](https://linkbreakers.com/help/article/verify-webhook-signatures).
