Runlog docs

Start building

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

Start

Agent sessions

For an open-ended goal. A run is a durable session bound to one project: you open it once, append messages, and read the answer back as a single JSON read or as a live stream — the agent decomposes the goal, retrieves repeatedly, and names the declarations each claim rested on. Nothing on this path mutates a codebase: when a turn concludes a change is needed it writes a plan — the reasoning, the proposed changes, the terms it read without changing — and stops. Applying it is a separate, explicit approval on the same run, which is what makes the surface safe to put behind an editor's "apply" button.

Ask

The question path: open a run, append the question, read or stream the answer with its citations.

POST/v1/api/agent-runsAPI key

Open a query run

Start a conversation bound to one project.

A run is cheap and durable. Open one per user conversation and keep appending; do not open one per question.

Body

FieldTypeRequiredDescription
projectIdstringoptionalThe codebase the run asks about. A run without one has no project memory to read.
titlestringoptionalDisplay title.
toolstringoptionalWhich capability set the run may reach. Defaults to the general set.
Request · curl
curl -X POST "https://runlogai.com/runlogapi/api/agent-runs" \
  -H "Authorization: Bearer $RUNLOG_API_KEY" \
  -H "X-Project-Id: p7Kd2mQx" \
  -H "Content-Type: application/json" \
  -d '{
  "projectId": "p7Kd2mQx",
  "title": "Why does checkout retry?",
  "tool": "atlas"
}'
Response · 200
{
  "createdAt": "2026-08-07T18:30:00Z",
  "projectId": "p7Kd2mQx",
  "runId": "run_9Fb3TnQ2",
  "status": "open",
  "title": "Why does checkout retry?"
}

Errors

StatuserrorWhen
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.
POST/v1/api/agent-runs/{runId}/messagesAPI key

Ask a question

Append a user message to a run; Ro answers on the run, with the cells and declarations it cited.

The answer is grounded: `source` parts carry the cell ids the claim rested on, so you can render a citation the user can click through to a file and line rather than a confidence score.

The route returns the message you sent as soon as it lands and never waits on the answer: Ro's reply lands on the run, so open the run's stream (mint a stream ticket first) or read the run until an assistant message follows yours.

Every message carries `metadata.createdAt` — when it was written, as an ISO-8601 UTC instant with milliseconds. It is what orders a thread and what measures how long a turn took.

Path parameters

FieldTypeRequiredDescription
runIdstringrequiredThe run to append to.

Body

FieldTypeRequiredDescription
textstringrequiredThe question.
Request · curl
curl -X POST "https://runlogai.com/runlogapi/api/agent-runs/run_9Fb3TnQ2/messages" \
  -H "Authorization: Bearer $RUNLOG_API_KEY" \
  -H "X-Project-Id: p7Kd2mQx" \
  -H "Content-Type: application/json" \
  -d '{
  "text": "Why does the checkout client retry on a declined card?"
}'
Response · 200
{
  "userMessage": {
    "id": "m_1",
    "metadata": {
      "createdAt": "2026-02-11T09:14:22.418Z"
    },
    "parts": [
      {
        "text": "Why does the checkout client retry on a declined card?",
        "type": "text"
      }
    ],
    "role": "user"
  }
}

Errors

StatuserrorWhen
400text is requiredThe message carried no content.
404not foundNo such run, or it belongs to another account.
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.
GET/v1/agent-runs/{runId}Session

Read a run

Read the whole conversation — every message with its parts and citations.

Path parameters

FieldTypeRequiredDescription
runIdstringrequiredThe run to read.
Request · curl
curl -X GET "https://runlogai.com/runlogapi/agent-runs/run_9Fb3TnQ2" \
  -H "Authorization: Bearer $RUNLOG_SESSION_TOKEN" \
  -H "X-Project-Id: p7Kd2mQx"
