jscheduler

Docs

jscheduler durably stores one-time, interval, and cron schedules. When an occurrence is due, it sends your JSON payload to the endpoint selected by that schedule.

Quickstart

Install and authenticate

curl -fsSL https://cli.jscheduler.com/install.sh | bash
jscheduler auth login --api-key jsk_your_key

Create an endpoint

Every schedule explicitly references one endpoint ID. Tags are metadata, not routing rules.

jscheduler endpoints create \
  --name lifecycle \
  --url https://example.com/webhooks/jscheduler

# Save the endpoint ID printed by this command.

Public endpoints may not resolve to loopback, private, link-local, or other non-global IP ranges.

Create a schedule

jscheduler events create \
  --name trial.expiring \
  --endpoint ENDPOINT_ID \
  --kind ONE_TIME \
  --fire-at 2030-07-13T09:00:00Z \
  --tags lifecycle \
  --payload '{"accountId":"acct_42"}'

The CLI is optional; the HTTP API uses the same organization API key:

curl https://api.jscheduler.com/v1/schedules \
  -H "Authorization: Bearer $JSCHEDULER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "endpointId": "ENDPOINT_ID",
    "eventName": "trial.expiring",
    "externalId": "trial_acct_42",
    "tags": ["lifecycle"],
    "payload": {"accountId": "acct_42"},
    "schedule": {
      "kind": "ONE_TIME",
      "fireAt": "2030-07-13T09:00:00Z"
    }
  }'

Schedules

ONE_TIME
Requires a future fireAt timestamp.
INTERVAL
Requires intervalSeconds and startAt.
CRON
Requires a five-field expression, evaluated in UTC.

One-time and interval timestamps have one-second public resolution. Cron has minute resolution.

Delivery contract

jscheduler sends an HTTP POST with Content-Type: application/json and an Idempotency-Key header equal to occurrenceId.

{
  "occurrenceId": "84eef20d-6cdd-4f38-b37c-b750d8a76c3f",
  "scheduleId": "8c9937cc-3467-41a4-9811-11b97a66c2a9",
  "scheduledFor": "2030-07-13T09:00:00Z",
  "eventName": "trial.expiring",
  "externalId": "trial_acct_42",
  "tags": ["lifecycle"],
  "payload": {"accountId": "acct_42"},
  "attempt": 1
}
RECEIVER_IDEMPOTENT
Retry retryable and ambiguous failures; the receiver deduplicates the idempotency key.
NO_DUPLICATE_SENDS
An ambiguous result becomes UNCERTAIN until you explicitly redrive it.

History and recovery

jscheduler events deliveries <schedule-id>
jscheduler events redrive <delivery-id>

Redrive preserves the occurrence ID so receiver-side idempotency remains effective.

API reference

Send Authorization: Bearer jsk_… on each organization request.

GET/v1/endpoints

List active delivery endpoints

POST/v1/endpoints

Create a delivery endpoint

DELETE/v1/endpoints/{endpointId}

Disable an endpoint

GET/v1/schedules

List and search schedules

POST/v1/schedules

Create a schedule

GET/v1/schedules/{scheduleId}

Read a schedule

DELETE/v1/schedules/{scheduleId}

Delete a schedule

POST/v1/schedules/{scheduleId}/disable

Pause future occurrences

POST/v1/schedules/{scheduleId}/enable

Resume a schedule

GET/v1/schedules/{scheduleId}/occurrences

Read occurrence history

GET/v1/deliveries?scheduleId={scheduleId}

Read delivery history

GET/v1/deliveries/{deliveryId}/attempts

Read attempt history

POST/v1/deliveries/{deliveryId}/redrive

Redrive a terminal delivery