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 supplyprojectIdper 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 viaPOST /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
| Call | Returns |
|---|---|
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}/usage | token/cost attribution, per substage |
GET /v1/runs/{id}/artifacts | files 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}/cancel | request 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/shellstages run with no pool.
See also: Author a workflow · Configuration.