Push Delivery
When to use push delivery
Section titled “When to use push delivery”Use push if you already have a server listening or want jobs delivered to serverless functions. No long-lived worker process needed. Chronos makes the HTTP request to you.
If you’d rather pull jobs from your own process, see Worker Setup.
Create a schedule with push delivery
Section titled “Create a schedule with push delivery”curl -X POST https://api.chronos.sh/v1/schedules \ -H "Authorization: Bearer chrns_your_api_key" \ -H "Content-Type: application/json" \ -d '{ "name": "Daily report", "handler": "generate-report", "cron": "0 9 * * *", "delivery": { "type": "push", "http": { "url": "https://your-app.com/hooks/chronos", "method": "POST" } }, "payload": { "format": "pdf" } }'The delivery.http object supports:
| Field | Type | Required | Description |
|---|---|---|---|
url | string | Yes | Your endpoint. Must be HTTPS in production. No private/localhost IPs. |
method | string | Yes | GET, POST, PUT, PATCH, or DELETE |
headers | Record<string, string> | No | Custom headers sent with every delivery |
Custom headers example:
"http": { "url": "https://your-app.com/hooks/chronos", "method": "POST", "headers": { "X-App-Secret": "your-shared-secret" }}What Chronos sends
Section titled “What Chronos sends”When a job is due, Chronos makes an HTTP request to your endpoint.
Request body
Section titled “Request body”{ "job_id": "a1b2c3d4-...", "handler": "generate-report", "execution_id": "e5f6g7h8-...", "scheduled_for": "2026-01-15T09:00:00.000Z", "attempt": 1, "payload": { "format": "pdf" }}| Field | Type | Description |
|---|---|---|
job_id | string | Stable job identifier. Same across retries. |
handler | string | null | The handler name from your schedule. null if not set. |
execution_id | string | Unique to this attempt. Use job_id for idempotency across retries. |
scheduled_for | string | ISO 8601 timestamp of when the job was scheduled. |
attempt | number | Attempt number. 1 on first try, increments on retries. |
payload | object | null | The payload from your schedule or job. |
Headers
Section titled “Headers”Every delivery includes these system headers:
| Header | Value | Description |
|---|---|---|
Content-Type | application/json | Always JSON |
User-Agent | chronos.sh/1.0 | Identifies Chronos |
X-Chronos-Signature | sha256=<hex> | HMAC-SHA256 signature for verification |
X-Chronos-Delivery-Id | <execution_id> | Same as execution_id in the body |
X-Chronos-Timestamp | <unix_seconds> | Request timestamp (seconds since epoch) |
Your custom headers (from delivery.http.headers) are included but system headers take precedence on conflict.
Chronos does not follow redirects.
Handle the delivery
Section titled “Handle the delivery”A minimal Express handler:
import express from 'express';
const app = express();app.use(express.json());
app.post('/hooks/chronos', async (req, res) => { const { handler, payload } = req.body;
switch (handler) { case 'generate-report': await generateReport(payload.format); break; case 'sync-tenant': await syncTenant(payload.tenantId); break; default: console.warn(`Unknown handler: ${handler}`); return res.sendStatus(400); }
res.sendStatus(200);});
app.listen(3000);Route by the handler field to dispatch different job types to a single endpoint. Returning 400 for unknown handlers prevents Chronos from recording work as completed when nothing ran. For handlers you expect to deploy soon, return 503 instead so Chronos retries.
Response rules
Section titled “Response rules”Your HTTP response tells Chronos what happened:
| Your response | Chronos records | Retries? |
|---|---|---|
| 2xx (any success) | completed | No |
| 4xx (client error) | failed | No: terminal |
| 5xx (server error) | failed | Yes: exponential backoff |
| Timeout (no response within limit) | timeout | Yes |
| Network error (connection refused, DNS failure) | failed | Yes |
Verify the signature
Section titled “Verify the signature”Every delivery is signed with your account’s signing secret (found in the dashboard under Settings → Signing Key). Verify the X-Chronos-Signature header to confirm requests come from Chronos and haven’t been tampered with.
See Signature Verification for a full implementation with timestamp freshness, key rotation, and Express raw body setup.
One-off jobs with push delivery
Section titled “One-off jobs with push delivery”The same delivery config works on one-off jobs:
curl -X POST https://api.chronos.sh/v1/jobs \ -H "Authorization: Bearer chrns_your_api_key" \ -H "Content-Type: application/json" \ -d '{ "name": "Send welcome email", "handler": "send-email", "delay": { "value": 30, "unit": "minute" }, "delivery": { "type": "push", "http": { "url": "https://your-app.com/hooks/chronos", "method": "POST" } }, "payload": { "userId": "usr_abc123", "template": "welcome" } }'Debugging tips
Section titled “Debugging tips”- Check your response time. Default timeout is 30 seconds. If your handler takes longer, increase
timeouton the schedule/job or optimize the handler. - 4xx is terminal. If you accidentally return 401/403 (e.g., middleware rejecting the request), Chronos won’t retry. Return 503 while you fix auth issues.
- Correlate with
X-Chronos-Delivery-Id. This matchesexecution_id. Use it to find the delivery in your logs and in the Chronos dashboard. attempttells you which retry you’re on. Use it to adjust behavior on later attempts (e.g., skip optional steps, use a simpler fallback).