Everything you need to schedule HTTP requests, fire one-shot delayed messages, and publish to durable queues. New to CronPilot? Start with the quickstart.
Base URL & authentication
The base URL is https://cronpilot.dev. Every request carries a Bearer token (cp_sk_…) created in Settings → API Keys. A missing, placeholder, invalid, or revoked key returns 401 with a descriptive message.
Schedules
Recurring HTTP requests driven by cron expressions. Each firing creates a message in the durable log. Full CRUD lives under /v1/schedules (GET, POST, GET /:id, PUT /:id, DELETE /:id); the create call is documented below.
POST/v1/schedulesBearertier daily quota
Create a recurring schedule. Returns the full schedule object including the first computed next_fire_at.
Request body
name
string·REQUIRED
Human-readable label for this schedule.
cron
string·REQUIRED
Standard 5-field cron expression, evaluated in the schedule's timezone.
"*/5 * * * *""0 */6 * * *""0 3 * * *"
url
string·REQUIRED
HTTPS endpoint to call. Plain HTTP and private/internal addresses are rejected (422).
method
string·OPTIONAL
HTTP verb used for each fire.
default·"POST"
"GET""POST""PUT""PATCH""DELETE"
headers
object·OPTIONAL
Custom headers included on every request.
body
string·OPTIONAL
Request body sent on each fire. Max 100KB.
timezone
string·OPTIONAL
IANA timezone the cron expression is interpreted in. DST is handled per IANA rules.
default·"UTC"
retries
integer·OPTIONAL
Max retry attempts on failure. Default is fire-and-forget; capped by your tier's max.
default·0
Responses
201Created
Schedule created and active. Body is the full schedule object, including the computed next_fire_at. If the target domain is unverified, a warning field is included.
400Bad Request
Validation failed — missing required field, invalid cron expression, non-HTTPS URL, unsupported method, or an interval below your tier's minimum.
error.codename is requiredcron is requiredInvalid cron expression: …Only HTTPS URLs are allowedminimum interval …
401Unauthorized
Missing, placeholder, invalid, or revoked API key.
422Unprocessable
SSRF guard blocked the target — the URL resolves to a private, loopback, or metadata address.
error.codeSSRF blocked
Request
POST /v1/schedules
curl-XPOST"https://cronpilot.dev/v1/schedules"\-H"Authorization: Bearer <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"}'
Response201Created
application/json
{
"id": "d290f1ee-6c54-4b01-90e6-d701748f0851",
"name": "Cleanup stale sessions",
"cron": "0 */6 * * *",
"timezone": "America/New_York",
"url": "https://api.example.com/cleanup",
"method": "POST",
"status": "active",
"next_fire_at": "2026-05-29T18:00:00.000Z",
"created_at": "2026-05-29T15:32:00.000Z"
}
Messages (delays)
One-shot HTTP requests delivered after a delay or at a specific time. Returns 202 Accepted — once accepted, the message is durable and will be delivered even if our infrastructure restarts.
Named queues with configurable concurrency. At concurrency: 1, messages are delivered strictly in order. Queues default to 3 retries — the semantic is "publish means deliver." Manage them under /v1/queues and publish to /v1/queues/{name}/messages.
POST/v1/queuesBearertier daily quota
Create a named queue with concurrency, ordering, failure-mode, and optional rate limits.
Request body
name
string·REQUIRED
Queue name. Used in the publish URL.
concurrency
integer·OPTIONAL
Max messages dispatched simultaneously (1–100). At 1, delivery is strictly in order.
default·1
retries
integer·OPTIONAL
Default retries for messages in this queue. Capped by tier.
default·3
ordering
string·OPTIONAL
Delivery ordering.
default·"fifo"
"fifo""lifo"
failure_mode
string·OPTIONAL
How a dead message affects the queue.
default·"continue"
"continue""block""pause"
rate_limit
integer·OPTIONAL
Max messages per rate_period_seconds. Must be set together with rate_period_seconds.
rate_period_seconds
integer·OPTIONAL
Window for rate_limit, in seconds.
signing_secret_id
string·OPTIONAL
Signing secret applied to every message in this queue.
default_target_url
string·OPTIONAL
Fallback HTTPS URL for messages published without an explicit url.
default_target_group_id
string·OPTIONAL
Target group to fan a publish out across. Must be a group you own.
Responses
201Created
Queue created. Body includes concurrency, ordering, failure_mode, rate limits, and the blocked flags (always false for a fresh queue).
400Bad Request
Missing name, concurrency out of 1–100, invalid ordering/failure_mode, or rate_limit set without rate_period_seconds (or vice-versa).
error.codename is requiredconcurrency must be between 1 and 100rate_limit and rate_period_seconds must be set togetherordering must be one of: fifo, lifo
404Not Found
default_target_group_id refers to a group you don't own.
Publish a message onto a named queue. It inherits the queue's signing secret, retry config, and concurrency limit.
Path parameters
name
string·REQUIRED
The queue name from the publish URL.
Request body
url
string·OPTIONAL
HTTPS endpoint to call. Optional when the queue has a default_target_url or default_target_group_id.
method
string·OPTIONAL
HTTP verb.
default·"POST"
"GET""POST""PUT""PATCH""DELETE"
headers
object·OPTIONAL
Custom headers.
body
string·OPTIONAL
Request body. Max 100KB.
deduplication_id
string·OPTIONAL
Unique key to prevent duplicate publishes.
callback_queue_id
string·OPTIONAL
Queue to publish the delivery result to.
Responses
202Accepted
Message accepted onto the queue. Body is { message_id, queue, scheduled_at }; a fan-out across a target group returns { fan_out, message_ids, count, … } instead.
A target group is a named set of HTTPS endpoints. Pass its id as default_target_group_id on a queue or message and a single publish fans out to every member — each delivery is tracked and retried independently. Members are validated through the same HTTPS-only, SSRF-guarded checks as any other target.
POST/v1/target-groupsBearertier daily quota
Create a target group. The name is unique within your account.
Request body
name
string·REQUIRED
Label for the group. Unique within your account; max 200 characters.
Responses
201Created
Group created. Body is { id, name, created_at, updated_at }.
400Bad Request
name missing, blank, or over 200 characters.
error.codename is required (max 200 chars)
401Unauthorized
Missing or invalid API key.
409Conflict
A target group with this name already exists in your account.
error.codeA target group with this name already exists
To prevent abuse, target domains must be verified before you can send unlimited messages to them. Unverified domains are limited to 10 messages per day per domain; verified domains use your full tier quota. Verifying a registrable domain covers all of its subdomains.
POST/v1/domainsBearertier daily quota
Begin verifying ownership of a domain. Returns both a .well-known file challenge and a DNS TXT record — satisfy either and call /v1/domains/verify.
Request body
domain
string·REQUIRED
The domain to verify (e.g. api.example.com). A URL is accepted and normalised to its hostname.
Responses
201Created
Verification initiated. Body returns both a well_known file challenge and a dns_txt record. (An already-verified domain returns 200 with status: verified.)
When a signing secret is configured (per schedule or queue), every outbound request includes three verification headers. Compute the expected signature over {timestamp}.{rawBody}, compare in constant time, and reject timestamps older than five minutes.