Event types
The 15 events a webhook endpoint can receive, when each one is sent, and what its data holds.
Every delivery's body is an event, the same object GET /events returns. Its type is always <object>.<new status>, and data.previous_status holds the status before the change.
{
"object": "event",
"id": "evt_5Rt8Wq2Lm7Xn3Kb9Vz1PdJ",
"type": "listing.live",
"created_at": "2026-10-08T14:17:01Z",
"livemode": false,
"project": "prj_4QzX1m9Lr2Vb7Nc8Tk3HwP",
"data": {
"object": {
"object": "listing",
"id": "lst_7Hk2Qn4Rt9Vx1Bm6Cz3LdK",
"livemode": false,
"project": "prj_4QzX1m9Lr2Vb7Nc8Tk3HwP",
"directory": {
"id": "dir_2Lm8Wq3Zk7Rt1Xc5Vb9NdF",
"name": "BetaList",
"domain_rating": 73,
"logo_url": "https://feed.example.net/a/x1Lq7Rt.k9Zm"
},
"status": "live",
"reason": null,
"action": null,
"live_url": "https://betalist.com/startups/ledgerly",
"proof_url": "https://feed.example.net/a/Pf8Kq2Wm.t6Lx9",
"created_at": "2026-10-03T14:00:00Z",
"submitted_at": "2026-10-04T09:00:00Z",
"live_at": "2026-10-08T14:00:00Z"
},
"previous_status": "submitted"
}
}The event object
| Field | What it holds |
|---|---|
id | The event's id, which is also the webhook-id header of its deliveries. |
type | One of the types below. |
created_at | When the change was recorded, exact to the second and within about a minute of the change. |
livemode | true for live events, false for test events. |
project | The project the event is about, or null for ping. |
data.object | The project, listing, report or webhook endpoint right after the change. |
data.previous_status | The status before the change, or null for project.listings_added and ping. |
data.listings_created | On project.launched only: how many listings the launch created. |
data.listings_added | On project.listings_added only: how many listings were added. |
Listing times inside data.object stay rounded down to the hour, as everywhere else.
Project events
| Type | Sent when |
|---|---|
project.launched | A project launches. It is the launch's only event: data.listings_created counts the new listings, all in pending, and data.previous_status is draft. |
project.listings_added | Directories are added to a launched project, by POST /projects/{project_id}/listings or by auto-replace. data.listings_added counts the new listings. |
project.in_progress | A project moves again after action_required, on_hold or completed. A launch does not send it. |
project.action_required | A project needs you or your client. data.object.action says what and who, and client_message is ready to forward. |
project.on_hold | Work on a project pauses. data.object.hold_reason is review while we review the project, which clears without action from you, or paused. |
project.completed | No listing is pending, in_progress or action_required, and auto-replace has nothing to add. Listings in submitted can still go live. |
Listing events
| Type | Sent when |
|---|---|
listing.in_progress | A listing leaves pending, or moves on after action_required. |
listing.action_required | A listing waits on you or your client. For install_badge, forward client_message to your client and verify the badge once it is on the site. |
listing.submitted | The directory has the listing and reviews it. Also sent, with data.previous_status live, when a live listing goes back to review. |
listing.live | The listing is published. data.object.live_url points to it, or is null when it went live without a link. |
listing.not_accepted | The directory declined the listing, could not take it, or took a live listing down. data.object.reason says which, and the place in the allowance comes back. |
listing.cancelled | We withdrew the listing before it reached the directory. The place in the allowance comes back. |
A new listing sends no event of its own: project.launched and project.listings_added count them. A listing can go live without a listing.submitted first.
Report events
| Type | Sent when |
|---|---|
report.ready | A report has been built. Download it with GET /reports/{report_id}/file before data.object.expires_at, 7 days later. |
report.failed | A report could not be built. Start a new one, and write to support with the event's id if it fails again. |
The test event
| Type | Sent when |
|---|---|
ping | You send a test event with POST /webhook_endpoints/{webhook_endpoint_id}/test or the console's test button. It goes to that endpoint only, whatever its events filter, and data.object is the endpoint. |
Choose what an endpoint receives
An endpoint's events lists the types it receives: types such as listing.live, groups such as listing.*, project.* and report.*, or ["*"] for every type, which is the default. A new endpoint receives the events created after it, not the ones before.
PATCH /webhook_endpoints/{webhook_endpoint_id} with a new events list replaces the whole list.
Order and repeats
Deliveries can arrive out of order, and the same event can arrive more than once. Store each webhook-id and skip the ones you have handled. Before you act on an event, compare data.previous_status with what you stored, or read the object again with its id.
A status can change after it looked final: How it works lists the corrections, and each sends its own event. New event types can appear, so answer 2xx to a type you do not know and ignore it.
Next steps
- Signatures: check that a delivery came from us.
- Retries and failures: what counts as delivered, and what happens when it is not.
- How it works: the statuses behind the events.
- Keep your data in sync: webhooks and the events feed together.