Integrate with the /v1 API

How your app submits and tracks runs. The whole surface is small and JSON.

Authenticating

Send Authorization: Bearer <token>. Two kinds of token:

  • Service token (POND_SERVICE_TOKEN) — a single trusted first-party credential that acts across all projects. You supply projectId per request. Best for your own backend talking to pond.
  • Per-project API key (pond_pk_…) — scoped to one project; pond infers the project from the key. Best for third-party consumers. Mint via POST /v1/keys (admin, Auth0-gated); the plaintext is shown once.

401 on a missing/garbled header; a uniform 403 on an unknown/revoked token.

Submitting a run

POST /v1/runs:

{
  "projectId": "…uuid…",          // required for a service token; omit with a project key
  "trigger": "api",
  "definition": { "stages": [ … ] },   // the workflow to run (see Author a workflow)
  "sources":   [ { "label": "app", "kind": "git_https", "config": {"url": "…"} } ],
  "sha": "optional commit sha",
  "context": { }                   // opaque metadata echoed back to you
}

Returns the created run (with id). definition is executed from a snapshot, so the run is reproducible regardless of later changes on your side.

Tracking it

CallReturns
GET /v1/runs/{id}run + its stages (status, progress)
GET /v1/runs/{id}/logs?since=<cursor>&limit=incremental log lines; poll with the last id as since
GET /v1/runs/{id}/usagetoken/cost attribution, per substage
GET /v1/runs/{id}/artifactsfiles the run produced
GET /v1/runs/{id}/artifacts/{name}one artifact’s bytes
GET /v1/runs?projectId=&status=&limit=list runs
POST /v1/runs/{id}/cancelrequest cancellation (cooperative)

A typical consumer: submit, then poll GET /v1/runs/{id} + …/logs?since= every ~1.5s until the run is terminal (done / failed / cancelled), then pull …/artifacts for the result.

Example

TOKEN=$POND_SERVICE_TOKEN
RUN=$(curl -sX POST $POND/v1/runs -H "Authorization: Bearer $TOKEN" \
  -H 'content-type: application/json' \
  -d '{"projectId":"…","definition":{"stages":[{"key":"x","name":"x","executor":{"kind":"noop"}}]}}' \
  | jq -r .id)

# poll
while :; do
  s=$(curl -s $POND/v1/runs/$RUN -H "Authorization: Bearer $TOKEN" | jq -r .run.status)
  echo "$s"; [ "$s" = done ] || [ "$s" = failed ] && break; sleep 1.5
done

Preflight (catch setup gaps early)

POST /v1/runs/preflight dry-runs a definition and tells you what would stop it from running well — without creating a run:

{ "ok": false, "problems": [
  { "stage": "fix", "severity": "error", "code": "sandbox_invalid",
    "message": "stage 'fix': stage declares no sandbox profile; …" },
  { "stage": "fix", "severity": "warning", "code": "no_capable_worker",
    "message": "stage 'fix': no attached worker can serve it …" }
] }

Submit applies the same checks: a blocking error (e.g. an invalid sandbox block) is rejected with 422 and the problems list, so you never create a doomed run. Warnings (no capable worker yet, an unregistered harness) don’t block — they’re written to the run’s log (source: "preflight"), visible via …/logs. Use the dry-run endpoint to validate before submitting, or in a UI to show “you’re not ready: …”.

If a stage still has no capable worker once it dispatches, the run fails fast with that actionable error after a short grace (POND_DISPATCH_NO_WORKER_GRACE_SEC, default 90s) — it won’t hang on the full stage timeout.

Notes

  • Artifact names are validated server-side — you can’t traverse out of the run’s output directory.
  • A swarm (agent) stage needs a worker pool attached, or it will sit queued. noop/builtin/shell stages run with no pool.

See also: Author a workflow · Configuration.