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.

Example Request
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.

Base URLhttps://api.justrun.sh

Rate 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.

200 OK
{
  "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.

400 Bad Request
{
  "error": {
    "code": "validation_error",
    "message": "cron_expression is required",
    "details": [
      {
        "field": "cron_expression",
        "issue": "must not be empty"
      }
    ]
  }
}

HTTP Status Codes

CodeMeaning
200Success
201Created
204Deleted (no content)
400Bad request -- check the error message
401Unauthorized -- invalid or missing API key
403Forbidden -- insufficient permissions
404Not found
409Conflict -- resource already exists or state conflict
422Unprocessable -- valid JSON but semantic error
429Rate limited -- slow down and retry after the Retry-After header
500Server 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.

GET/v1/jobs

Retrieve 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).

NameTypeRequiredDescription
pageintegerNoPage number (default: 1)
per_pageintegerNoResults per page, 1-100 (default: 20)
statusstringNoFilter by status: active, paused, or all (default: all)
space_idstringNoFilter by space ID
Request
curl "https://api.justrun.sh/v1/jobs?page=1&per_page=20&status=active" \
  -H "Authorization: Bearer jrun_sk_a1b2c3d4e5f6..."
Response — 200 OK
{
  "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
  }
}
POST/v1/jobs

Create 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.

NameTypeRequiredDescription
namestringYesHuman-readable name for the job (max 100 chars)
cron_expressionstringYesStandard 5-field cron expression (e.g., "*/5 * * * *")
urlstringYesTarget URL to call. Must be HTTPS in production.
methodstringNoHTTP method: GET, POST, PUT, PATCH, DELETE (default: GET)
timezonestringNoIANA timezone for the cron expression (default: UTC)
timeout_secintegerNoMax seconds to wait for a response, 1-300 (default: 30)
retry_policystringNoRetry strategy: none, fixed, linear, exponential (default: none)
retry_maxintegerNoMax retry attempts, 0-10 (default: 0)
retry_delay_secintegerNoBase delay between retries in seconds (default: 60)
tagsstring[]NoArray of tags for organizing jobs (max 10)
space_idstringNoAssign the job to a space
headersobjectNoCustom HTTP headers to send with the request (key-value pairs)
bodystringNoRequest body (for POST/PUT/PATCH). Sent as-is.
store_response_bodybooleanNoStore the response body for each execution (default: false)
Request
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
  }'
Response — 201 Created
{
  "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"
  }
}
GET/v1/jobs/:id

Retrieve full details for a single job, including its current status, schedule, configuration, and last execution result.

NameTypeRequiredDescription
idstringYesThe job ID (path parameter)
Request
curl "https://api.justrun.sh/v1/jobs/job_7kR3mNxQ2v" \
  -H "Authorization: Bearer jrun_sk_a1b2c3d4e5f6..."
Response — 200 OK
{
  "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"
  }
}
PATCH/v1/jobs/:id

Update 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.

NameTypeRequiredDescription
idstringYesThe job ID (path parameter)
namestringNoUpdated job name
cron_expressionstringNoUpdated cron expression
urlstringNoUpdated target URL
methodstringNoUpdated HTTP method
timezonestringNoUpdated timezone
timeout_secintegerNoUpdated timeout
retry_policystringNoUpdated retry strategy
retry_maxintegerNoUpdated max retries
retry_delay_secintegerNoUpdated retry delay
tagsstring[]NoReplace all tags (send empty array to clear)
headersobjectNoReplace all headers
bodystringNoReplace request body
Request
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"]
  }'
Response — 200 OK
{
  "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"
  }
}
DELETE/v1/jobs/:id

Permanently delete a job and all of its execution history. This action cannot be undone.

NameTypeRequiredDescription
idstringYesThe job ID (path parameter)
Request
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.

POST/v1/jobs/:id/run

Trigger 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.

NameTypeRequiredDescription
idstringYesThe job ID (path parameter)
Request
curl -X POST "https://api.justrun.sh/v1/jobs/job_7kR3mNxQ2v/run" \
  -H "Authorization: Bearer jrun_sk_a1b2c3d4e5f6..."
Response — 200 OK
{
  "data": {
    "execution_id": "exec_8nK2pWxR4m",
    "job_id": "job_7kR3mNxQ2v",
    "status": "pending",
    "triggered_by": "manual",
    "started_at": "2026-04-10T14:40:00Z"
  }
}
POST/v1/jobs/:id/pause

