Skip to content

Jobs

A job is one HTTP request that Delay sends for you, right away or after a delay of up to 24 hours. Every job belongs to a queue, and the queue decides the timeout, retries, concurrency and assertions.

A job from start to finish

Schedule a request for 15 minutes from now:

Terminal window
curl -X POST https://app.delay.run/api/jobs \
-H "Authorization: Bearer $DELAY_TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{"queue": "reminders", "url": "https://api.yourapp.com/trial-reminder", "payload": {"user_id": 42}, "delay": 900}'

Delay replies with the job’s id and the time it will run:

{
"success": true,
"queue_id": "019a0c4e-1f3b-7c55-8a1e-6d2f9b0c4a71",
"job_id": "019a0c4e-7b21-7d3a-9f4e-2c1b8a6d5e40",
"scheduled_at": "2026-10-18T14:15:00+00:00",
"message": "Scheduled successfully"
}

At 14:15 UTC the job starts. From there it goes one of three ways:

pending --> processing --+--> completed 2xx and every assertion passed
|
+--> failed --+--> retry scheduled a new job (attempt 2, 3, ...) starts at pending
|
+--> dead-lettered no retries left, listed under Dead letters

Statuses

Each job has a status, shown as the badge in Jobs, and a final_status that records what happened after the attempt. The dashboard doesn’t show final_status. You see it as the status field of a callback.

statusfinal_statusWhat it means
pendingnoneWaiting for its scheduled time, for a free slot, or for a paused queue to resume.
processingnoneA worker has picked it up and the request is in flight.
completedsuccessThe response was 2xx and every assertion passed.
failedretryingThis attempt failed and a retry was scheduled as a new job.
faileddead_letteredThis attempt failed and no retries are left (or retries are off). The job is under Dead letters.
failedfailed_no_retriesYour team had reached its monthly request limit, so nothing was sent. Not retried and not dead-lettered.

A failed attempt stays failed even if a later retry succeeds. To see how a job ended, look at its last attempt.

Delay and scheduled_at

  • delay is in seconds, from 0 to 86,400 (24 hours). It defaults to 0.
  • scheduled_at is the time of your API call plus delay, in UTC. It’s the earliest the job can start.
  • There’s no field for an absolute run time. Compute delay from the time you want. Nothing can be scheduled more than 24 hours ahead.

How close to scheduled_at a job starts depends on the delay:

delayWhen the job starts
60 seconds or lessWithin seconds of scheduled_at, depending on how busy the workers are.
More than 60 secondsWithin about a minute after scheduled_at, because Delay looks for due jobs once a minute.

Retries

When an attempt fails, Delay retries it if the queue has retries on and delays left. The defaults give 4 attempts over about 3.5 hours (retry settings).

What counts as a failure. Anything other than a 2xx response that passes every assertion:

  • 3xx responses (Delay doesn’t follow redirects)
  • 4xx responses, including 404 and 429
  • 5xx responses
  • timeouts, and connection, DNS and TLS errors
  • URLs that resolve to a private or reserved address
  • failed assertions

All of these are retried the same way. The one failure that is never retried is the monthly request limit. Each failure gets a reason label, listed in failure reasons.

Each retry is a new job. When an attempt fails and a retry is due, Delay creates a new job with:

  • a new job_id
  • an attempt_number one higher than the failed attempt
  • the same queue, URL, method, headers, payload and callback URL
  • a start time of now plus the next retry delay

What that means in practice:

  • The job_id the API returned belongs to attempt 1 only. If attempt 3 succeeds, the success is recorded on a different id.
  • A callback carries the id and attempt_number of the attempt that finished. To match it to your own records, put a reference in the callback URL (how).
  • In Jobs, each attempt is its own row. A retry row shows ↻ with the first attempt’s id, and its page shows Attempt #n. The Retrying tab lists first attempts whose retry is still waiting.
  • Each attempt counts as one request toward your monthly quota (usage).

Cron jobs never retry.

Callbacks

Set callback_url and Delay sends one POST when the job ends: when an attempt succeeds, or when the last attempt fails and the job is dead-lettered. Failed attempts that will be retried don’t send one. Payloads and delivery rules: Completion callbacks.

Dead letter queue

When a job’s last attempt fails, that attempt is listed under Dead letters, where you can replay or purge it. It’s kept for the queue’s Dead letter retention (14 days by default). See Replay failed jobs.

Next steps