Connect

One endpoint. One key.

Your agent connects over MCP and keeps every tool it already has. What changes is where its writes go: into a queue a person answers, rather than straight into your workspace.

Works with any MCP-compatible agent · Two POSTs if you would rather not use MCP at all

01

Connect

One endpoint, one key. Your agent keeps the tools and the framework it already has. The key resolves to exactly one agent in exactly one workspace, and nothing in a request can name a different one.

Remote MCP · Live

02

Land

Its writes become proposals instead of changes. Each one carries what it wants to do, the evidence behind it, and the confidence it reported. Nothing has moved in your workspace yet.

Proposal queue · Live

03

Decide

Approve it, untick the parts you do not want, or deny it and remember that answer as a standing rule. Your verdict is the write.

Web and desktop · Live

Nothing lands until a decision is recorded.

Quickstart

From nothing to a proposal in somebody's queue.

01

Mint a key

A workspace owner or admin creates the agent and mints its key in the desktop app. On a fresh install there is no Approvals entry in the sidebar yet: open Settings, then Cloud, then "Mint a key in Approvals". Approvals stays in the sidebar once you have been there. The plaintext is shown once. The ceiling you pick there is the ceiling for good: viewer, commenter, or editor.

Every agent request carries two headers
apikey: <the project's publishable key>
authorization: Bearer alk_<16 hex>_<48 hex>
content-type: application/json

The apikey header is the gateway's, not a second credential. It appears in every browser client of the same project. Authorization is entirely the agent key's job. The base URL and that publishable key are handed to you with the key itself.

02

Point a host at it, or POST it yourself

The MCP route is the one with no integration code in it. Point your host at the URL, give it the same key, and the Life OS appears as tools: eleven that read, seven that propose. Every mutation answers with a proposal id and says in the response that nothing has changed yet.

MCP server config
{
  "mcpServers": {
    "aurum-life": {
      "type": "http",
      "url": "$AURUM_API/functions/v1/work_mcp_v1",
      "headers": { "Authorization": "Bearer alk_..." }
    }
  }
}

If your agent is not an MCP host, submit the batch yourself. One idempotency key per proposal, the events it wants to write, and a confidence figure if you have one worth reporting.

POST $AURUM_API/functions/v1/work_submit_v1
{
  "proposals": [
    {
      "idempotency_key": "archive-onboarding-2026-08-20",
      "title": "Archive the three finished onboarding projects",
      "events": [
        {
          "entity_kind": "entity",
          "op": "update",
          "entity_id": "6f1b6b0a-...",
          "payload": { "status": "archived" }
        }
      ],
      "confidence": 0.82,
      "expires_in_hours": 48
    }
  ]
}
200
{
  "results": [
    {
      "idempotency_key": "archive-onboarding-2026-08-20",
      "status": "queued",
      "proposal_id": "..."
    }
  ]
}

Queued means the work is durable. Your process may exit. A proposal that nobody answers expires on a stated date, which defaults to a fortnight and which you can set per proposal.

03

Poll for the verdict

Ask for everything above the cursor you last acted on. A fresh process with the stored number picks up exactly what it missed, and the poll is per agent rather than per proposal, so waiting on one answer reads every other answer too.

POST $AURUM_API/functions/v1/work_verdicts_v1
{ "after_seq": 41, "limit": 50 }
200
{
  "verdicts": [
    {
      "proposal_id": "...",
      "idempotency_key": "archive-onboarding-2026-08-20",
      "verdict": "approved_with_edits",
      "verdict_seq": 42,
      "verdict_reason": "keep only the first two",
      "edited_events": [ ... ],
      "applied_event_ids": [ "..." ]
    }
  ],
  "next_after_seq": 42,
  "has_more": false
}

An empty body is a valid poll meaning everything since the beginning, so the smallest client that works is a bare POST with a key.

The surface

Five endpoints, and you write two.

POST work_submit_v1

called by an agent · authenticated by alk_ key

Propose work. One call carries a batch, and every proposal in it is answered by idempotency key rather than by position.

POST work_verdicts_v1

called by an agent · authenticated by alk_ key

Poll for verdicts since your cursor. An empty body is a valid poll meaning everything since the beginning.

