> ## Documentation Index
> Fetch the complete documentation index at: https://docs.trychert.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Events

> Inspect, stream, and replay webhook deliveries.

Every webhook delivery is persisted to a tenant-scoped event log before any HTTP attempt. The endpoints below let you read, stream, and replay events without leaving the API.

| Endpoint                          | Use when                                              |
| --------------------------------- | ----------------------------------------------------- |
| `GET /api/v1/events`              | List events with filters and cursor pagination.       |
| `GET /api/v1/events/{id}`         | Inspect one event including full attempt history.     |
| `POST /api/v1/events/{id}/replay` | Re-fire a previously-recorded event.                  |
| `GET /api/v1/events/stream`       | Server-Sent Events feed of new events.                |
| `GET /api/v1/dev-inbox`           | Pull events when you have not yet wired up a webhook. |

All endpoints require the standard `x-chert-tenant` + signature/bearer auth.

## List events

```
GET /api/v1/events
```

| Query     | Type     | Description                                       |
| --------- | -------- | ------------------------------------------------- |
| `type`    | string   | Exact event type, e.g. `message.received`.        |
| `status`  | enum     | `pending`, `delivered`, `failed`, or `dev_inbox`. |
| `lead_id` | string   | Only events tied to this lead.                    |
| `since`   | ISO 8601 | Events with `created_at > since`.                 |
| `limit`   | int      | 1–200, default 50.                                |
| `cursor`  | string   | Opaque cursor from a prior `next_cursor`.         |

```json Response theme={null}
{
  "events": [
    {
      "id": "evt_01H...",
      "type": "message.received",
      "event_id": "chert:msg:0D36042D-C551-4CB4-9BE7-853832D6F2FE",
      "lead_id": "ld_01H...",
      "convo_id": "cv_01H...",
      "payload": { /* full event envelope */ },
      "delivery": {
        "status": "delivered",
        "url": "https://your-app.example/hooks/chert",
        "attempts": [
          {
            "at": "2026-04-29T18:15:26Z",
            "status": 200,
            "duration_ms": 142,
            "subscription_id": "0f2d3c1a-8b4e-4f6a-90d2-1a3b4c5d6e7f"
          }
        ],
        "delivered_at": "2026-04-29T18:15:26Z"
      },
      "created_at": "2026-04-29T18:15:25Z"
    }
  ],
  "next_cursor": "2026-04-29T18:14:11Z|evt_01H...",
  "count": 1
}
```

Pass `next_cursor` back as `cursor` to read the next page. When `next_cursor` is `null`, you have reached the end.

## Get one event

```
GET /api/v1/events/{id}
```

Returns the same shape as a list row, with the full `delivery.attempts` array. Each attempt records the HTTP status, duration, and any error string.

## Replay an event

```
POST /api/v1/events/{id}/replay
```

Re-POSTs the original payload to matching active webhook subscriptions, appends fresh attempts, and updates `delivery.status`. Use this after fixing a bug in your webhook handler.

To replay to one subscription, add `?subscription_id=<subscription_id>`.

```json 200 OK theme={null}
{
  "ok": true,
  "downstream_status": 200,
  "message": "event re-delivered"
}
```

```json 502 Bad Gateway theme={null}
{
  "ok": false,
  "error": "non-2xx response",
  "downstream_status": 503,
  "code": 3004,
  "message": "non-2xx response",
  "retryable": true,
  "trace_id": "d5e6f7a8"
}
```

The replay route keeps its `{ ok, error, downstream_status }` shape and adds the canonical numeric `code`, a `message`, a `retryable` boolean, and a `trace_id`. A `404` (`code 2011` — event not found) and a `400` (`code 1002` — no delivery destination configured, or a dev-inbox tenant) are both `retryable: false`; a `502` delivery failure (`code 3004`) is `retryable: true`. See [Errors](/api/errors).

## Stream events

```
GET /api/v1/events/stream
```

Server-Sent Events feed. Useful when developing locally without a public URL — equivalent to `stripe listen`.

* Connection lives 60 seconds, then closes with `event: close`. Clients should reconnect.
* Heartbeat comments arrive every 15 seconds to keep middleboxes from idling out the socket.
* Pass `?since=<iso>` to resume from a known cursor after a reconnect.

```bash theme={null}
curl -N https://console.trychert.com/api/v1/events/stream \
  -H "x-chert-tenant: $SLUG" \
  -H "authorization: Bearer $SECRET"
```

```
: connected 2026-04-29T18:15:25Z since=2026-04-29T18:15:25Z

data: {"id":"evt_01H...","type":"message.received","payload":{...},"created_at":"..."}

: heartbeat

event: close
data: {"reason":"ttl_reached","reconnect_with_since":"..."}
```

## Dev inbox

When you create a subscription with `url: "https://dev-inbox"` (the short sentinel), deliveries are recorded but no outbound POST is attempted. Pull them with:

```
GET /api/v1/dev-inbox?since=<iso>&limit=<n>
```

Switch to a real URL before going to production. Dev-inbox events accumulate indefinitely.

## See also

* [Receiving replies](/api/receiving-replies)
* [Errors](/api/errors)
