Runlog docs

Start building

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

Start

Keys

Three credentials reach this API. An account key (`rlka_`) authenticates you and names its target project per request, so it can never reach past what you can reach and loses a project the moment your access does. A project key (`rlk_`) is bound to one project at a fixed tier — what you hand a service that should reach that project and nothing else. A site key belongs to a published site's page code and authenticates the site, never a person. The first two are bearer tokens on any client, from any origin — a key does not care whether the caller was built with Runlog.

Account keys

The `rlka_` key: your reach, per request. Mint one when the integration acts as you across projects.

POST/v1/api-keysSession

Mint a key

Create an account API key. The plaintext is returned exactly once.

Only a hash is persisted. If you lose the plaintext, revoke the key and mint another — it cannot be recovered.

Body

FieldTypeRequiredDescription
labelstringoptionalHuman-readable label, shown in the key list.
rolestringoptionalWho the key acts as: "owner" (the default) runs every request as you, at your live access on the project it names; "user" acts for one of your own end users instead — bound to one project, read and project-write only, and never above your tier there. A "user" key names the end user it acts for with the X-Runlog-End-User header, so writes attribute to them. "user" is minted only from a storefront's own account settings: the key binds to the tenant the minting session is on, and a session on no storefront is answered 403.
Request · curl
curl -X POST "https://runlog-7613480744.us-central1.run.app/v1/api-keys" \
  -H "Authorization: Bearer $RUNLOG_SESSION_TOKEN" \
  -H "X-Project-Id: p7Kd2mQx" \
  -H "Content-Type: application/json" \
  -d '{
  "label": "LingCode production",
  "role": "owner"
}'
Response · 200
{
  "createdAt": "2026-08-07T18:00:00Z",
  "key": "rlka_key2Xm9Qd_…",
  "keyId": "key_2Xm9Qd",
  "label": "LingCode production"
}

Errors

StatuserrorWhen
400invalid request bodyThe body is not JSON, or exceeds the 1 MB request-body cap.
401unauthorizedThis route takes your signed-in session, not an API key — a key cannot mint another key.
GET/v1/api-keysSession

List keys

List your keys — metadata only, never plaintext.

Request · curl
curl -X GET "https://runlog-7613480744.us-central1.run.app/v1/api-keys" \
  -H "Authorization: Bearer $RUNLOG_SESSION_TOKEN" \
  -H "X-Project-Id: p7Kd2mQx"
Response · 200
[
  {
    "keyId": "key_2Xm9Qd",
    "label": "LingCode production",
    "lastUsedAt": "2026-08-07T18:31:44Z"
  }
]

Errors

StatuserrorWhen
401unauthorizedNo valid session.
DELETE/v1/api-keys/{keyId}Session

Revoke a key

Revoke a key immediately.

Path parameters

FieldTypeRequiredDescription
keyIdstringrequiredThe key to revoke.
Request · curl
curl -X DELETE "https://runlog-7613480744.us-central1.run.app/v1/api-keys/key_2Xm9Qd" \
  -H "Authorization: Bearer $RUNLOG_SESSION_TOKEN" \
  -H "X-Project-Id: p7Kd2mQx"
Response · 200
{
  "keyId": "key_2Xm9Qd",
  "revoked": true
}

Errors

StatuserrorWhen
404not foundNo such key on your account.
401unauthorizedNo valid session.

Project keys

The `rlk_` key: one project at a fixed tier. Mint one when a service should reach exactly one project and nothing else.

POST/v1/mcp-keysAPI key

Mint a project key

Create a key bound to one project at a fixed access tier.

Integrator provisioning: an Editor-level account key mints `mcp`- and `ingest`-scoped project keys for the projects it just created, so a whole customer setup runs without a browser. The signed-in session calls the same route.

Minting an `api`-scoped project key grants that project to the key itself, so it is an ADMIN act on the project — an editor cannot mint one, and no account key reaches the admin tier: mint `api`-scoped keys from the app.

A project key names no project on its requests: it carries one. Sending `X-Project-Id` for a different project is refused rather than honoured.

The tier is fixed at mint and may not be `admin` — no key of either kind reaches the admin level, so a leaked key can never re-grant access.

Body

FieldTypeRequiredDescription
labelstringoptionalHuman-readable label, shown in the key list.
scopestringoptional`api` for the public API, `ingest` for the ingestion lane, `mcp` for the memory MCP server. Defaults to `mcp`.
levelstringoptional`viewer` or `editor`. Defaults to `viewer` — the narrowest grant that still serves.
Request · curl
curl -X POST "https://runlog-7613480744.us-central1.run.app/v1/mcp-keys" \
  -H "Authorization: Bearer $RUNLOG_API_KEY" \
  -H "X-Project-Id: p7Kd2mQx" \
  -H "Content-Type: application/json" \
  -d '{
  "label": "checkout-service",
  "level": "editor",
  "scope": "api"
}'
Response · 201
{
  "key": "rlk_p7Kd2mQx_key4Bn7Wr_…",
  "record": {
    "createdAt": "2026-08-07T18:00:00Z",
    "keyId": "key4Bn7Wr",
    "label": "checkout-service",
    "level": "ACCESS_LEVEL_EDITOR",
    "projectId": "p7Kd2mQx",
    "scope": "api"
  }
}

