---
title: Dead Letter Queue
description: Failed job management, inspection, replay, and purge.
---

The dead letter queue (DLQ) captures jobs that have permanently failed after exhausting all retry attempts.

## How entries are created

When a job's `RetryCount` reaches `MaxRetries` and the handler still returns an error, the executor calls `dlq.Service.Push` to create a DLQ entry preserving the full context:

- Original job name, queue, and payload
- Final error message
- Total retry count and max retries
- Scope app/org IDs

The job state is set to `failed` and an `ext.JobDLQ` event fires.

## Listing DLQ entries

```go
entries, err := eng.DLQService().DLQStore().ListDLQ(ctx, dlq.ListOpts{
    Limit:  50,
    Offset: 0,
})
```

Or use the admin API:

```bash
GET /v1/dlq
GET /v1/dlq/:entryId
GET /v1/dlq/count
```

## Replay

Replay re-enqueues a DLQ entry as a new `pending` job with the original payload. The `ReplayedAt` field is set on the DLQ entry.

Via admin API:

```bash
POST /v1/dlq/:entryId/replay
```

## Purge

Remove all DLQ entries:

```bash
POST /v1/dlq/purge
```

This is destructive and irreversible. Use with caution.

## DLQ service

The `dlq.Service` is accessible via the engine:

```go
svc := eng.DLQService()

// Push is called automatically by the executor.
svc.Push(ctx, failedJob, err)

// Access the store directly for list/count/purge.
svc.DLQStore().ListDLQ(ctx, opts)
svc.DLQStore().PurgeDLQ(ctx)
```