Response · 200
{
  "messages": [
    {
      "id": "m_2",
      "metadata": {
        "createdAt": "2026-02-11T09:14:22.418Z"
      },
      "parts": [
        {
          "text": "It retries once against the fallback processor…",
          "type": "text"
        }
      ],
      "role": "assistant"
    }
  ],
  "pendingWrites": [],
  "run": {
    "projectId": "p7Kd2mQx",
    "runId": "run_9Fb3TnQ2",
    "status": "open"
  }
}

Errors

StatuserrorWhen
404not foundNo such run, or it belongs to another account.
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.
POST/v1/api/agent-runs/{runId}/stream-ticketAPI key

Mint a stream ticket

Receive the short-lived ticket that opens the run's stream without a header.

A normal keyed call. The ticket opens the stream once, within 120 seconds, so the key itself never travels in a URL. Mint one per connection.

Path parameters

FieldTypeRequiredDescription
runIdstringrequiredThe run to stream.
Request · curl
curl -X POST "https://runlogai.com/runlogapi/api/agent-runs/run_9Fb3TnQ2/stream-ticket" \
  -H "Authorization: Bearer $RUNLOG_API_KEY" \
  -H "X-Project-Id: p7Kd2mQx"
Response · 200
{
  "expiresInSeconds": 120,
  "ticket": "st_7Qk2Mv…"
}

Errors

StatuserrorWhen
404not foundNo such run, or it belongs to another account.
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.
GET/v1/api/agent-runs/{runId}/streamAPI key

Stream an answer

Receive the answer as it forms, over server-sent events.

Two credentials open it: the API key as a bearer header, or a stream ticket on the query string for a client that cannot set a header (EventSource). The ticket comes from POST /v1/api/agent-runs/{runId}/stream-ticket, opens the stream once within 120 seconds, and the stream then lives as long as the connection.

The stream speaks the AI SDK UI message-stream protocol — `data:`-only SSE frames with a `type` discriminator — announced by the `x-vercel-ai-ui-message-stream: v1` response header. Any client built against that protocol consumes it unchanged.

Every part event carries the stable id `messageId:partIdx`, so a late-arriving part places itself correctly regardless of arrival order.

Reconnecting replays every finished turn from persistence before any new delta, so a dropped connection never loses an answer.

Path parameters

FieldTypeRequiredDescription
runIdstringrequiredThe run to watch.

Query parameters

FieldTypeRequiredDescription
ticketstringoptionalA stream ticket from the stream-ticket route, in place of the bearer header. Opens once, within 120 seconds.
The flow
# 1. Mint a ticket: a normal keyed call. It opens the stream once, within two minutes.
curl -X POST "https://runlogai.com/runlogapi/api/agent-runs/run_9Fb3TnQ2/stream-ticket" \
  -H "Authorization: Bearer $RUNLOG_API_KEY" \
  -H "X-Project-Id: p7Kd2mQx"
# → {"ticket": "st_7Qk2Mv…", "expiresInSeconds": 120}

# 2. Open the stream with it. No header, so EventSource can; it lives as long as the connection.
curl -N "https://runlogai.com/runlogapi/api/agent-runs/run_9Fb3TnQ2/stream?ticket=$TICKET"
Request · curl
curl -X GET "https://runlogai.com/runlogapi/api/agent-runs/run_9Fb3TnQ2/stream" \
  -H "Authorization: Bearer $RUNLOG_API_KEY" \
  -H "X-Project-Id: p7Kd2mQx"
Response · 200
data: {"type":"text-delta","id":"m_2:0","delta":"It retries once against"}

Errors

StatuserrorWhen
404not foundNo such run, or it belongs to another account.
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.
POST/v1/agent-runs/{runId}/attachmentsSession

Upload an attachment

Send a file with a message; the run stages it for the worker to use.

Path parameters

FieldTypeRequiredDescription
runIdstringrequiredThe run to attach to.

Body