Errors

StatuserrorWhen
400invalid request bodyThe body is not JSON, or exceeds the 1 MB request-body cap.
400a key may not hold the admin level`level` named `admin`.
403admin access requiredAn `api`-scoped mint by someone who is not an admin of the project — every account key included, since a key never holds admin.
401invalid API keyThe `Authorization` header is missing, malformed, or names a revoked key.
GET/v1/mcp-keysSession

List project keys

List your keys in one project — metadata only, never plaintext.

Query parameters

FieldTypeRequiredDescription
projectIdstringrequiredThe project whose keys to list. The `X-Project-Id` header serves equally.
Request · curl
curl -X GET "https://runlog-7613480744.us-central1.run.app/v1/mcp-keys?projectId=p7Kd2mQx" \
  -H "Authorization: Bearer $RUNLOG_SESSION_TOKEN" \
  -H "X-Project-Id: p7Kd2mQx"
Response · 200
{
  "keys": [
    {
      "keyId": "key4Bn7Wr",
      "label": "checkout-service",
      "lastUsedAt": "2026-08-07T18:31:44Z",
      "projectId": "p7Kd2mQx",
      "scope": "api"
    }
  ]
}

Errors

StatuserrorWhen
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.
401unauthorizedNo valid session.
DELETE/v1/mcp-keys/{keyId}Session

Revoke a project key

Revoke a project key and withdraw the project access it held.

The record is kept, marked revoked, so the audit trail survives the credential.

Path parameters

FieldTypeRequiredDescription
keyIdstringrequiredThe key to revoke.
Request · curl
curl -X DELETE "https://runlog-7613480744.us-central1.run.app/v1/mcp-keys/key_2Xm9Qd" \
  -H "Authorization: Bearer $RUNLOG_SESSION_TOKEN" \
  -H "X-Project-Id: p7Kd2mQx"
Response · 204

Errors

StatuserrorWhen
404key not foundNo such key in this project, or it is not yours.
403admin access requiredRetiring an `api`-scoped key withdraws a project grant, so it is the same ADMIN act as minting one.
401unauthorizedNo valid session.

Site keys

The third credential lives beside the other two. A PFW is a website an owner builds in the Design Lens and publishes at a subdomain; its page code carries a site key, which is public by construction — anyone who views source has it — so it authenticates the site, never a person. The key's whole authority is the action list the owner published on it, floored at reads, and it reaches exactly one project.

POST/v1/public/sites/actionsSite key

Dispatch a published action

Run one action the PFW's owner published, against the PFW's own project.

The site key is the credential and it travels in the body, not the `Authorization` header — the caller is page code, not a server holding a secret. Treat it as public: it grants only what the owner published, and rotating it re-keys the site.

The published set is floored at reads. A write action reaches a PFW only when the owner publishes it explicitly, and no publishing raises a visitor to the admin tier.

60 requests per minute per PFW, not per visitor — a busy site shares one bucket.

The generated `Runlog` client on a published page wraps this route; calling it directly is the same contract.

A visitor signed in on the site's own identity tenant sends their ID token as `Authorization: Bearer`; the dispatch then attributes to them. A token on any other tenant — a Runlog session included — is refused exactly as an unresolvable site is.

Body

FieldTypeRequiredDescription
projectIdstringrequiredThe project the PFW belongs to.
keystringrequiredThe PFW's site key, as embedded in its page code.
actionstringrequiredThe catalogued action id to run.
inputobjectoptionalThe action's input, as documented in the action catalog.
sessionIdPlannedstringoptionalThe visitor session to act within. Uploads land in that session's own child project rather than the PFW's.
Request · curl
curl -X POST "https://runlog-7613480744.us-central1.run.app/v1/public/sites/actions" \
  -H "Content-Type: application/json" \
  -d '{
  "action": "code_context_pack",
  "input": {
    "task": "what does this product do?"
  },
  "key": "site_9Qd2Xm",
  "projectId": "p7Kd2mQx"
}'
Response · 200
{
  "action": "code_context_pack",
  "result": {
    "pack": "…"
  }
}

Errors

StatuserrorWhen
404not foundThe request did not resolve to a published PFW (bad or revoked key, unknown origin), the action was not published on this PFW, or it is outside the lane's consequence floor. One status for all of them — the edge never confirms which; the distinction is recorded server-side in the security-event audit.
404unknown actionNo such action in the catalog.
429rate limit exceededThe PFW passed 60 requests/minute. The bucket is per site, so every visitor shares it.
400invalid request bodyThe body is not JSON, or exceeds the 1 MB request-body cap.
500internal errorThe relay failed to serve the request. Retry with backoff; the cause is logged server-side against your request.