Allow tenant administrators to view and replay their own failed async jobs without
requiring platform-admin intervention. Server enforces tenant isolation — callers
can only see and replay jobs where job.data.tenant_id matches their JWT tenant_id.
Queues exposed to tenants (jobs that directly impact their operations):
- credential.state-update — failed to correlate a webhook from the agent to an Operation
- webhook.dispatch — delivery to the tenant's registered webhook URL failed after all retries
- audit.write — failed to persist an audit log entry
- credential.bulk-item — a single item in a bulk issuance/revocation batch failed
Endpoints:
- GET /api/v1/tenants/:tenantId/events/dead-letter
Query params: queue (filter), since, until, cursor, limit
Returns: TenantDeadLetterJob[] — redacted of sensitive fields; correlation IDs only
- POST /api/v1/tenants/:tenantId/events/dead-letter/:jobId/replay
Validates job.data.tenant_id === jwt.tenant_id before calling boss.resume(id)
Returns 404 if job not found or belongs to another tenant (indistinguishable by design)
Returns 202 on success
Security:
- tenant_id filter applied server-side from JWT (never from request params)
- Job data is redacted before returning — only correlation IDs (operation_id, external_id,
webhook_id) are exposed, not raw credential attributes or connector secrets
- Requires tenants:admin scope
Implementation note:
- Queries
pgboss.job WHERE state = 'failed' AND data->>'tenant_id' = $1
- Index on
(data->>'tenant_id', state) required for acceptable query performance
- Add GIN index on job.data for JSONB path filtering
Dependencies
Definition of Done
Allow tenant administrators to view and replay their own failed async jobs without
requiring platform-admin intervention. Server enforces tenant isolation — callers
can only see and replay jobs where
job.data.tenant_idmatches their JWT tenant_id.Queues exposed to tenants (jobs that directly impact their operations):
Endpoints:
Query params: queue (filter), since, until, cursor, limit
Returns: TenantDeadLetterJob[] — redacted of sensitive fields; correlation IDs only
Validates job.data.tenant_id === jwt.tenant_id before calling boss.resume(id)
Returns 404 if job not found or belongs to another tenant (indistinguishable by design)
Returns 202 on success
Security:
webhook_id) are exposed, not raw credential attributes or connector secrets
Implementation note:
pgboss.jobWHERE state = 'failed' AND data->>'tenant_id' = $1(data->>'tenant_id', state)required for acceptable query performanceDependencies
Definition of Done