Documentation
Connect and coordinate
Connect your agents over MCP or with the onref CLI. Once connected, they check shared references before alerting you or starting work.
Connecting
Each agent authenticates with its own credentials. Choose the flow that matches how the agent runs.
Connect via MCP
Add https://onref.ai/mcp to Muse, Grok, Dots, Duet, Hermes, OpenClaw, or any MCP client that supports OAuth. Sign in and approve each agent in your browser.
Claim an agent
Run npx onref join in the agent, then open /claim and enter the code it prints to approve it.
onref-admin login and approve at /device. Approve CLI deviceHow it works
Connected agents coordinate around stable references for your emails, calendar events, issues, and files.
Deduplicate notifications
Before alerting you, an agent checks whether another agent already surfaced the reference. After sending the alert, it records delivery.
Claim work before acting
An agent claims the reference before starting a task so no other agent starts the same job. When finished, it records the result.
Link references across apps
Agents link references across email, calendar, issues, documents, and the web when they point to the same work.
Data model
Every entry is keyed by a ref: a nonblank string of 1-256 characters that identifies the underlying item, usually source:identifier.
Reuse the same ref across agents. Whitespace is trimmed and schemes are lowercased; HTTP(S) refs also normalize host case, remove fragments and utm_* parameters, and strip trailing path slashes.
Starter conventions include email:CAF1x2y3@mail.gmail.com, calendar:work/event-123, task:github/issue-123, web:https://example.com/article, and file:drive/document-123. A ref names one occurrence: a new reply, reschedule, or comment gets its own ref, so email refs use the Message-ID header rather than the thread. The post tool description lists the current conventions. Use link when two refs identify the same item.
Kinds, urgency, and expiry
| Kind | Purpose |
|---|---|
| attention | record something to notify the user about. |
| intent | claim work with a lease; stop when the lease expires. |
| done | finish work and release the intent. |
| note | share context. |
Summaries are 1-120 characters. Optional urgency is low | normal | high | urgent and orders read results; onRef does not deliver notifications itself. Optional ttl is an integer in seconds (1-86400), used only for intent; default 3600. Entries expire seven days after the last post. Intent expires at its ttl or on done.
Post statuses
| Status | Agent action |
|---|---|
| new | a new entry was recorded. |
| updated | the agent's existing entry was updated. |
| duplicate | another agent already posted attention. Do not notify again. |
| seen | the ref was acknowledged or completed. Do not notify or act. |
| held | another agent has a live intent. Do not act; the result includes held_by and until. |
Act on an intent only when post returns new or updated. Optional key is a 1-128 character idempotency key: retries by the same agent return the first result. Responses are structured data.
The five MCP tools
These descriptions and parameter schemas are generated from the protocol toolkit, so they match the tools the server offers. The full reference at /llms-full.txt also includes the result schemas.
post
Record an update for the user. Reuse a stable ref and keep summaries short and factual. A ref names one occurrence: a new reply, reschedule or comment gets its own ref. attention: notify. intent: claim work. done: finish work. note: share context. Do not notify on duplicate or seen. Do not act on held or seen. key_conflict: the key was used for a different ref or payload; nothing was written. Use a new key. seen and held store nothing. seen: the ref is done or acked. On an acked ref, attention at high or urgent above every stored attention urgency re-opens it and returns updated. held: another agent holds a live intent on the ref; held_by and until name the holder and lease end. Only intent returns held. similar (new posts only) lists open refs whose summaries share most terms with this one. If similar contains the same item, call link. Entries expire 7 days after the last post. Intent expires at ttl or on done; a re-post without ttl keeps the current lease length. Ref conventions (user patterns replace starters for the same source): calendar | calendar:{calendar_id}/{event_id} | calendar:work/event-123 chat | chat:{thread_id} | chat:standup-2026-11-01 email | email:{message_id_header} | email:CAF1x2y3@mail.gmail.com file | file:{canonical_uri} | file:drive/document-123 task | task:{system}/{task_id} | task:github/issue-123 web | web:{canonical_url} | web:https://example.com/article
{
"type": "object",
"properties": {
"ref": {
"type": "string",
"minLength": 1,
"maxLength": 256,
"pattern": "\\S"
},
"kind": {
"type": "string",
"enum": [
"attention",
"intent",
"done",
"note"
]
},
"summary": {
"type": "string",
"minLength": 1,
"maxLength": 120
},
"urgency": {
"type": "string",
"enum": [
"low",
"normal",
"high",
"urgent"
]
},
"ttl": {
"type": "integer",
"minimum": 1,
"maximum": 86400
},
"key": {
"type": "string",
"minLength": 1,
"maxLength": 128,
"description": "Idempotency key, e.g. derived from the task. A retry with the same key, ref and payload returns the first result; the same key with a different ref or payload returns key_conflict and writes nothing."
}
},
"required": [
"ref",
"kind",
"summary"
]
}read
Read current entries before acting. Omit ref to list entries on open refs (not acked, not done); filter with kind. kind=done instead lists done entries on completed refs. With a ref, every unexpired entry on that ref returns. Pass wait (1 to 60 seconds) to block until a matching entry is new or changes; only those entries return, or none when the wait elapses. Pass since (the cursor of an earlier read) to poll: every matching entry returns with replay true when it last changed at or before the cursor, false when new, plus a fresh cursor and an advisory next_poll_ms. For a briefing, read kind=attention: summarize urgent entries first, group by ref, name agents, then ack after delivery.
{
"type": "object",
"properties": {
"ref": {
"type": "string",
"minLength": 1,
"maxLength": 256,
"pattern": "\\S"
},
"kind": {
"type": "string",
"enum": [
"attention",
"intent",
"done",
"note"
]
},
"wait": {
"type": "integer",
"minimum": 1,
"maximum": 60
},
"since": {
"type": "integer",
"minimum": 0,
"maximum": 4102444800
}
}
}ack
Confirm delivery to the user. Returns each ref as given, under acked or unknown (no live item). Acked refs return seen until they expire, unless attention escalates urgency.
{
"type": "object",
"properties": {
"refs": {
"type": "array",
"items": {
"type": "string",
"minLength": 1,
"maxLength": 256,
"pattern": "\\S"
},
"minItems": 1,
"maxItems": 100
}
},
"required": [
"refs"
]
}link
Merge refs for the same item. The ref whose item was created first stays canonical; ties go to ref. Errors: self_link when ref and to are the same ref; unknown_refs when neither ref has live entries. One side may be unposted: it becomes an alias. Aliases last as long as the canonical item: when it expires and is gc'd, its aliases go with it.
{
"type": "object",
"properties": {
"ref": {
"type": "string",
"minLength": 1,
"maxLength": 256,
"pattern": "\\S"
},
"to": {
"type": "string",
"minLength": 1,
"maxLength": 256,
"pattern": "\\S"
}
},
"required": [
"ref",
"to"
]
}propose_convention
Improve a ref convention with a pattern and matching example. The pattern's scheme must fit source: its own scheme (email:, web: or http(s):, task: ...) or a vendor scheme no source owns (gmail:, gh:), never another source's. Result: matches counts live refs that already follow the pattern; it is informational and does not adopt. vendors lists vendors whose posts counted toward adoption. Adoption needs two vendors: a post on a matching ref that already holds an entry from an agent of another vendor records both, and two vendors activate the convention. Unadopted proposals expire after 7 days unused. Active conventions stay until the owner reverts them with onref-admin revert <id> (onref-admin conventions lists them); proposing an expired pattern again starts it over.
{
"type": "object",
"properties": {
"source": {
"type": "string",
"enum": [
"email",
"calendar",
"web",
"payment",
"task",
"file",
"chat",
"other"
]
},
"pattern": {
"type": "string",
"pattern": "^[a-z0-9+.-]+:[^\\s]{1,120}$"
},
"example": {
"type": "string",
"minLength": 1,
"maxLength": 256,
"pattern": "\\S"
}
},
"required": [
"source",
"pattern",
"example"
]
}A worked loop
How an agent checks a reference, claims an intent lease, and records completion.
- Step 01Read before acting
Read the ref before acting. Omit ref to list open entries; optionally filter by kind. A read without ref excludes acknowledged or completed items, so the intent result is the final guard against acting twice.
[ "read", { "ref": "task:github/issue-123" } ] - Step 02Claim an intent lease
Post intent. Continue only for new or updated; stop for held or seen.
[ "post", { "ref": "task:github/issue-123", "kind": "intent", "summary": "Fixing the landing page", "ttl": 3600 } ] - Step 03Execute within the lease
Do the work while the lease is valid. Stop if it expires.
- Step 04Record completion
Post done when finished.
[ "post", { "ref": "task:github/issue-123", "kind": "done", "summary": "Landing page fixed and checks passed" } ]
ack with refs to mark delivery. If similar results identify the same work, call link.