FieldTypeRequiredDescription
filestringrequiredThe file payload.
Request · curl
curl -X POST "https://runlogai.com/runlogapi/agent-runs/run_9Fb3TnQ2/attachments" \
  -H "Authorization: Bearer $RUNLOG_SESSION_TOKEN" \
  -H "X-Project-Id: p7Kd2mQx" \
  -H "Content-Type: application/json" \
  -d '{
  "file": "..."
}'
Response · 200
{
  "runId": "run_9Fb3TnQ2",
  "uploaded": true
}

Errors

StatuserrorWhen
404not foundNo such run, or it belongs to another account.
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.

Steer

The control path: stop a turn in flight, ask for proposed changes, read the plan of record, and approve or reject each staged write by id.

POST/v1/agent-runs/{runId}/interruptSession

Interrupt a run

Stop the turn in flight — the cancel behind a user hitting stop.

Path parameters

FieldTypeRequiredDescription
runIdstringrequiredThe run to interrupt.
Request · curl
curl -X POST "https://runlogai.com/runlogapi/agent-runs/run_9Fb3TnQ2/interrupt" \
  -H "Authorization: Bearer $RUNLOG_SESSION_TOKEN" \
  -H "X-Project-Id: p7Kd2mQx" \
  -H "Content-Type: application/json" \
  -d '{}'
Response · 200
{
  "interrupted": true,
  "runId": "run_9Fb3TnQ2"
}

Errors

StatuserrorWhen
404not foundNo such run.
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.
POST/v1/agent-runs/{runId}/messagesSession

Request changes

Ask for a change; the run starts what the ask plainly wants and stages the rest for you to decide.

This is the same message route as a question, and it answers once your message has landed: the turn runs on the worker, and its reply, its plan of record and its writes arrive on the run's stream.

A change the ask plainly wants starts as the reply lands, and the reply says so. One that rests on a guess about what you meant, or that the consequence floor holds, is staged onto the run's `pendingWrites` — show it to the user, then confirm or decline each one by id.

Path parameters

FieldTypeRequiredDescription
runIdstringrequiredThe run to append to.

Body

FieldTypeRequiredDescription
textstringrequiredThe change you want made.
Request · curl
curl -X POST "https://runlogai.com/runlogapi/agent-runs/run_9Fb3TnQ2/messages" \
  -H "Authorization: Bearer $RUNLOG_SESSION_TOKEN" \
  -H "X-Project-Id: p7Kd2mQx" \
  -H "Content-Type: application/json" \
  -d '{
  "text": "Add exponential backoff to Router.send and update its callers."
}'
Response · 200
{
  "userMessage": {
    "id": "m_3",
    "parts": [
      {
        "text": "Add exponential backoff to Router.send and update its callers.",
        "type": "text"
      }
    ],
    "role": "user"
  }
}

Errors

StatuserrorWhen
404not foundNo such run.
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.
GET/v1/agent-runs/{runId}Session

Read a plan artifact

Read the plan of record and the writes still staged for approval.

The plan is persisted, not discarded — it names the changes it would make and the terms it read without changing, each citing what it rests on. A plan you did not approve is still readable later.

Path parameters

FieldTypeRequiredDescription
runIdstringrequiredThe run holding the plan.
Request · curl
curl -X GET "https://runlogai.com/runlogapi/agent-runs/run_9Fb3TnQ2" \
  -H "Authorization: Bearer $RUNLOG_SESSION_TOKEN" \
  -H "X-Project-Id: p7Kd2mQx"
Response · 200
{
  "pendingWrites": [
    {
      "id": "pw_3Rt7Yu",
      "kind": "cell_edit",
      "summary": "Router.send — wrap the request in a backoff loop capped at 3 attempts"
    },
    {
      "id": "pw_8Kq1Za",
      "kind": "source_transition",
      "summary": "Promote Artifacts/design/retry-note.md to source"
    }
  ],
  "run": {
    "projectId": "p7Kd2mQx",
    "runId": "run_9Fb3TnQ2"
  }
}

Errors

StatuserrorWhen
404not foundNo such run.
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.