Cron webhooks: call any URL on a schedule

cronservices calls any HTTP endpoint on a cron schedule: a webhook, an internal route like /api/cron/cleanup, or a third-party API. Use it in place of a crontab line on a server you have to keep alive, a scheduled serverless function, or a "cron job" add-on that can't send headers.

  • Any method, headers, and a JSON or text body, with {{ }} placeholders for dates and data.
  • Schedules in any time zone, from every minute to "the last weekday of the month", or once at an exact time.
  • Secrets in encrypted credentials, sent as Authorization: Bearer, Basic auth, or an API key header.
  • Every run recorded with its status code, duration, and the start of the response when it fails.

Example: a nightly cleanup route

Call your cleanup endpoint at 02:00 New York time, with a shared secret the endpoint checks:

json
{
  "name": "Nightly cleanup",
  "cron": { "expression": "0 2 * * *", "time_zone": "America/New_York" },
  "nodes": [
    { "id": "cleanup", "request": {
        "method": "POST",
        "endpoint": "https://app.example.com/api/cron/cleanup",
        "credential_id": "YOUR_SHARED_SECRET_ID",
        "body": "{ \"older_than\": \"{{ now() - days(30) }}\" }" } }
  ]
}

The body renders as { "older_than": "2026-09-30T06:00:00Z" } and is sent as application/json.

Example: chain two APIs

Each step receives the previous step's response, so you can read from one API and write to another in one job:

json
{
  "name": "Sync open orders",
  "cron": { "expression": "*/10 * * * *" },
  "nodes": [
    { "id": "orders", "request": { "method": "GET", "endpoint": "https://shop.example.com/api/orders?status=open", "credential_id": "YOUR_SHOP_TOKEN_ID" } },
    { "id": "push", "request": {
        "method": "POST",
        "endpoint": "https://warehouse.example.com/v1/picklists",
        "credential_id": "YOUR_WAREHOUSE_TOKEN_ID",
        "headers": [{ "name": "Idempotency-Key", "value": "orders-{{ format(now(), \"YYYYMMDDHHmm\") }}" }],
        "body": "{ \"orders\": { \"{{#each $.orders}}\": { \"id\": \"{{ $.id }}\", \"sku\": \"{{ $.sku }}\", \"qty\": \"{{ $.quantity }}\" } } }" } }
  ]
}

Securing the endpoint

Store a long random secret as a bearer_token credential, reference it with credential_id, and have the endpoint reject requests without Authorization: Bearer <secret>. If the work takes more than 10 seconds, return 202 and finish in the background.

More ideas

  • Warm a cache every 5 minutes: */5 * * * *.
  • Rotate logs or expire sessions nightly: 0 3 * * *.
  • Kick off a month-end export: 0 6 1 * *, or on the last day: 0 18 L * *.
  • End a trial in 14 days with a one-time run_at instead of a delayed-job queue.

See the docs for every request field, and Use cases for complete jobs.