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:
| Field | Notes |
|---|---|
id | Server-assigned UUID. |
job | The parent job's slug. |
job_version | The job's version at execution time — so history can flag "ran against v7, job is now v9." |
environment | The 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. |
trigger | Why 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_of | The source run's UUID when trigger is RERUN; null otherwise. |
retry | Retry-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_for | The intended fire time. null for manual runs and reruns. |
status | PENDING, RUNNING, SUCCEEDED, FAILED, or CANCELED. |
started_at / finished_at | Execution start and end. |
pending_duration_ms / run_duration_ms / total_duration_ms | Read-only timing, computed. |
failure_reason | Set only when FAILED: TIMEOUT, CONNECTION_ERROR, NON_SUCCESS_STATUS, SSRF_BLOCKED, QUOTA_EXCEEDED, or WORKER_LOST. |
error | Optional free-text detail. |
request | Snapshot of the request actually sent — method, URL, headers with values redacted, body. Forensics only. |
result | The 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:
run—POST /api/v1/jobs/{id}/actions/runspawns 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 (409otherwise). 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.cancel—POST /api/v1/runs/{id}/actions/cancelcancels aPENDINGorRUNNINGrun (an already-terminal run returns409, an unknown run404). Cancel means "stop tracking," not "guaranteed it didn't happen": a run that has already startedRUNNINGstill counts toward your monthly run allotment even if you cancel it — its request may already have been dispatched — while a run cancelled while stillPENDINGdoes not count.rerun—POST /api/v1/runs/{id}/actions/rerunspawns a new run (trigger = RERUN) using the job's current configuration. Returns404for an unknown run and409if the parent job has been deleted. Rerun does not replay the source run's request byte-for-byte; the sourcerequestsnapshot 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
FAILEDwithfailure_reason = QUOTA_EXCEEDED; no HTTP call is made and further writes return402. 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 withQUOTA_EXCEEDED.
overage_budget_cents— required whenCAPPED, 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/httpsschemes 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.
Related
- Smpl Jobs overview — the job model, scheduling, and environments
- Retry policies — automatic re-attempts for failed runs
- Subscriptions and entitlements — plans, limits, and metering
- API Reference — Jobs — full schema and filters

