API Documentation
The JustRun REST API gives you full programmatic control over your cron jobs, spaces, alerts, billing, and more. Everything you can do in the dashboard is available through the API.
Overview
JustRun is a cron-as-a-service platform that executes HTTP requests on a schedule. You define a cron expression, a target URL, and we handle the execution, retries, monitoring, and alerting. The API follows REST conventions and returns JSON for all responses.
Protocol
HTTPS only
Content Type
application/json
API Version
v1 (stable)
Authentication
All API requests require a Bearer token in the Authorization header. You can use either a session JWT from the dashboard or an API key generated in Settings → API Keys → Generate Key.
API keys are long-lived and scoped to your account. They are shown only once when created, so copy the key immediately and store it securely.
curl https://api.justrun.sh/v1/jobs \
-H "Authorization: Bearer jrun_sk_a1b2c3d4e5f6..." \
-H "Content-Type: application/json"Keep your API keys safe
API keys have full read-write access to your account. Never expose them in client-side code, public repositories, or logs. If a key is compromised, revoke it immediately from the dashboard and generate a new one.
Base URL & Response Format
All API endpoints are served from a single base URL. Requests must be made over HTTPS.
https://api.justrun.shRate limits at a glance
- General endpoints: 100 requests/minute
- Run endpoint (
POST /v1/jobs/:id/run): 10 requests/minute
Successful response
Successful responses return a data key with the result. Paginated responses also include a meta object.
{
"data": { ... },
"meta": {
"page": 1,
"per_page": 20,
"total": 142
}
}Error Handling
All errors follow a consistent JSON shape with a machine-readable code, a human-readable message, and an optional details array for validation errors.
{
"error": {
"code": "validation_error",
"message": "cron_expression is required",
"details": [
{
"field": "cron_expression",
"issue": "must not be empty"
}
]
}
}HTTP Status Codes
| Code | Meaning |
|---|---|
| 200 | Success |
| 201 | Created |
| 204 | Deleted (no content) |
| 400 | Bad request -- check the error message |
| 401 | Unauthorized -- invalid or missing API key |
| 403 | Forbidden -- insufficient permissions |
| 404 | Not found |
| 409 | Conflict -- resource already exists or state conflict |
| 422 | Unprocessable -- valid JSON but semantic error |
| 429 | Rate limited -- slow down and retry after the Retry-After header |
| 500 | Server error -- contact support if persistent |
Jobs
Jobs are the core resource in JustRun. A job defines a cron schedule, a target URL, the HTTP method, headers, body, retry policy, and timeout. When the schedule fires, JustRun sends the configured HTTP request and records the result.
/v1/jobsRetrieve a paginated list of all jobs in your account. Filter by status, space, or search by name. Results are ordered by creation date (newest first).
| Name | Type | Required | Description |
|---|---|---|---|
| page | integer | No | Page number (default: 1) |
| per_page | integer | No | Results per page, 1-100 (default: 20) |
| status | string | No | Filter by status: active, paused, or all (default: all) |
| space_id | string | No | Filter by space ID |
curl "https://api.justrun.sh/v1/jobs?page=1&per_page=20&status=active" \
-H "Authorization: Bearer jrun_sk_a1b2c3d4e5f6..."{
"data": [
{
"id": "job_7kR3mNxQ2v",
"name": "Daily DB Backup",
"cron_expression": "0 3 * * *",
"url": "https://api.example.com/backup",
"method": "POST",
"timezone": "UTC",
"status": "active",
"timeout_sec": 30,
"retry_policy": "exponential",
"retry_max": 3,
"retry_delay_sec": 60,
"tags": ["production", "database"],
"space_id": "sp_xyz789",
"next_run_at": "2026-04-11T03:00:00Z",
"last_run_at": "2026-04-10T03:00:01Z",
"last_status": "success",
"created_at": "2026-01-15T10:30:00Z"
}
],
"meta": {
"page": 1,
"per_page": 20,
"total": 42
}
}/v1/jobsCreate a new cron job. At minimum you need a name, cron expression, and target URL. JustRun validates the cron expression and schedules the first execution immediately.
| Name | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | Human-readable name for the job (max 100 chars) |
| cron_expression | string | Yes | Standard 5-field cron expression (e.g., "*/5 * * * *") |
| url | string | Yes | Target URL to call. Must be HTTPS in production. |
| method | string | No | HTTP method: GET, POST, PUT, PATCH, DELETE (default: GET) |
| timezone | string | No | IANA timezone for the cron expression (default: UTC) |
| timeout_sec | integer | No | Max seconds to wait for a response, 1-300 (default: 30) |
| retry_policy | string | No | Retry strategy: none, fixed, linear, exponential (default: none) |
| retry_max | integer | No | Max retry attempts, 0-10 (default: 0) |
| retry_delay_sec | integer | No | Base delay between retries in seconds (default: 60) |
| tags | string[] | No | Array of tags for organizing jobs (max 10) |
| space_id | string | No | Assign the job to a space |
| headers | object | No | Custom HTTP headers to send with the request (key-value pairs) |
| body | string | No | Request body (for POST/PUT/PATCH). Sent as-is. |
| store_response_body | boolean | No | Store the response body for each execution (default: false) |
curl -X POST "https://api.justrun.sh/v1/jobs" \
-H "Authorization: Bearer jrun_sk_a1b2c3d4e5f6..." \
-H "Content-Type: application/json" \
-d '{
"name": "Nightly Report Generator",
"cron_expression": "0 2 * * *",
"url": "https://api.example.com/reports/generate",
"method": "POST",
"timezone": "Europe/Stockholm",
"timeout_sec": 120,
"retry_policy": "exponential",
"retry_max": 3,
"retry_delay_sec": 30,
"tags": ["reports", "nightly"],
"space_id": "sp_xyz789",
"headers": {
"X-API-Key": "your-service-key",
"X-Correlation-ID": "jr-{{execution_id}}"
},
"body": "{"format": "pdf", "recipients": ["[email protected]"]}",
"store_response_body": true
}'{
"data": {
"id": "job_9pT4wLxM8n",
"name": "Nightly Report Generator",
"cron_expression": "0 2 * * *",
"url": "https://api.example.com/reports/generate",
"method": "POST",
"timezone": "Europe/Stockholm",
"status": "active",
"timeout_sec": 120,
"retry_policy": "exponential",
"retry_max": 3,
"retry_delay_sec": 30,
"tags": ["reports", "nightly"],
"space_id": "sp_xyz789",
"headers": {
"X-API-Key": "your-service-key",
"X-Correlation-ID": "jr-{{execution_id}}"
},
"body": "{"format": "pdf", "recipients": ["[email protected]"]}",
"store_response_body": true,
"next_run_at": "2026-04-11T02:00:00Z",
"created_at": "2026-04-10T14:22:00Z"
}
}/v1/jobs/:idRetrieve full details for a single job, including its current status, schedule, configuration, and last execution result.
| Name | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | The job ID (path parameter) |
curl "https://api.justrun.sh/v1/jobs/job_7kR3mNxQ2v" \
-H "Authorization: Bearer jrun_sk_a1b2c3d4e5f6..."{
"data": {
"id": "job_7kR3mNxQ2v",
"name": "Daily DB Backup",
"cron_expression": "0 3 * * *",
"url": "https://api.example.com/backup",
"method": "POST",
"timezone": "UTC",
"status": "active",
"timeout_sec": 30,
"retry_policy": "exponential",
"retry_max": 3,
"retry_delay_sec": 60,
"tags": ["production", "database"],
"space_id": "sp_xyz789",
"headers": {},
"body": null,
"store_response_body": false,
"next_run_at": "2026-04-11T03:00:00Z",
"last_run_at": "2026-04-10T03:00:01Z",
"last_status": "success",
"last_duration_ms": 1245,
"last_status_code": 200,
"created_at": "2026-01-15T10:30:00Z",
"updated_at": "2026-04-10T03:00:01Z"
}
}/v1/jobs/:idUpdate one or more fields on an existing job. Only include the fields you want to change. The schedule is recalculated if you update the cron expression or timezone.
| Name | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | The job ID (path parameter) |
| name | string | No | Updated job name |
| cron_expression | string | No | Updated cron expression |
| url | string | No | Updated target URL |
| method | string | No | Updated HTTP method |
| timezone | string | No | Updated timezone |
| timeout_sec | integer | No | Updated timeout |
| retry_policy | string | No | Updated retry strategy |
| retry_max | integer | No | Updated max retries |
| retry_delay_sec | integer | No | Updated retry delay |
| tags | string[] | No | Replace all tags (send empty array to clear) |
| headers | object | No | Replace all headers |
| body | string | No | Replace request body |
curl -X PATCH "https://api.justrun.sh/v1/jobs/job_7kR3mNxQ2v" \
-H "Authorization: Bearer jrun_sk_a1b2c3d4e5f6..." \
-H "Content-Type: application/json" \
-d '{
"cron_expression": "*/30 * * * *",
"timeout_sec": 60,
"tags": ["production", "database", "critical"]
}'{
"data": {
"id": "job_7kR3mNxQ2v",
"name": "Daily DB Backup",
"cron_expression": "*/30 * * * *",
"timeout_sec": 60,
"tags": ["production", "database", "critical"],
"next_run_at": "2026-04-10T15:00:00Z",
"updated_at": "2026-04-10T14:35:00Z"
}
}/v1/jobs/:idPermanently delete a job and all of its execution history. This action cannot be undone.
| Name | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | The job ID (path parameter) |
curl -X DELETE "https://api.justrun.sh/v1/jobs/job_7kR3mNxQ2v" \
-H "Authorization: Bearer jrun_sk_a1b2c3d4e5f6..."Returns 204 No Content on success with an empty response body.
/v1/jobs/:id/runTrigger an immediate, out-of-schedule execution of a job. The job runs with the same configuration (URL, method, headers, body) as a scheduled run. This does not affect the normal cron schedule. Rate limited to 10 requests/minute.
| Name | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | The job ID (path parameter) |
curl -X POST "https://api.justrun.sh/v1/jobs/job_7kR3mNxQ2v/run" \
-H "Authorization: Bearer jrun_sk_a1b2c3d4e5f6..."{
"data": {
"execution_id": "exec_8nK2pWxR4m",
"job_id": "job_7kR3mNxQ2v",
"status": "pending",
"triggered_by": "manual",
"started_at": "2026-04-10T14:40:00Z"
}
}/v1/jobs/:id/pausePause a job. While paused, the job will not execute on its cron schedule. You can still trigger manual runs. Pausing an already-paused job returns a 409 Conflict.
curl -X POST "https://api.justrun.sh/v1/jobs/job_7kR3mNxQ2v/pause" \
-H "Authorization: Bearer jrun_sk_a1b2c3d4e5f6..."{
"data": {
"id": "job_7kR3mNxQ2v",
"status": "paused",
"paused_at": "2026-04-10T14:42:00Z"
}
}/v1/jobs/:id/resumeResume a paused job. The next execution is rescheduled automatically based on the cron expression. Resuming an active job returns a 409 Conflict.
curl -X POST "https://api.justrun.sh/v1/jobs/job_7kR3mNxQ2v/resume" \
-H "Authorization: Bearer jrun_sk_a1b2c3d4e5f6..."{
"data": {
"id": "job_7kR3mNxQ2v",
"status": "active",
"next_run_at": "2026-04-10T15:00:00Z",
"resumed_at": "2026-04-10T14:43:00Z"
}
}/v1/jobs/:id/executionsRetrieve a paginated list of past executions for a specific job, newest first. Each execution includes the HTTP status code, duration, and optionally the response body if store_response_body is enabled.
| Name | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | The job ID (path parameter) |
| page | integer | No | Page number (default: 1) |
| per_page | integer | No | Results per page, 1-100 (default: 20) |
curl "https://api.justrun.sh/v1/jobs/job_7kR3mNxQ2v/executions?page=1&per_page=5" \
-H "Authorization: Bearer jrun_sk_a1b2c3d4e5f6..."{
"data": [
{
"id": "exec_8nK2pWxR4m",
"job_id": "job_7kR3mNxQ2v",
"status": "success",
"status_code": 200,
"duration_ms": 342,
"triggered_by": "schedule",
"response_body": null,
"executed_at": "2026-04-10T03:00:01Z"
},
{
"id": "exec_3jL9qYvN1p",
"job_id": "job_7kR3mNxQ2v",
"status": "failed",
"status_code": 503,
"duration_ms": 30012,
"triggered_by": "schedule",
"response_body": null,
"error": "Request timed out after 30s",
"executed_at": "2026-04-09T03:00:01Z"
}
],
"meta": {
"page": 1,
"per_page": 5,
"total": 180
}
}/v1/jobs/sparklinesRetrieve execution sparkline data for all of your jobs. Returns the last 24 hours of execution results as compact arrays, suitable for rendering mini status charts in a dashboard.
curl "https://api.justrun.sh/v1/jobs/sparklines" \
-H "Authorization: Bearer jrun_sk_a1b2c3d4e5f6..."{
"data": {
"job_7kR3mNxQ2v": [1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1],
"job_9pT4wLxM8n": [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
}
}Sparkline values
Each value is 1 for success, 0 for failure, or -1 for skipped/paused. Array is ordered oldest to newest.
/v1/jobs/bulkPerform an action on multiple jobs at once. Useful for pausing all jobs before a deploy, resuming them after, or bulk-organizing with tags and spaces.
| Name | Type | Required | Description |
|---|---|---|---|
| job_ids | string[] | Yes | Array of job IDs to act on (max 100) |
| action | string | Yes | One of: pause, resume, delete, move_to_space, add_tags, remove_tags |
| space_id | string | No | Required when action is move_to_space |
| tags | string[] | No | Required when action is add_tags or remove_tags |
curl -X POST "https://api.justrun.sh/v1/jobs/bulk" \
-H "Authorization: Bearer jrun_sk_a1b2c3d4e5f6..." \
-H "Content-Type: application/json" \
-d '{
"job_ids": ["job_7kR3mNxQ2v", "job_9pT4wLxM8n", "job_2bH5sKzP6q"],
"action": "pause"
}'{
"data": {
"succeeded": 3,
"failed": 0,
"errors": []
}
}curl -X POST "https://api.justrun.sh/v1/jobs/bulk" \
-H "Authorization: Bearer jrun_sk_a1b2c3d4e5f6..." \
-H "Content-Type: application/json" \
-d '{
"job_ids": ["job_7kR3mNxQ2v", "job_9pT4wLxM8n"],
"action": "move_to_space",
"space_id": "sp_abc456"
}'Spaces
Spaces let you organize jobs into logical groups like "Production", "Staging", or "Marketing". You can pause or resume all jobs in a space with a single API call, making it easy to freeze an entire environment during maintenance windows.
/v1/spacesRetrieve all spaces in your account with job counts.
curl "https://api.justrun.sh/v1/spaces" \
-H "Authorization: Bearer jrun_sk_a1b2c3d4e5f6..."{
"data": [
{
"id": "sp_xyz789",
"name": "Production",
"timezone": "UTC",
"status": "active",
"job_count": 12,
"created_at": "2026-01-10T08:00:00Z"
},
{
"id": "sp_abc456",
"name": "Staging",
"timezone": "Europe/Stockholm",
"status": "active",
"job_count": 3,
"created_at": "2026-02-05T12:00:00Z"
}
]
}/v1/spacesCreate a new space. Spaces have a name and a default timezone that new jobs in the space inherit.
| Name | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | Space name (max 50 chars) |
| timezone | string | No | Default IANA timezone for jobs in this space (default: UTC) |
curl -X POST "https://api.justrun.sh/v1/spaces" \
-H "Authorization: Bearer jrun_sk_a1b2c3d4e5f6..." \
-H "Content-Type: application/json" \
-d '{
"name": "Production",
"timezone": "UTC"
}'{
"data": {
"id": "sp_nR4kLm8wQ2",
"name": "Production",
"timezone": "UTC",
"status": "active",
"job_count": 0,
"created_at": "2026-04-10T15:00:00Z"
}
}/v1/spaces/:idUpdate a space name or default timezone. Existing jobs in the space are not affected by timezone changes.
| Name | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | The space ID (path parameter) |
| name | string | No | Updated space name |
| timezone | string | No | Updated default timezone |
curl -X PATCH "https://api.justrun.sh/v1/spaces/sp_xyz789" \
-H "Authorization: Bearer jrun_sk_a1b2c3d4e5f6..." \
-H "Content-Type: application/json" \
-d '{ "name": "Production (EU)" }'{
"data": {
"id": "sp_xyz789",
"name": "Production (EU)",
"timezone": "UTC",
"status": "active",
"job_count": 12,
"updated_at": "2026-04-10T15:05:00Z"
}
}/v1/spaces/:idDelete a space. The space must be empty (no jobs). Move or delete all jobs in the space first, or use the bulk move action.
| Name | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | The space ID (path parameter) |
curl -X DELETE "https://api.justrun.sh/v1/spaces/sp_abc456" \
-H "Authorization: Bearer jrun_sk_a1b2c3d4e5f6..."Returns 204 No Content on success.
/v1/spaces/:id/pausePause all jobs in a space with a single call. Useful for maintenance windows or deploys. Individual jobs retain their own status so resuming the space only resumes jobs that were previously active.
curl -X POST "https://api.justrun.sh/v1/spaces/sp_xyz789/pause" \
-H "Authorization: Bearer jrun_sk_a1b2c3d4e5f6..."{
"data": {
"id": "sp_xyz789",
"status": "paused",
"jobs_paused": 12,
"paused_at": "2026-04-10T15:10:00Z"
}
}/v1/spaces/:id/resumeResume all previously active jobs in a paused space. Jobs that were individually paused before the space pause remain paused.
curl -X POST "https://api.justrun.sh/v1/spaces/sp_xyz789/resume" \
-H "Authorization: Bearer jrun_sk_a1b2c3d4e5f6..."{
"data": {
"id": "sp_xyz789",
"status": "active",
"jobs_resumed": 12,
"resumed_at": "2026-04-10T15:15:00Z"
}
}Alert Channels
Alert channels define where notifications are sent when jobs fail, recover, or meet custom conditions. JustRun supports Slack, Discord, email, and generic webhooks. Each channel can be tested before going live.
/v1/alerts/channelsList all configured alert channels for your account.
curl "https://api.justrun.sh/v1/alerts/channels" \
-H "Authorization: Bearer jrun_sk_a1b2c3d4e5f6..."{
"data": [
{
"id": "ch_sL3kNm9wR2",
"type": "slack",
"name": "Engineering Alerts",
"config": {
"webhook_url": "https://hooks.slack.com/services/T00/B00/xxx"
},
"created_at": "2026-03-01T10:00:00Z"
},
{
"id": "ch_pQ7jVx4tK8",
"type": "email",
"name": "On-Call Email",
"config": {
"addresses": ["[email protected]"]
},
"created_at": "2026-03-15T08:00:00Z"
}
]
}/v1/alerts/channelsCreate a new alert channel. The config object varies by channel type. See examples below for each type.
| Name | Type | Required | Description |
|---|---|---|---|
| type | string | Yes | Channel type: slack, discord, email, or webhook |
| name | string | Yes | Display name for the channel |
| config | object | Yes | Type-specific configuration (see examples below) |
Slack
curl -X POST "https://api.justrun.sh/v1/alerts/channels" \
-H "Authorization: Bearer jrun_sk_a1b2c3d4e5f6..." \
-H "Content-Type: application/json" \
-d '{
"type": "slack",
"name": "Engineering Alerts",
"config": {
"webhook_url": "https://hooks.slack.com/services/T00/B00/xxx"
}
}'Discord
curl -X POST "https://api.justrun.sh/v1/alerts/channels" \
-H "Authorization: Bearer jrun_sk_a1b2c3d4e5f6..." \
-H "Content-Type: application/json" \
-d '{
"type": "discord",
"name": "DevOps Discord",
"config": {
"webhook_url": "https://discord.com/api/webhooks/1234567890/abcdef..."
}
}'curl -X POST "https://api.justrun.sh/v1/alerts/channels" \
-H "Authorization: Bearer jrun_sk_a1b2c3d4e5f6..." \
-H "Content-Type: application/json" \
-d '{
"type": "email",
"name": "On-Call Team",
"config": {
"addresses": ["[email protected]", "[email protected]"]
}
}'Webhook
curl -X POST "https://api.justrun.sh/v1/alerts/channels" \
-H "Authorization: Bearer jrun_sk_a1b2c3d4e5f6..." \
-H "Content-Type: application/json" \
-d '{
"type": "webhook",
"name": "PagerDuty Integration",
"config": {
"url": "https://events.pagerduty.com/v2/enqueue",
"headers": {
"Content-Type": "application/json"
},
"secret": "whsec_your_signing_secret"
}
}'{
"data": {
"id": "ch_nR4kLm8wQ2",
"type": "slack",
"name": "Engineering Alerts",
"config": {
"webhook_url": "https://hooks.slack.com/services/T00/B00/xxx"
},
"created_at": "2026-04-10T15:20:00Z"
}
}/v1/alerts/channels/:idDelete an alert channel. Jobs using this channel will no longer send notifications to it.
| Name | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | The channel ID (path parameter) |
curl -X DELETE "https://api.justrun.sh/v1/alerts/channels/ch_sL3kNm9wR2" \
-H "Authorization: Bearer jrun_sk_a1b2c3d4e5f6..."Returns 204 No Content on success.
/v1/alerts/channels/:id/testSend a test notification through the channel to verify your configuration works correctly. Returns immediately with the delivery status.
| Name | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | The channel ID (path parameter) |
curl -X POST "https://api.justrun.sh/v1/alerts/channels/ch_sL3kNm9wR2/test" \
-H "Authorization: Bearer jrun_sk_a1b2c3d4e5f6..."{
"data": {
"channel_id": "ch_sL3kNm9wR2",
"status": "delivered",
"message": "Test notification sent successfully"
}
}API Keys
API keys provide programmatic access to your JustRun account. You can create multiple keys for different services and revoke them individually. Keys are prefixed with jrun_sk_ for easy identification.
/v1/api-keysList all API keys for your account. The full key value is not returned for security. Only the prefix and last 4 characters are shown.
curl "https://api.justrun.sh/v1/api-keys" \
-H "Authorization: Bearer jrun_sk_a1b2c3d4e5f6..."{
"data": [
{
"id": "key_mN4kPw9xR2",
"name": "CI/CD Pipeline",
"prefix": "jrun_sk_",
"last_four": "f6g7",
"last_used_at": "2026-04-10T14:00:00Z",
"created_at": "2026-03-01T10:00:00Z"
},
{
"id": "key_qL8jTv3sK5",
"name": "Monitoring Script",
"prefix": "jrun_sk_",
"last_four": "h8i9",
"last_used_at": "2026-04-09T22:00:00Z",
"created_at": "2026-03-20T15:00:00Z"
}
]
}/v1/api-keysCreate a new API key. The full key is returned only in this response. Store it securely -- you cannot retrieve it again.
| Name | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | A descriptive name for the key (e.g., "CI/CD Pipeline") |
curl -X POST "https://api.justrun.sh/v1/api-keys" \
-H "Authorization: Bearer jrun_sk_a1b2c3d4e5f6..." \
-H "Content-Type: application/json" \
-d '{ "name": "Deploy Script" }'{
"data": {
"id": "key_xR7nLp2wM4",
"name": "Deploy Script",
"key": "jrun_sk_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
"created_at": "2026-04-10T15:30:00Z"
}
}Copy your key now
The key field is only included in the creation response. It is not stored in plain text and cannot be retrieved later.
/v1/api-keys/:idPermanently revoke an API key. Any requests using this key will immediately return 401 Unauthorized. This action cannot be undone.
| Name | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | The key ID (path parameter) |
curl -X DELETE "https://api.justrun.sh/v1/api-keys/key_mN4kPw9xR2" \
-H "Authorization: Bearer jrun_sk_a1b2c3d4e5f6..."Returns 204 No Content on success.
Billing
Check your current plan usage, create checkout sessions for upgrades, and manage your subscription through the Stripe customer portal.
/v1/billing/usageReturns your current plan, resource usage, and limits for the current billing period. Use this to monitor consumption and build custom dashboards.
curl "https://api.justrun.sh/v1/billing/usage" \
-H "Authorization: Bearer jrun_sk_a1b2c3d4e5f6..."{
"data": {
"plan": "maker",
"billing_period": {
"start": "2026-04-01T00:00:00Z",
"end": "2026-04-30T23:59:59Z"
},
"jobs": {
"used": 12,
"limit": 50
},
"executions": {
"used": 8420,
"limit": null
},
"ai_diagnoses": {
"used": 4,
"limit": 20,
"resets_at": "2026-04-11T00:00:00Z"
}
}
}/v1/billing/checkoutCreate a Stripe Checkout session to upgrade or change your plan. Returns a URL that redirects the user to Stripe's hosted checkout page.
| Name | Type | Required | Description |
|---|---|---|---|
| price_id | string | Yes | Stripe price ID for the target plan (e.g., price_maker_monthly) |
curl -X POST "https://api.justrun.sh/v1/billing/checkout" \
-H "Authorization: Bearer jrun_sk_a1b2c3d4e5f6..." \
-H "Content-Type: application/json" \
-d '{ "price_id": "price_maker_monthly" }'{
"data": {
"checkout_url": "https://checkout.stripe.com/c/pay/cs_live_a1b2c3...",
"expires_at": "2026-04-10T16:30:00Z"
}
}/v1/billing/portalGenerate a Stripe customer portal URL where the user can manage their payment methods, view invoices, and cancel their subscription.
curl -X POST "https://api.justrun.sh/v1/billing/portal" \
-H "Authorization: Bearer jrun_sk_a1b2c3d4e5f6..."{
"data": {
"portal_url": "https://billing.stripe.com/p/session/bps_1a2b3c..."
}
}Migration
Import your existing cron jobs from other services into JustRun with a single API call. We support direct imports from EasyCron and cron-job.org, as well as a generic JSON import for custom migrations.
/v1/migrate/easycronImport all jobs from your EasyCron account. Provide your EasyCron API key and JustRun fetches your jobs, converts them, and creates them in your account.
| Name | Type | Required | Description |
|---|---|---|---|
| api_key | string | Yes | Your EasyCron API key |
| space_id | string | No | Place all imported jobs in this space |
curl -X POST "https://api.justrun.sh/v1/migrate/easycron" \
-H "Authorization: Bearer jrun_sk_a1b2c3d4e5f6..." \
-H "Content-Type: application/json" \
-d '{
"api_key": "ec_your_easycron_api_key",
"space_id": "sp_xyz789"
}'{
"data": {
"imported": 14,
"skipped": 1,
"errors": [
{
"original_name": "Legacy Cron #7",
"reason": "Unsupported cron expression: @reboot"
}
]
}
}/v1/migrate/cronjob-orgImport all jobs from your cron-job.org account using their API key. JustRun maps cron-job.org fields to JustRun equivalents automatically.
| Name | Type | Required | Description |
|---|---|---|---|
| api_key | string | Yes | Your cron-job.org API key |
| space_id | string | No | Place all imported jobs in this space |
curl -X POST "https://api.justrun.sh/v1/migrate/cronjob-org" \
-H "Authorization: Bearer jrun_sk_a1b2c3d4e5f6..." \
-H "Content-Type: application/json" \
-d '{
"api_key": "your_cronjob_org_api_key",
"space_id": "sp_xyz789"
}'{
"data": {
"imported": 8,
"skipped": 0,
"errors": []
}
}/v1/migrate/jsonImport jobs from a raw JSON array. Use this for custom migrations from services we do not directly integrate with, or to restore a backup. Each object in the array follows the same schema as the Create Job endpoint.
| Name | Type | Required | Description |
|---|---|---|---|
| jobs | object[] | Yes | Array of job objects (see schema below) |
| space_id | string | No | Place all imported jobs in this space |
curl -X POST "https://api.justrun.sh/v1/migrate/json" \
-H "Authorization: Bearer jrun_sk_a1b2c3d4e5f6..." \
-H "Content-Type: application/json" \
-d '{
"space_id": "sp_xyz789",
"jobs": [
{
"name": "Health Check",
"cron_expression": "*/5 * * * *",
"url": "https://api.example.com/health",
"method": "GET",
"timeout_sec": 10
},
{
"name": "Sync Inventory",
"cron_expression": "0 */2 * * *",
"url": "https://api.example.com/inventory/sync",
"method": "POST",
"timezone": "America/New_York",
"timeout_sec": 120,
"retry_policy": "exponential",
"retry_max": 3,
"retry_delay_sec": 30,
"headers": {
"X-API-Key": "inv_key_abc123"
},
"body": "{"full_sync": true}",
"tags": ["inventory", "critical"]
}
]
}'{
"data": {
"imported": 2,
"skipped": 0,
"errors": [],
"job_ids": ["job_xR7nLp2wM4", "job_qL8jTv3sK5"]
}
}Status Badges
Embed live status and uptime badges in your README, status page, or wiki. Badges are served as SVG images and update in real time. No authentication required -- badges use the job ID as a public token.
Status Badge
Shows the current status of a job: passing, failing, or paused.
https://api.justrun.sh/badges/job_7kR3mNxQ2v/status.svgUptime Badge
Shows the uptime percentage for the last 30 days.
https://api.justrun.sh/badges/job_7kR3mNxQ2v/uptime.svgMarkdown Embed
Copy-paste these into your README or documentation.
<!-- Status badge -->