POST work_mcp_v1

called by an agent host · authenticated by alk_ key

The same machinery as MCP tools. Eleven tools read, seven propose, and every mutation answers with a proposal id.

POST work_oauth_token_v1

called by an OAuth client · authenticated by client id and secret

Exchange client credentials for a short-lived agent key, for runtimes that know POST /token and do not know a pasted header.

POST work_verdict_v1

called by a person · authenticated by a signed-in session

Approve, narrow, or deny. Documented for completeness: an agent never calls this one.

The OAuth exchange authorizes nothing new. It resolves a client id and secret to an agent that already exists and mints an ordinary key with an hour on it, under that agent's own ceiling. There is no scope parameter, because one that was accepted and then ignored would be a lying label. Creating the credential pair has no screen in the app yet, so ask and we will mint it with you.

Hosts that speak the 2025-11-25 task surface can ask for a task handle instead of a plain result, and a task id is a proposal id. No shipped agent host speaks that extension unprompted today, so the plain result above is the path to build on and the one that is verified over the wire.

The key

What a key can do, and what it cannot.

One agent, one workspace. The key is the whole scope. Nothing in a request body can name a different agent or a different space, so cross-workspace targeting is not refused, it is unrepresentable.

Capped below admin. A ceiling of viewer, commenter, or editor. Managing members and deleting a workspace are not capabilities an agent can hold, at any tier, by any setting.

The plaintext exists once. Minting returns the token and stores only its hash, so no later read recovers it. A short prefix identifies a key in a list or a log line without the key being readable from it.

Rotate without downtime. Mint the new key, deploy it, revoke the old one. An agent may hold several at a time, so the narrow tool exists for the ordinary case.

One switch stops everything. Disabling the agent stops every key it holds at once, for when you are not sure how many are in the wild.

Refusals are opaque on purpose. A missing header, a malformed key, an unknown key, a revoked key and a disabled agent all answer the same 401. An informative refusal would turn the endpoint into a probe.

Before you write the client

The six that fail quietly.

Every one of these is a client bug that reports nothing. They are on this page rather than in a footnote for that reason.

The cursor

Each verdict's sequence number is allocated inside the statement that records it, so acceptance order and poll order are one order by construction. Keep the highest value you have acted on, persist it somewhere that outlives the process, and ask for what is strictly above it. A page is always a prefix, and it is cut short rather than carrying you past a verdict whose result has not been written yet.

Backoff

Nothing here long-polls, because the thing you are waiting for is a person. Poll on a schedule that backs off toward the minute mark. A tight interval buys no speed and a rate-limited response is one of the two answers you should retry.

Idempotency

Re-submitting a key you have already used returns the same proposal and its live status, including a verdict you missed while your process was down. That property is what makes retrying a transient failure safe rather than a way to duplicate somebody's inbox.

A narrowed approval

approved_with_edits means the person kept some of what you proposed. Reconcile against the edited events, never against your own submission. An absent edit list means it ran as submitted; an empty one means nothing was approved. Those are different statements and a client has to keep them apart.

A partial or unsettled apply

An approved verdict beside an apply error is an honest half-done state, and it is the one outcome that otherwise reads as a clean success. A third state exists as its own field: the writes never reported and no longer can. Reconcile that one by hand and never bank it as applied.

An open vocabulary

Statuses and verdicts widen ahead of your client, which is what lets a new kind ship without breaking every integration at once. Never read an unrecognised value as pending, because pending is the one answer that makes an agent wait forever. Never collapse an unknown token onto denied, because that asserts something nobody said.

The client

MIT, dependency-free, copied in.

A TypeScript client implements the loop, the cursor, the backoff, and every trap above. It is MIT-licensed and it depends on nothing.

It is not published to npm, and its manifest is marked private so it cannot be published by accident. Publishing is a decision nobody has made yet. You copy the file into your project, or you write the two POSTs above yourself, which is the reason they are on this page in full rather than behind a signup.

Ask for the client with your pilot, or write your own against this page.

Start a pilotGet the desktop app

Free tier: one workspace, one agent, no card. Accounts are created in the desktop app. See pricing →