Capabilities
Workflows
Run user-defined multi-step automations over the vault. Workflows are authored in the dashboard and triggered from your backend via the public API.
Lifecycle
Every workflow run follows the same pattern:
- Call
capabilitywith the workflow ID and optional inputs. - Get back a
field. The run starts in statestate. - Poll
capabilityevery 2–5 seconds. - Run states transition:
states. - When
condition, thefieldfield contains the result.
Authoring is dashboard-only
The public API does not expose workflow creation or editing. Build the workflow once in the dashboard, copy its ID, then run it from code.
workflows.list
GET
/api/v2/workflowsList workflows available to the API key's user.
scope: workflows:readcost: 1
Request
bashcurl -sS "https://api.alloovium.com/api/v2/workflows?limit=25" \ -H "Authorization: Bearer $ALLOOVIUM_API_KEY"
Response
json{ "data": [ { "id": "wf_1f02d9ef-...", "name": "Weekly submittal review", "description": "Scan new submittals for noncompliance and summarise.", "enabled": true, "trigger_type": "manual", "created_at": "2026-03-12T08:00:00+00:00", "updated_at": "2026-04-01T12:14:00+00:00" } ], "next_cursor": null, "has_more": false }
workflows.run
POST
/api/v2/workflows/{workflow_id}/runsKick off a new run of a workflow. Returns a run_id you poll for status.
scope: workflows:writecost: 25
Request body
json{ "inputs": { "start_date": "2026-04-01", "end_date": "2026-04-08" }, "project_id": "8f2e3c1a-..." }
field— optional map of the workflow's declared input variables.field— optional scope. Required for workflows that referenceblockblocks.
bashcurl -sS https://api.alloovium.com/api/v2/workflows/wf_1f02d9ef-.../runs \ -H "Authorization: Bearer $ALLOOVIUM_API_KEY" \ -H "Idempotency-Key: weekly-review-2026-04-08" \ -H "Content-Type: application/json" \ -d '{ "inputs": {"start_date": "2026-04-01", "end_date": "2026-04-08"}, "project_id": "8f2e3c1a-..." }'
Response
json{ "run_id": "run_c1d2e3f4-...", "workflow_id": "wf_1f02d9ef-...", "status": "pending", "started_at": null }
Attach an Idempotency-Key
Workflow runs cost 25 tokens and produce side effects. An idempotency key ensures a dropped response does not kick off the same run twice — see Idempotency.
workflows.get_run_status
GET
/api/v2/workflows/runs/{run_id}Poll a run's progress, stage, outputs, and error state.
scope: workflows:readcost: 1
Response (running)
json{ "run_id": "run_c1d2e3f4-...", "workflow_id": "wf_1f02d9ef-...", "status": "running", "progress": 42, "stage": "Analysing submittals", "inputs": {"start_date": "2026-04-01", "end_date": "2026-04-08"}, "outputs": null, "error_message": null, "started_at": "2026-04-08T10:12:05+00:00", "completed_at": null, "created_at": "2026-04-08T10:12:00+00:00" }
Response (completed)
json{ "run_id": "run_c1d2e3f4-...", "workflow_id": "wf_1f02d9ef-...", "status": "completed", "progress": 100, "stage": "done", "inputs": {"start_date": "2026-04-01", "end_date": "2026-04-08"}, "outputs": { "summary": "5 submittals reviewed, 2 flagged for non-compliance.", "items": [ {"document_id": "4a1c2b3d-...", "status": "non-compliant", "reason": "..."} ] }, "error_message": null, "started_at": "2026-04-08T10:12:05+00:00", "completed_at": "2026-04-08T10:14:18+00:00", "created_at": "2026-04-08T10:12:00+00:00" }
Status states
| Status | Terminal? | Notes |
|---|---|---|
| pending | no | Queued; not yet claimed by a worker. |
| running | no | Currently executing. progress + stage update as it runs. |
| completed | yes | Finished successfully. outputs is populated. |
| failed | yes | Runtime error. error_message contains a human-readable reason. |
| cancelled | yes | Cancelled by the user or by a timeout. outputs may be null or partial. |
Polling pattern
pythonimport time, httpx def wait_for_run(run_id: str, api_key: str, timeout_s: int = 900): url = f"https://api.alloovium.com/api/v2/workflows/runs/{run_id}" headers = {"Authorization": f"Bearer {api_key}"} deadline = time.monotonic() + timeout_s while time.monotonic() < deadline: resp = httpx.get(url, headers=headers) resp.raise_for_status() payload = resp.json() status = payload["status"] if status in ("completed", "failed", "cancelled"): return payload time.sleep(3) # 2-5s polling is ideal raise TimeoutError(f"run {run_id} did not finish in {timeout_s}s")
Do not tight-loop
Polling costs 1 token per call. A 3-second loop on a 10-minute run costs ~200 tokens; a 50ms tight loop would blow your bucket in seconds and earn you a 429.
See also
- Templates — document-fill jobs use the same pending/polling pattern.
- Idempotency — never kick off the same run twice.