Documentation menu

Cron and background jobs

Errors → Monitors observes scheduled jobs and manual background executions. It does not run your job or replace its scheduler. A missing report means the platform did not receive evidence; it does not prove that the job failed.

Create a monitor in the correct environment. Start in Manual / on demand mode, verify receipt, then enable a cron schedule if every occurrence should run. Manual mode checks reported failures, duration and overlap without missed-start alerts.

Verify receipt

Use a secret ingest key from the monitor's application and environment. Public keys, Management tokens and requests with an Origin header are refused. Keep this key on your server, never in browser code, URLs or logs.

export SPM_HOST=https://your-control-plane.example
# Set SPM_SECRET_KEY through your deployment's secret store.
curl "$SPM_HOST/api/v1/ingest/cron" \
  -H "Authorization: Bearer $SPM_SECRET_KEY" \
  -H "Content-Type: application/json" \
  --data '{"monitorId":"YOUR_MONITOR_UUID","status":"test"}'

A 202 response containing accepted: true confirms receipt. Refresh the monitor to see its last receipt. This test works while paused and never creates an execution, satisfies a deadline or sends a notification.

Node.js

import { startCronRun, testCronMonitor } from "@saaspro/node/cron";

const options = {
  key: process.env.SPM_SECRET_KEY!,
  host: process.env.SPM_HOST,
  monitorId: "YOUR_MONITOR_UUID",
};
const receiptAccepted = await testCronMonitor(options);
const run = startCronRun(options);
const startAccepted = await run.started;
try {
  await runYourJob();
  const completionAccepted = await run.success();
  // If false, retry run.success() on this same object.
} catch (error) {
  await run.failure();
  throw error;
}

The helper reports delivery as a boolean; it does not turn a transport failure into a job failure. Repeated completion calls share an in-flight request and retain the exact final payload for retries. Changing success to failure, or failure to success, returns false. Each execution has a UUID and an immutable start time. Start and completion can arrive out of order. Hosts require HTTPS outside validated loopback addresses; redirects are refused. Each request times out after five seconds. No periodic thread or interval is created.

Python

import os
from saaspro import SaaSProMax
from saaspro.cron import CronMonitor

with SaaSProMax(os.environ["SPM_SECRET_KEY"], host=os.environ["SPM_HOST"]) as client:
    monitor = CronMonitor(client, "YOUR_MONITOR_UUID")
    receipt_accepted = monitor.test()
    with monitor.run() as execution:
        run_your_job()
    completion_accepted = execution.delivered

The context manager reports success on normal exit and failure on exceptions, then preserves the original job exception. monitor.start() exposes explicit success() and failure() methods for custom lifecycles. start_delivered and delivered describe transport confirmation. Completion retries preserve the original final timestamp. This helper uses the Python client's bounded, redirect-refusing secret-key transport and refuses to finish an execution inherited across a process fork.

HTTP execution contract

POST /api/v1/ingest/cron with a Bearer secret ingest key. The closed JSON body is at most 4 KiB:

{
  "monitorId": "MONITOR_UUID",
  "runId": "EXECUTION_UUID",
  "startedAt": "2026-09-08T02:00:00.000Z",
  "scheduledAt": "2026-09-08T02:00:00.000Z",
  "status": "start"
}

For completion, reuse the identity and set status to success or failure, with finishedAt in UTC. Only completion accepts finishedAt. Never include job arguments, exception messages, personal identifiers or private context. Completion can recover a lost start. An identical final retry, or a delayed start after completion, acknowledges the existing outcome. Conflicting identity, key or terminal outcome returns 409. Keys can write execution observations; they cannot create, edit, pause, delete or read private monitor configuration.

scheduledAt is optional for cron monitors and forbidden for manual monitors. Supply the exact intended UTC occurrence for delayed jobs. Without it, the latest schedule occurrence at startedAt is selected. Starts before activation, starts more than 48 hours old (or outside shorter application retention), times more than one minute into the future, and durations beyond 24 hours are refused. Pausing or changing schedule/timezone starts a new observation window; existing in-flight runs become cancelled and cannot be resumed by a retry.

