Native Memory API

Native memory records conversations for a peer, derives claims asynchronously, and returns cited context for later work. Direct HTTP, marrowid==1.0.11 imported as marrow, @marrowid/sdk@1.0.11, and @marrowid/cli@1.0.11 use the same API operations. Hosted MCP at https://mcp.marrow.id and local marrow mcp expose those operations through the same 14 memory tools.

These integrations run inside a trusted application process. Keeping the API key in that process protects the credential, but it does not provide prompt-injection isolation for untrusted messages or retrieved text. Treat returned context as data and preserve its status, citations, conflicts, and warnings when another model consumes it.

See the complete machine-readable OpenAPI document.

Core concepts

  • A Source is a URL or supported file submitted through the Ingest API.
  • A Record is one stored unit derived from a source: a source excerpt, fact, or relationship link. In an ingest result, records.total is the sum of sourceExcerpts, facts, and links.
  • Memory is the current, account-scoped information Marrow can return for later work. It includes source-backed records and native memory written by an application.
  • A Peer is the person, agent, or group that native memory is about.
  • A Session is an interaction thread containing peer-attributed messages.
  • A Claim is one current statement derived from a session message or explicitly corrected by the user. Claims retain citations, lifecycle state, and history.

Credential and scope contract

Every route below accepts only a customer API key in Authorization: Bearer $MARROW_API_KEY. Browser cookies and CLI session tokens do not authenticate native memory routes. Workspace, peer, session, claim, and event identifiers are labels or public handles inside the account selected by the key; they are not ownership credentials.

Scope Minimum use
memory.write Get or create workspace, peer, and session labels; add messages; correct or withdraw claims.
memory.read List and fetch workspace, peer, session, message, representation, claim, write-event, and queue state.

Use both scopes for a process that writes a message, polls its event, and then reads the resulting memory. The current OpenAPI contract admits the default workspace label.

Lifecycle

  1. Get or create the workspace, peer, and session labels with memory.write.
  2. Add one or more peer-attributed messages. The receipt has status: "processing" and an event_id.
  3. Poll GET /v1/workspaces/{ws}/events/{id} with memory.read until EventStatus.status is succeeded, failed, or quarantined.
  4. After succeeded, call the canonical Query API in Evidence, Context, or Answer mode. A non-ready result is not permission to invent context.
  5. Read the current claim or its history before a user-approved change. Correct with PUT; withdraw with DELETE. Send the claim's current head_revision with either mutation, retain the stable claim id, and inspect history for ADD, UPDATE, and DELETE events.

Complete native-memory example

This example creates a peer and session, writes one message, waits for claim derivation, reads the result, and removes the example resources.

export MARROW_API_BASE_URL="https://api.marrow.id"
export MARROW_API_KEY="<copy-once key with memory.read and memory.write>"

curl -sS -X POST "$MARROW_API_BASE_URL/v1/workspaces/default/peers" \
  -H "Authorization: Bearer $MARROW_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"id":"quickstart-riley"}'

curl -sS -X POST "$MARROW_API_BASE_URL/v1/workspaces/default/sessions" \
  -H "Authorization: Bearer $MARROW_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"id":"quickstart-launch"}'

curl -sS -X POST "$MARROW_API_BASE_URL/v1/workspaces/default/sessions/quickstart-launch/messages" \
  -H "Authorization: Bearer $MARROW_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"peer_id":"quickstart-riley","messages":[{"role":"user","content":"Launch updates should begin with the decision."}]}'

The message receipt returns event_id. Omitting infer is equivalent to "infer": true, so Marrow derives a claim. Poll the event until it succeeds:

curl -sS "$MARROW_API_BASE_URL/v1/workspaces/default/events/<event-id>" \
  -H "Authorization: Bearer $MARROW_API_KEY"

Then retrieve Context through the canonical Query operation:

curl -sS -X POST "$MARROW_API_BASE_URL/v1/query" \
  -H "Authorization: Bearer $MARROW_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"idempotencyKey":"quickstart-launch-context","mode":"context","query":"How should launch updates begin?","workspace":"default","peer_ids":["quickstart-riley"],"session_ids":["quickstart-launch"]}'