Pause 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.

Request
curl -X POST "https://api.justrun.sh/v1/jobs/job_7kR3mNxQ2v/pause" \
  -H "Authorization: Bearer jrun_sk_a1b2c3d4e5f6..."
Response — 200 OK
{
  "data": {
    "id": "job_7kR3mNxQ2v",
    "status": "paused",
    "paused_at": "2026-04-10T14:42:00Z"
  }
}
POST/v1/jobs/:id/resume

Resume a paused job. The next execution is rescheduled automatically based on the cron expression. Resuming an active job returns a 409 Conflict.

Request
curl -X POST "https://api.justrun.sh/v1/jobs/job_7kR3mNxQ2v/resume" \
  -H "Authorization: Bearer jrun_sk_a1b2c3d4e5f6..."
Response — 200 OK
{
  "data": {
    "id": "job_7kR3mNxQ2v",
    "status": "active",
    "next_run_at": "2026-04-10T15:00:00Z",
    "resumed_at": "2026-04-10T14:43:00Z"
  }
}
GET/v1/jobs/:id/executions

Retrieve 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.

NameTypeRequiredDescription
idstringYesThe job ID (path parameter)
pageintegerNoPage number (default: 1)
per_pageintegerNoResults per page, 1-100 (default: 20)
Request
curl "https://api.justrun.sh/v1/jobs/job_7kR3mNxQ2v/executions?page=1&per_page=5" \
  -H "Authorization: Bearer jrun_sk_a1b2c3d4e5f6..."
Response — 200 OK
{
  "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
  }
}
GET/v1/jobs/sparklines

Retrieve 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.

Request
curl "https://api.justrun.sh/v1/jobs/sparklines" \
  -H "Authorization: Bearer jrun_sk_a1b2c3d4e5f6..."
Response — 200 OK
{
  "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.

POST/v1/jobs/bulk

Perform 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.

NameTypeRequiredDescription
job_idsstring[]YesArray of job IDs to act on (max 100)
actionstringYesOne of: pause, resume, delete, move_to_space, add_tags, remove_tags
space_idstringNoRequired when action is move_to_space
tagsstring[]NoRequired when action is add_tags or remove_tags
Request — Pause multiple jobs
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"
  }'
Response — 200 OK
{
  "data": {
    "succeeded": 3,
    "failed": 0,
    "errors": []
  }
}
Request — Move jobs to a space
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.

GET/v1/spaces

Retrieve all spaces in your account with job counts.

Request
curl "https://api.justrun.sh/v1/spaces" \
  -H "Authorization: Bearer jrun_sk_a1b2c3d4e5f6..."
Response — 200 OK
{
  "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"
    }
  ]
}
POST/v1/spaces

Create a new space. Spaces have a name and a default timezone that new jobs in the space inherit.

NameTypeRequiredDescription
namestringYesSpace name (max 50 chars)
timezonestringNoDefault IANA timezone for jobs in this space (default: UTC)
Request
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"
  }'
Response — 201 Created
{
  "data": {
    "id": "sp_nR4kLm8wQ2",
    "name": "Production",
    "timezone": "UTC",
    "status": "active",
    "job_count": 0,
    "created_at": "2026-04-10T15:00:00Z"
  }
}
PATCH/v1/spaces/:id

Update a space name or default timezone. Existing jobs in the space are not affected by timezone changes.

NameTypeRequiredDescription
idstringYesThe space ID (path parameter)
namestringNoUpdated space name
timezonestringNoUpdated default timezone
Request
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)" }'
Response — 200 OK
{
  "data": {
    "id": "sp_xyz789",
    "name": "Production (EU)",
    "timezone": "UTC",
    "status": "active",
    "job_count": 12,
    "updated_at": "2026-04-10T15:05:00Z"
  }
}
DELETE/v1/spaces/:id

Delete a space. The space must be empty (no jobs). Move or delete all jobs in the space first, or use the bulk move action.

NameTypeRequiredDescription
idstringYesThe space ID (path parameter)
Request
curl -X DELETE "https://api.justrun.sh/v1/spaces/sp_abc456" \
  -H "Authorization: Bearer jrun_sk_a1b2c3d4e5f6..."

Returns 204 No Content on success.

