REST API
Onboarding and jobs
Onboarding compiles your documents into memory the model answers from. Start it, watch it, cancel it, and know exactly when the base is answering.
Onboarding is the step that turns parsed text into resident memory. It runs on the serving hardware, takes minutes rather than seconds, and is the only part of the flow worth watching.
What onboarding does
Documents land as rows with extracted text. Onboarding compiles that text into the memory the model answers from, one batch at a time, and marks each document ready as its build lands. It is incremental by design: adding three files to a base of five thousand builds three documents, not five thousand and three, and everything already built keeps answering throughout.
You rarely have to start it by hand. A commit with onboard: true (the default) starts
it, an upsert starts it, a source sync starts it, and uploading into a base that has already been
onboarded dispatches an incremental build on its own.
Start a run
curl -X POST https://api.engramdynamics.org/v1/corpora/c_7a1f.../train \
-H "Authorization: Bearer <your key>"
{
"id": "j_4b8e...",
"corpus_id": "c_7a1f...",
"kind": "train",
"status": "running",
"detail": "Building 2 documents",
"progress": 0.0,
"eta_seconds": 240,
"parent_job_id": null,
"created_at": "2026-09-13T10:07:00Z",
"updated_at": "2026-09-13T10:07:00Z"
}
Needs the ingest scope and an active plan. Rate limited to 5 calls per minute,
because a run occupies the hardware for minutes and stacking them helps nobody. A base with no
documents is a 400 rather than an empty run.
Watch progress
curl https://api.engramdynamics.org/v1/jobs/j_4b8e... \
-H "Authorization: Bearer <your key>"
curl "https://api.engramdynamics.org/v1/corpora/c_7a1f.../jobs?limit=50" \
-H "Authorization: Bearer <your key>"
status is pending, running, succeeded,
failed or canceled. progress is 0.0 to 1.0,
eta_seconds is an estimate and null when there is nothing to estimate, and
detail is a plain sentence you can put on screen.
The run and its batches
The job listing returns two kinds of row and telling them apart matters:
- The run is the row you started, with
kind: "train"andparent_job_id: null. Poll this one. Its progress and its clock cover the whole run. - A batch has
kind: "build"and names the run inparent_job_id. It covers one batch of documents. Its progress deliberately stops at 0.9, because the last tenth belongs to the run's own finishing work, and itscreated_atis when that batch started rather than when the run did.
Show the run. A client that mistakes a batch for the run displays a bar that stops at 90 percent and a clock that restarts.
import time
def wait_for(job_id, timeout=1800):
deadline = time.time() + timeout
while time.time() < deadline:
job = requests.get(f"{BASE}/jobs/{job_id}", headers=HEADERS, timeout=30).json()
if job["status"] in ("succeeded", "failed", "canceled"):
return job
print(f"{job['progress']:.0%} {job['detail']}")
time.sleep(5)
raise TimeoutError(job_id)
Readiness is the other half of the picture, and often the more useful one: GET
/v1/corpora/{id} carries documents_ready, documents_onboarding and
documents_failed, and the base answers questions as soon as the first of those is above
zero. You do not have to wait for the run to finish before you start asking.
Cancel a run
curl -X POST https://api.engramdynamics.org/v1/corpora/c_7a1f.../cancel \
-H "Authorization: Bearer <your key>"
Cancellation is cooperative: the flag goes on the run, the batches under it read it, and work stops at the next safe point, including a batch already inside a build. Documents that finished building stay built. With no run in progress the call is a 409, which is the honest answer rather than a silent success.
Refresh the catalog description
After onboarding, a short description of the base is written so an MCP client can tell an agent what this base is about. Regenerate it without retraining:
curl -X POST https://api.engramdynamics.org/v1/corpora/c_7a1f.../describe \
-H "Authorization: Bearer <your key>"
{"description": "Support policies, escalation paths and refund rules for the EU team."}
The base has to be ready, since a base mid-build has no stable inventory to describe.
One generation per call, rate limited to 5 per minute. description comes back null when
the pass could not produce a complete sentence, which is deliberate: half a sentence in a tool
description is worse than none.
Get told instead of polling
A pipeline that pushes and then waits should be called back, not left hammering a status route.
Subscribe an endpoint and corpus.ready fires the moment a base finishes onboarding and
is answering, with the document count in the payload.
{
"event": "corpus.ready",
"sent_at": "2026-09-13T10:11:42Z",
"data": {
"corpus_id": "c_7a1f...",
"name": "Support handbook",
"job_id": "j_4b8e...",
"documents_ready": 2
}
}
Setting that up takes one call: Subscribe an endpoint.
Next
Ask the base something: Answers, chat and feedback.