Runlog docs

Start building

A plan buys a weekly allowance, sized for steady work.

Start

Artifacts

For the document your agent hands back. An artifact is generated from the project's own corpus — a report, a memo, a takeoff — and the reason to generate one here rather than in a chat window is that it comes back with its provenance attached. Every section names the cells it was derived from, and a section nothing in the corpus supports is returned as unsupported rather than written confidently. An artifact is ordinary corpus once it lands: retrievable, citable, and answerable for what it rests on — and promoting one to source evidence is the folder move documented here.

POST/v1/api/actionsAPI key

Generate an artifact

Build a structured artifact from the project's corpus, with each section citing the cells it rests on.

State the purpose in prose — what the document should produce — rather than a template. The corpus decides the sections; the purpose decides what is worth saying.

**Generation is detached.** The dispatch returns a running task; poll it, or watch the file stream to see the artifact land. Naming an existing `artifactId` is refused on the API surface today — omit it to generate a new one.

The artifact lands under `Artifacts/`, which is the classification rather than a naming convention: the pipeline treats it as a derived product and will not read it back as evidence. Promoting it to source is the folder move in the Changes lane, and is a deliberate act.

Dispatched as `{"action": "artifact_update", "input": {"purpose": "..."}}`.

Body

FieldTypeRequiredDescription
actionstringrequiredMust be `"artifact_update"`.
input.artifactIdstringoptionalExisting artifact id to update; omit to generate a new one.
input.purposestringrequiredWhat the artifact should produce.
Request · curl
curl -X POST "https://runlog-7613480744.us-central1.run.app/v1/api/actions" \
  -H "Authorization: Bearer $RUNLOG_API_KEY" \
  -H "X-Project-Id: p7Kd2mQx" \
  -H "Content-Type: application/json" \
  -d '{
  "action": "artifact_update",
  "input": {
    "purpose": "an envelope takeoff for the north elevation, with the glazing schedule"
  }
}'
Response · 200
{
  "action": "artifact_update",
  "result": {
    "artifactId": "art_5Hn2Kp",
    "status": "running",
    "taskId": "tk_6Wr4Bn"
  }
}

Errors

StatuserrorWhen
400purpose is requiredThe input named no purpose. There is nothing to build the document against.
400artifactId is not accepted on this surfaceA revision was requested. Omit the id to generate a new artifact.
403action not available to an API keyThe key's tier does not reach this action.
400invalid request bodyThe body is not JSON, or exceeds the 1 MB request-body cap.
401invalid API keyThe `Authorization` header is missing, malformed, or names a revoked key.
400name the target project via the X-Project-Id header or projectId queryNo project was named and the key is not bound to one.
429rate limit exceededYou passed 60 requests/minute on this key. `Retry-After` carries the seconds to wait.
500internal errorThe relay failed to serve the request. Retry with backoff; the cause is logged server-side against your request.
PATCH/v1/projects/{projectId}/files/{fileId}API key

Promote or demote an artifact

Move a file across the `.runlog/Artifacts/` boundary to reclassify it as generated or as ingested source.

There is no separate promote endpoint, and that is deliberate. The `.runlog/Artifacts/` folder boundary IS the classification, so one move gesture is the whole operation and the tree can never disagree with the label.

**Demote** — moving a file INTO `.runlog/Artifacts/` relabels it generated: it becomes a derived product, and the pipeline stops treating it as evidence.

**Promote** — moving a file OUT of `.runlog/Artifacts/` relabels it ingested: it becomes source evidence, and the derivation cascade re-reconciles everything that rests on it.

Each crossing triggers its reconciliation cascade, and discrepancies the cascade finds are surfaced rather than silently resolved. A batch that fails lands on each file's own status as `error`; nothing is skipped quietly.

Path parameters

FieldTypeRequiredDescription
projectIdstringrequiredThe owning project.
fileIdstringrequiredThe file to reclassify.

Body

FieldTypeRequiredDescription
filepathstringrequiredDestination folder. A path under `.runlog/Artifacts/` demotes; a path outside it promotes.
Request · curl
curl -X PATCH "https://runlog-7613480744.us-central1.run.app/v1/projects/p7Kd2mQx/files/4mHqZ1nR8vTbC0sWyLpAeGkJ3dXfU6iO2rN" \
  -H "Authorization: Bearer $RUNLOG_API_KEY" \
  -H "X-Project-Id: p7Kd2mQx" \
  -H "Content-Type: application/json" \
  -d '{
  "filepath": ".runlog/Artifacts/design"
}'
Response · 200
{
  "chunkIds": [
    "4mHqZ1nR8vTbC0sWyLpAeGkJ3dXfU6iO2rN"
  ],
  "chunking": "",
  "createdAt": "2026-08-07T18:22:10Z",
  "ext": "swift",
  "fileId": "4mHqZ1nR8vTbC0sWyLpAeGkJ3dXfU6iO2rN",
  "fileSize": 41822,
  "fileType": "code",
  "filepath": ".runlog/Artifacts/design",
  "name": "Router.swift",
  "projectId": "p7Kd2mQx",
  "sourceClass": "generated",
  "status": "queued",
  "updatedAt": "2026-08-07T18:22:10Z"
}

Errors

StatuserrorWhen
404file not foundNo such file in this project.
403editor access requiredYou can read the project but not write to it.
404project not foundThe project does not exist, or you have no access to it. Projects are non-enumerable, so a denied read is indistinguishable from a missing one.
401invalid API keyThe `Authorization` header is missing, malformed, or names a revoked key.
400name the target project via the X-Project-Id header or projectId queryNo project was named and the key is not bound to one.
429rate limit exceededYou passed 60 requests/minute on this key. `Retry-After` carries the seconds to wait.
500internal errorThe relay failed to serve the request. Retry with backoff; the cause is logged server-side against your request.