POST/v1/spaces/:id/pause

Pause 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.

Request
curl -X POST "https://api.justrun.sh/v1/spaces/sp_xyz789/pause" \
  -H "Authorization: Bearer jrun_sk_a1b2c3d4e5f6..."
Response — 200 OK
{
  "data": {
    "id": "sp_xyz789",
    "status": "paused",
    "jobs_paused": 12,
    "paused_at": "2026-04-10T15:10:00Z"
  }
}
POST/v1/spaces/:id/resume

Resume all previously active jobs in a paused space. Jobs that were individually paused before the space pause remain paused.

Request
curl -X POST "https://api.justrun.sh/v1/spaces/sp_xyz789/resume" \
  -H "Authorization: Bearer jrun_sk_a1b2c3d4e5f6..."
Response — 200 OK
{
  "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.

GET/v1/alerts/channels

List all configured alert channels for your account.

Request
curl "https://api.justrun.sh/v1/alerts/channels" \
  -H "Authorization: Bearer jrun_sk_a1b2c3d4e5f6..."
Response — 200 OK
{
  "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"
    }
  ]
}
POST/v1/alerts/channels

Create a new alert channel. The config object varies by channel type. See examples below for each type.

NameTypeRequiredDescription
typestringYesChannel type: slack, discord, email, or webhook
namestringYesDisplay name for the channel
configobjectYesType-specific configuration (see examples below)

Slack

Request
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

Request
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..."
    }
  }'

Email

Request
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

Request
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"
    }
  }'
Response — 201 Created
{
  "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"
  }
}
DELETE/v1/alerts/channels/:id

Delete an alert channel. Jobs using this channel will no longer send notifications to it.

NameTypeRequiredDescription
idstringYesThe channel ID (path parameter)
Request
curl -X DELETE "https://api.justrun.sh/v1/alerts/channels/ch_sL3kNm9wR2" \
  -H "Authorization: Bearer jrun_sk_a1b2c3d4e5f6..."

Returns 204 No Content on success.

POST/v1/alerts/channels/:id/test

Send a test notification through the channel to verify your configuration works correctly. Returns immediately with the delivery status.

NameTypeRequiredDescription
idstringYesThe channel ID (path parameter)
Request
curl -X POST "https://api.justrun.sh/v1/alerts/channels/ch_sL3kNm9wR2/test" \
  -H "Authorization: Bearer jrun_sk_a1b2c3d4e5f6..."
Response — 200 OK
{
  "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.

GET/v1/api-keys

List all API keys for your account. The full key value is not returned for security. Only the prefix and last 4 characters are shown.

Request
curl "https://api.justrun.sh/v1/api-keys" \
  -H "Authorization: Bearer jrun_sk_a1b2c3d4e5f6..."
Response — 200 OK
{
  "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"
    }
  ]
}
POST/v1/api-keys

Create a new API key. The full key is returned only in this response. Store it securely -- you cannot retrieve it again.

NameTypeRequiredDescription
namestringYesA descriptive name for the key (e.g., "CI/CD Pipeline")
Request
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" }'
Response — 201 Created
{
  "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.

DELETE/v1/api-keys/:id

Permanently revoke an API key. Any requests using this key will immediately return 401 Unauthorized. This action cannot be undone.

NameTypeRequiredDescription
idstringYesThe key ID (path parameter)
Request
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.

GET/v1/billing/usage

Returns your current plan, resource usage, and limits for the current billing period. Use this to monitor consumption and build custom dashboards.

Request
curl "https://api.justrun.sh/v1/billing/usage" \
  -H "Authorization: Bearer jrun_sk_a1b2c3d4e5f6..."
Response — 200 OK
{
  "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"
    }
  }
}
POST/v1/billing/checkout

Create a Stripe Checkout session to upgrade or change your plan. Returns a URL that redirects the user to Stripe's hosted checkout page.

NameTypeRequiredDescription
price_idstringYesStripe price ID for the target plan (e.g., price_maker_monthly)
Request
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" }'
Response — 200 OK
{
  "data": {
    "checkout_url": "https://checkout.stripe.com/c/pay/cs_live_a1b2c3...",
    "expires_at": "2026-04-10T16:30:00Z"
  }
}
POST/v1/billing/portal

Generate a Stripe customer portal URL where the user can manage their payment methods, view invoices, and cancel their subscription.

