Skip to content

API workflow

The Axum API is mounted at /api/v2. Health is public; operational resources require a bearer token, and mutating administrative surfaces require a superuser. Output publication receipts return through the authenticated remote control plane rather than a graph-facing HTTP callback.

Authenticate

Use the password setup printed once. It is not stored in .env.

export BASE=http://127.0.0.1:18080
export ADMIN_USER="${ADMIN_USER:-admin}"
export ADMIN_PASSWORD="${ADMIN_PASSWORD:?set to the password setup printed}"
LOGIN_BODY=$(jq -n \
  --arg username "$ADMIN_USER" \
  --arg password "$ADMIN_PASSWORD" \
  '{username:$username,password:$password}')
export TOKEN=$(curl -fsS -X POST "$BASE/api/v2/login" \
  -H 'Content-Type: application/json' \
  -d "$LOGIN_BODY" \
  | jq -er .access_token)
export AUTH="Authorization: Bearer $TOKEN"

curl -fsS "$BASE/api/v2/user/me" -H "$AUTH" | jq .

Access and refresh tokens carry jti claims. Refresh rotates the refresh token; logout blacklists token hashes. Public user responses never include password hashes.

Resource order

Project and profile

curl -fsS -X POST "$BASE/api/v2/project-configs" \
  -H "$AUTH" -H 'Content-Type: application/x-yaml' \
  --data-binary @config/examples/minimal_survey.v2.yaml | jq .

curl -fsS -X POST "$BASE/api/v2/deployment-profiles" \
  -H "$AUTH" -H 'Content-Type: application/json' \
  -d @profile.json | jq .

Project uploads create immutable versions. Profile responses are redacted and future executions pin the selected revision.

List installed Slurm SSH credential slots (names and file presence only; never key material) before choosing deployment.ssh_credential:

curl -fsS "$BASE/api/v2/slurm/credentials" -H "$AUTH" | jq .
curl -fsS "$BASE/api/v2/slurm/credentials/hpc" -H "$AUTH" | jq .

Init, import, and copy-id remain CLI. Empty credential roots return { "slots": [] }.

Source and discovery

SOURCE=$(curl -fsS -X POST "$BASE/api/v2/sources" \
  -H "$AUTH" -H 'Content-Type: application/json' \
  -d '{"project_module":"minimal_survey","source_identifier":"source-1","enabled":true}')
SOURCE_ID=$(jq -r .uuid <<<"$SOURCE")

curl -fsS -X POST "$BASE/api/v2/sources/discover" \
  -H "$AUTH" -H 'Content-Type: application/json' \
  -d '{"project_module":"minimal_survey","source_identifier":"source-1"}' | jq .

curl -fsS "$BASE/api/v2/sources/$SOURCE_ID/status" -H "$AUTH" | jq .
curl -fsS "$BASE/api/v2/sources/$SOURCE_ID/metadata" -H "$AUTH" | jq .
curl -fsS "$BASE/api/v2/sources/$SOURCE_ID/events" -H "$AUTH" | jq .

sources/discover marks matching enabled sources for rediscovery. The scheduler and workers perform the durable claim/query/persistence path asynchronously.

Metadata is grouped by the neutral group_key field. Each metadata_json payload contains records; every prepared record has a record_id. Those names are stable Core API fields regardless of how a project renames collections in its emitted manifest.

Prepare and execute

Use the same body for preflight and creation:

cat > /tmp/execution.json <<'JSON'
{
  "project_module": "minimal_survey",
  "sources": [
    {"source_identifier": "source-1", "groups": ["group-1"]}
  ],
  "archive_name": "catalog",
  "deployment_profile_name": "local-rest"
}
JSON

curl -fsS -X POST "$BASE/api/v2/executions/prepare" \
  -H "$AUTH" -H 'Content-Type: application/json' \
  -d @/tmp/execution.json | jq .

EXEC=$(curl -fsS -X POST "$BASE/api/v2/executions" \
  -H "$AUTH" -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: operator-intent-20260822-001' \
  -d @/tmp/execution.json)
EXEC_ID=$(jq -r .uuid <<<"$EXEC")

curl -fsS -X POST "$BASE/api/v2/executions/$EXEC_ID/execute" \
  -H "$AUTH" -H 'Content-Type: application/json' \
  -d '{"do_stage":false,"do_submit":false}' | jq .

Creation keys are scoped to the authenticated user and one request body. The first request returns 201, an exact retry returns the same execution with 200, and reuse with different content returns 409. Persist the key before sending the request so a lost response can be resumed safely.

Execution start is intrinsically idempotent for the execution UUID. Exact queued, running, and completed retries return 202 with the same job ID; different do_stage/do_submit flags return 409. do_submit:false is a preparation-only boundary. Use do_submit:true only after the pinned profile doctor passes and real backends are deliberately enabled.

sources[].groups is optional; omit it to select all ready groups for that source. A supplied list is validated against persisted group_key values during preparation.

Inspect exact state instead of polling only the compact status:

curl -fsS "$BASE/api/v2/executions/$EXEC_ID/status" -H "$AUTH" | jq .
curl -fsS "$BASE/api/v2/executions/$EXEC_ID/ledger-snapshot" -H "$AUTH" | jq .
curl -fsS "$BASE/api/v2/executions/$EXEC_ID/observations" -H "$AUTH" | jq .
curl -fsS "$BASE/api/v2/executions/$EXEC_ID/artifacts" -H "$AUTH" | jq .
curl -fsS "$BASE/api/v2/executions/$EXEC_ID/events" -H "$AUTH" | jq .

When the pinned project requires durable output verification (in progress), use the terminal beampipe-publish application from standalone beampipe-palette. The trusted Slurm path retrieves its canonical receipt over authenticated SFTP after scheduler success, so the remote graph needs no route or credential back to Core. There is no publisher callback or execution token to put in graph or scheduler artifacts. The complete pull, inventory, ordering, and idempotent-retry contract is in Output verification (in progress).

Clean-break field migration

Core v2 no longer accepts project-specific selection aliases. API clients and saved request bodies must use:

Resource Field
Execution source selection sources[].groups
Archive metadata response group_key
Prepared item identity record_id
Stored items within a group metadata_json.records

Provider terms may still appear as ordinary project data or explicitly renamed manifest output fields. They do not change request or persistence schemas.

Contract

The generated API schema is the field-level source of truth. Export it after Rust request/response changes:

beampipe openapi export > openapi.json
cp openapi.json boilerplate_docs/openapi.json

Swagger UI and JSON are also served by the running API at /api/v2/docs and /api/v2/openapi.json when documentation is enabled.