Skip to main content
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.
EndpointUse when
GET /api/v1/eventsList events with filters and cursor pagination.
GET /api/v1/events/{id}Inspect one event including full attempt history.
POST /api/v1/events/{id}/replayRe-fire a previously-recorded event.
GET /api/v1/events/streamServer-Sent Events feed of new events.
GET /api/v1/dev-inboxPull 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
QueryTypeDescription
typestringExact event type, e.g. message.received.
statusenumpending, delivered, failed, or dev_inbox.
lead_idstringOnly events tied to this lead.
sinceISO 8601Events with created_at > since.
limitint1–200, default 50.
cursorstringOpaque cursor from a prior next_cursor.
Response
{
  "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>.
200 OK
{
  "ok": true,
  "downstream_status": 200,
  "message": "event re-delivered"
}
502 Bad Gateway
{
  "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.

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.
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