Office link
Proposed (decision 0016). Link version: 2. The office link connects an office to a monitoring or management service, for example a dashboard. The office connects out to the service; the service never connects to an office. The service keeps no office data: it asks the office over this connection, so an office that is offline shows nothing. The messages are defined as schemas in the @outstaff/protocol package.
Connecting
Section titled “Connecting”- The office opens a WebSocket to
link.urlfrom the team file, with the headersAuthorization: Bearer <OUTSTAFF_LINK_TOKEN>andOutstaff-Link-Version: 2. A service that does not accept the token refuses the upgrade (HTTP 401 or 403). - Messages are JSON text frames:
{ "type": "request", "payload": { ... } }. A frame that does not parse is ignored; a request that does not, when itsidcan be read, gets aninvaliderror. - The service sends
welcomefirst. With anotherversionthe office closes the connection with code 4426. After the welcome the office sendspeople, then notifications as things change, and answers requests. - Either side sends
pingwhen it has been quiet; the other answerspong. The office closes a connection that stays silent for three of its ping intervals. - When the connection drops or is refused, the office tries again with a backoff that doubles up to a cap. The link never holds up the office.
Requests
Section titled “Requests”The service sends request with an id, a method and its params; the office answers one response with that id and either result or error. Requests are answered in any order. A request the office cannot answer in time gets timeout. Times are ISO-8601 UTC; cursors are times, never ids.
Error codes: unknown_method, invalid, not_found, timeout, failed.
Methods
Section titled “Methods”people
Section titled “people”The people snapshot: each person, connected or not, with the item it works on and its queue in run order, each with why it is queued.
Params:
No fields.
Result:
| Field | Type | Required | Description |
|---|---|---|---|
at |
string | yes | ISO-8601 time, UTC. |
people |
list of objects | yes | |
people[].handle |
string | yes | |
people[].name |
string | yes | |
people[].role |
string | yes | |
people[].runtime |
string | yes | The runtime adapter the person is configured with, for example claude: their own runtime, else the team’s integrations.runtime. |
people[].model |
string | yes | The model the person is configured with: their model from team.yaml, which every person has. |
people[].connected |
boolean | yes | Whether the person’s agent is connected to the office now. |
people[].counts |
object | yes | How much work the person has. |
people[].counts.queued |
integer | yes | Items waiting to run. |
people[].counts.running |
integer | yes | Items running now. |
people[].current |
object (id, kind, title, subject, startedAt, runtime, model, lastHeartbeat) or null | yes | The item the agent is working on; null when idle. |
people[].queue |
list of objects | yes | Queued items, in the order they run. |
people[].queue[].id |
string | yes | Id of the work item. |
people[].queue[].kind |
string | yes | What the item is, for example assigned or review_feedback. |
people[].queue[].title |
string | yes | |
people[].queue[].subject |
object (type, ref) or null | yes | What the item is about: a ticket key, a pull request (host/owner/name#n) or a document id. |
people[].queue[].priority |
integer | yes | Lower runs first. |
people[].queue[].trigger |
human or agent or automation or schedule |
yes | |
people[].queue[].reason |
string | yes | Why the item is queued, in words. |
people[].queue[].scheduledBy |
object (policy, eventId) or null | yes | The policy and event that queued it, when the event pipeline did; null otherwise. |
events
Section titled “events”Traces of the events the pipeline judged, newest first, filtered on the office, with the total that match. Page with before = nextBefore.
Params:
| Field | Type | Required | Description |
|---|---|---|---|
since |
string | no | Only events after this time. |
before |
string | no | Only events before this time: the cursor for the next page. |
limit |
integer | no | How many, at most. Default 50. |
source |
string | no | Only events from this source, for example vcs or tracker. |
person |
string | no | Only events this person caused, or that wake or act as them. |
ticket |
string | no | Only events about this ticket. |
pr |
string | no | Only events about this pull request: owner/name#number. |
Result:
| Field | Type | Required | Description |
|---|---|---|---|
events |
list of objects | yes | Newest first. |
events[].event |
object | yes | |
events[].event.id |
string | yes | <source>:<external id>; the id ost explain takes. |
events[].event.source |
vcs or tracker or knowledge or chat or office |
yes | Where the event came from: the code host, the tracker, the knowledge base, chat, or the office itself. |
events[].event.type |
string | yes | |
events[].event.at |
string | yes | When the event happened. |
events[].event.subject |
object | yes | |
events[].event.subject.ticket |
string | no | |
events[].event.subject.pr |
object | no | |
events[].event.subject.pr.repo |
object | yes | |
events[].event.subject.pr.repo.host |
string | yes | |
events[].event.subject.pr.repo.owner |
string | yes | |
events[].event.subject.pr.repo.name |
string | yes | |
events[].event.subject.pr.number |
integer | yes | |
events[].event.subject.doc |
string | no | |
events[].event.subject.chat |
object | no | |
events[].event.subject.chat.channel |
string | yes | |
events[].event.subject.chat.thread |
string | no | |
events[].event.actor |
object | yes | |
events[].event.actor.kind |
human or agent or automation |
yes | |
events[].event.actor.id |
string | yes | |
events[].event.data |
object | yes | Event data as intake normalized it: a ticket move has canonical statuses in from and to and the tracker’s names in fromName and toName. |
events[].urls |
object | yes | Links to the event’s subject, where the office can resolve them. |
events[].urls.ticket |
string | no | |
events[].urls.pr |
string | no | |
events[].urls.doc |
string | no | |
events[].urls.chat |
string | no | |
events[].policies |
list of objects | yes | Every policy, built-in then custom, in order. |
events[].policies[].policy |
string | yes | |
events[].policies[].matched |
boolean | yes | |
events[].policies[].reason |
string | yes | Why it matched or did not. |
events[].policies[].failed |
any | no | For a policy that listened but did not match: the condition that failed. |
events[].policies[].proposals |
list of any | yes | What it proposed, when it matched. |
events[].aggregation |
list of objects | yes | What the aggregation made of each proposal, in order. |
events[].aggregation[].policy |
string | yes | |
events[].aggregation[].proposal |
integer | yes | The proposal’s index in its policy’s proposals. |
events[].aggregation[].outcome |
kept or merged or suppressed or rejected or dropped or error |
yes | kept as a decision, merged into another wake of the same person and subject, suppressed by another policy, rejected by an invariant, dropped as pointless or by a conflict it lost, error when the policy threw. |
events[].aggregation[].reason |
string | no | |
events[].aggregation[].decision |
integer | no | The index in decisions of the decision it became or was merged into. |
events[].decisions |
list of any | yes | What the office does: the aggregation’s result. |
events[].results |
object | no | Each act’s progress, by act id (<event id>:<policy>): the agent it is for makes the changes, so an act for an offline agent waits until it connects. |
total |
integer | yes | How many events match the filters, however many this page holds. |
nextBefore |
string | no | Pass as before for the next page; absent when there are no more events. |
One event’s trace, by event id. not_found when the office has none.
Params:
| Field | Type | Required | Description |
|---|---|---|---|
id |
string | yes | The event’s id, as in its trace. |
Result:
| Field | Type | Required | Description |
|---|---|---|---|
trace |
object | yes | |
trace.event |
object | yes | |
trace.event.id |
string | yes | <source>:<external id>; the id ost explain takes. |
trace.event.source |
vcs or tracker or knowledge or chat or office |
yes | Where the event came from: the code host, the tracker, the knowledge base, chat, or the office itself. |
trace.event.type |
string | yes | |
trace.event.at |
string | yes | When the event happened. |
trace.event.subject |
object | yes | |
trace.event.subject.ticket |
string | no | |
trace.event.subject.pr |
object | no | |
trace.event.subject.pr.repo |
object | yes | |
trace.event.subject.pr.repo.host |
string | yes | |
trace.event.subject.pr.repo.owner |
string | yes | |
trace.event.subject.pr.repo.name |
string | yes | |
trace.event.subject.pr.number |
integer | yes | |
trace.event.subject.doc |
string | no | |
trace.event.subject.chat |
object | no | |
trace.event.subject.chat.channel |
string | yes | |
trace.event.subject.chat.thread |
string | no | |
trace.event.actor |
object | yes | |
trace.event.actor.kind |
human or agent or automation |
yes | |
trace.event.actor.id |
string | yes | |
trace.event.data |
object | yes | Event data as intake normalized it: a ticket move has canonical statuses in from and to and the tracker’s names in fromName and toName. |
trace.urls |
object | yes | Links to the event’s subject, where the office can resolve them. |
trace.urls.ticket |
string | no | |
trace.urls.pr |
string | no | |
trace.urls.doc |
string | no | |
trace.urls.chat |
string | no | |
trace.policies |
list of objects | yes | Every policy, built-in then custom, in order. |
trace.policies[].policy |
string | yes | |
trace.policies[].matched |
boolean | yes | |
trace.policies[].reason |
string | yes | Why it matched or did not. |
trace.policies[].failed |
any | no | For a policy that listened but did not match: the condition that failed. |
trace.policies[].proposals |
list of any | yes | What it proposed, when it matched. |
trace.aggregation |
list of objects | yes | What the aggregation made of each proposal, in order. |
trace.aggregation[].policy |
string | yes | |
trace.aggregation[].proposal |
integer | yes | The proposal’s index in its policy’s proposals. |
trace.aggregation[].outcome |
kept or merged or suppressed or rejected or dropped or error |
yes | kept as a decision, merged into another wake of the same person and subject, suppressed by another policy, rejected by an invariant, dropped as pointless or by a conflict it lost, error when the policy threw. |
trace.aggregation[].reason |
string | no | |
trace.aggregation[].decision |
integer | no | The index in decisions of the decision it became or was merged into. |
trace.decisions |
list of any | yes | What the office does: the aggregation’s result. |
trace.results |
object | no | Each act’s progress, by act id (<event id>:<policy>): the agent it is for makes the changes, so an act for an offline agent waits until it connects. |
Work items, newest first by when they were created, optionally of one status or person, with the total that match. Page with before = nextBefore.
Params:
| Field | Type | Required | Description |
|---|---|---|---|
status |
queued or active or waiting or done or blocked or canceled |
no | Only items with this status. |
person |
string | no | Only this person’s items. |
before |
string | no | Only items created before this time: the cursor for the next page. |
limit |
integer | no | How many, at most. Default 50. |
Result:
| Field | Type | Required | Description |
|---|---|---|---|
items |
list of objects | yes | Newest first, by when they were created. |
items[].id |
string | yes | Id of the work item. |
items[].kind |
string | yes | What the item is, for example assigned or review_feedback. |
items[].title |
string | yes | |
items[].subject |
object (type, ref) or null | yes | What the item is about: a ticket key, a pull request (host/owner/name#n) or a document id. |
items[].person |
string | yes | |
items[].status |
queued or active or waiting or done or blocked or canceled |
yes | |
items[].priority |
integer | yes | |
items[].trigger |
human or agent or automation or schedule |
yes | |
items[].createdAt |
string | yes | ISO-8601 time, UTC. |
items[].updatedAt |
string | yes | ISO-8601 time, UTC. |
items[].scheduledBy |
object (policy, eventId) or null | yes | The policy and event that queued it, when the event pipeline did. |
total |
integer | yes | How many items match the filters, however many this page holds. |
nextBefore |
string | no | Pass as before for the next page; absent when there are no more items. |
One work item with its runs, the end of its journal and the traces of the events that queued or joined it. not_found when there is no such item.
Params:
| Field | Type | Required | Description |
|---|---|---|---|
id |
string | yes | The work item’s id. |
Result:
| Field | Type | Required | Description |
|---|---|---|---|
item |
object | yes | |
item.id |
string | yes | Id of the work item. |
item.kind |
string | yes | What the item is, for example assigned or review_feedback. |
item.title |
string | yes | |
item.subject |
object (type, ref) or null | yes | What the item is about: a ticket key, a pull request (host/owner/name#n) or a document id. |
item.person |
string | yes | |
item.status |
queued or active or waiting or done or blocked or canceled |
yes | |
item.priority |
integer | yes | |
item.trigger |
human or agent or automation or schedule |
yes | |
item.createdAt |
string | yes | ISO-8601 time, UTC. |
item.updatedAt |
string | yes | ISO-8601 time, UTC. |
item.scheduledBy |
object (policy, eventId) or null | yes | The policy and event that queued it, when the event pipeline did. |
item.runs |
list of objects | yes | Every run, oldest first. |
item.runs[].startedAt |
string | yes | ISO-8601 time, UTC. |
item.runs[].finishedAt |
string or null | yes | |
item.runs[].outcome |
string or null | yes | How the run ended, for example done or blocked. |
item.runs[].error |
string or null | yes | |
item.runs[].runtime |
string or null | yes | The runtime adapter that ran it, for example claude; null until the agent said. |
item.runs[].model |
string or null | yes | The model that ran it: the concrete id the runtime reported, else the one the person is configured with; null when neither is known. |
item.runs[].usage |
object (totals, models) or null | yes | The tokens and API-equivalent cost of the run; null when the agent reported none (an unfinished run, or one that ended without the agent). |
item.journal |
object | yes | The item’s journal, in short. |
item.journal.entries |
integer | yes | How many entries the journal has. |
item.journal.latest |
list of objects | yes | The latest few entries, oldest first; long ones are cut. |
item.journal.latest[].at |
string | yes | ISO-8601 time, UTC. |
item.journal.latest[].author |
string | yes | |
item.journal.latest[].text |
string | yes | |
traces |
list of objects | yes | The events whose wakes queued or joined the item, oldest first. |
traces[].event |
object | yes | |
traces[].event.id |
string | yes | <source>:<external id>; the id ost explain takes. |
traces[].event.source |
vcs or tracker or knowledge or chat or office |
yes | Where the event came from: the code host, the tracker, the knowledge base, chat, or the office itself. |
traces[].event.type |
string | yes | |
traces[].event.at |
string | yes | When the event happened. |
traces[].event.subject |
object | yes | |
traces[].event.subject.ticket |
string | no | |
traces[].event.subject.pr |
object | no | |
traces[].event.subject.pr.repo |
object | yes | |
traces[].event.subject.pr.repo.host |
string | yes | |
traces[].event.subject.pr.repo.owner |
string | yes | |
traces[].event.subject.pr.repo.name |
string | yes | |
traces[].event.subject.pr.number |
integer | yes | |
traces[].event.subject.doc |
string | no | |
traces[].event.subject.chat |
object | no | |
traces[].event.subject.chat.channel |
string | yes | |
traces[].event.subject.chat.thread |
string | no | |
traces[].event.actor |
object | yes | |
traces[].event.actor.kind |
human or agent or automation |
yes | |
traces[].event.actor.id |
string | yes | |
traces[].event.data |
object | yes | Event data as intake normalized it: a ticket move has canonical statuses in from and to and the tracker’s names in fromName and toName. |
traces[].urls |
object | yes | Links to the event’s subject, where the office can resolve them. |
traces[].urls.ticket |
string | no | |
traces[].urls.pr |
string | no | |
traces[].urls.doc |
string | no | |
traces[].urls.chat |
string | no | |
traces[].policies |
list of objects | yes | Every policy, built-in then custom, in order. |
traces[].policies[].policy |
string | yes | |
traces[].policies[].matched |
boolean | yes | |
traces[].policies[].reason |
string | yes | Why it matched or did not. |
traces[].policies[].failed |
any | no | For a policy that listened but did not match: the condition that failed. |
traces[].policies[].proposals |
list of any | yes | What it proposed, when it matched. |
traces[].aggregation |
list of objects | yes | What the aggregation made of each proposal, in order. |
traces[].aggregation[].policy |
string | yes | |
traces[].aggregation[].proposal |
integer | yes | The proposal’s index in its policy’s proposals. |
traces[].aggregation[].outcome |
kept or merged or suppressed or rejected or dropped or error |
yes | kept as a decision, merged into another wake of the same person and subject, suppressed by another policy, rejected by an invariant, dropped as pointless or by a conflict it lost, error when the policy threw. |
traces[].aggregation[].reason |
string | no | |
traces[].aggregation[].decision |
integer | no | The index in decisions of the decision it became or was merged into. |
traces[].decisions |
list of any | yes | What the office does: the aggregation’s result. |
traces[].results |
object | no | Each act’s progress, by act id (<event id>:<policy>): the agent it is for makes the changes, so an act for an offline agent waits until it connects. |
activity
Section titled “activity”How much happened in each of the last few windows of time (default the last hour and the last day): events that happened and work items created. Exact, since the office counts them in its store.
Params:
| Field | Type | Required | Description |
|---|---|---|---|
windows |
list of integer | no | Window lengths in seconds, each counted back from now. Default one hour and one day. |
Result:
| Field | Type | Required | Description |
|---|---|---|---|
at |
string | yes | The office’s time of the count; each window ends here. |
windows |
list of objects | yes | In the order requested. |
windows[].seconds |
integer | yes | The window’s length, as requested. |
windows[].since |
string | yes | Where the window starts: at minus seconds. |
windows[].events |
integer | yes | Events that happened in the window. |
windows[].items |
integer | yes | Work items created in the window. |
Tokens and API-equivalent cost of agent runs that finished in a window (default all of them): per person, with the part of each person’s runs that names no ticket (unattributed), and per day. The cost is what the tokens cost at API list prices, not what a subscription bills.
Params:
| Field | Type | Required | Description |
|---|---|---|---|
since |
string | no | Count runs that finished at or after this time. |
until |
string | no | Count runs that finished before this time. Default now. |
Result:
| Field | Type | Required | Description |
|---|---|---|---|
since |
string or null | yes | As asked; null for since the start. |
until |
string | yes | ISO-8601 time, UTC. |
byPerson |
list of objects | yes | Each person with runs in the window, largest cost first. |
byPerson[].person |
string | yes | |
byPerson[].totals |
object | yes | Every run of the person. |
byPerson[].totals.inputTokens |
integer | yes | |
byPerson[].totals.outputTokens |
integer | yes | |
byPerson[].totals.cacheReadTokens |
integer | yes | |
byPerson[].totals.cacheCreationTokens |
integer | yes | |
byPerson[].totals.tokens |
integer | yes | All four token counts added up. |
byPerson[].totals.costUsd |
number | yes | What the tokens cost at API list prices, in USD. API-equivalent: on a subscription it is not what is billed. |
byPerson[].totals.runs |
integer | yes | Runs counted. |
byPerson[].unattributed |
object | yes | The person’s runs of work items that name no ticket, for example a chat that filed nothing. |
byPerson[].unattributed.inputTokens |
integer | yes | |
byPerson[].unattributed.outputTokens |
integer | yes | |
byPerson[].unattributed.cacheReadTokens |
integer | yes | |
byPerson[].unattributed.cacheCreationTokens |
integer | yes | |
byPerson[].unattributed.tokens |
integer | yes | All four token counts added up. |
byPerson[].unattributed.costUsd |
number | yes | What the tokens cost at API list prices, in USD. API-equivalent: on a subscription it is not what is billed. |
byPerson[].unattributed.runs |
integer | yes | Runs counted. |
byDay |
list of objects | yes | Each day with runs in the window, by when the runs finished, oldest first. |
byDay[].day |
string | yes | YYYY-MM-DD, UTC. |
byDay[].totals |
object | yes | |
byDay[].totals.inputTokens |
integer | yes | |
byDay[].totals.outputTokens |
integer | yes | |
byDay[].totals.cacheReadTokens |
integer | yes | |
byDay[].totals.cacheCreationTokens |
integer | yes | |
byDay[].totals.tokens |
integer | yes | All four token counts added up. |
byDay[].totals.costUsd |
number | yes | What the tokens cost at API list prices, in USD. API-equivalent: on a subscription it is not what is billed. |
byDay[].totals.runs |
integer | yes | Runs counted. |
ticket_usage
Section titled “ticket_usage”One ticket’s tokens and API-equivalent cost: its own runs (the runs of every work item whose subject or references name it) by person, model and kind of round, and its total with its sub-tickets rolled up, as the tracker says which they are.
Params:
| Field | Type | Required | Description |
|---|---|---|---|
ticket |
string | yes | Key of the ticket. |
Result:
| Field | Type | Required | Description |
|---|---|---|---|
ticket |
string | yes | Key of the ticket. |
own |
object | yes | Every run of every work item whose subject or references name the ticket: its own rounds, reviews and merges of its pull requests, comment rounds and chat sessions attached to it. |
own.inputTokens |
integer | yes | |
own.outputTokens |
integer | yes | |
own.cacheReadTokens |
integer | yes | |
own.cacheCreationTokens |
integer | yes | |
own.tokens |
integer | yes | All four token counts added up. |
own.costUsd |
number | yes | What the tokens cost at API list prices, in USD. API-equivalent: on a subscription it is not what is billed. |
own.runs |
integer | yes | Runs counted. |
total |
object | yes | The ticket’s own usage plus its sub-tickets’ totals. |
total.inputTokens |
integer | yes | |
total.outputTokens |
integer | yes | |
total.cacheReadTokens |
integer | yes | |
total.cacheCreationTokens |
integer | yes | |
total.tokens |
integer | yes | All four token counts added up. |
total.costUsd |
number | yes | What the tokens cost at API list prices, in USD. API-equivalent: on a subscription it is not what is billed. |
total.runs |
integer | yes | Runs counted. |
subTickets |
list of objects | yes | The sub-tickets rolled into total, each with its own total. |
subTickets[].ticket |
string | yes | |
subTickets[].total |
object | yes | |
subTickets[].total.inputTokens |
integer | yes | |
subTickets[].total.outputTokens |
integer | yes | |
subTickets[].total.cacheReadTokens |
integer | yes | |
subTickets[].total.cacheCreationTokens |
integer | yes | |
subTickets[].total.tokens |
integer | yes | All four token counts added up. |
subTickets[].total.costUsd |
number | yes | What the tokens cost at API list prices, in USD. API-equivalent: on a subscription it is not what is billed. |
subTickets[].total.runs |
integer | yes | Runs counted. |
byPerson |
list of objects | yes | The ticket’s own usage per person, largest cost first. |
byPerson[].key |
string | yes | The person’s handle, the model id, or the kind of round. |
byPerson[].totals |
object | yes | |
byPerson[].totals.inputTokens |
integer | yes | |
byPerson[].totals.outputTokens |
integer | yes | |
byPerson[].totals.cacheReadTokens |
integer | yes | |
byPerson[].totals.cacheCreationTokens |
integer | yes | |
byPerson[].totals.tokens |
integer | yes | All four token counts added up. |
byPerson[].totals.costUsd |
number | yes | What the tokens cost at API list prices, in USD. API-equivalent: on a subscription it is not what is billed. |
byPerson[].totals.runs |
integer | yes | Runs counted. |
byModel |
list of objects | yes | The ticket’s own usage per model id, largest cost first. |
byModel[].key |
string | yes | The person’s handle, the model id, or the kind of round. |
byModel[].totals |
object | yes | |
byModel[].totals.inputTokens |
integer | yes | |
byModel[].totals.outputTokens |
integer | yes | |
byModel[].totals.cacheReadTokens |
integer | yes | |
byModel[].totals.cacheCreationTokens |
integer | yes | |
byModel[].totals.tokens |
integer | yes | All four token counts added up. |
byModel[].totals.costUsd |
number | yes | What the tokens cost at API list prices, in USD. API-equivalent: on a subscription it is not what is billed. |
byModel[].totals.runs |
integer | yes | Runs counted. |
byKind |
list of objects | yes | The ticket’s own usage per kind of round (the work item’s kind, for example assigned or review_feedback), largest cost first. |
byKind[].key |
string | yes | The person’s handle, the model id, or the kind of round. |
byKind[].totals |
object | yes | |
byKind[].totals.inputTokens |
integer | yes | |
byKind[].totals.outputTokens |
integer | yes | |
byKind[].totals.cacheReadTokens |
integer | yes | |
byKind[].totals.cacheCreationTokens |
integer | yes | |
byKind[].totals.tokens |
integer | yes | All four token counts added up. |
byKind[].totals.costUsd |
number | yes | What the tokens cost at API list prices, in USD. API-equivalent: on a subscription it is not what is billed. |
byKind[].totals.runs |
integer | yes | Runs counted. |
health
Section titled “health”The doctor checks: the ones the office runs at start, every day at office.doctor.daily_at, on demand and, while one fails or warns, every office.doctor.retry_minutes, and the ones each agent reports when it starts. A failing check stays failing until a later run passes.
Params:
| Field | Type | Required | Description |
|---|---|---|---|
run |
boolean | no | Run the office’s checks now and answer with the result. A run that outlasts the request is answered with timeout; the health notification carries its result. |
Result:
| Field | Type | Required | Description |
|---|---|---|---|
at |
string | yes | ISO-8601 time, UTC. |
summary |
object | yes | How many checks have each status. |
summary.ok |
integer | yes | |
summary.warn |
integer | yes | |
summary.fail |
integer | yes | |
lastRun |
string or null | yes | When the office last ran its own checks; null before the first run ends. |
checks |
list of objects | yes | Every check, failures first, then warnings, then the rest; by scope and name within each. |
checks[].name |
string | yes | What is checked, for example Tools install. |
checks[].status |
ok or warn or fail |
yes | |
checks[].detail |
string | yes | What was found, naming the value that is wrong. |
checks[].fix |
string | no | What to change, when the check is not ok. |
checks[].key |
string | no | The secret key the check is about, when it is about one. |
checks[].adapter |
string | no | The adapter that made the check, if one did. |
checks[].scope |
string | yes | Whose check it is: office for the checks the office runs, else the handle of the person whose agent reported it. |
checks[].firstSeen |
string | yes | When the check first reported this status. |
checks[].lastSeen |
string | yes | When a run last reported the check. |
Messages
Section titled “Messages”welcome
Section titled “welcome”First message after the service accepts the connection: which office this is. The office answers requests and sends notifications only after it.
Sent by: the service.
| Field | Type | Required | Description |
|---|---|---|---|
version |
integer | yes | LINK_VERSION the service speaks. |
office |
object | yes | How the service knows this office. |
office.id |
string | yes | |
office.name |
string | no |
Sent right after the welcome: the office’s team, version and pipeline mode.
Sent by: the office.
| Field | Type | Required | Description |
|---|---|---|---|
office |
object | yes | Which office this is. |
office.team |
string | yes | The team’s name from its team file. |
office.version |
string | yes | The office’s version. |
request
Section titled “request”Asks the office for data; see Methods. Answered by one response with the same id.
Sent by: the service.
The fields depend on the method; see Methods.
response
Section titled “response”The answer to a request: its result, or an error.
Sent by: the office.
The fields depend on the method; see Methods.
people
Section titled “people”Every person with the item it works on and its queue, as the people method gives it. Sent when the welcome comes and whenever it changes, at most about once a second.
Sent by: the office.
| Field | Type | Required | Description |
|---|---|---|---|
at |
string | yes | ISO-8601 time, UTC. |
people |
list of objects | yes | |
people[].handle |
string | yes | |
people[].name |
string | yes | |
people[].role |
string | yes | |
people[].runtime |
string | yes | The runtime adapter the person is configured with, for example claude: their own runtime, else the team’s integrations.runtime. |
people[].model |
string | yes | The model the person is configured with: their model from team.yaml, which every person has. |
people[].connected |
boolean | yes | Whether the person’s agent is connected to the office now. |
people[].counts |
object | yes | How much work the person has. |
people[].counts.queued |
integer | yes | Items waiting to run. |
people[].counts.running |
integer | yes | Items running now. |
people[].current |
object (id, kind, title, subject, startedAt, runtime, model, lastHeartbeat) or null | yes | The item the agent is working on; null when idle. |
people[].queue |
list of objects | yes | Queued items, in the order they run. |
people[].queue[].id |
string | yes | Id of the work item. |
people[].queue[].kind |
string | yes | What the item is, for example assigned or review_feedback. |
people[].queue[].title |
string | yes | |
people[].queue[].subject |
object (type, ref) or null | yes | What the item is about: a ticket key, a pull request (host/owner/name#n) or a document id. |
people[].queue[].priority |
integer | yes | Lower runs first. |
people[].queue[].trigger |
human or agent or automation or schedule |
yes | |
people[].queue[].reason |
string | yes | Why the item is queued, in words. |
people[].queue[].scheduledBy |
object (policy, eventId) or null | yes | The policy and event that queued it, when the event pipeline did; null otherwise. |
An event the pipeline judged, as soon as the office records its trace.
Sent by: the office.
| Field | Type | Required | Description |
|---|---|---|---|
recordedAt |
string | yes | When the office recorded it. A trace is sent again when an act’s result is added. |
trace |
object | yes | |
trace.event |
object | yes | |
trace.event.id |
string | yes | <source>:<external id>; the id ost explain takes. |
trace.event.source |
vcs or tracker or knowledge or chat or office |
yes | Where the event came from: the code host, the tracker, the knowledge base, chat, or the office itself. |
trace.event.type |
string | yes | |
trace.event.at |
string | yes | When the event happened. |
trace.event.subject |
object | yes | |
trace.event.subject.ticket |
string | no | |
trace.event.subject.pr |
object | no | |
trace.event.subject.pr.repo |
object | yes | |
trace.event.subject.pr.repo.host |
string | yes | |
trace.event.subject.pr.repo.owner |
string | yes | |
trace.event.subject.pr.repo.name |
string | yes | |
trace.event.subject.pr.number |
integer | yes | |
trace.event.subject.doc |
string | no | |
trace.event.subject.chat |
object | no | |
trace.event.subject.chat.channel |
string | yes | |
trace.event.subject.chat.thread |
string | no | |
trace.event.actor |
object | yes | |
trace.event.actor.kind |
human or agent or automation |
yes | |
trace.event.actor.id |
string | yes | |
trace.event.data |
object | yes | Event data as intake normalized it: a ticket move has canonical statuses in from and to and the tracker’s names in fromName and toName. |
trace.urls |
object | yes | Links to the event’s subject, where the office can resolve them. |
trace.urls.ticket |
string | no | |
trace.urls.pr |
string | no | |
trace.urls.doc |
string | no | |
trace.urls.chat |
string | no | |
trace.policies |
list of objects | yes | Every policy, built-in then custom, in order. |
trace.policies[].policy |
string | yes | |
trace.policies[].matched |
boolean | yes | |
trace.policies[].reason |
string | yes | Why it matched or did not. |
trace.policies[].failed |
any | no | For a policy that listened but did not match: the condition that failed. |
trace.policies[].proposals |
list of any | yes | What it proposed, when it matched. |
trace.aggregation |
list of objects | yes | What the aggregation made of each proposal, in order. |
trace.aggregation[].policy |
string | yes | |
trace.aggregation[].proposal |
integer | yes | The proposal’s index in its policy’s proposals. |
trace.aggregation[].outcome |
kept or merged or suppressed or rejected or dropped or error |
yes | kept as a decision, merged into another wake of the same person and subject, suppressed by another policy, rejected by an invariant, dropped as pointless or by a conflict it lost, error when the policy threw. |
trace.aggregation[].reason |
string | no | |
trace.aggregation[].decision |
integer | no | The index in decisions of the decision it became or was merged into. |
trace.decisions |
list of any | yes | What the office does: the aggregation’s result. |
trace.results |
object | no | Each act’s progress, by act id (<event id>:<policy>): the agent it is for makes the changes, so an act for an offline agent waits until it connects. |
A work item was queued or its status changed, with what a list of items shows (title, subject, kind, person, status), so a service can patch its list without asking.
Sent by: the office.
| Field | Type | Required | Description |
|---|---|---|---|
id |
string | yes | |
person |
string | yes | |
kind |
string | yes | |
title |
string | yes | |
subject |
object (type, ref) or null | yes | What the item is about: a ticket key, a pull request (host/owner/name#n) or a document id. |
status |
queued or active or waiting or done or blocked or canceled |
yes | |
updatedAt |
string | yes | ISO-8601 time, UTC. |
health
Section titled “health”The doctor checks, as the health method gives them. Sent when the welcome comes and whenever a check is added, removed or changes, not when a run only confirms them.
Sent by: the office.
| Field | Type | Required | Description |
|---|---|---|---|
at |
string | yes | ISO-8601 time, UTC. |
summary |
object | yes | How many checks have each status. |
summary.ok |
integer | yes | |
summary.warn |
integer | yes | |
summary.fail |
integer | yes | |
lastRun |
string or null | yes | When the office last ran its own checks; null before the first run ends. |
checks |
list of objects | yes | Every check, failures first, then warnings, then the rest; by scope and name within each. |
checks[].name |
string | yes | What is checked, for example Tools install. |
checks[].status |
ok or warn or fail |
yes | |
checks[].detail |
string | yes | What was found, naming the value that is wrong. |
checks[].fix |
string | no | What to change, when the check is not ok. |
checks[].key |
string | no | The secret key the check is about, when it is about one. |
checks[].adapter |
string | no | The adapter that made the check, if one did. |
checks[].scope |
string | yes | Whose check it is: office for the checks the office runs, else the handle of the person whose agent reported it. |
checks[].firstSeen |
string | yes | When the check first reported this status. |
checks[].lastSeen |
string | yes | When a run last reported the check. |
Checks that the other side is there; answer with pong.
Sent by: either side.
No fields.
Answers ping.
Sent by: either side.
No fields.