Examples
Each recipe is one API call you can paste, plus the settings that matter for it. The first seven are crons (recurring requests). The last three are jobs (one-off requests).
Before you start:
- Set
DELAY_TOKENto your API token (see the quick start) and replace thehttps://api.yourapp.com/...URLs with your own. - Crons default to
GETand jobs toPOST. - A cron’s
payloadis a string, usually JSON. A job’spayloadis a JSON object. - Cron schedules run in UTC.
- The API can’t set success assertions or queue settings. Add those in the dashboard after you create the cron or send the first job.
- Every run, retries included, counts toward your monthly requests: 1,000 requests a month on the free plan. See runs and usage.
Health check every minute
Call your health endpoint every minute and get an alert when it fails twice in a row.
curl -X POST https://app.delay.run/api/crons \ -H "Authorization: Bearer $DELAY_TOKEN" \ -H "Content-Type: application/json" \ -H "Accept: application/json" \ -d '{ "name": "API health check", "url": "https://api.yourapp.com/health", "cron_expression": "* * * * *", "timeout_seconds": 10, "alert_threshold": 2 }'- Check the body too: open the cron, click Edit, and under Success assertions click Add assertion. For example, JSON path equals with
status=okif the endpoint returns{"status": "ok"}, or Response time under (ms) with2000. See success assertions. - Alerts: crons don’t retry, so a threshold of 2 rides out a single blip. The first failure only raises a warning. See set up alerts.
- Usage: every minute is 44,640 requests in a 31-day month. Every 5 minutes (
*/5 * * * *) is 8,928.
Nightly backup at 03:00 UTC
Start a backup once a day at 03:00 UTC.
curl -X POST https://app.delay.run/api/crons \ -H "Authorization: Bearer $DELAY_TOKEN" \ -H "Content-Type: application/json" \ -H "Accept: application/json" \ -d '{ "name": "Nightly backup", "url": "https://api.yourapp.com/tasks/backup", "method": "POST", "payload": "{\"type\": \"full\"}", "cron_expression": "0 3 * * *", "timeout_seconds": 60, "alert_threshold": 1 }'- Payload: a cron’s
payloadis a string. When the cron runs, Delay parses it and sends the JSON as the request body. - Long tasks: Delay waits at most
timeout_seconds(300 at most). Return202as soon as the backup has started and do the work in the background. See endpoint design. - No retries: a failed backup waits until the next night, so alert on the first failure.
Weekday morning report
Send a report at 08:00 UTC, Monday to Friday.
curl -X POST https://app.delay.run/api/crons \ -H "Authorization: Bearer $DELAY_TOKEN" \ -H "Content-Type: application/json" \ -H "Accept: application/json" \ -d '{ "name": "Daily sales report", "url": "https://api.yourapp.com/reports/daily-sales", "method": "POST", "payload": "{\"recipients\": \"sales-team\"}", "cron_expression": "0 8 * * 1-5", "alert_threshold": 1 }'- Days:
1-5is Monday to Friday (0 is Sunday). Day names work too:0 8 * * MON-FRI. - Daylight saving time: schedules stay in UTC all year. 08:00 UTC is 09:00 in London in summer and 08:00 in winter. See cron schedules.
- Hear back: add
"callback_url": "https://api.yourapp.com/webhooks/delay"to get a POST with the outcome after every run. See callbacks.
Poll an API that has no webhooks
When a provider can’t push changes to you, have Delay call your sync endpoint on a schedule. The endpoint fetches what changed and stores it.
curl -X POST https://app.delay.run/api/crons \ -H "Authorization: Bearer $DELAY_TOKEN" \ -H "Content-Type: application/json" \ -H "Accept: application/json" \ -d '{ "name": "Sync orders", "url": "https://api.yourapp.com/sync/orders?source=warehouse", "headers": {"X-Api-Key": "your-endpoint-key"}, "cron_expression": "*/15 * * * *", "timeout_seconds": 120, "alert_threshold": 3 }'- Query string: this is a
GET(the cron default), and Delay calls the URL with its query string as written. - Headers:
headersis a JSON object sent with every run. Use it for a key your endpoint checks, or verify Delay’s signature instead. - Overlap: Delay doesn’t wait for one run to finish before starting the next. If a sync can take longer than 15 minutes, make the endpoint skip the run when a sync is already in progress.
- Usage: every 15 minutes is 2,976 requests in a 31-day month.
Post to Slack every weekday
Slack incoming webhooks accept a JSON POST, so a cron can post to a channel with no code on your side.
curl -X POST https://app.delay.run/api/crons \ -H "Authorization: Bearer $DELAY_TOKEN" \ -H "Content-Type: application/json" \ -H "Accept: application/json" \ -d '{ "name": "Standup reminder", "url": "https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX", "method": "POST", "payload": "{\"text\": \"Standup starts in 10 minutes.\"}", "cron_expression": "50 9 * * 1-5" }'- Webhook URL: create an incoming webhook for the channel in Slack and paste its URL. Anyone with the URL can post, so keep it private.
- Content type: Delay sends the payload as a JSON body with
Content-Type: application/json, which is what Slack expects. - Discord: a Discord channel webhook works the same way. Use its URL and a payload of
"{\"content\": \"Standup starts in 10 minutes.\"}". - This is separate from Delay’s own Slack alerts, which you set up in set up alerts.
Warm a cache every 10 minutes
Rebuild cached pages before visitors ask for them.
curl -X POST https://app.delay.run/api/crons \ -H "Authorization: Bearer $DELAY_TOKEN" \ -H "Content-Type: application/json" \ -H "Accept: application/json" \ -d '{ "name": "Warm page cache", "url": "https://api.yourapp.com/cache/warm", "method": "POST", "payload": "{\"pages\": [\"/\", \"/pricing\", \"/docs\"]}", "cron_expression": "*/10 * * * *", "timeout_seconds": 60 }'- Use the final URL: Delay doesn’t follow redirects, so a
301fromhttp://tohttps://, or to awww.host, counts as a failure. Use the address that answers with a 2xx. - Catch slow runs: add a Response time under (ms) assertion in the dashboard. See success assertions.
- Usage: every 10 minutes is 4,464 requests in a 31-day month.
Keep a free-tier app awake
Some hosts put a free app to sleep after a stretch with no traffic. A regular request keeps it awake.
curl -X POST https://app.delay.run/api/crons \ -H "Authorization: Bearer $DELAY_TOKEN" \ -H "Content-Type: application/json" \ -H "Accept: application/json" \ -d '{ "name": "Keep app awake", "url": "https://api.yourapp.com/health", "cron_expression": "*/10 * * * *", "timeout_seconds": 60 }'- Interval: pick one shorter than your host’s idle timeout.
- Timeout: an app that was asleep can take a while to start, so allow 60 seconds instead of the default 30.
- Usage: every 10 minutes is 4,464 requests in a 31-day month, more than the 1,000 requests a month on the free plan. Hourly (
0 * * * *) is 744, if your host lets an app idle that long.
Send an email in 15 minutes
When a user signs up, schedule a follow-up email. The job waits 900 seconds, then POSTs the payload to your endpoint.
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": "emails", "url": "https://api.yourapp.com/emails/send", "payload": {"template": "welcome-tips", "user_id": 4821}, "delay": 900 }'- Timing: a job with a delay over 60 seconds is picked up by a once-a-minute check, so it can start up to about a minute after its
scheduled_at. - Retries: failed jobs are retried on the queue’s schedule (by default after 1 minute, 30 minutes and 3 hours). A timeout can still mean the email went out, so make the endpoint skip users it has already emailed. See retries and queue design.
- Cancel: delete the job on the Jobs tab before it runs. There’s no API for that, so also have the endpoint check that the email is still wanted.
Send a reminder in 24 hours
86,400 seconds (24 hours) is the longest delay a job can have.
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/reminders/send", "payload": {"reminder_id": "rem_8812"}, "delay": 86400 }'- Further ahead: a larger
delayis rejected with a 422 (“The delay cannot exceed 86400 seconds (24 hours).”). Store the reminder in your database and let a daily cron send the ones that are due, or have the endpoint schedule another 24-hour job until the date arrives. - Check before sending: send an ID rather than the message, and have the endpoint look the reminder up and skip it if it was cancelled in the meantime.
Charge a card, then get a callback
Run a payment request right away, with retries, and get a POST when it has succeeded or finally failed.
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": "payments", "url": "https://api.yourapp.com/billing/charge", "payload": {"order_id": "ord_1042", "amount_cents": 4900}, "callback_url": "https://api.yourapp.com/webhooks/delay?order=ord_1042" }'- When the callback fires: once, when the job succeeds (
"status": "success") or runs out of retries ("status": "dead_lettered"), not after each failed attempt. Callbacks are not retried. See callbacks. - Match it to the order: after a retry, the callback’s
job_idis the retry’s ID, andoriginal_job_idis thejob_idthe API returned. Your own ID in the callback URL, as above, also survives a replay, which gets a new ID. - Avoid double charges: every failure is retried, including timeouts and 4xx responses. If a timeout hides a charge that went through, the retry charges again. Pass the order ID to your payment provider as an idempotency key, or open the
paymentsqueue in the dashboard and untick Enable automatic retries. See retries and queue design.
Next steps
- Schedule a recurring request: create and manage crons in the dashboard.
- Send a one-off or delayed request: the same for jobs.
- Queues: every queue setting, including retry delays.
- POST /api/crons and POST /api/jobs: every field and error.