Request
curl -X POST "https://api.justrun.sh/v1/billing/portal" \
  -H "Authorization: Bearer jrun_sk_a1b2c3d4e5f6..."
Response — 200 OK
{
  "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.

POST/v1/migrate/easycron

Import all jobs from your EasyCron account. Provide your EasyCron API key and JustRun fetches your jobs, converts them, and creates them in your account.

NameTypeRequiredDescription
api_keystringYesYour EasyCron API key
space_idstringNoPlace all imported jobs in this space
Request
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"
  }'
Response — 200 OK
{
  "data": {
    "imported": 14,
    "skipped": 1,
    "errors": [
      {
        "original_name": "Legacy Cron #7",
        "reason": "Unsupported cron expression: @reboot"
      }
    ]
  }
}
POST/v1/migrate/cronjob-org

Import all jobs from your cron-job.org account using their API key. JustRun maps cron-job.org fields to JustRun equivalents automatically.

NameTypeRequiredDescription
api_keystringYesYour cron-job.org API key
space_idstringNoPlace all imported jobs in this space
Request
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"
  }'
Response — 200 OK
{
  "data": {
    "imported": 8,
    "skipped": 0,
    "errors": []
  }
}
POST/v1/migrate/json

Import 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.

NameTypeRequiredDescription
jobsobject[]YesArray of job objects (see schema below)
space_idstringNoPlace all imported jobs in this space
Request
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"]
      }
    ]
  }'
Response — 200 OK
{
  "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.

URL
https://api.justrun.sh/badges/job_7kR3mNxQ2v/status.svg

Uptime Badge

Shows the uptime percentage for the last 30 days.

URL
https://api.justrun.sh/badges/job_7kR3mNxQ2v/uptime.svg

Markdown Embed

Copy-paste these into your README or documentation.

Markdown
<!-- Status badge -->
![Job Status](https://api.justrun.sh/badges/job_7kR3mNxQ2v/status.svg)

<!-- Uptime badge -->
![Uptime](https://api.justrun.sh/badges/job_7kR3mNxQ2v/uptime.svg)

<!-- Badge linking to the dashboard -->
[![Job Status](https://api.justrun.sh/badges/job_7kR3mNxQ2v/status.svg)](https://justrun.sh/dashboard/jobs/job_7kR3mNxQ2v)

HTML Embed

HTML
<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.

Webhook 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

EventFired When
job.executedA job execution completes (success or failure)
job.failedA job execution fails (non-2xx response or timeout) after all retries
job.recoveredA 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>

Node.js Verification Example
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.

Run with npx
npx @justrun/mcp

Configuration 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).

claude_desktop_config.json
{
  "mcpServers": {
    "justrun": {
      "command": "npx",
      "args": ["@justrun/mcp"],
      "env": {
        "JUSTRUN_API_KEY": "jrun_sk_a1b2c3d4e5f6..."
      }
    }
  }
}

Configuration for Claude Code

.mcp.json (project root)
{
  "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:

Cursor MCP Settings
{
  "name": "justrun",
  "command": "npx",
  "args": ["@justrun/mcp"],
  "env": {
    "JUSTRUN_API_KEY": "jrun_sk_a1b2c3d4e5f6..."
  }
}

Available Tools

ToolDescription
list_jobsList all cron jobs with optional filtering
create_jobCreate a new cron job from natural language or explicit config
get_jobGet details for a specific job
update_jobUpdate job configuration
delete_jobDelete a job
run_jobTrigger an immediate execution
pause_jobPause a job
resume_jobResume a paused job
get_executionsView execution history for a job
list_spacesList all spaces
create_spaceCreate a new space
get_usageCheck 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

PlanGeneral (req/min)/run (req/min)
Free605
Maker30010
Pro1,00030
Scale5,000100

Rate Limit Headers

Every API response includes rate limit headers so you can monitor your consumption programmatically.

HeaderDescription
X-RateLimit-LimitYour plan's requests-per-minute limit
X-RateLimit-RemainingRequests remaining in the current window
X-RateLimit-ResetUnix timestamp when the window resets
Retry-AfterSeconds 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.

429 Too Many Requests
{
  "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.

JS

Node.js / TypeScript

@justrun/sdk

Coming Soon
PY

Python

justrun-python

Coming Soon
GO

Go

go-justrun

Coming Soon

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.