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:
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 lettersStatuses
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.
status | final_status | What it means |
|---|---|---|
pending | none | Waiting for its scheduled time, for a free slot, or for a paused queue to resume. |
processing | none | A worker has picked it up and the request is in flight. |
completed | success | The response was 2xx and every assertion passed. |
failed | retrying | This attempt failed and a retry was scheduled as a new job. |
failed | dead_lettered | This attempt failed and no retries are left (or retries are off). The job is under Dead letters. |
failed | failed_no_retries | Your 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
delayis in seconds, from 0 to 86,400 (24 hours). It defaults to 0.scheduled_atis the time of your API call plusdelay, in UTC. It’s the earliest the job can start.- There’s no field for an absolute run time. Compute
delayfrom 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:
delay | When the job starts |
|---|---|
| 60 seconds or less | Within seconds of scheduled_at, depending on how busy the workers are. |
| More than 60 seconds | Within 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_numberone 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_idthe 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_numberof 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
- Send a one-off or delayed request: create, fire, cancel and bulk-edit jobs in the dashboard.
- Queues: every queue setting with its default.
- POST /api/jobs: all request fields, the response and the errors.
- Runs and failure reasons: what Delay records for each attempt.