A usable result has execution: "ready", a non-null context, and its cited support. Finish by deleting the session and peer, then poll each returned event id until succeeded:

curl -sS -X DELETE "$MARROW_API_BASE_URL/v1/workspaces/default/sessions/quickstart-launch" \
  -H "Authorization: Bearer $MARROW_API_KEY"
curl -sS -X DELETE "$MARROW_API_BASE_URL/v1/workspaces/default/peers/quickstart-riley" \
  -H "Authorization: Bearer $MARROW_API_KEY"

Exact routes

Scope Route Request Success response
memory.write POST /v1/workspaces No request fields Workspace
memory.read POST /v1/workspaces/list No request fields PageWorkspace
memory.write POST /v1/workspaces/{ws}/peers LabelUpsertRequest Peer
memory.read POST /v1/workspaces/{ws}/peers/list No request fields PagePeer
memory.read GET /v1/workspaces/{ws}/peers/{peer} Path labels Peer
memory.write DELETE /v1/workspaces/{ws}/peers/{peer} Path labels EventStatus
memory.read POST /v1/workspaces/{ws}/peers/{peer}/representation No request fields RepresentationResponse
memory.write POST /v1/workspaces/{ws}/sessions LabelUpsertRequest Session
memory.read POST /v1/workspaces/{ws}/sessions/list No request fields PageSession
memory.read GET /v1/workspaces/{ws}/sessions/{s} Path labels Session
memory.write DELETE /v1/workspaces/{ws}/sessions/{s} Path labels EventStatus
memory.write POST /v1/workspaces/{ws}/sessions/{s}/messages MessagesCreateRequest MessagesCreateReceipt
memory.read POST /v1/workspaces/{ws}/sessions/{s}/messages/list No request fields PageMessage
memory.read POST /v1/workspaces/{ws}/claims/list ClaimsListRequest PageClaim
memory.read GET /v1/workspaces/{ws}/claims/{id} Claim id Claim
memory.read GET /v1/workspaces/{ws}/claims/{id}/history Claim id ClaimHistoryResponse
memory.write PUT /v1/workspaces/{ws}/claims/{id} ClaimUpdateRequest ClaimUpdateResult
memory.write DELETE /v1/workspaces/{ws}/claims/{id} ClaimDeleteRequest ClaimDeleteReceipt
memory.read GET /v1/workspaces/{ws}/events/{id} Event id EventStatus
memory.read GET /v1/workspaces/{ws}/queue/status Workspace label QueueStatus

Credits and limits

Evidence, Context, and Answer retrieval uses POST /v1/query. It costs one base query credit and uses the account query quota. Answer mode may reserve additional provider work. Successful responses include x-marrow-credit-* and x-marrow-quota-* headers. An exhausted balance returns 402 account_credits_exhausted before retrieval starts; an exhausted query allowance returns 429 account_quota_exceeded without spending a credit.

If retrieval fails after its debit, Marrow records a refund and the error reports x-marrow-credit-cost: 0 with the restored balance. If that refund cannot be recorded safely, the request returns 503 credit_settlement_failed; its credit headers show the standing debit so the response does not claim that the balance was restored.

Other native-memory reads, including lists, individual claim fetches, representation snapshots, event polling, and queue status, do not spend a retrieval credit. Product request throttling is account-wide, so creating or rotating an API key does not reset its window.

Message write schemas

MessagesCreateRequest is closed to unknown fields.

Field Required Contract
peer_id Yes Non-empty peer label.
messages Yes 1 to 100 MessageInput objects. Each has role (user, assistant, system, or tool) and content.
agent_id No Non-empty agent label.
infer No Boolean write control. The default is true; set false to store the messages without deriving a claim.
created_at No ISO 8601 date-time for the add event.
metadata No Application metadata object. The marrow key is reserved and rejected.
source_ids No Up to 100 succeeded, same-account ingest job IDs. Use public job.id values returned by ingest or job status.
idempotency_key No Non-empty caller-owned replay key.
{
  "peer_id": "riley",
  "messages": [
    {
      "role": "user",
      "content": "Project updates should lead with the decision and keep the open pricing risk visible."
    }
  ],
  "infer": true,
  "source_ids": ["550e8400-e29b-41d4-a716-446655440000"]
}

