Skip to content

Jobs

A job is a single HTTP request queued for execution. Jobs belong to a queue and inherit the queue’s retry, timeout, and assertion settings.

Job lifecycle

A job progresses through these statuses:

StatusDescription
pendingJob is created and waiting to be dispatched
processingHTTP request is in flight
completedRequest succeeded (2xx and all assertions passed)
failedRequest failed — may trigger a retry or move to the dead-letter queue

Final status

After a job reaches a terminal state, it also receives a final_status that describes the outcome of the entire retry chain:

Final statusDescription
successJob completed successfully
retryingJob failed but a retry is scheduled
failed_with_retriesA retry attempt failed (the original job has retrying or dead_lettered)
failed_no_retriesJob failed and retries are disabled on the queue
dead_letteredAll retries exhausted — job moved to the dead-letter queue

Creating jobs

Push a job to a queue via the API:

Terminal window
curl -X POST https://app.recurohq.com/api/jobs \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"queue": "webhooks",
"url": "https://api.yourapp.com/webhook",
"method": "POST",
"payload": { "event": "order.completed", "order_id": 9876 },
"delay": 0
}'

You can also create jobs from the dashboard via Jobs > New Job.

Delayed jobs

Set delay (in seconds, 0-86,400) to schedule a job for the future. A delay of 1800 runs the job 30 minutes from now.

  • If delay is 60 seconds or less, the job is dispatched to the queue worker immediately with the configured delay
  • If delay is greater than 60 seconds, the job is picked up by the minute-level polling process once scheduled_at arrives

Retry flow

When a job fails and the queue has retries_enabled: true:

  1. The current job is marked as failed with final_status: retrying
  2. A new job is created with attempt_number incremented by 1 and is_retry: true
  3. The new job’s scheduled_at is set to now + retry_delay for that attempt
  4. The original job is flagged with has_pending_retry: true

The retry delay for each attempt is determined by the queue’s retry_delays array:

Attemptretry_delays indexDefault delay
2 (1st retry)[0]60 seconds
3 (2nd retry)[1]1,800 seconds (30 min)
4 (3rd retry)[2]10,800 seconds (3 hours)

When a retry succeeds, the has_pending_retry flag is cleared on the original job.

Dead-letter queue

When a job exhausts all retry attempts, it is moved to the dead-letter queue (DLQ):

  • final_status is set to dead_lettered
  • dead_lettered_at timestamp is recorded
  • A failure alert is sent (if alerts_enabled on the queue)
  • The completion callback fires (if configured)

Working with the DLQ

From the dashboard (Dead Letter Queue in the sidebar), you can:

  • Replay a dead-lettered job — creates a new pending job with attempt_number: 1 and the same configuration, dispatched immediately
  • Bulk replay — replay multiple jobs at once
  • Purge — permanently delete a dead-lettered job
  • Bulk purge — delete multiple jobs at once

Dead-lettered jobs are automatically cleaned up after the queue’s dlq_retention_days (default: 14 days).

Completion callbacks

If a job has a callback_url, Recuro sends a POST request after the job reaches a terminal state:

{
"type": "job_execution",
"job_id": "uuid",
"status": "success",
"response_status": 200,
"duration_ms": 87,
"error_message": null,
"attempt_number": 1,
"completed_at": "2026-04-10T12:00:01Z"
}

The status field reflects the final status: success, retrying, failed_with_retries, failed_no_retries, or dead_lettered.

Callbacks are fire-and-forget with a 10-second timeout and are signed if your team has a signing secret configured.

Bulk operations

From the dashboard, you can perform bulk operations on jobs:

  • Bulk delete — Delete selected jobs, all failed jobs, or all pending jobs
  • Bulk retry — Create new jobs for selected failed jobs
  • Bulk fire — Immediately dispatch selected pending jobs

Next steps