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

# CDP event debugger and DLQ: inspect the live stream and replay failed deliveries

> Watch every inbound CDP event as it arrives, filter by any field, expand the exact payload, and re-drive deliveries that failed a destination — without touching server logs.

# CDP event debugger and DLQ

Two tabs on the CDP page close the usual operational gaps between "is my SDK sending?" and "why didn't that event reach the destination":

* **Event debugger** — a live tail of the raw event stream with per-field filters and payload introspection. Use it to validate instrumentation the moment you ship it, before you declare anything in the tracking plan.
* **DLQ — failed destination deliveries** — every delivery that failed a destination across all of your subscriptions, grouped by destination, with single-row retry and per-destination bulk replay built in.

Both live in the dashboard at **Integrations → CDP**. Everything below also works from the API.

## 1. Events debugger

Open **Integrations → CDP → Event debugger**. The tab tails the raw stream of events your apps send through the ingest endpoints — `track`, `identify`, `page`, `screen`, `group`, `alias` — refreshing every few seconds while live is enabled.

Pause the poll with the **Pause** button (or leave it running — "Go live" re-enables it) to hold a payload on screen while you inspect it. The poll is refresh-based: filters are edited in draft fields and applied with **Apply filters**, so the live tail doesn't re-fire on every keystroke. **Clear** resets everything to the unfiltered stream.

### Find an event

Each event is a row; the newest sit on top. Point the cursor at any of these to narrow the stream:

* **Search payload** — free-text. Matches against the identity columns and the event's `properties` / `context` JSON, so an email, a SKU, a flag value, or a phone number finds the event that carries it.
* **Type** — `track`, `identify`, `page`, `screen`, `group`, or `alias`.
* **Event name** — the exact `event` string your SDK sent (case-sensitive).
* **User ID** and **Anonymous ID** — which identity the event carried.
* **Payload storage** — where the event body ended up: `inline` (stored in the event row), `spilled (GCS)` (the body exceeded the inline size limit, so it lives in object storage), or `payload lost` (the spill failed — see below).

Set several at once; they're AND-combined. Enter in any text field also applies the draft filters.

The table shows, per row: **Received** (timestamps render in your zone), **Type**, **Event** name, **Identity** (`user_id` when present, otherwise the anonymous id), and the **Source** the event arrived through.

### Inspect a single event end-to-end

Click a row (or press Enter on it) to expand the full record:

* **properties** and **context** — the exact JSON your SDK sent, pretty-printed. `context` carries the environment your SDK attached (page, user agent, library, campaign), which is what destinations use for their own enrichment decisions.
* **id**, **message\_id**, **contact\_id**, **anonymous\_id** — the identity chain, plus the resolved `contact_id` when the event has already been stitched to a contact.

A red banner on the expanded row means the original payload is unrecoverable: the event exceeded the inline size limit and the upload to object storage failed. The event still persists and fans out to destinations, but the values past the storage failure were dropped and replaced with placeholders — so the `properties` you see below may be truncated, and there is no full body to fetch from anywhere. The **payload lost** storage filter collects every event in that state so you can find the producers sending oversized payloads.

### How destinations see the same event

The debugger surfaces ingest-side truth: what arrived, when, and what it carried. Downstream, each destination subscription evaluates its own event-name filter, sampling rate, and policy gates against every event; the deliveries that fail turn up in the [DLQ below](#dlq). To re-run a successful window of history through destinations (after a schema fix, or to seed a new destination), that's a separate surface — use the **Backfill events** card on the same page, not the DLQ.

## 2. DLQ — failed destination deliveries

Open **Integrations → CDP → DLQ**. One tab shows every failed delivery across all of your subscriptions, so a destination that's silently broken shows up here before it shows up in your downstream reports.

### What ends up in the DLQ

A row appears when a destination rejects or fails to receive a dispatch: your endpoint returns a non-2xx status, times out, refuses the connection, or fails TLS. Each row exposes:

* **Event** — the event type of the failed dispatch.
* **HTTP** — the status code your endpoint returned, when the request got that far (`404` for a wrong path, `401`/`403` for a rejected signature, `5xx` for your handler erroring). Transport failures leave this empty and put the reason in the error column.
* **Error** — the destination's own explanation, taken from the response: hover the truncated cell for the full text.
* **Attempt** — how many times the dispatcher has tried this delivery.
* **When** — the timestamp of the last attempt, in your timezone — the column you cross-check against your server's own logs.

Failed deliveries are also what trip a destination's circuit breaker: after enough consecutive failures a subscription is marked open and excluded from new dispatches until you reset it. Replay routes through the same circuit-breaker check, so a destination you haven't fixed yet won't accept a flood of re-dispatches.

### Retry a single delivery

Each row has a **Retry** action. It re-dispatches exactly that delivery's payload through the normal destination pipeline — the same path the live event took, with the same subscription filters and circuit-breaker rules — so one click tells you whether the destination is back.

Two replies mean the delivery can't be re-driven:

* **404** — the delivery row is gone or has aged past its retention window. Re-ingest the event at the source instead.
* **422 payload not recoverable** — the stored body began to exceed the preview the delivery keeps, so the original event can't be reconstructed for replay. Re-ingest at the source rather than sending your destination a stub.

### Replay a destination's backlog

Each group header (one per destination with failures) has a **Replay failed** action. It re-dispatches the most recent failed deliveries for that destination, up to 50 per run, and returns a summary — *"Replayed 12, skipped 3 (no recoverable payload)"* — so a bulk re-drive after a confirmed outage is one click, not one per row. Skipped rows are always the no-recoverable-payload case above; everything replayable re-enters the normal pipeline.

Replay is audit-logged per run (who, when, how many re-dispatched), so a fleet-wide re-drive after a destination outage is traceable.

## 3. End-to-end: push, break, replay

The full loop takes a couple of minutes against a real destination.

1. **Send a synthetic event.** From your app's SDK — or any HTTP client — post to your ingest endpoint (the ingest id and secret come from the source's card on the same page):

