Skip to content

Running jobs

Runs and history

Every execution produces a run. A run records why it ran, when, how long it took, and the full request/response detail:

FieldNotes
idServer-assigned UUID.
jobThe parent job's slug.
job_versionThe job's version at execution time — so history can flag "ran against v7, job is now v9."
environmentThe environment the run executed in. A scheduled run inherits the firing job-environment; a manual run uses the environment it was triggered in; a rerun copies its source run's environment.
triggerWhy the run exists: SCHEDULE, MANUAL (Run now), RERUN (the manual rerun action), or RETRY (an automatic retry of a failed run, driven by the job's retry policy).
rerun_ofThe source run's UUID when trigger is RERUN; null otherwise.
retryRetry-chain position, present only when trigger is RETRY: { "of": <uuid>, "attempt": <int> }, where of is the id of the chain's original run (the first attempt that failed) and attempt is which retry this run is (1 for the first retry, 2 for the second, and so on). null otherwise.
scheduled_forThe intended fire time. null for manual runs and reruns.
statusPENDING, RUNNING, SUCCEEDED, FAILED, or CANCELED.
started_at / finished_atExecution start and end.
pending_duration_ms / run_duration_ms / total_duration_msRead-only timing, computed.
failure_reasonSet only when FAILED: TIMEOUT, CONNECTION_ERROR, NON_SUCCESS_STATUS, SSRF_BLOCKED, QUOTA_EXCEEDED, or WORKER_LOST.
errorOptional free-text detail.
requestSnapshot of the request actually sent — method, URL, headers with values redacted, body. Forensics only.
resultThe response. For http: { status, headers, body, body_truncated, body_bytes }.

List runs with GET /api/v1/runs, scoped to one job with filter[job]={slug} and to one or more environments with filter[environment]={env} (omit it to cover every environment you can access). Scope by why the run exists with filter[trigger]SCHEDULE, MANUAL, RERUN, or RETRY, or a comma-separated list to match any (e.g. SCHEDULE,RETRY to see a job's automatic runs). Pass last_run_only=true (a bare query parameter, not a filter[...]) to collapse the result to the latest completed run per job-and-environment — completed meaning a terminal state (succeeded, failed, or canceled), so a job currently running still shows its previous completed result. The other filters apply first, then the results collapse. The runs list is cursor-paginated.

Response bodies are captured inline, capped at 64 KB. When the response exceeds that, body_truncated is true and body_bytes records the original size.

At-least-once-attempt

If a worker crashes mid-execution, the run is recovered and marked FAILED with failure_reason = WORKER_LOST — even though the HTTP request may already have reached your endpoint. Likewise, cancelling a RUNNING run stops smplkit from tracking it, but the request may already be in flight. Make the endpoints your jobs call idempotent.

Triggering and controlling runs

Three actions operate on runs:

  • runPOST /api/v1/jobs/{id}/actions/run spawns one immediate run (trigger = MANUAL), naming the environment it executes in with an optional request body {"environment": "..."}. Omit the body when the target is unambiguous — when the job is enabled in exactly one environment, or your credential is scoped to a single environment, that environment is used. The environment must be one the job is enabled in (409 otherwise). The job's schedule and enablement are untouched, so a recurring job's cadence is unaffected — and this is the primary way to run a manual job.
  • cancelPOST /api/v1/runs/{id}/actions/cancel cancels a PENDING or RUNNING run (an already-terminal run returns 409, an unknown run 404). Cancel means "stop tracking," not "guaranteed it didn't happen": a run that has already started RUNNING still counts toward your monthly run allotment even if you cancel it — its request may already have been dispatched — while a run cancelled while still PENDING does not count.
  • rerunPOST /api/v1/runs/{id}/actions/rerun spawns a new run (trigger = RERUN) using the job's current configuration. Returns 404 for an unknown run and 409 if the parent job has been deleted. Rerun does not replay the source run's request byte-for-byte; the source request snapshot is the forensic record of what was sent.

A rerun is a manual, on-demand action you invoke; an automatic retry policy re-attempt is distinct — it happens on its own when a run fails in a way the policy matches and produces a run with trigger = RETRY rather than RERUN.

Usage, quota, and pricing

Smpl Jobs is on the same four-tier plan ladder as every other smplkit product (Free / Standard / Pro / Enterprise). Each plan includes a monthly run allotment, plus structural limits — how many permanent jobs (recurring + manual) you can have, the minimum interval between recurring fires, the maximum per-request timeout, and how long run history is retained.

Runs are metered:

  • Paid plans meter past the included allotment. Once you exceed your monthly run allotment, each additional run is billed at a per-run overage rate rather than blocked — so a traffic spike costs a little more instead of hitting a wall.
  • The Free plan hard-stops. A free account that exhausts its included runs gets the run marked FAILED with failure_reason = QUOTA_EXCEEDED; no HTTP call is made and further writes return 402. Free never incurs an overage charge.

Check your current-period counters — runs used against the allotment, overage incurred, and active-job count — with GET /api/v1/usage?filter[period]=current. Your plan and usage are also visible under Account → Subscriptions in the console.

Controlling overage

You can constrain overage independently of your plan through account settings (GET / PUT /api/v1/accounts/current/settings, under the jobs key):

  • overage_policy:
    • ALLOW (default) — meter and bill past the included allotment.
    • HARD_STOP — never exceed the included allotment, even though your plan would permit overage.
    • CAPPED — allow overage until the cumulative overage cost this month would exceed your budget, then reject further runs with QUOTA_EXCEEDED.
  • overage_budget_cents — required when CAPPED, omitted otherwise.

See Subscriptions and entitlements for the metered-allotment model shared with Smpl Audit.

Outbound request safety

Jobs call URLs you supply, from inside smplkit's network — a classic SSRF surface — so every request is validated before it goes out:

  • Only http/https schemes and allowed ports are permitted.
  • Any URL that resolves to a non-public address (private ranges, loopback, link-local including 169.254.169.254, and the IPv6 equivalents) is rejected.
  • Redirects are disabled, and the connection is pinned to the validated IP so DNS can't be rebound between validation and connection.

A blocked request fails the run with failure_reason = SSRF_BLOCKED. Configuration is never silently sanitized — if a URL won't pass, the run fails loudly rather than calling a different address.

Wiping an account

Jobs and their run history are cleared as part of the platform-level account wipe — POST /api/v1/accounts/current/actions/wipe on the app service. There is no jobs-only wipe endpoint. The wipe is irreversible; the console renders a confirmation dialog before submitting it.