Response Meaning
202, accepted true Receipt or execution persisted, including exact retries
400 Invalid body, timestamp or schedule binding
401 Missing, unknown or revoked key
403 Wrong key capability, browser origin or disabled Errors module
404 Monitor is not in this key's application/environment
409 Paused monitor, old observation window or conflicting execution
413 Body exceeds 4 KiB
429 Ingest rate or monitor storage/admission limit
503 Storage unavailable; retry the same execution payload

Schedule and incident semantics

Schedules use five numeric cron fields with *, commas, ranges and steps. Specify day-of-month or day-of-week, not both. Seconds, aliases and randomized fields are not supported. The policy shows five upcoming occurrences in the selected IANA timezone and UTC; compare these to the actual scheduler before activation.

Timezone calculation uses cron-parser. A daily 01:30 in America/New_York occurs once on the autumn fallback date, at the first 01:30. A daily 02:30 on the spring-forward date shifts to 03:30 local time. UTC schedules avoid seasonal shifts. Other scheduler implementations can differ; the preview is the platform's authoritative interpretation.

Each scheduled occurrence is checked independently after its start grace deadline. A run starting after grace remains late even if it completes successfully. A duration incident means no completion arrived within the configured limit or a reported completion exceeded it. Overlap incidents use peak overlap of reported execution intervals and do not terminate the job. A run without a completion is treated as potentially active for up to 48 hours; lost completion reports can therefore limit concurrency evidence. Duration limits are captured at run admission; later policy edits affect new runs.

There is at most one active incident per monitor and kind: missed start, reported failure, duration or overlap. Repeated observations extend the incident. A newer, timely successful execution within its duration limit can recover older incidents; an older success cannot hide a newer failure. Duration incidents remain active while a retained running execution exceeds its captured limit; overlap incidents remain active while observed running concurrency exceeds the configured limit. These conditions do not block recovery of independent failure or missed-start incidents. Operators may acknowledge or manually resolve with a reason. Acknowledgement does not silence new incident kinds.

Pausing suspends execution pings, scheduling and pending notification dispatch. Manual mode is active monitoring without a schedule. Never-reported, cancelled, running and unknown outcomes are distinct from success. A started execution without a final report ages to unknown after 48 hours; no crash outcome is inferred.

Notifications and operating limits

Select up to five existing application notification channels. Changing recipients requires Metrics write access; monitor changes and incident actions require Errors write and a passkey verified within ten minutes. Provider credentials continue through the existing vault-backed notification adapters. With no channels, incidents remain in the console.

Opening and recovering an incident queues one notification per selected channel through the existing job outbox. Current module, pause, channel selection and enabled-channel state are checked before dispatch. Claimed sends are not automatically repeated: a worker interruption can make external delivery uncertain. The monitor exposes pending, sending, sent, failed, skipped and unknown outcomes. After a ten-minute stale claim, retention maintenance marks it unknown; external delivery cannot be guaranteed exactly once.

Each environment supports 100 monitors. Each monitor accepts at most 10,000 new runs per UTC day, 100 active runs and 100,000 retained runs. Exact retries do not consume admission quota. Existing runs may finish when new admissions are capped. The normal ingest-key rate limiter also applies.

The worker evaluates up to 50 monitors every 30 seconds, oldest evaluation first, and at most 60 overdue schedule occurrences per monitor/pass. Large backlogs remain explicit in the console. Windows whose execution evidence expired before evaluation are disclosed as unknown coverage gaps, never fabricated missed runs. History follows application retention up to 30 days; hourly maintenance processes 50 monitors, deleting at most 5,000 expired runs and resolved incidents per monitor. Daily quota rows persist for three UTC dates. Resolved incidents and their delivery records expire after 30 days; active incidents remain until resolved.

GET /api/v1/apps/{app}/errors/monitors?env=production reads the scoped overview with an authorized Management token and Errors read. Add monitorId and a 1–4000 page for 25 retained runs, up to 50 incidents and 25 delivery records. Read queries have an eight-second statement limit. This read capability is separate from secret-key check-ins.