One curl creates a job that fires every 5 minutes against any HTTPS URL. Grab a key from Settings → API Keys, swap in your URL, and paste this into your terminal. The rest of this page explains each step in detail.
CronPilot turns a single POST into a reliable, retried, time-zoned schedule that fires HTTP requests at the cadence you specify and reports back what happened.
What it does
Schedule HTTP requests on a cron expression or one-shot delay
Automatic retries, deduplication keys, signed delivery requests
Durable message log: status, duration, response, every attempt
What it does not do
↛ Not usRun code in our infrastructure
We hit your HTTP endpoint. We don't execute user code.
→ try fly.io / lambda / your own workers
↛ Not usCatch a missed schedule
CronPilot tells you when a fire failed. PingWatch tells you when a fire never happened.
→ try PingWatch
↛ Not usShow schedule status to your users
Public-facing receipts live in FlipSign incidents and components.
→ try FlipSign
Authentication
Every API request carries a Bearer token. Create a key in Settings → API Keys — the full key (it starts with cp_sk_) is shown once at creation, so store it somewhere safe. Keys are hashed on our side; only the prefix stays visible in the dashboard for identification.
Header
Authorization:Bearercp_sk_a1b2c3d4e5f6...
Create a schedule
A schedule is a recurring HTTP request driven by a standard 5-field cron expression. The required fields are namerequired, cronrequired, and urlrequired; everything else has a sensible default.
curl
curl-XPOST"https://cronpilot.dev/v1/schedules"\-H"Authorization: Bearer <CRONPILOT_API_KEY>"\-H"Content-Type: application/json"\-d{"name":"Cleanup stale sessions","cron":"0 */6 * * *","url":"https://api.example.com/cleanup","method":"POST","timezone":"America/New_York"}'
One-shot messages (delays)
Need a single fire after a delay instead of a recurring schedule? Publish a message. It returns 202 Accepted — once accepted, the message is in the durable log and will be delivered even if our infrastructure restarts. Pass a deduplication_id to make the publish safe to retry.
When a signing secret is configured (per schedule or queue), every outbound request CronPilot fires at your endpoint carries an X-CronPilot-Signature header (v1=<hex HMAC-SHA256>) computed over {timestamp}.{body}, plus an X-CronPilot-Timestamp and an X-CronPilot-Message-Id for idempotency on your side. Verify with a constant-time compare and reject timestamps older than five minutes.
synced
import{createHmac}from"crypto";functionverify(req,secret){constsigreq.headers["x-cronpilot-signature"];// v1=<hex>consttsreq.headers["x-cronpilot-timestamp"];// unix seconds// Reject if the timestamp is too old (replay protection)if(Math.abs(Date.now()1000Number(ts))300){thrownewError("Timestamp too old");}constpayloadts"."req.body;// raw body, not parsedconstexpected"v1="createHmac("sha256",secret).update(payload).digest("hex");if(sigexpected)thrownewError("Invalid signature");}