API Reference
Base URL:
https://runlater.eu/api/v1
Interactive docs: Try the API directly in your browser with our Swagger UI →
Authentication
All API requests require an API key. Include it in the Authorization header:
Authorization: Bearer pk_live_xxx.sk_live_yyy
Tasks API
Create and manage one-time tasks. Run immediately, after a delay, or at a specific time. For recurring cron schedules, see the Schedules API below.
List all tasks for your organization.
curl https://runlater.eu/api/v1/tasks \
-H "Authorization: Bearer pk_live_xxx"
Get a single task by ID.
curl https://runlater.eu/api/v1/tasks/task_abc123 \
-H "Authorization: Bearer pk_live_xxx"
Create a new task. The type is inferred from the parameters you pass: omit timing fields for immediate execution, use
delay
to defer, or run_at
for a specific time. For recurring cron schedules, use POST /schedules.
Headers
| Header | Type | Description |
|---|---|---|
Idempotency-Key |
string | Unique key to prevent duplicate requests. If the same key is sent again within 24 hours, the original response is returned without creating a new task. (optional) |
Request Body
| Field | Type | Description |
|---|---|---|
url |
string | The URL to call (required) |
name |
string | Display name (auto-generated if omitted) |
method |
string | HTTP method: GET, POST, PUT, PATCH, DELETE (default: POST) |
headers |
object | Custom headers to send (optional) |
body |
string | Request body, max 256KB (optional) |
delay |
string |
Delay before execution: "30s", "5m", "2h", "1d". Runs immediately if omitted. (optional)
|
run_at |
string | ISO 8601 timestamp for one-time scheduled execution (optional) |
timeout_ms |
integer | Request timeout in milliseconds (default: 30000) |
retry_attempts |
integer | Retry attempts for one-time tasks (default: 5) |
callback_url |
string | URL to receive a POST callback when execution completes. See Callbacks. (optional) |
lane |
string | Lane for serialized execution. Tasks in the same lane run one at a time within your organization — the next task won't start until the current one finishes. Leave empty for default parallel execution. (optional) |
enabled |
boolean | Whether the task is active (default: true) |
notify_on_failure |
boolean | null |
Override org-level failure notification setting. null
= use org default, true
= always notify, false
= never notify. (optional)
|
notify_on_recovery |
boolean | null |
Override org-level recovery notification setting. null
= use org default, true
= always notify, false
= never notify. (optional)
|
expected_status_codes |
string | Comma-separated status codes that count as success, e.g. "200,201". Empty means any 2xx. (optional) |
expected_body_pattern |
string | Response body must contain this text to count as success. Empty means any body. (optional) |
on_failure_url |
string | URL to POST to when the task fails. Receives a JSON payload with task and execution details. (optional) |
on_recovery_url |
string | URL to POST to when the task recovers after a failure. Receives a JSON payload with task and execution details. (optional) |
script |
string |
Lua script with on_response
function. Runs after execution completes. Pro only. See Lua Scripting. (optional)
|
debounce |
string |
Fixed debounce window: "30s", "5m", "2h", "1d". The first request executes at the end of the window. Subsequent requests within the same window replace it — but the scheduled time stays fixed (no sliding). Requires
debounce_key
and lane. (optional)
|
debounce_key |
string |
Key to group debounced requests. Requests with the same lane
+ debounce_key
supersede each other within the fixed window — only the last one runs. Requires
debounce
and lane. (optional)
|
curl -X POST https://runlater.eu/api/v1/tasks \ -H "Authorization: Bearer pk_live_xxx" \ -H "Content-Type: application/json" \ -d '{ "url": "https://myapp.com/api/webhook", "method": "POST", "body": "{\"event\": \"test\"}" }'
curl -X POST https://runlater.eu/api/v1/tasks \ -H "Authorization: Bearer pk_live_xxx" \ -H "Content-Type: application/json" \ -d '{ "url": "https://myapp.com/api/charge", "method": "POST", "body": "{\"user_id\": 42}", "lane": "payments" }'
curl -X POST https://runlater.eu/api/v1/tasks \ -H "Authorization: Bearer pk_live_xxx" \ -H "Content-Type: application/json" \ -d '{ "url": "https://myapp.com/api/remind", "delay": "15m" }'
# Fixed-window debounce: the first call schedules at now + 30s. # Subsequent calls replace it but keep the same scheduled time. curl -X POST https://runlater.eu/api/v1/tasks \ -H "Authorization: Bearer pk_live_xxx" \ -H "Content-Type: application/json" \ -d '{ "url": "https://myapp.com/api/sync-user", "body": "{\"userId\": 123}", "lane": "sync", "debounce": "30s", "debounce_key": "user-123" }'
curl -X POST https://runlater.eu/api/v1/tasks \ -H "Authorization: Bearer pk_live_xxx" \ -H "Content-Type: application/json" \ -d '{ "name": "Send reminder", "url": "https://myapp.com/api/remind", "run_at": "2026-03-01T09:00:00Z" }'
Update a task. Only include the fields you want to change.
curl -X PUT https://runlater.eu/api/v1/tasks/task_abc123 \ -H "Authorization: Bearer pk_live_xxx" \ -H "Content-Type: application/json" \ -d '{ "url": "https://myapp.com/api/new-endpoint", "timeout_ms": 60000 }'
Delete a task and all its execution history.
curl -X DELETE https://runlater.eu/api/v1/tasks/task_abc123 \
-H "Authorization: Bearer pk_live_xxx"
Trigger a task to run immediately. Creates a new execution that runs right away.
curl -X POST https://runlater.eu/api/v1/tasks/task_abc123/trigger \
-H "Authorization: Bearer pk_live_xxx"
Create many tasks at once. Each task in the tasks
array defines its own url
and can optionally override method, headers, body, and name.
Top-level fields serve as defaults. Use the lane name with
DELETE /tasks?lane=name
to cancel them as a group.
Request Body
| Field | Type | Description |
|---|---|---|
lane |
string | Lane name that groups the batch — used for bulk cancel (required) |
tasks |
array |
Array of task objects (max 1000). Each must have a url
and can include method, headers, body, name. (required)
|
method |
string | Default HTTP method for tasks (default: POST) |
headers |
object | Default headers merged into each task (optional) |
run_at |
string | ISO 8601 timestamp — schedule all tasks for this time (optional) |
delay |
string |
Delay before execution: "30s", "5m", "2h",
"1d"
(optional)
|
timeout_ms |
integer | Default request timeout in milliseconds (default: 30000) |
retry_attempts |
integer | Default retry attempts per task (default: 5) |
callback_url |
string | Default URL to receive a POST callback per task on completion (optional) |
curl -X POST https://runlater.eu/api/v1/tasks/batch \ -H "Authorization: Bearer pk_live_xxx" \ -H "Content-Type: application/json" \ -d '{ "lane": "march-newsletter", "method": "POST", "headers": { "Authorization": "Bearer xxx" }, "run_at": "2026-03-01T09:00:00Z", "tasks": [ { "url": "https://myapp.com/api/send-email", "body": { "to": "user1@example.com", "name": "Alice" } }, { "url": "https://myapp.com/api/send-email", "body": { "to": "user2@example.com", "name": "Bob" } } ] }'
# Response (201 Created) { "data": { "lane": "march-newsletter", "created": 2, "scheduled_for": "2026-03-01T09:00:00Z" }, "message": "2 tasks created" }
Cancel all tasks in a lane. Soft-deletes every non-deleted task matching the lane name and removes their pending executions. Use this to cancel a batch you created earlier.
Query Parameters
| Field | Type | Description |
|---|---|---|
lane |
string | The lane name to cancel (required) |
curl -X DELETE "https://runlater.eu/api/v1/tasks?lane=march-newsletter" \ -H "Authorization: Bearer pk_live_xxx"
# Response (200 OK) { "data": { "cancelled": 1000 }, "message": "1000 tasks cancelled" }
Executions
Get execution history for a task.
Query Parameters
| Field | Type | Description |
|---|---|---|
limit |
integer | Max results to return, 1-100 (default: 50) |
# Response { "data": [{ "id": "exec_123", "status": "success", "scheduled_for": "2026-02-06T10:00:00Z", "started_at": "2026-02-06T10:00:01Z", "finished_at": "2026-02-06T10:00:01Z", "status_code": 200, "duration_ms": 145, "attempt": 1 }] }
Schedules API
Create and manage recurring cron schedules. Schedules run on a cron expression and are managed separately from one-time tasks.
List all schedules for your organization.
curl https://runlater.eu/api/v1/schedules \
-H "Authorization: Bearer pk_live_xxx"
Get a single schedule by ID.
curl https://runlater.eu/api/v1/schedules/sched_abc123 \
-H "Authorization: Bearer pk_live_xxx"
Create a new recurring schedule. Requires a cron expression.
Request Body
| Field | Type | Description |
|---|---|---|
url |
string | The URL to call (required) |
cron |
string | Cron expression for recurring execution (required) |
name |
string | Display name (auto-generated if omitted) |
method |
string | HTTP method: GET, POST, PUT, PATCH, DELETE (default: GET) |
headers |
object | Custom headers to send (optional) |
body |
string | Request body, max 256KB (optional) |
timeout_ms |
integer | Request timeout in milliseconds (default: 30000) |
enabled |
boolean | Whether the schedule is active (default: true) |
notify_on_failure |
boolean | null | Override org-level failure notification setting. (optional) |
notify_on_recovery |
boolean | null | Override org-level recovery notification setting. (optional) |
expected_status_codes |
string | Comma-separated status codes that count as success, e.g. "200,201". (optional) |
script |
string |
Lua script with on_response function. Pro only. (optional)
|
curl -X POST https://runlater.eu/api/v1/schedules \ -H "Authorization: Bearer pk_live_xxx" \ -H "Content-Type: application/json" \ -d '{ "name": "Daily backup", "url": "https://myapp.com/api/backup", "cron": "0 6 * * *" }'
Update a schedule. Only include the fields you want to change.
curl -X PUT https://runlater.eu/api/v1/schedules/sched_abc123 \ -H "Authorization: Bearer pk_live_xxx" \ -H "Content-Type: application/json" \ -d '{ "cron": "0 7 * * *", "enabled": false }'
Delete a schedule and all its execution history.
curl -X DELETE https://runlater.eu/api/v1/schedules/sched_abc123 \
-H "Authorization: Bearer pk_live_xxx"
Trigger a schedule to run immediately. Creates a new execution that runs right away.
curl -X POST https://runlater.eu/api/v1/schedules/sched_abc123/trigger \
-H "Authorization: Bearer pk_live_xxx"
Get execution history for a schedule.
Query Parameters
| Field | Type | Description |
|---|---|---|
limit |
integer | Max results to return, 1-100 (default: 50) |
Monitors API
CRUD operations for heartbeat monitors (dead man's switches). See the Cron Monitoring docs for details.
List all monitors for your organization.
curl https://runlater.eu/api/v1/monitors \
-H "Authorization: Bearer pk_live_xxx"
Get a single monitor by ID. Response includes the ping_url.
curl https://runlater.eu/api/v1/monitors/mon_abc123 \
-H "Authorization: Bearer pk_live_xxx"
Create a new monitor.
Request Body
| Field | Type | Description |
|---|---|---|
name |
string | Display name (required) |
schedule_type |
string | "interval" or "cron" (required) |
interval_seconds |
integer | Expected interval, 60-604800 (required for interval type) |
cron_expression |
string | Cron expression (required for cron type) |
grace_period_seconds |
integer | Grace period before alerting, 0-3600 (default: 300) |
enabled |
boolean | Whether monitoring is active (default: true) |
notify_on_failure |
boolean | null |
Override org-level failure notification setting. null
= use org default. (optional)
|
notify_on_recovery |
boolean | null |
Override org-level recovery notification setting. null
= use org default. (optional)
|
on_failure_url |
string | URL to POST to when the monitor goes down. Receives a JSON payload with monitor details. (optional) |
on_recovery_url |
string | URL to POST to when the monitor recovers after downtime. Receives a JSON payload with monitor details. (optional) |
curl -X POST https://runlater.eu/api/v1/monitors \ -H "Authorization: Bearer pk_live_xxx" \ -H "Content-Type: application/json" \ -d '{ "name": "Nightly Backup", "schedule_type": "interval", "interval_seconds": 86400, "grace_period_seconds": 1800 }'
Update a monitor. Only include the fields you want to change.
curl -X PUT https://runlater.eu/api/v1/monitors/mon_abc123 \ -H "Authorization: Bearer pk_live_xxx" \ -H "Content-Type: application/json" \ -d '{ "grace_period_seconds": 3600 }'
Delete a monitor and all its ping history.
curl -X DELETE https://runlater.eu/api/v1/monitors/mon_abc123 \
-H "Authorization: Bearer pk_live_xxx"
Get recent ping history for a monitor.
Query Parameters
| Field | Type | Description |
|---|---|---|
limit |
integer | Max results to return, 1-100 (default: 50) |
# Response { "data": [{ "id": "ping_123", "received_at": "2026-02-06T02:05:00Z" }] }
Ping Monitor
Record a ping from your cron job. No API key needed — the token in the URL is the authentication. Accepts both GET and POST.
Record a ping. Returns the monitor name and status. No authentication header needed.
Responses
| Status | Meaning |
|---|---|
200 |
Ping recorded successfully |
404 |
Invalid token |
410 |
Monitor is paused/disabled |
curl -fsS --retry 3 https://runlater.eu/ping/pm_your_token_here
Endpoints API
Inbound webhook endpoints receive events from external services and forward them to your app. See the Endpoints docs for the full guide.
List all endpoints for your organization.
curl https://runlater.eu/api/v1/endpoints \
-H "Authorization: Bearer pk_live_xxx"
Get a single endpoint by ID. Response includes the inbound_url.
curl https://runlater.eu/api/v1/endpoints/ep_abc123 \
-H "Authorization: Bearer pk_live_xxx"
Create a new inbound endpoint.
Request Body
| Field | Type | Description |
|---|---|---|
name |
string | Display name (required) |
forward_urls |
array | List of URLs to forward events to (required, at least one) |
retry_attempts |
integer | Retry attempts for forwarding, 0-10 (default: 5) |
use_lane |
boolean | Serialized delivery (one at a time). false for parallel. (default: true) |
enabled |
boolean | Whether the endpoint accepts events (default: true) |
forward_headers |
object | Custom headers merged with the original inbound headers when forwarding. (optional) |
forward_body |
string | Custom body replacing the original inbound body when forwarding. (optional) |
forward_method |
string | Custom HTTP method replacing the original inbound method when forwarding: GET, POST, PUT, PATCH, DELETE. (optional) |
on_failure_url |
string | URL to POST to when forwarding fails. Receives a JSON payload with endpoint and execution details. (optional) |
on_recovery_url |
string | URL to POST to when forwarding recovers after a failure. Receives a JSON payload with endpoint and execution details. (optional) |
script |
string |
Lua script for webhook processing. Define optional functions: verify(), filter(), transform(), route(). See Lua Scripting. (optional, Pro only)
|
curl -X POST https://runlater.eu/api/v1/endpoints \ -H "Authorization: Bearer pk_live_xxx" \ -H "Content-Type: application/json" \ -d '{ "name": "Stripe webhooks", "forward_urls": ["https://myapp.com/webhooks/stripe"], "retry_attempts": 5, "use_lane": true }'
Update an endpoint. Only include the fields you want to change.
curl -X PUT https://runlater.eu/api/v1/endpoints/ep_abc123 \ -H "Authorization: Bearer pk_live_xxx" \ -H "Content-Type: application/json" \ -d '{ "retry_attempts": 0, "use_lane": false }'
Delete an endpoint and all its inbound events.
curl -X DELETE https://runlater.eu/api/v1/endpoints/ep_abc123 \
-H "Authorization: Bearer pk_live_xxx"
Get inbound event history for an endpoint.
Query Parameters
| Field | Type | Description |
|---|---|---|
limit |
integer | Max results to return, 1-100 (default: 50) |
# Response { "data": [{ "id": "evt_123", "method": "POST", "source_ip": "1.2.3.4", "received_at": "2026-02-06T10:00:00Z", "execution_id": "exec_456", "execution_status": "success" }] }
Replay an inbound event. Creates a new forwarding execution for the original payload.
curl -X POST https://runlater.eu/api/v1/endpoints/ep_abc123/events/evt_456/replay \
-H "Authorization: Bearer pk_live_xxx"
Declarative Sync
Push your entire configuration in one call. Perfect for infrastructure-as-code workflows.
Sync your entire project configuration. This is idempotent - safe to call repeatedly with the same config.
Request Body
| Field | Type | Description |
|---|---|---|
schedules |
array | Array of recurring schedule configurations |
schedules[].key |
string | Unique key for this schedule (your identifier) |
schedules[].name |
string | Display name for this schedule |
schedules[].url |
string | The URL to call |
schedules[].method |
string | HTTP method (default: GET) |
schedules[].cron |
string | Cron expression for recurring execution |
schedules[].headers |
object | Custom headers (optional) |
schedules[].body |
string | Request body, max 256KB (optional) |
monitors |
array | Array of monitor configurations (optional) |
monitors[].key |
string | Unique key for this monitor (your identifier) |
monitors[].name |
string | Display name for this monitor |
monitors[].schedule_type |
string | "cron" or "interval" |
monitors[].cron_expression |
string | Cron expression (when schedule_type is "cron") |
monitors[].interval_seconds |
integer | Expected ping interval in seconds (when schedule_type is "interval") |
monitors[].grace_seconds |
integer | Grace period before alerting (default: 300) |
delete_removed |
boolean | Delete schedules/monitors not in config (default: false) |
curl -X PUT https://runlater.eu/api/v1/sync \ -H "Authorization: Bearer pk_live_xxx" \ -H "Content-Type: application/json" \ -d '{ "schedules": [{ "key": "daily-backup", "name": "Daily backup", "url": "https://myapp.com/api/backup", "cron": "0 6 * * *" }], "monitors": [{ "key": "nightly-etl", "name": "Nightly ETL", "schedule_type": "cron", "cron_expression": "0 2 * * *", "grace_seconds": 600 }], "delete_removed": true }'
Limits
| Limit | Value | Description |
|---|---|---|
| Request body size | 256 KB |
Maximum size of the body field sent to webhooks |
| Response body storage | 256 KB |
Responses larger than this are truncated in execution history |
| Request timeout | 1s - 5min |
Configurable per task (default: 30s) |
| Retry attempts | 0 - 10 |
For one-time tasks only (default: 5) |
Errors
The API returns standard HTTP status codes:
| Code | Meaning |
|---|---|
200 |
Success |
201 |
Created |
202 |
Accepted (for async operations like queue/trigger) |
204 |
No content (successful delete) |
400 |
Bad request - check your parameters |
401 |
Unauthorized - invalid API key |
404 |
Not found |
422 |
Validation error |
429 |
Rate limited - slow down |
500 |
Server error - try again |
Error responses include details:
{
"error": {
"message": "Invalid cron expression",
"field": "cron"
}
}