```bash theme={null}
curl -X POST "https://api.orbit.devotel.io/api/v1/cdp/v1/$INGEST_ID/track" \
  -H "Authorization: Bearer $INGEST_SECRET" \
  -H "Content-Type: application/json" \
  -d '{"event": "Order Completed", "userId": "user_123", "properties": {"order_id": "ord_debug_1", "total": 99.0}}'
```

2. **Watch it in the debugger.** Open the Event debugger tab — the event lands in the tail within seconds. Filter by **Event name** = `Order Completed` or search the payload for `ord_debug_1`, then expand the row to confirm the properties and identity chain arrived as you sent them.

3. **Break the destination.** Point your destination webhook at a dead endpoint — or take your receiver down — then send the event once more.

4. **Watch it arrive in the DLQ.** The DLQ tab gains a row for the failed delivery: the event type, the HTTP status or transport error, the attempt count, and the time of the last attempt.

5. **Fix and replay.** Restore the endpoint, then click **Retry** on the row — or **Replay failed** on the destination header if you accumulated a backlog. The summary reports how many re-dispatched, and the table empties as the destination starts answering 2xx again.

## 4. Pick the right tool

These surfaces overlap deliberately, so you land on the right one on your first visit:

| If you're asking…                                                               | Use                                                                                                                           | Where                               |
| ------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | ----------------------------------- |
| "Did my SDK send it, and what did the payload look like?"                       | **Event debugger** (this page)                                                                                                | Integrations → CDP → Event debugger |
| "Did a destination receive a specific event, or did delivery fail?"             | **DLQ** (this page)                                                                                                           | Integrations → CDP → DLQ            |
| "Does my endpoint code accept and respond correctly to any event body?"         | **[Webhook Tester](/guides/webhook-tester)** — fires a synthetic event at an ad-hoc URL and shows the raw status/headers/body | Developer → Webhook Tester          |
| "How do I wire a destination, set its filters, or manage subscriptions at all?" | **[Event sinks and destinations](/guides/event-sinks-streaming)** — destination configuration, transports, delivery semantics | Integrations → CDP                  |
| "Can I re-run last week's successful events through a new destination?"         | **Backfill events** (CDP page) — replays stored history, not failures                                                         | Integrations → CDP                  |

## 5. Permissions

* **Roles.** The CDP read surface (events debugger, DLQ listing) and the write actions (single retry, bulk replay, backfill) are exposed to **owner**, **admin**, and **developer** roles. Replay and retry fan live dispatches out to your endpoints, so there's no read-only path to them — restrict those roles accordingly.
* **API keys.** A key-based caller needs the matching scopes. The events debugger returns full `properties` + `context` payloads, which routinely carry email, phone, or checkout PII, so an API key without the scope that grants contact-data reads is fenced away from the endpoint — a key that can list subscriptions but not read contact data cannot tunnel payloads out through the debugger. Replay/retry inherits the same subscription-write gates the rest of the CDP API uses.

Every replay and retry lands in the audit log with the actor's user id and IP, so access reviews stay grounded in the audit log, not the dashboard.

## API surface

The tabs are UI over these endpoints:

| Method & path                                      | What it does                                                                                                                                                         |
| -------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `GET /api/v1/cdp/events/debug`                     | Live tail with `type`, `event_name`, `source`, `user_id`, `anonymous_id`, `contact_id`, `message_id`, `spillover_status`, `q` filters and `after` / `before` cursors |
| `GET /api/v1/cdp/dlq`                              | Failed deliveries, filterable by `subscription_id`, cursor-paginated                                                                                                 |
| `POST /api/v1/cdp/deliveries/:id/retry`            | Re-dispatch one failed delivery                                                                                                                                      |
| `POST /api/v1/cdp/subscriptions/:id/replay-failed` | Bulk replay for one destination (`max_deliveries` ≤ 200)                                                                                                             |
| `POST /api/v1/cdp/events/backfill`                 | Replay a stored history window through all matching destinations                                                                                                     |

For the underlying pipeline — subscriptions, filters, circuit breaker, HMAC signing, and delivery semantics — see [Event sinks and destinations](/guides/event-sinks-streaming), [CDP tracking plan](/guides/cdp-tracking-plan), and the [CDP event model](/concepts/cdp-event-model).
