Troubleshooting and FAQ
Find your symptom, then the cause and the fix. For what each failure reason on a run means, see failure reasons. To work through a failed run step by step, see Debug a failed run.
API requests
The API returns a 302 redirect or an HTML page
Cause: the request failed validation and didn’t send Accept: application/json. Without that header, Delay answers validation errors with a redirect instead of an error message.
Fix: send -H "Accept: application/json". You then get a 422 that names each bad field. All headers and error shapes: API overview.
A 422 says a field is required, but you sent it
Cause: the body wasn’t read as JSON, usually because Content-Type: application/json is missing. curl -d sends form data unless you set the header.
Fix: add -H "Content-Type: application/json" and check that the body is valid JSON.
401 Missing authorization token or Invalid webhook token
Cause: “Missing authorization token” means there was no Authorization: Bearer ... header. “Invalid webhook token” means the token is wrong: mistyped, or regenerated since you copied it. (“Webhook token” is an old name for the API token.)
Fix: copy the token again from Settings > API token and send it as Authorization: Bearer $DELAY_TOKEN.
You get a 422 although the token is wrong
Cause: Delay validates the body before it checks the token.
Fix: fix the body first. Once the body is valid, a bad token shows up as a 401.
429 Too Many Attempts
Cause: more than 60 API requests in a minute from your IP address. POST /api/jobs and POST /api/crons share that limit, and so does every service that calls Delay from the same IP address. Rejected requests count too.
Fix: wait the number of seconds in the Retry-After header, then send the rest one after another. See rate limit.
The job or cron was created in the wrong team
Cause: your token acts on the team you have selected in the dashboard at the moment of the call, not on a fixed team.
Fix: select the right team in the header, then call the API again. Crons and jobs can’t be moved between teams, so delete the copy in the wrong team. For scripts and CI, use one account per environment.
The API returns a 500
Cause: your account has no team selected, which happens when the team you had selected was deleted.
Fix: pick a team with the team switcher in the dashboard.
Crons
The cron runs at the wrong hour
Cause: cron expressions run in UTC. The Timezone in your profile only changes how dates are shown.
Fix: write the expression in UTC. For 09:00 in New York, that’s 0 14 * * * in winter (UTC-5) and 0 13 * * * in summer (UTC-4). A fixed UTC expression moves by an hour in local time when daylight saving time starts or ends. See cron schedules.
The cron didn’t run
Open the cron and check, in this order:
- Status: a paused cron doesn’t run. Click Start on the cron’s page.
- Next run: if it’s in the future, the cron hasn’t come due yet. Remember that times are UTC.
- Recent executions: if runs are listed, the cron is running but failing. Click View all and expand a run to see the response or error.
- Monthly limit: if runs fail with “Monthly limit reached”, the team has used up its monthly requests. See below.
Runs of the same cron overlap
Cause: Delay starts each run on time, even if the previous one is still waiting for your endpoint. A run that takes longer than the interval overlaps with the next.
Fix: keep the cron’s Request timeout shorter than its interval, and make the endpoint safe to run twice (idempotency).
Failed runs
The run fails because your endpoint redirects
Cause: Delay doesn’t follow redirects. A 301, 302, 307 or 308 counts as a failure (shown as “Unknown error”), and job attempts are retried.
Fix: use the final URL. curl -sI https://api.yourapp.com/health shows the Location your server redirects to. Common culprits: http instead of https, a missing www, or a missing or extra trailing slash.
URL resolves to a blocked IP address, or Cannot resolve host
Cause: Delay only calls public addresses. It refuses hostnames that resolve to private or reserved addresses such as 127.0.0.1, 10.x.x.x, 172.16.x.x to 172.31.x.x, 192.168.x.x or 169.254.x.x. “Cannot resolve host” means the hostname has no public IPv4 address: a typo, missing DNS records, or a host that only has IPv6. Delay checks this when the request is sent, so creating the job or cron works and the run fails.
Fix: use a public hostname. To test against your laptop, expose it through a tunnel service that gives you a public HTTPS URL.
An assertion fails although the response looks right
Cause: usually one of these:
- JSON path equals needs the value as JSON writes it, without quotes around strings:
status=ok,healthy=true. A field that isn’t in the response always fails. - Body contains is case-sensitive and exact.
- Response time under (ms) must be strictly less than the value.
- Assertions only run on
2xxresponses. A404fails as a4xx, whatever your assertions say.
Fix: expand the run and compare the error (Assertion failed: ...) with the response body. Details: success assertions.
The run times out
Cause: your endpoint didn’t answer within the request timeout (30 seconds by default, up to 300).
Fix: reply first and do slow work in the background, or raise Request timeout on the cron or queue. See endpoint design.
Your endpoint got the same job more than once
Cause: Delay retries a job attempt after any failure: 4xx, 5xx, redirects, timeouts and failed assertions. After a timeout, your endpoint may have finished the work anyway. Each retry is a new job with a new ID, and Delay sends no idempotency header.
Fix: make the endpoint safe to call twice, for example with your own ID in the payload. If a job must never repeat, turn off retries on its queue. See retry strategy.
A job stays Pending
Cause: its queue is paused, or its scheduled time hasn’t come yet. Jobs with a delay of more than 60 seconds can start up to about a minute after their scheduled time.
Fix: check the queue’s status on Queues. A paused queue holds its jobs until you tick Enable this queue again. See queues.
Usage limit
Runs fail with Monthly limit reached
Cause: the team used all its requests for this calendar month (1,000 requests a month on the free plan). Until the next month starts (in UTC) or you upgrade, no requests are sent:
- jobs fail without retries, dead letters, callbacks or alerts;
- crons stay on schedule and record a failed run each time they come due;
- the API still accepts new jobs and crons with a
201.
Fix: the team owner can upgrade from the team page under Billing > Manage billing. Or run crons less often, or wait for the next month. See how usage is counted.
Signatures
Signature verification fails
Cause: most often your code verifies a parsed and re-encoded body instead of the raw bytes, uses the secret of another team, or uses a secret that was regenerated. Requests without a body are signed over an empty string.
Fix: go through if verification fails.
Requests arrive without X-Delay-Signature
Cause: the team has no signing secret yet, or the request isn’t from Delay. Slack and Discord alert posts are never signed.
Fix: a team admin generates one under Settings > Webhook signing. See verify requests from Delay.
Alerts
Cron alerts don’t arrive by email
Cause: cron alerts are never emailed. They appear on the Alerts tab, and in Slack or Discord when that’s the team channel. Only queue job alerts are emailed.
Fix: set the team channel to Slack or Discord. See Set up alerts.
No alert arrived at all
Check these in order:
- Cron: is Failure alerting set? It’s Disabled by default, including for crons created through the API without
alert_threshold. - Threshold: with 2 or 3, the first failure only raises a warning. The Failure comes when the streak reaches the threshold.
- Already alerted: a cron raises one Failure per streak and stays quiet until a run succeeds.
- Queue: is Enable email alerts for job failures ticked on the queue?
- Maintenance: an active window on the Maintenance tab mutes notifications. The alert is still on the Alerts tab.
- Channel: click Send Test Notification under Settings > Notifications. “Test notification sent successfully.” only means Delay sent it, so check that it shows up in the channel.
- Usage limit: runs blocked by the monthly limit raise no alerts.
- Email: look in your spam folder.
Too many alert emails
Cause: queue job alerts are per job: a warning when the first attempt fails and a failure when the job is dead-lettered, each emailed to every team member.
Fix: untick Enable email alerts for job failures on busy queues and watch Dead letters instead, or mute planned work with a maintenance window. More in avoid alert noise.
The usage warning didn’t reach Slack, Discord or email
Cause: the usage limit warning is shown in the app only, on the Alerts tab.
Fix: check the Usage tab now and then.
Teams
You joined a team but don’t see its crons
Cause: joining doesn’t switch you to the new team.
Fix: click the team name in the header and pick the team under Switch team.
You can’t open Webhook signing or save Notifications
Cause: both are for team admins. Billing is for the team owner.
Fix: ask an admin to change your role or make the change. See roles.
An invitation has already been sent to this email
Cause: there’s already an invitation for that address, possibly an expired one. Invitations can’t be resent.
Fix: delete it under Pending invitations on the team page and invite the person again.
FAQ
Which HTTP methods can Delay send?
GET, POST, PUT, PATCH and DELETE, in uppercase. Jobs default to POST and crons to GET.
Can I schedule a job more than 24 hours ahead?
No. delay is at most 86,400 seconds (24 hours), and there’s no absolute run time. For recurring work, use a cron.
Can a cron run every few seconds?
No. Cron expressions have five fields, so the shortest interval is one minute (* * * * *).
Do failed cron runs retry?
No. The cron runs again at its next scheduled time. If you need retries, send the work as a queue job.
Can I list, update or delete jobs and crons with the API?
No. The API only creates jobs and crons. Everything else happens in the dashboard. See what the API can’t do.
Is there an SDK?
No. The API is two JSON endpoints, so any HTTP client works. Examples: API overview.
Which IP addresses does Delay send requests from?
Delay doesn’t publish a list. To make sure a request came from Delay, verify its signature.
How long is run history kept?
Run history is kept. Dead-lettered jobs are the exception: Delay deletes them after their queue’s Dead letter retention (14 days by default). See dead letters.
Next steps
- Debug a failed run: read a run and find what went wrong.
- Runs and failure reasons: every failure reason, and how usage is counted.
- API overview and authentication: headers, errors and the rate limit.