Generate obligations for every step of a workflow¶
Submit a collection of agent descriptions as one Workflow, poll its asynchronous assessment, and retrieve obligations separately for each step.
| What you want to do | API |
|---|---|
| Submit your first Workflow | POST /api/v1/eli/workflows |
| Poll the entire assessment | GET /api/v1/eli/workflow-assessments/{assessment_id} |
| Download all step results | GET /api/v1/eli/workflow-assessments/{assessment_id}/result |
| Read a single step’s obligations | GET /api/v1/eli/workflows/{workflow_id}/components/{step_id}/obligations?version=1 |
| Submit a revised Workflow | POST /api/v1/eli/workflows/{workflow_id}/assessments |
Base URL: https://app.carveragents.ai. All requests below use your organisation user’s key in X-API-Key. Copy it from Eli → Settings → API access and keep it in your server environment. You do not need a super-admin key or access to the internal assessment-worker APIs.
1. How do I submit different agent descriptions and get obligations per step?¶
Put each description in its own entry in workflow.steps. A step is one unit of assessment: give it a stable ID, a name, and a description or system prompt. You can supply both. Each step receives its own obligations while remaining part of the same Workflow.
Save the following as workflow-v1.json. This is a fictional example: replace the descriptions and deployment context with your actual facts.
{
"schema_version": "1.0",
"base_version": 0,
"workflow": {
"client_ref": "patient-follow-up",
"name": "Patient follow-up",
"description": "Prepare and send staff-approved appointment reminders.",
"context": {
"jurisdictions": ["GB"],
"sector": "healthcare",
"data_categories": ["contact information", "appointment date and time"],
"customer_facing": true
},
"metadata": {
"integration": "example-clinic",
"notes": "Shared information relevant to every step."
},
"steps": [
{
"id": "prepare-reminder",
"name": "Prepare reminder",
"description": "Read the scheduled appointment and draft a minimal reminder for clinic staff review. This step cannot send messages or access clinical notes.",
"system_prompt": "Draft a reminder using the appointment date, time, and clinic contact details. Do not send it. Ask staff about missing or inconsistent information.",
"tools": ["read_appointment", "save_reminder_draft"],
"next": ["send-reminder"],
"metadata": {
"owner_team": "Clinic operations",
"implementation_notes": "Drafts are reviewed in the staff console."
}
},
{
"id": "send-reminder",
"name": "Send approved reminder",
"description": "Send a reminder only after staff approval, using the verified destination and stored communication preference. Record delivery status. Stop if approval or recipient verification is missing.",
"system_prompt": "Send only an explicitly staff-approved reminder. Do not infer approval or change the approved content. Record the delivery outcome.",
"tools": ["read_staff_approval", "send_reminder", "write_delivery_audit"],
"next": [],
"metadata": {
"owner_team": "Messaging operations",
"channel_configuration": { "channels": ["email", "sms"] }
}
}
]
}
}
Use an environment variable for your key, then submit:
export ELI_API_KEY='YOUR_API_KEY'
curl --fail-with-body --silent --show-error \
-X POST 'https://app.carveragents.ai/api/v1/eli/workflows' \
-H "X-API-Key: $ELI_API_KEY" \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: patient-follow-up-initial-20261023' \
--data-binary @workflow-v1.json \
--output workflow-v1-accepted.json
Replace the sample key and choose an idempotency key unique to your submission. Reuse that same key and body if a network interruption leaves you unsure whether the request succeeded. You receive the same accepted workflow/assessment IDs; reusing the key for different input returns 409.
Important input rules:
client_refis your stable identifier for this Workflow within your organisation. Do not POST a new Workflow each time you change a description.- Step IDs remain stable across updates. They are identifiers, not step numbers. Inserting a new step must not rename all later IDs.
nextdescribes connections. Its IDs must exist in the same snapshot. Independent steps can all usenext: []; branches and cycles are supported.contextcarries shared deployment/business facts. Each step has its own description and prompt. No submitted tools or workflow actions are executed by this API.metadatais optional at both levels and accepts JSON objects with arbitrary nested values. Shared workflow metadata is merged with each step’s metadata; the step overrides duplicate keys. Nested values are preserved, not deep-merged. Metadata passes separately alongside the ordinary assessment description.- Unknown schema fields, duplicate step IDs, and invalid connections are rejected. Existing request/step allowances apply; see limits and full API reference.
2. Is obligation generation asynchronous?¶
Yes. A successful submission returns HTTP 202 Accepted after registration. It does not wait for obligation generation.
Illustrative response excerpt; UUIDs are placeholders:
{
"assessment_id": "22222222-2222-4222-8222-222222222222",
"workflow_id": "11111111-1111-4111-8111-111111111111",
"version": 1,
"status": "queued",
"complete": false,
"completed_steps": 0,
"total_steps": 2,
"components": [
{ "id": "prepare-reminder", "name": "Prepare reminder", "status": "queued" },
{ "id": "send-reminder", "name": "Send approved reminder", "status": "queued" }
]
}
Save both IDs:
workflow_ididentifies the long-lived Workflow. Use it for detail reads and future revisions.assessment_ididentifies this exact submitted version. Use it for polling and result retrieval. A v2 submission returns a different assessment ID for the same Workflow.
The response may already show progress if processing starts quickly. A 202 response means accepted, not completed. Processing time depends on your input and service workload; do not assume a fixed completion time.
3. How do I poll the status of the whole workflow?¶
Use the existing whole-assessment status endpoint with the assessment_id returned by submission. You do not need to poll each step separately.
Set these from your saved response (replace the sample UUIDs):
export WORKFLOW_ID='11111111-1111-4111-8111-111111111111'
export ASSESSMENT_ID='22222222-2222-4222-8222-222222222222'
curl --fail-with-body --silent --show-error \
-H "X-API-Key: $ELI_API_KEY" \
"https://app.carveragents.ai/api/v1/eli/workflow-assessments/$ASSESSMENT_ID"
An assessment with one completed step can look like this:
{
"assessment_id": "22222222-2222-4222-8222-222222222222",
"workflow_id": "11111111-1111-4111-8111-111111111111",
"version": 1,
"status": "processing",
"complete": false,
"completed_steps": 1,
"total_steps": 2,
"components": [
{ "id": "prepare-reminder", "name": "Prepare reminder", "status": "completed", "error": "" },
{ "id": "send-reminder", "name": "Send approved reminder", "status": "processing", "error": "" }
]
}
These examples show selected fields. Actual component status also includes internal agent/version references and updated_at; these are useful for support, but use your stable step id to associate results with your workflow.
| Status | Meaning | What your integration should do |
|---|---|---|
queued |
Work is accepted and waiting for preparation or assessment | Continue polling |
processing |
Assessment has started or some steps have completed while others remain pending | Continue polling; inspect component statuses for detail |
completed |
Every step has returned an assessment; complete is true |
Retrieve the combined result |
failed |
At least one step failed | Inspect its error; read any partial results and retry the failed step if appropriate |
needs_input |
At least one step cannot yet be assessed | Supply the missing facts through a revised full Workflow submission |
stalled |
At least one unfinished step has had no recorded progress for 30 minutes | Check with your operator or explicitly retry; do not assume the external worker has necessarily stopped |
Treat complete: true as the whole-workflow completion signal. completed_steps counts steps, not obligations. A successful step does not make the entire Workflow complete. failed and stalled are action states; other steps may still be running. They do not mean successful results were discarded.
Poll every 5–10 seconds, with a client-side timeout. Back off on transient HTTP errors and rate limiting. The following runnable Python polling helper uses requests; set the same environment variables as above:
import os
import time
import requests
BASE = "https://app.carveragents.ai/api/v1/eli"
session = requests.Session()
session.headers["X-API-Key"] = os.environ["ELI_API_KEY"]
assessment_id = os.environ["ASSESSMENT_ID"]
def wait_for_workflow(assessment_id, timeout_seconds=1200):
deadline = time.monotonic() + timeout_seconds
status_url = f"{BASE}/workflow-assessments/{assessment_id}"
while time.monotonic() < deadline:
remaining = deadline - time.monotonic()
try:
response = session.get(status_url, timeout=max(0.1, min(30, remaining)))
except (requests.ConnectionError, requests.Timeout):
time.sleep(min(10, max(0, deadline - time.monotonic())))
continue
if response.status_code in (429, 500, 502, 503, 504):
time.sleep(min(10, max(0, deadline - time.monotonic())))
continue
response.raise_for_status()
status = response.json()
print(status["status"], status["completed_steps"], "/", status["total_steps"])
if status["complete"]:
result = session.get(f"{status_url}/result", timeout=30)
result.raise_for_status()
return result.json()
if status["status"] in {"failed", "needs_input", "stalled"}:
details = [(step["id"], step["status"], step.get("error", ""))
for step in status["components"]]
raise RuntimeError(f"Assessment needs attention: {details}")
time.sleep(min(10, max(0, deadline - time.monotonic())))
raise TimeoutError("Workflow is not complete yet; keep the assessment ID and resume polling later.")
result = wait_for_workflow(assessment_id)
for step in result["components"]:
print(step["id"], len(step.get("obligations", [])), "obligations")
A timeout does not cancel the accepted assessment. Resume polling the same ID later; do not submit duplicate Workflow versions just to check progress.
For a failed or stalled step, an explicit retry uses the same assessment ID:
curl --fail-with-body --silent --show-error \
-X POST \
-H "X-API-Key: $ELI_API_KEY" \
-H 'Content-Type: application/json' \
--data '{"component_id":"send-reminder"}' \
"https://app.carveragents.ai/api/v1/eli/workflow-assessments/$ASSESSMENT_ID/retry"
Then resume polling. Retry is available only for failed/stalled steps and has a bounded retry allowance. It does not edit the submitted description. Use a new Workflow version when changing input facts.
4. What does the completed outcome look like?¶
Download all results in one JSON response, grouped by step:
curl --fail-with-body --silent --show-error \
-H "X-API-Key: $ELI_API_KEY" \
"https://app.carveragents.ai/api/v1/eli/workflow-assessments/$ASSESSMENT_ID/result" \
--output workflow-v1-result.json
Illustrative excerpt only: these legal identifiers and findings are fictional, and additional response fields are omitted.
{
"workflow_id": "11111111-1111-4111-8111-111111111111",
"assessment_id": "22222222-2222-4222-8222-222222222222",
"version": 1,
"status": "completed",
"complete": true,
"completed_steps": 2,
"total_steps": 2,
"components": [
{
"id": "prepare-reminder",
"name": "Prepare reminder",
"status": "completed",
"assessment": {
"acts": [
{
"act_id": "EXAMPLE-ACT",
"obligations": [
{ "obligation_id": "EXAMPLE-1", "text": "Illustrative duty to restrict access to the records used by this step." }
]
}
]
},
"obligations": [
{
"act_id": "EXAMPLE-ACT",
"obligation_id": "EXAMPLE-1",
"text": "Illustrative duty to restrict access to the records used by this step."
}
]
},
{
"id": "send-reminder",
"name": "Send approved reminder",
"status": "completed",
"assessment": { "acts": [] },
"obligations": []
}
]
}
Each component includes:
id: your step ID, so you can match output to your original input.statusanderror: processing outcome for that step.assessment: the stored assessment JSON, with returned regulations and substantive assessment fields.obligations: a flattened convenience list of the returned obligations, each withact_id.coverage: assessment coverage information, when a result exists.- Internal agent/version identifiers for traceability. Components remain inside their Workflow; they do not appear in the standalone Agents listing.
The result endpoint also works before all steps finish: successful steps have results, unfinished ones may have assessment: null and an empty obligations list, and the overall complete remains false. An unfinished empty list must not be interpreted as “no obligations apply.”
Even a completed assessment can have partial legal coverage or undetermined applicability. Inspect coverage and substantive findings. The same duty may appear on several steps; do not treat those associations as distinct legal duties without checking their identity. Completion of obligation generation is separate from Legal/Engineering/release approval.
To retrieve just one step for version 1:
curl --fail-with-body --silent --show-error \
-H "X-API-Key: $ELI_API_KEY" \
"https://app.carveragents.ai/api/v1/eli/workflows/$WORKFLOW_ID/components/prepare-reminder/obligations?version=1"
5. How do I post v2 of the same Workflow with tweaks?¶
Read the current version before editing:
curl --fail-with-body --silent --show-error \
-H "X-API-Key: $ELI_API_KEY" \
"https://app.carveragents.ai/api/v1/eli/workflows/$WORKFLOW_ID" \
--output workflow-current.json
The current version is assessment.version. For a new revision:
- Keep the same
workflow_idin the URL and the sameworkflow.client_refin the body. - Set
base_versionto the current version, for example1when creating v2. Leaveschema_versionas"1.0"; it describes the JSON format, not your Workflow revision. - Submit the complete Workflow snapshot, including unchanged steps. Keep IDs for steps that retain their identity, add new IDs for new steps, and omit steps you want removed from the new version.
- Use a new Idempotency-Key for the new revision.
- Save the returned new
assessment_idand poll that ID.
For example, create workflow-v2.json from v1 and change one prompt:
python3 - <<'PY'
import json
from pathlib import Path
payload = json.loads(Path("workflow-v1.json").read_text())
payload["base_version"] = 1
step = next(step for step in payload["workflow"]["steps"] if step["id"] == "send-reminder")
step["system_prompt"] += " If delivery fails, escalate to clinic staff; do not retry automatically."
Path("workflow-v2.json").write_text(json.dumps(payload, indent=2))
PY
curl --fail-with-body --silent --show-error \
-X POST \
-H "X-API-Key: $ELI_API_KEY" \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: patient-follow-up-revision-2-20261023' \
--data-binary @workflow-v2.json \
"https://app.carveragents.ai/api/v1/eli/workflows/$WORKFLOW_ID/assessments" \
--output workflow-v2-accepted.json
The response is 202, with the same Workflow ID, version: 2, and a new assessment ID. Every step is reassessed in this initial implementation, including unchanged steps. Prior versions and their accepted results remain available through their original assessment IDs. You can also read the old source with GET /workflows/{workflow_id}?version=1.
If another user submitted a new version first, your old base_version returns 409. Read the current Workflow, reconcile your changes, and submit a new request with an appropriate fresh Idempotency-Key. Do not silently overwrite someone else’s revision.
Common responses¶
| HTTP response | Action |
|---|---|
| 202 | Submission accepted; save IDs and poll |
| 200 | Status, detail, or result read succeeded; inspect complete |
| 401 / 403 | Check the key, account access, and active organisation membership |
| 404 | Check the ID and organisation; other organisations’ resources are unavailable |
| 402 | Workflow/step allowance exceeded; check your plan or administrator |
| 409 | Resolve a version/idempotency conflict, or check whether a retry is allowed |
| 413 / 422 | Correct the size or schema errors in the submitted JSON |
| 429 | Back off; submission or retry limits have been reached |
For exact fields, configured allowances, metadata semantics, and release APIs, see the Workflow API reference.