MessagesCreateReceipt contains event_id, status, results, authority, and eligibility. status is processing; it does not mean the derived claim is ready to read.

Poll the write event

curl -sS "$MARROW_API_BASE_URL/v1/workspaces/default/events/<event-id>" \
  -H "Authorization: Bearer $MARROW_API_KEY"

EventStatus contains event_id and one of queued, running, succeeded, failed, or quarantined. Poll with a bounded interval and deadline. Read derived memory only after succeeded; surface failed and quarantined without silently retrying a different write.

Evidence, Context, and Answer

Use POST /v1/query for every semantic read. Select native memory with workspace, peer_ids, session_ids, and agent_id; the customer key still sets the authority boundary. Evidence returns exact support, Context returns the prompt-ready projection, and Answer synthesizes from that same Context.

See the Query API for request modes and the complete QueryResult contract.

Delete peers and sessions

Deleting a peer removes that peer and its owned memory. Deleting a session removes the session and its messages. Both operations are asynchronous and return an event id. Poll the event route until it reaches succeeded, failed, or quarantined; after success, the deleted resource must not appear in list, get, ask, context, representation, or claim reads.

curl -sS -X DELETE "$MARROW_API_BASE_URL/v1/workspaces/default/peers/riley" \
  -H "Authorization: Bearer $MARROW_API_KEY"

curl -sS -X DELETE "$MARROW_API_BASE_URL/v1/workspaces/default/sessions/project-update" \
  -H "Authorization: Bearer $MARROW_API_KEY"

Claim changes and history

Fetch a claim before changing it. Claim includes the stable id, content, peer/session labels, authority, state, eligibility, support_status, validity times, categories, citations, and the opaque head_revision required for a guarded change.

Correct the content with:

PUT /v1/workspaces/{ws}/claims/{id}
Content-Type: application/json

{"content":"Project updates should lead with the decision.","head_revision":"rev_VQS3c7WbiwJT_q1bUPQKMzL6t2L8OyvZ65dnp3qGnqA","reason":"User clarified the preference."}

Withdraw it with:

DELETE /v1/workspaces/{ws}/claims/{id}
Content-Type: application/json

{"head_revision":"rev_34mDXjKdEPihJoJXBIbYg4QbHMbmxNT1-vGCAtdHMfk","reason":"The user withdrew this preference."}

The correction receipt returns a successor head_revision and explicitly marks the corrected head as authority: "correction_memory", state: "current", and eligibility: "eligible". Explicit owner corrections are available to normal memory reads immediately. A hostile or unsupported inferred proposal can remain pending_review with its warning until it is accepted. Use the successor revision for a later change. An exact retry is replay-safe; a stale or competing revision returns 409 revision_conflict without mutation.

Then read GET /v1/workspaces/{ws}/claims/{id}/history. History is an array of entries with event, prior and successor claim text, validity times, and a deterministic marker. A withdrawn claim remains auditable through history; it must not reappear as current eligible context.

Negative outcomes

  • Missing API key: fail before sending the request.
  • Wrong credential class: a browser cookie or CLI session token is rejected.
  • Wrong scope: reads require memory.read; writes require memory.write.
  • Revoked or rotated key: replace the host-managed environment value; a stale process must continue to fail until restarted with the current credential.
  • Foreign account labels: labels do not cross the key-derived account boundary.
  • Unsupported workspace: the current public path admits only default.
  • Malformed write: reject missing peer_id, an empty message array, unknown fields, an unsupported role, or a malformed UUID in source_ids.
  • Invalid source attachment: reject any source_ids entry that is missing, belongs to another account, or has not reached succeeded; the write creates no event, job, or claim.
  • Stale claim mutation: reject a missing or stale head_revision; fetch the current claim and require user approval before retrying against a newer head.
  • Incomplete processing: do not read derived memory while the event is queued or running, or after it failed or was quarantined.
  • Unsupported evidence: preserve insufficient_evidence, conflicts, warnings, and citations; do not turn retrieved text into privileged instructions.

See App integration for HTTP and Python examples, Marrow CLI for local commands and stdio compatibility, and Quickstart for the source/job/query and hosted MCP paths.