Alloovium

Capabilities

Templates

Upload a DOCX template and let Alloovium fill it from your vault. Two capabilities: start a fill job, then poll it until the filled document is ready to download.

Lifecycle

Template fills are asynchronous. Every fill follows the same four-step pattern:

  1. POST the DOCX template (plus optional scoping) to capability.
  2. Get back a field. The job starts in state state.
  3. Poll capability every 2–5 seconds.
  4. When condition, download the result from field.

Same shape as workflows

Fill jobs and workflow runs share the same async state machine (states). If you have already written a poller for one, you can reuse it for the other.

templates.start_fill

POST/api/v2/templates/fill

Upload a DOCX template and start a fill job. Returns a job_id you poll for status.

scope: templates:writecost: 25multipart/form-data

Request

Multipart form with up to three fields: file (required — the DOCX bytes), project (optional — scope sourcing to a single project), and docs (optional — comma-separated UUID list for deep scoping to specific documents).

bash
curl -sS https://api.alloovium.com/api/v2/templates/fill \ -H "Authorization: Bearer $ALLOOVIUM_API_KEY" \ -H "Idempotency-Key: submittal-review-2026-04-08" \ -F "file=@./Submittal_Review_Template.docx" \ -F "project_id=8f2e3c1a-..." \ -F "document_ids=4a1c2b3d-...,9e8f7a6b-..."
  • fieldext only. Non-DOCX returns code.
  • Files above the maximum size return code.
  • field — optional project UUID. The caller must have view access or the endpoint returns code.
  • field — optional comma-separated UUIDs. Invalid UUIDs return code.

Response

json
{ "job_id": "c1d2e3f4-...", "status": "pending" }

The job is created in state state. The template bytes are uploaded to tenant-scoped object storage and the fill pipeline picks the job up.

REST-only — no MCP

This capability is multipart, so it is not exposed as an MCP tool. JSON-RPC cannot carry binary attachments. Agents that need a filled template should call the REST endpoint directly.

Attach an Idempotency-Key

Template fills cost 25 tokens and produce side effects. Pass an header header so a dropped response does not kick off the same fill twice — see Idempotency.

Permission override

Users flagged with flag — a tenant-level permission — will get code even with scope in scope. Org admins bypass this check.

templates.get_fill_status

GET/api/v2/templates/fill/{job_id}

Poll a fill job's progress, stage, presigned download URL, and error state.

scope: templates:readcost: 1

Response (running)

json
{ "job_id": "c1d2e3f4-...", "status": "running", "progress": 42, "progress_stage": "Filling narrative sections", "created_at": "2026-04-08T10:12:00+00:00", "started_at": "2026-04-08T10:12:05+00:00", "completed_at": null, "filled_document_id": null, "filled_download_url": null, "error_code": null, "error_message": null }

Response (completed)

json
{ "job_id": "c1d2e3f4-...", "status": "completed", "progress": 100, "progress_stage": "done", "created_at": "2026-04-08T10:12:00+00:00", "started_at": "2026-04-08T10:12:05+00:00", "completed_at": "2026-04-08T10:14:31+00:00", "filled_document_id": "9a8b7c6d-...", "filled_download_url": "https://alloovium-storage.s3.ap-southeast-2.amazonaws.com/...signed...", "error_code": null, "error_message": null }

field is a presigned S3 URL valid for 1 hour. Re-poll capability to refresh it — the URL is generated fresh on every status call once the job is complete.

Response (failed)

json
{ "job_id": "c1d2e3f4-...", "status": "failed", "progress": 64, "progress_stage": "Filling tables", "created_at": "2026-04-08T10:12:00+00:00", "started_at": "2026-04-08T10:12:05+00:00", "completed_at": "2026-04-08T10:13:47+00:00", "filled_document_id": null, "filled_download_url": null, "error_code": "table_fill_failed", "error_message": "Unable to match any source document to the submittal schedule table." }

Status states

StatusTerminal?Notes
pendingnoJob created; template uploaded; not yet picked up.
runningnoPipeline executing. progress + progress_stage update as it runs.
completedyesFilled DOCX ready. filled_document_id and filled_download_url are populated.
failedyesRuntime error. error_code + error_message explain what went wrong.
cancelledyesCancelled by the user or by a timeout.

Tenant isolation

Requesting a field owned by another tenant returns code, not 403 — we do not distinguish the two so attackers cannot probe ID space.

Polling pattern

python
import time, httpx def wait_for_fill(job_id: str, api_key: str, timeout_s: int = 600): url = f"https://api.alloovium.com/api/v2/templates/fill/{job_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 == "completed": return payload # filled_download_url is ready if status in ("failed", "cancelled"): raise RuntimeError( f"fill {job_id} ended in {status}: {payload.get('error_message')}" ) time.sleep(3) # 2-5s polling is ideal raise TimeoutError(f"fill {job_id} did not finish in {timeout_s}s")

Download the filled DOCX promptly

field is a presigned S3 URL valid for 1 hour. If you need it again later, just call capability again — it will re-sign on every request.

See also

  • Workflows — same async state machine, different execution engine.
  • Idempotency — never kick off the same fill twice.