<!-- Uptime badge -->

<!-- Badge linking to the dashboard -->
[](https://justrun.sh/dashboard/jobs/job_7kR3mNxQ2v)HTML Embed
<img src="https://api.justrun.sh/badges/job_7kR3mNxQ2v/status.svg" alt="Job Status" />
<img src="https://api.justrun.sh/badges/job_7kR3mNxQ2v/uptime.svg" alt="Uptime" />Webhooks
JustRun can send webhook notifications to your server when job events occur. Webhooks are delivered as POST requests with a JSON body and a signature header for verification.
Event Format
Every webhook delivery has a consistent envelope with the event type, a unique delivery ID, a timestamp, and the event-specific payload.
{
"id": "evt_xR7nLp2wM4",
"type": "job.failed",
"created_at": "2026-04-10T15:45:00Z",
"data": {
"job_id": "job_7kR3mNxQ2v",
"job_name": "Daily DB Backup",
"execution_id": "exec_3jL9qYvN1p",
"status_code": 503,
"duration_ms": 30012,
"error": "Request timed out after 30s",
"executed_at": "2026-04-10T15:44:30Z"
}
}Event Types
| Event | Fired When |
|---|---|
| job.executed | A job execution completes (success or failure) |
| job.failed | A job execution fails (non-2xx response or timeout) after all retries |
| job.recovered | A previously failing job succeeds again |
Signature Verification
Every webhook request includes an X-JustRun-Signature header containing an HMAC-SHA256 signature of the raw request body, computed using your webhook signing secret. Always verify this signature to ensure the request came from JustRun.
The signature format is: sha256=<hex-encoded-hmac>
import crypto from 'crypto';
import express from 'express';
const app = express();
app.use(express.raw({ type: 'application/json' }));
const WEBHOOK_SECRET = process.env.JUSTRUN_WEBHOOK_SECRET;
function verifySignature(payload, signature) {
const expected = crypto
.createHmac('sha256', WEBHOOK_SECRET)
.update(payload)
.digest('hex');
const sig = signature.replace('sha256=', '');
return crypto.timingSafeEqual(
Buffer.from(sig, 'hex'),
Buffer.from(expected, 'hex')
);
}
app.post('/webhooks/justrun', (req, res) => {
const signature = req.headers['x-justrun-signature'];
if (!signature || !verifySignature(req.body, signature)) {
return res.status(401).json({ error: 'Invalid signature' });
}
const event = JSON.parse(req.body);
console.log(`Received event: ${event.type}`);
switch (event.type) {
case 'job.failed':
// Handle job failure — page the on-call team, etc.
console.log(`Job ${event.data.job_name} failed: ${event.data.error}`);
break;
case 'job.recovered':
// Handle recovery — resolve the alert
console.log(`Job ${event.data.job_name} recovered`);
break;
}
res.status(200).json({ received: true });
});
app.listen(3000);Use constant-time comparison
Always use crypto.timingSafeEqual (or equivalent) to compare signatures. Simple string comparison (===) is vulnerable to timing attacks.
MCP Server
JustRun provides a Model Context Protocol (MCP) server that lets AI assistants manage your cron jobs through natural language. MCP is an open standard that connects AI models to external tools and data sources.
Installation
The MCP server runs as a local process that your AI assistant connects to. No global install needed.
npx @justrun/mcpConfiguration for Claude Desktop
Add this to your Claude Desktop config file at ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%/Claude/claude_desktop_config.json (Windows).
{
"mcpServers": {
"justrun": {
"command": "npx",
"args": ["@justrun/mcp"],
"env": {
"JUSTRUN_API_KEY": "jrun_sk_a1b2c3d4e5f6..."
}
}
}
}Configuration for Claude Code
{
"mcpServers": {
"justrun": {
"command": "npx",
"args": ["@justrun/mcp"],
"env": {
"JUSTRUN_API_KEY": "jrun_sk_a1b2c3d4e5f6..."
}
}
}
}Configuration for Cursor
In Cursor, go to Settings → MCP Servers → Add Server and configure:
{
"name": "justrun",
"command": "npx",
"args": ["@justrun/mcp"],
"env": {
"JUSTRUN_API_KEY": "jrun_sk_a1b2c3d4e5f6..."
}
}Available Tools
| Tool | Description |
|---|---|
| list_jobs | List all cron jobs with optional filtering |
| create_job | Create a new cron job from natural language or explicit config |
| get_job | Get details for a specific job |
| update_job | Update job configuration |
| delete_job | Delete a job |
| run_job | Trigger an immediate execution |
| pause_job | Pause a job |
| resume_job | Resume a paused job |
| get_executions | View execution history for a job |
| list_spaces | List all spaces |
| create_space | Create a new space |
| get_usage | Check current plan usage and limits |
Example Conversation
You
Create a cron job that pings https://api.example.com/health every 5 minutes and alerts me on Slack if it fails.
Claude (via JustRun MCP)
I created a job called "Health Check - api.example.com" with a */5 * * * *schedule. It sends a GET request to your health endpoint with a 10-second timeout and 3 exponential retries. Your existing Slack channel "Engineering Alerts" is already configured for failure notifications. The job is now active and the first execution is scheduled in 3 minutes.
Rate Limits
API requests are rate-limited per account to ensure fair usage and platform stability. Limits vary by plan and endpoint.
Per-Plan Limits
| Plan | General (req/min) | /run (req/min) |
|---|---|---|
| Free | 60 | 5 |
| Maker | 300 | 10 |
| Pro | 1,000 | 30 |
| Scale | 5,000 | 100 |
Rate Limit Headers
Every API response includes rate limit headers so you can monitor your consumption programmatically.
| Header | Description |
|---|---|
| X-RateLimit-Limit | Your plan's requests-per-minute limit |
| X-RateLimit-Remaining | Requests remaining in the current window |
| X-RateLimit-Reset | Unix timestamp when the window resets |
| Retry-After | Seconds to wait before retrying (only on 429 responses) |
What Happens When You Are Rate Limited
When you exceed the rate limit, the API returns a 429 Too Many Requests response. Wait for the number of seconds specified in the Retry-After header before making another request.
{
"error": {
"code": "rate_limit_exceeded",
"message": "Too many requests. Please retry after 12 seconds.",
"retry_after": 12
}
}Implement exponential backoff
For production integrations, implement exponential backoff with jitter rather than waiting the exact Retry-After duration. This prevents thundering herd problems when multiple clients hit the limit simultaneously.
SDKs & Libraries
Official client libraries for popular languages are on our roadmap. In the meantime, the REST API works with any HTTP client.
Node.js / TypeScript
@justrun/sdk
Python
justrun-python
Go
go-justrun
Want early access?
If you would like to beta test an SDK or contribute to its development, reach out at [email protected] or open an issue on GitHub.