HTTP API
Generated from
packages/envoy/internal/dispatch/api/routes_table.gobydocs/site/generators/dispatch-api.ts. Edit the source, not this page.
Dispatch serves 188 routes under /api/v1. A running server lists the same table at GET /api/v1, which needs no credential.
Who may call a route
Section titled “Who may call a route”- Anyone: no credential is needed.
- Human or agent: a signed-in human (browser session) or an agent (bearer token).
- Agent only: an agent’s bearer token; a browser session is refused.
The scopes a route needs
Section titled “The scopes a route needs”A route can need OAuth scopes of the credential that calls it, and the credential must hold every one it names. A credential that holds none — the shared agent token a deployment configures, a Kubernetes service-account token, a broker-issued token scoped to a project — is refused 403 OAUTH_SCOPE_REQUIRED naming the scope, under WWW-Authenticate: Bearer error="insufficient_scope", scope="<scope>" (RFC 6750 section 3.1).
person: this credential acts as its person. A browser sign-in holds it, and so does a personal token unless its owner took it away.approve: besideperson, this credential may do what settles work or controls a credential: a document review, the answer of an ask of kindapproval, a machine-login decision, a credential-grant or machine-login revoke, and minting, changing or revoking a personal token. No credential holdsapprovewithoutperson.
Path parameters are the {name} segments of a path.
GET /api/v1
Section titled “GET /api/v1”This index: every route with its method, auth, and purpose.
- Who may call: Anyone
Agents
Section titled “Agents”GET /api/v1/agents
Section titled “GET /api/v1/agents”Live sessions with roles, capabilities, open asks, and last activity, from Dispatch’s copy of the Envoy listener’s session list, at most 5 s old.
- Who may call: Human or agent
GET /api/v1/agents/{session_id}/answers
Section titled “GET /api/v1/agents/{session_id}/answers”What people wrote that Dispatch delivered to one session, newest first by at, then by id: each ask’s current answer (replaced when it changed an earlier answer), each comment at its current body (edited_at when edited; a resolved comment stays), each message, and each ask a person declined to answer, once however it arrived. It reads the ask.answered, comment.created, message.created, message.answered and ask.resolved events a person wrote whose published_destinations hold the session’s own topic, the session’s sent message deliveries (targeted and broadcast messages) and its sent comment deliveries (mentions); of an ask’s resolutions it lists the decline alone, and it never lists an agent’s or the system’s words, a retraction, a withdrawal, another resolution, a hand-back, or a delivery to a role: route’s topic. Each item names its relation to the session (mentioned_you over replied_on_your_ask over replied_in_a_comment_thread over commented_on_a_document over commented_on_an_issue for a comment; replied_to_your_message over sent_you_a_message for a message; answered_your_ask for an answer; declined_your_ask for a decline), the ask and its whole question on an answer, on a decline and on a reply in an ask’s thread, its owner, dispatch:// ref and href (null owner and ref, and /agents/<session_id>/live, for a direct message). A decline carries a kind of its own, declined, at the resolution’s own time, its id the ask’s, and its words in two parts: text, the person’s own note, empty when they wrote none, and dispatch_words, the sentences Dispatch wrote for the asking agent; nobody chose an option, so it carries no selected. ?since= (RFC3339, default 72 hours ago; older than 7 days or in the future is 400 INVALID_QUERY) starts the window; ?max_chars= (1-100000, else 400 INVALID_QUERY) stops the list after the item whose words take the running sum past it, counting the UTF-16 units of text and, on a decline, of dispatch_words beside it, while total counts every item in the window; {session_id, since, total, items}. A session_id that is not a session id is 400 INVALID_SESSION_ID, as on the agent artifact routes. Not an authorization boundary: direct-message text already reaches every authenticated caller through GET /api/v1/events, and a person may read any session’s list.
- Who may call: Human or agent
- Path parameters:
session_id
POST /api/v1/agents/{session_id}/artifacts
Section titled “POST /api/v1/agents/{session_id}/artifacts”Upload a file or image to an agent’s conversation (multipart, as an issue upload, its kind read from its bytes): ref_key agent/<session_id>/<slug>, addressed dispatch://agent/<session_id>/artifact/<slug>. It holds files and images only: a markdown document (JSON content or a text/markdown file) is 400 ARTIFACT_INPUT, since a document belongs to an issue or a project. No event is appended. A caller whose credential proves a session of its own uploads only into that session: any other session_id is 403 SESSION_SCOPE. A session_id that is empty or holds /, ?, #, whitespace, a control character or a character that ends a reference in text (<>“’`[]|) is 400 INVALID_SESSION_ID on every agent artifact route.
- Who may call: Human or agent
- Path parameters:
session_id
GET /api/v1/agents/{session_id}/artifacts/{slug}
Section titled “GET /api/v1/agents/{session_id}/artifacts/{slug}”Read an agent’s conversation’s file or image and its versions by slug.
- Who may call: Human or agent
- Path parameters:
session_id,slug
GET /api/v1/agents/{session_id}/artifacts/{slug}/versions/{number}
Section titled “GET /api/v1/agents/{session_id}/artifacts/{slug}/versions/{number}”Download one version of an agent’s conversation’s file or image, as the issue form serves it (Cache-Control: private, max-age=31536000, immutable).
- Who may call: Human or agent
- Path parameters:
session_id,slug,number
GET /api/v1/agents/{session_id}/messages
Section titled “GET /api/v1/agents/{session_id}/messages”A session’s targeted messages, newest first.
- Who may call: Human or agent
- Needs the scope:
person - Path parameters:
session_id
POST /api/v1/agents/{session_id}/messages
Section titled “POST /api/v1/agents/{session_id}/messages”Send an issue-less targeted message to a session: {body, delivery, in_reply_to?}, answered once its own delivery attempt has settled, on past a client that gives up. Under an Idempotency-Key header (as every POST and PATCH takes one), a repeat with the same request answers 201 with the message the key made and its deliveries as they stand, Idempotency-Replayed: ?1, first sending the one attempt it still owes (none yet, or one stranded past its claim) and nothing else; a listener it cannot reach is recorded on the attempt, so a repeat is answered while the listener is down. An attempt a stopped process left owed is resumed by the server’s delivery sweep whether anyone asks again or not.
- Who may call: Human or agent
- Needs the scope:
person - Path parameters:
session_id
Artifacts
Section titled “Artifacts”DELETE /api/v1/artifacts/{id}
Section titled “DELETE /api/v1/artifacts/{id}”Remove a document of an issue that is still untouched: made by typing a title and never written in. It answers 204 and appends artifact.removed on the issue, and it leaves no row behind. A document that holds writing or a later version, an edit that has not settled, or a comment, ask, review, approval request, check result, read mark or link from anything in Dispatch is 409 ARTIFACT_NOT_UNTOUCHED, saying which of those it is; the issue’s spec, a document that plays a role and an artifact that is no document read the same refusal; and a project’s document is 400 ARTIFACT_PROJECT_DOCUMENT, since a project document’s events are its own and its removal event would need the row the removal deletes.
- Who may call: Human or agent
- Needs the scope:
person - Path parameters:
id
GET /api/v1/artifacts/{id}
Section titled “GET /api/v1/artifacts/{id}”Read a document’s metadata and current version.
- Who may call: Human or agent
- Path parameters:
id
PATCH /api/v1/artifacts/{id}
Section titled “PATCH /api/v1/artifacts/{id}”Correct a document’s title: {name}. It writes the name alone - the slug, the reference key it gives and the role the document plays do not move - and answers the document as it now is. The owner’s name it already holds is 409 ARTIFACT_NAME_TAKEN, a name whose slug is one of the documents Dispatch makes itself, on a document that is not that one, 400 ARTIFACT_ROLE_NAME, the issue’s spec 400 ARTIFACT_PRIMARY, an artifact that is no document 400 ARTIFACT_KIND_MISMATCH, and a name that is empty or too long 400 INVALID_ARTIFACT.
- Who may call: Human or agent
- Needs the scope:
person - Path parameters:
id
POST /api/v1/artifacts/{id}/approval-requests
Section titled “POST /api/v1/artifacts/{id}/approval-requests”Open an approval ask for a document’s latest version, with an optional summary, and with include_verification for the issue’s Verification document beside it (409 NO_VERIFICATION_DOCUMENT when it has none), each at its own current version; the answer reviews every document the ask names. include_verification is for the issue’s spec alone: on any other document, the Verification document itself included, it is 409 NOT_SPEC_DOCUMENT. A document a human approved before carries that approval on the ask (approval.approved: version, at, by). An open ask follows later versions of any document it names and waits on its agent until this route hands the same row back to a human. Every answer carries coverage, one line per requirement of the issue’s spec with no check, or the single line the spec marks no requirements for a spec that marks none (GET /api/v1/issues/{key}/coverage), and none at all for a spec every requirement of which has a check; it is empty for a document that is not a spec, never names a missing plan task, and a gap refuses nothing. A coverage read that fails answers coverage_unread true and records the request all the same.
- Who may call: Human or agent
- Path parameters:
id
GET /api/v1/artifacts/{id}/asks
Section titled “GET /api/v1/artifacts/{id}/asks”List a document’s asks.
- Who may call: Human or agent
- Path parameters:
id
POST /api/v1/artifacts/{id}/asks
Section titled “POST /api/v1/artifacts/{id}/asks”Open an ask on a document. After the write commits, a related-item check searches the document’s project for the question and waits at most 5 seconds; the response’s advice.suggestions says whether it found related items (outcome, with the reason in missing when not made), how it searched, and the three items most like it with each ask item’s answer state.
- Who may call: Human or agent
- Path parameters:
id
GET /api/v1/artifacts/{id}/blocks
Section titled “GET /api/v1/artifacts/{id}/blocks”A document’s blocks with markdown ranges, tokens, reference counts and each block’s embedding, where meaning search stands on it (embedded, pending, or none while no window covers it); a typed block carries attributes, its type’s attributes in declaration order as its opening line writes them ({name, value, kind}, the value unescaped and unquoted and the kind the one the block schema declares it, so a reader follows a block[] value as the pointers it holds and reads every other kind as text), and a table block lists its rows and cells by the indexes delete_row and delete_column take.
- Who may call: Human or agent
- Path parameters:
id
GET /api/v1/artifacts/{id}/blocks/{block_id}
Section titled “GET /api/v1/artifacts/{id}/blocks/{block_id}”Where one block stands in a document: its path from the top-level block down (type, id, child index), and for a table block, row or cell the table’s id, the row index (0 is the header), the cell’s column index, the text of the header cell drawn above it and the row’s cells; 404 TARGET_NOT_FOUND for an id the live document does not hold.
- Who may call: Human or agent
- Path parameters:
id,block_id
POST /api/v1/artifacts/{id}/blocks/{block_id}/keep
Section titled “POST /api/v1/artifacts/{id}/blocks/{block_id}/keep”Record that the caller agrees with the decision that block opens, as the text they read it at: {text_sha256}, the decision’s own text_sha256 from GET /api/v1/issues/{key}/decisions. A decision whose text changed since is 409 DECISION_CHANGED with nothing written, and a body without the sum is 400 INVALID_KEEP; a repeat of the sum as it stands moves the row. The block must be the heading block of a decision of an issue’s decisions.md outside ## Replaced: any other block is 404 TARGET_NOT_FOUND. Answers the keep and appends decision.kept, which wakes nobody.
- Who may call: Human or agent
- Needs the scope:
person - Path parameters:
id,block_id
GET /api/v1/artifacts/{id}/comments
Section titled “GET /api/v1/artifacts/{id}/comments”List a document’s comments. The answer carries an ETag, which moves for every write the list shows, a delivery attempt that appends no event included; If-None-Match answers 304.
- Who may call: Human or agent
- Path parameters:
id
POST /api/v1/artifacts/{id}/comments
Section titled “POST /api/v1/artifacts/{id}/comments”Comment on a document or one of its quotes.
- Who may call: Human or agent
- Path parameters:
id
POST /api/v1/artifacts/{id}/edits
Section titled “POST /api/v1/artifacts/{id}/edits”Apply quote-anchored edit ops with an optional optimistic-concurrency precondition.
- Who may call: Human or agent
- Path parameters:
id
GET /api/v1/artifacts/{id}/events
Section titled “GET /api/v1/artifacts/{id}/events”Page a document’s event log.
- Who may call: Human or agent
- Path parameters:
id
POST /api/v1/artifacts/{id}/plan-reviews
Section titled “POST /api/v1/artifacts/{id}/plan-reviews”Record a plan review’s approval of a plan version: {version}, on an issue’s plan.md alone (409 NOT_PLAN_DOCUMENT otherwise). A plan is never sent to a person for approval, so POST /api/v1/artifacts/{id}/approval-requests refuses it with 409 PLAN_APPROVED_BY_REVIEW; a later plan version makes this approval stale, as a spec’s goes stale.
- Who may call: Human or agent
- Path parameters:
id
POST /api/v1/artifacts/{id}/rebuild
Section titled “POST /api/v1/artifacts/{id}/rebuild”Rebuild a document history ygo cannot load from its latest saved markdown; a live document is 409 DOCUMENT_LIVE and one that loads is 409 DOCUMENT_LOADS.
- Who may call: Human or agent
- Needs the scope:
person - Path parameters:
id
GET /api/v1/artifacts/{id}/references
Section titled “GET /api/v1/artifacts/{id}/references”Cross-references to and from a document.
- Who may call: Human or agent
- Path parameters:
id
GET /api/v1/artifacts/{id}/reviews
Section titled “GET /api/v1/artifacts/{id}/reviews”List a document’s approval reviews.
- Who may call: Human or agent
- Path parameters:
id
POST /api/v1/artifacts/{id}/reviews
Section titled “POST /api/v1/artifacts/{id}/reviews”Approve or request changes on a document’s latest settled version; answers the open approval ask when present, preserving its thread, and reviews every document that ask named.
- Who may call: Human or agent
- Needs the scope:
personandapprove - Path parameters:
id
GET /api/v1/artifacts/{id}/subscribers
Section titled “GET /api/v1/artifacts/{id}/subscribers”Sessions subscribed to a document’s topics.
- Who may call: Human or agent
- Needs the scope:
person - Path parameters:
id
DELETE /api/v1/artifacts/{id}/subscribers/{session_id}
Section titled “DELETE /api/v1/artifacts/{id}/subscribers/{session_id}”Unsubscribe a session from a document.
- Who may call: Human or agent
- Needs the scope:
person - Path parameters:
id,session_id
GET /api/v1/artifacts/{id}/text
Section titled “GET /api/v1/artifacts/{id}/text”A document’s canonical markdown, its whole-document token and its version. With ?blocks=true the answer carries the document’s blocks as well, each as GET /api/v1/artifacts/{id}/blocks reports it, rendered from the one tree the markdown and the token come from: a listing read this way cannot be split by another session’s write.
- Who may call: Human or agent
- Path parameters:
id
POST /api/v1/artifacts/{id}/versions
Section titled “POST /api/v1/artifacts/{id}/versions”Name the current document version.
- Who may call: Human or agent
- Path parameters:
id
GET /api/v1/artifacts/{id}/versions/{number}
Section titled “GET /api/v1/artifacts/{id}/versions/{number}”One named or settled document version.
- Who may call: Human or agent
- Path parameters:
id,number
GET /api/v1/asks/open
Section titled “GET /api/v1/asks/open”Open asks in one scope, with counts: ?author_session= for one session’s own, or ?project= for every open ask on a project’s issues and documents; exactly one is required.
- Who may call: Human or agent
GET /api/v1/asks/{id}
Section titled “GET /api/v1/asks/{id}”Read one ask with its replies, edit history, answers and followers, plus claimant (the session holding its issue’s claim) and route (its issue’s route), whom a person’s answer and replies also reach, and route_holder (the live session that route reaches now: the session itself or the role’s holder); each is null when absent, route_holder also when nobody holds the route or the Envoy listener did not answer, and all are null on a project document’s ask.
- Who may call: Human or agent
- Path parameters:
id
PATCH /api/v1/asks/{id}
Section titled “PATCH /api/v1/asks/{id}”Reword an open ask (its author, or a credential holding the person scope); a block ask’s :::ask block is rewritten with it.
- Who may call: Human or agent
- Path parameters:
id
POST /api/v1/asks/{id}/answer
Section titled “POST /api/v1/asks/{id}/answer”Answer an ask with selected options and/or text.
- Who may call: Human or agent
- Needs the scope:
person - Path parameters:
id
PUT /api/v1/asks/{id}/asked-of
Section titled “PUT /api/v1/asks/{id}/asked-of”Hand an open ask to another person {asked_of}, the email of somebody who has signed in (400 ASSIGNEE_NOT_ALLOWED otherwise): the ask waits on them from now on, wherever its issue goes, and the Inbox lists it under them. It needs the person scope, since handing a question on is one person asking another. An ask that is not open is 409 ASK_NOT_OPEN and one whose issue is closed 409 ISSUE_CLOSED. Naming the assignee of the ask’s own issue empties the field instead, so the ask follows its issue again; either way the row records who handed it on and when (asked_of_by, asked_of_at), and one ask.asked_of event names the person it waited on before and the one it waits on now. Answers the ask.
- Who may call: Human or agent
- Needs the scope:
person - Path parameters:
id
POST /api/v1/asks/{id}/decline
Section titled “POST /api/v1/asks/{id}/decline”Decline to answer an ask: the person closes it with no answer, and Dispatch writes beside their note the words that tell the asking agent to decide without one. The note is optional, and at most the event body limit (400 CAP_EXCEEDED); the ask resolves with the kind declined, a block ask’s block records it as every other resolution does, and an approval request is 409 ASK_NOT_DECLINABLE, being answered with Approve or Request changes.
- Who may call: Human or agent
- Needs the scope:
person - Path parameters:
id
GET /api/v1/asks/{id}/followers
Section titled “GET /api/v1/asks/{id}/followers”List the sessions an ask’s answer and replies reach.
- Who may call: Human or agent
- Path parameters:
id
DELETE /api/v1/asks/{id}/followers/{session_id}
Section titled “DELETE /api/v1/asks/{id}/followers/{session_id}”Unfollow an ask (a credential holding the person scope for any session; every other for its own session alone).
- Who may call: Human or agent
- Path parameters:
id,session_id
PUT /api/v1/asks/{id}/followers/{session_id}
Section titled “PUT /api/v1/asks/{id}/followers/{session_id}”Follow an ask (a credential holding the person scope for any session; every other for its own session alone).
- Who may call: Human or agent
- Path parameters:
id,session_id
POST /api/v1/asks/{id}/resolve
Section titled “POST /api/v1/asks/{id}/resolve”Retract or resolve an ask without answering it (kind retracted or resolved; withdrawn is written only by an issue’s close, and an ask on a closed issue is 409 ISSUE_CLOSED); a block ask’s block records it too. A person who will not answer declines instead, on a route of their own, POST /api/v1/asks/{id}/decline.
- Who may call: Human or agent
- Path parameters:
id
Block links
Section titled “Block links”GET /api/v1/block-links
Section titled “GET /api/v1/block-links”Links from blocks of one document to blocks of another: ?from=<document ref>&to=<document ref>; both block nodes, the edge kind, and when and in which version the source wrote each, newest first.
- Who may call: Human or agent
Broadcasts
Section titled “Broadcasts”GET /api/v1/broadcasts
Section titled “GET /api/v1/broadcasts”Recent broadcasts, newest first, with their recipient and reply counts.
- Who may call: Human or agent
- Needs the scope:
person
POST /api/v1/broadcasts
Section titled “POST /api/v1/broadcasts”Send one message to many sessions: {body, delivery, session_ids}, one targeted message per recipient under a shared broadcast, answered once it is committed while each recipient’s delivery runs behind the answer. A selected session that is not live or does not advertise the mode is excluded and named in the response, never switched to another mode. Under an Idempotency-Key header (as every POST and PATCH takes one), a repeat with the same request answers 201 at once with the broadcast the key made, every recipient’s attempts as they stand and the first answer’s exclusions, Idempotency-Replayed: ?1, and puts every recipient it still owes on its way behind the answer. A recipient a stopped process left owed is resumed by the server’s delivery sweep whether anyone asks again or not.
- Who may call: Human or agent
- Needs the scope:
person
GET /api/v1/broadcasts/{id}
Section titled “GET /api/v1/broadcasts/{id}”One broadcast with every recipient’s delivery attempts and replies, recipients in the order the send named them; a broadcast from before that order was stored falls back to created_at, id.
- Who may call: Human or agent
- Needs the scope:
person - Path parameters:
id
Ci reconcile
Section titled “Ci reconcile”GET /api/v1/ci-reconcile/passes
Section titled “GET /api/v1/ci-reconcile/passes”The CI reconcile passes of the last 24 hours, newest first: each one’s id, the server process that ran it, when it started and ended, what it did (graphql_queries, graphql_points, rest_requests, rest_not_modified, heads_over_rest, records, heads, heads_known) and where it left the cursor, which is where the pass after it takes the order up: the repository, from (the number naming the page of its open pull requests, one above the last head of the page before it), number (the first pull request of that page the pass did not finish, 0 for the whole page), rest (the page is read from the repository’s REST listing rather than from a query) and counted (its heads are already among the heads the cycle knows the repository has). Both the counts and the cursor are null until the pass ends. state is running, complete, deadline (its own five-minute bound cut it short), rate_limit (a shared GitHub limit ended it), cancelled (its process shut down under it) or ended_without_completing, which is a pass whose process died before it could record its end. Rows are kept for 48 hours and pruned by the pass that ends.
- Who may call: Human or agent
Comments
Section titled “Comments”GET /api/v1/comments/{id}
Section titled “GET /api/v1/comments/{id}”Read one comment with its thread.
- Who may call: Human or agent
- Path parameters:
id
PATCH /api/v1/comments/{id}
Section titled “PATCH /api/v1/comments/{id}”Edit a comment’s body. The dashboard editor also sends blocks ([{id, from}], from in UTF-16 code units into the new body) and base_revision, the revision GET /api/v1/comments/{id}/blocks gave, so each block keeps its id; without them the ids follow the text.
- Who may call: Human or agent
- Needs the scope:
person - Path parameters:
id
POST /api/v1/comments/{id}/accept
Section titled “POST /api/v1/comments/{id}/accept”Accept a suggestion, applying its replacement.
- Who may call: Human or agent
- Needs the scope:
person - Path parameters:
id
GET /api/v1/comments/{id}/blocks
Section titled “GET /api/v1/comments/{id}/blocks”A comment’s current blocks in body order: {item, revision, indexed, blocks: [{id, type, text, ref, from, to, changed_revision, changed_at, embedding}]}, item the comment’s graph node, revision its indexed revision, from and to UTF-16 code units into its stored body, changed_revision and changed_at the revision its block’s text last changed in, embedding where meaning search stands on the block (embedded, pending, or none while no window covers it). While the comment is owed a block index, or was indexed from another body than it stores, indexed is false and blocks empty; the read writes nothing. An id written short - 8 characters or more of the hex digits and hyphens an id starts with - names the one comment that carries it, is 409 ID_AMBIGUOUS where two or more do, and is 404 NOT_FOUND where none does; any other non-uuid id is 400 COMMENT_ID_INPUT.
- Who may call: Human or agent
- Path parameters:
id
POST /api/v1/comments/{id}/deliveries
Section titled “POST /api/v1/comments/{id}/deliveries”Retry a comment mention delivery; target is required when it has multiple mentions.
- Who may call: Human or agent
- Path parameters:
id
POST /api/v1/comments/{id}/reject
Section titled “POST /api/v1/comments/{id}/reject”Reject a suggestion.
- Who may call: Human or agent
- Needs the scope:
person - Path parameters:
id
POST /api/v1/comments/{id}/reopen
Section titled “POST /api/v1/comments/{id}/reopen”Reopen a resolved comment thread.
- Who may call: Human or agent
- Needs the scope:
person - Path parameters:
id
POST /api/v1/comments/{id}/reply
Section titled “POST /api/v1/comments/{id}/reply”The mentioned session’s reply to a delivery; the session names itself in actor.
- Who may call: Agent only
- Path parameters:
id
POST /api/v1/comments/{id}/resolve
Section titled “POST /api/v1/comments/{id}/resolve”Resolve a comment thread.
- Who may call: Human or agent
- Path parameters:
id
Credential grants
Section titled “Credential grants”GET /api/v1/credential-grants
Section titled “GET /api/v1/credential-grants”List credential grants the caller may revoke (?approver=me only).
- Who may call: Human or agent
- Needs the scope:
person
POST /api/v1/credential-grants/{id}/revoke
Section titled “POST /api/v1/credential-grants/{id}/revoke”Revoke a credential grant as the caller, its approver or its enrollment’s operator.
- Who may call: Human or agent
- Needs the scope:
personandapprove - Path parameters:
id
Credential requests
Section titled “Credential requests”GET /api/v1/credential-requests
Section titled “GET /api/v1/credential-requests”List credential requests the caller may decide (?approver=me only): those naming the caller, a shared secret’s and a service’s machine login, which anyone signed in decides, each secret request carrying approve_url, the secrets broker’s page where it is decided; null without a configured secrets broker.
- Who may call: Human or agent
- Needs the scope:
person
POST /api/v1/credential-requests/machine-lookup
Section titled “POST /api/v1/credential-requests/machine-lookup”Resolve a pending machine login by its typed confirmation code, returning its facts.
- Who may call: Human or agent
- Needs the scope:
personandapprove
POST /api/v1/credential-requests/{id}/approve
Section titled “POST /api/v1/credential-requests/{id}/approve”Approve a machine login as the caller with its typed code; the broker decides whether the caller is its approver, and refuses a secret request with 403 DECIDE_ON_BROKER_PAGE naming its page on the secrets broker.
- Who may call: Human or agent
- Needs the scope:
personandapprove - Path parameters:
id
POST /api/v1/credential-requests/{id}/deny
Section titled “POST /api/v1/credential-requests/{id}/deny”Deny a machine login as the caller with its typed code; the broker decides whether the caller is its approver, and refuses a secret request with 403 DECIDE_ON_BROKER_PAGE naming its page on the secrets broker.
- Who may call: Human or agent
- Needs the scope:
personandapprove - Path parameters:
id
Credentials
Section titled “Credentials”GET /api/v1/credentials/capabilities
Section titled “GET /api/v1/credentials/capabilities”What the credential pages may offer against this deployment’s secrets broker, relayed from the broker’s own RFC 8414 metadata rather than from any Dispatch setting: machine_code_page says whether the broker still serves the device grant, so whether a machine ever prints a code for a person to type.
- Who may call: Human or agent
- Needs the scope:
person
Dashboard
Section titled “Dashboard”GET /api/v1/dashboard/builds
Section titled “GET /api/v1/dashboard/builds”Which dashboard builds are served: {serving, live}, serving the entry chunk (/assets/index-*.js) of the build this process serves (empty when none), live the distinct entry chunks of the builds every live server process serves, sorted. A tab offers its reload notice only when live holds exactly one entry and it is not the tab’s own.
- Who may call: Human or agent
Delivery
Section titled “Delivery”GET /api/v1/delivery/measures
Section titled “GET /api/v1/delivery/measures”The four delivery measures and Sami’s KPI targets for [from, to) (default the last 7 days) and the same facets the timeline takes: deploys a day, lead time (merge, first commit and open to production; open to merge), change failure rate per PR and per deploy (flags_source says whether stored flags fed it), time to restore, rework share, deploy-run success, a per-UTC-day series, the open P0 issues with no owner, and whether each target is met; 404 DELIVERY_NOT_CONFIGURED without a delivery_settings row.
- Who may call: Human or agent
GET /api/v1/delivery/pipeline
Section titled “GET /api/v1/delivery/pipeline”The Pipeline page: over the seven UTC days ending today (now, or ?at= for a fixed day), by the day each run started, the PR-checks workflow’s run wall time and success, its five slowest jobs (50 timed results to rank) and the jobs that failed in the last 24 hours, the duration and result of the staging end-to-end, staging deploy and production deploy jobs the settings name in push runs on main, and merge-to-production and PR-open-to-merge per merge day against their targets; not filtered by the timeline’s window or facets; 400 INVALID_QUERY for a malformed at, 404 DELIVERY_NOT_CONFIGURED without a delivery_settings row.
- Who may call: Human or agent
GET /api/v1/delivery/runs/{id}
Section titled “GET /api/v1/delivery/runs/{id}”One deploy-repository run with every job it ran, by start, each with its result and timings; 400 RUN_ID_INPUT for an id that is not a positive integer, 404 NOT_FOUND for no such run, 404 DELIVERY_NOT_CONFIGURED without a delivery_settings row.
- Who may call: Human or agent
- Path parameters:
id
POST /api/v1/delivery/session-groups
Section titled “POST /api/v1/delivery/session-groups”Create a Delivery session group {name, sessions}: the group becomes the Parent agent of every pull request whose first session it holds; 409 SESSION_IN_GROUP for a session another group holds, 409 SESSION_GROUP_NAME_TAKEN for a name another group has, 409 SESSION_GROUP_NAME_IS_SESSION_TITLE for a name that is the title of a session outside the group. The timeline lists every group (session_groups).
- Who may call: Human or agent
DELETE /api/v1/delivery/session-groups/{id}
Section titled “DELETE /api/v1/delivery/session-groups/{id}”Delete a Delivery session group; its sessions show as themselves again.
- Who may call: Human or agent
- Path parameters:
id
PATCH /api/v1/delivery/session-groups/{id}
Section titled “PATCH /api/v1/delivery/session-groups/{id}”Rename a Delivery session group, add sessions to it and remove sessions from it {name?, add?, remove?}, with the create route’s refusals; 400 SESSION_GROUP_INPUT for an edit that would leave it empty.
- Who may call: Human or agent
- Path parameters:
id
GET /api/v1/delivery/timeline
Section titled “GET /api/v1/delivery/timeline”The delivery timeline within [from, to) (default the last 7 days), the search q (title or PR id) and the given facets (repeatable repo, parent_agent, session, issue, priority (P0-P3), component (a parent includes its children), author, rework, deployed; __no_issue__, __no_priority__, __no_component__ and __no_session__ select by cause): merges with each PR’s issue title, priority and effective components, successful production deploys with every window PR they shipped first, pipeline failures with each run’s production job and failing step, and merged work waiting to deploy, each PR’s deployed_status (deployed, waiting, not_tracked) computed from stored facts; facet_counts (each facet counted with every other facet applied and its own ignored), color_counts (with none applied), the components and issue titles the window names, data freshness, and measures (what GET /api/v1/delivery/measures answers for the same window, search and facets); 404 DELIVERY_NOT_CONFIGURED without a delivery_settings row.
- Who may call: Human or agent
Events
Section titled “Events”GET /api/v1/events/log
Section titled “GET /api/v1/events/log”Page the whole event log by id, whatever owns each event (an issue, a document, a project or none): the events with id > after (default 0), oldest first, at most limit (1 to 1000, default 1000).
- Who may call: Human or agent
Github
Section titled “Github”GET /api/v1/github/app-installation/{owner}/{repo}
Section titled “GET /api/v1/github/app-installation/{owner}/{repo}”Whether Dispatch’s GitHub App, which GitHub sends the repository’s webhook events through, is installed on the repository: 200 {installed}, false when GitHub answers 404; 503 GITHUB_APP_UNCONFIGURED when Dispatch holds no App key, 502 GITHUB_UNREADABLE naming the repository and GitHub’s error on any other failure.
- Who may call: Human or agent
- Path parameters:
owner,repo
GET /api/v1/going/capability
Section titled “GET /api/v1/going/capability”Whether this deployment has a text model configured: {model_configured} and nothing else, so no endpoint, deployment name or credential can appear in it. Any caller Dispatch admits reads it, a pod’s own agent token included.
- Who may call: Human or agent
GET /api/v1/going/catalogue
Section titled “GET /api/v1/going/catalogue”The catalogue of operations an agent may propose on Dispatch’s graph: the dispatch tools that write, twenty of the thirty-four tool specs agents operate Dispatch with, each carried whole - its own name, its own description, the arguments it takes with an example call, the rows of this table it writes, and the graph relations its verbs write. Twenty actions is a list read whole, so the route takes no query and serves no page. An action the catalogue does not hold is no action at all: nothing serves it.
- Who may call: Human or agent
POST /api/v1/going/requests
Section titled “POST /api/v1/going/requests”Ask a question of what Dispatch holds {words, project}: records the question and answers 201 with its run - {id, author, project, words, created_at, ends_at}, whose ends_at is the 30s the run has to answer in - and answers it behind that, each frame of the answer stored for GET /api/v1/going/requests/{id}/stream to carry. words is required (400 GOING_INPUT when it is blank) and at most 2000 characters (400 CAP_EXCEEDED); project is the project key the question is asked of, empty asking of every project (400 INVALID_PROJECT when it is not a key), and a credential scoped to one project asks only of that project. The run belongs to the credential that asked it, which the stream compares its reader with, so a credential that names nobody - the deployment’s shared token - is refused 403 GOING_ASKER_NAMES_NOBODY and records nothing: no reader of that run could ever be served it. With no model configured the run is recorded all the same and its stream carries the error frame NO_MODEL_CONFIGURED, so a reader learns that from the stream, where every other failure of a run is reported, and not from a status of this route’s own.
- Who may call: Human or agent
GET /api/v1/going/requests/{id}/stream
Section titled “GET /api/v1/going/requests/{id}/stream”One run’s answer as server-sent events: each frame is id: its sequence, event: its name (text, item, cut, changed, none, end, error) and data: the JSON that frame carries, flushed as it is written, and the stream ends on end or error. Last-Event-ID resumes after that sequence, so a reader that drops rereads nothing. Any pod serves it, since the frames are rows and not the memory of the process that took the POST: a run past its ends_at that stored no ending is ended by the reader itself, with the error ANSWER_DID_NOT_ARRIVE, which is how a stream ends when the pod that took the POST is gone. A run the record does not hold, an id that is not a uuid, and a run that is another caller’s are one answer, 404 GOING_REQUEST_NOT_FOUND, so an id tells a caller nothing it did not already know.
- Who may call: Human or agent
- Path parameters:
id
Handoffs
Section titled “Handoffs”GET /api/v1/handoffs/{id}
Section titled “GET /api/v1/handoffs/{id}”One handoff by id with its body, the exact text written; 404 HANDOFF_NOT_FOUND when there is none, 400 HANDOFF_ID_INPUT for an id that is not a uuid.
- Who may call: Human or agent
- Path parameters:
id
GET /api/v1/inbox
Section titled “GET /api/v1/inbox”Open asks waiting on the caller, grouped by whose turn it is; ?project= and ?assignee=me|unassigned|<email> filter on the person each ask waits on, which is three steps resolved as the ask is read: the person the question was handed to, then the issue’s assignee, then the project’s default assignee (unassigned is every ask nobody waits on; an email nobody has signed in with is 400 ASSIGNEE_NOT_ALLOWED).
- Who may call: Human or agent
- Needs the scope:
person
Issues
Section titled “Issues”GET /api/v1/issues
Section titled “GET /api/v1/issues”List issues; filters project, status (repeatable: the issues standing in any of those statuses; a blank value names none, and a value is matched as written, so a status the lifecycle does not hold matches no issue: alone it answers none, and beside another status it adds nothing), parent, label, priority (repeatable: 0-3, or none for unset), advances (repeatable: a priority id, none for the work that advances no live priority or any for the work that advances one, own or inherited; several are a union), open, updated_since, closed_since and closed_before (RFC3339; the issues closed at or after closed_since and before closed_before, so an open issue matches neither), route_status (live, no_holder or unknown; open issues only); ?pinned=true needs the person scope, since the pins are one person’s. A malformed updated_since, closed_since or closed_before is 400 INVALID_UPDATED_SINCE, INVALID_CLOSED_SINCE or INVALID_CLOSED_BEFORE, and a closed_before at or before closed_since 400 INVALID_CLOSED_WINDOW. Each issue carries closed_at, when it was closed or null while it is open, and progress: {tasks, children}, its spec’s task-list items and its direct children as {done, total}, each null when it has none. ?moved=true adds moved_at and moved_by to each issue standing in backlog or icebox: when it was moved there and who moved it, read from its newest issue.updated event that set one of the two statuses, as GET /api/v1/priorities’ parked work reads them; an issue standing elsewhere, or whose events do not reach back to the move, carries neither, and a listing without moved=true carries neither on any issue. Pages with limit (1-250) and offset (0 or more; alone it pages 50): a paged answer is {issues, total, limit, offset}, total counting every issue the filters match; without either it is every matching issue as an array. cursor is 400 INVALID_QUERY.
- Who may call: Human or agent
POST /api/v1/issues
Section titled “POST /api/v1/issues”Create an issue (native, or from a GitHub owner/repo#n ref); assignee defaults to the creating person, the personal token’s owner, the parent’s assignee, or the project’s default assignee. A title over 1000 characters is 400 CAP_EXCEEDED. After the write commits, a related-item check searches the project by meaning and by its rarest words and waits at most 5 seconds; the response’s advice.suggestions says whether it found related items (outcome found, none or not_made with the reason in missing), how it searched (search, search_note), the three items most like the new issue, one per owner, with each ask item’s answer state, words to search with by hand (search_query), and under force the near copies forced past (title_matches), or the likely duplicate an unforced create was told (likely_duplicate).
- Who may call: Human or agent
GET /api/v1/issues/resolve
Section titled “GET /api/v1/issues/resolve”Resolve ?ref=<KEY | owner/repo#n> to an issue key.
- Who may call: Human or agent
GET /api/v1/issues/{key}
Section titled “GET /api/v1/issues/{key}”Read an issue with its open asks, primary document, children, and progress: {tasks, children}, its spec’s task-list items (- [ ] / - [x], nested lists included) and its direct children as {done, total}, each null when it has none.
- Who may call: Human or agent
- Path parameters:
key
PATCH /api/v1/issues/{key}
Section titled “PATCH /api/v1/issues/{key}”Update title (over 1000 characters is 400 CAP_EXCEEDED), status, priority, labels (or labels_add, which adds to the labels the issue holds when the write commits rather than replacing them; not with labels), route, parent, rank, assignee (the email of a person who has signed in, or null; anyone may reassign), or components ({mode: inherit|explicit|none, ids, reason}; allowed on a closed issue). Closing the issue (status done) withdraws every open ask it holds, each recorded resolved with resolution kind withdrawn naming the closer; reopening it opens exactly those asks again. expected_sequence makes the write conditional: 409 ISSUE_CHANGED when the issue’s last event is no longer that sequence.
- Who may call: Human or agent
- Path parameters:
key
GET /api/v1/issues/{key}/artifacts
Section titled “GET /api/v1/issues/{key}/artifacts”List an issue’s documents and files.
- Who may call: Human or agent
- Path parameters:
key
POST /api/v1/issues/{key}/artifacts
Section titled “POST /api/v1/issues/{key}/artifacts”Upload an issue document or file (JSON or multipart). A text/markdown upload is a document; any other is an image only when its bytes are a PNG, JPEG, GIF or WebP, stored under the type its bytes are whatever was declared, and otherwise a file under its declared type. A name no artifact has yet is 400 INVALID_ARTIFACT when it, or the reference key <owner>/<slug> it gives the new artifact, is over 2692 bytes; an upload under an existing artifact’s name is never refused for its length.
- Who may call: Human or agent
- Path parameters:
key
GET /api/v1/issues/{key}/artifacts/{slug}
Section titled “GET /api/v1/issues/{key}/artifacts/{slug}”Read an issue document by slug or unambiguous filename.
- Who may call: Human or agent
- Path parameters:
key,slug
GET /api/v1/issues/{key}/artifacts/{slug}/blocks
Section titled “GET /api/v1/issues/{key}/artifacts/{slug}/blocks”An issue document’s blocks and tokens, by slug or unambiguous filename.
- Who may call: Human or agent
- Path parameters:
key,slug
POST /api/v1/issues/{key}/artifacts/{slug}/edits
Section titled “POST /api/v1/issues/{key}/artifacts/{slug}/edits”Apply edit ops with an optional optimistic-concurrency precondition to an issue document, by slug or unambiguous filename.
- Who may call: Human or agent
- Path parameters:
key,slug
GET /api/v1/issues/{key}/artifacts/{slug}/text
Section titled “GET /api/v1/issues/{key}/artifacts/{slug}/text”An issue document’s canonical markdown, whole-document token and version, by slug or unambiguous filename; ?blocks=true carries its blocks beside them, from the one tree.
- Who may call: Human or agent
- Path parameters:
key,slug
POST /api/v1/issues/{key}/artifacts/{slug}/versions
Section titled “POST /api/v1/issues/{key}/artifacts/{slug}/versions”Name the current version of an issue document, by slug or unambiguous filename.
- Who may call: Human or agent
- Path parameters:
key,slug
GET /api/v1/issues/{key}/artifacts/{slug}/versions/{number}
Section titled “GET /api/v1/issues/{key}/artifacts/{slug}/versions/{number}”One version of an issue document, by slug or unambiguous filename.
- Who may call: Human or agent
- Path parameters:
key,slug,number
GET /api/v1/issues/{key}/asks
Section titled “GET /api/v1/issues/{key}/asks”List an issue’s asks, each with its newest reply; ?state= filters, and ?thread=1 adds each ask’s thread (replies, rewordings, answers, followers, claimant, route, route_holder).
- Who may call: Human or agent
- Path parameters:
key
POST /api/v1/issues/{key}/asks
Section titled “POST /api/v1/issues/{key}/asks”Open a question ask on an issue (options optional). After the write commits, a related-item check searches the project for the question and waits at most 5 seconds; the response’s advice.suggestions says whether it found related items (outcome, with the reason in missing when not made), how it searched, and the three items most like it with each ask item’s answer state.
- Who may call: Human or agent
- Path parameters:
key
DELETE /api/v1/issues/{key}/claim
Section titled “DELETE /api/v1/issues/{key}/claim”Release the issue’s claim: its holder, a caller whose credential holds the person scope, or anyone when the holding session is no longer live. The status does not move. The answer is the issue with legion_managed_status beside it: true when the issue carries the legion label in any case, or sits under one that does, which means the Legion daemon owns the status and a status another writer sets is undone. 409 ISSUE_CLAIMED names the holder this caller may not release (there is no force on this route); 409 CLAIM_CONTENDED means the holder changed twice while the request ran, so nothing was applied and nobody’s liveness was checked.
- Who may call: Human or agent
- Path parameters:
key
POST /api/v1/issues/{key}/claim
Section titled “POST /api/v1/issues/{key}/claim”Claim the issue for the calling session (or human): records who is working it. The status does not move. The answer is the issue with legion_managed_status beside it: true when the issue carries the legion label in any case, or sits under one that does, which means the Legion daemon owns the status and a status another writer sets is undone. 409 ISSUE_CLAIMED names a live holder (or a human one, whose claim has no session to end); a caller whose credential holds the person scope may pass {force: true} to take it anyway, and one that does not is 403 OAUTH_SCOPE_REQUIRED. 409 CLAIM_CONTENDED means the holder changed twice while the request ran, so nothing was applied and nobody’s liveness was checked: read the issue and retry.
- Who may call: Human or agent
- Path parameters:
key
GET /api/v1/issues/{key}/collaborators
Section titled “GET /api/v1/issues/{key}/collaborators”Everyone who worked an issue, running or not, as {issue_key, people, agents, legion_registry}. people (kind user) is one row per canonical login: the assignee, everyone who claimed it, posted a comment, message, ask or document version on it or its documents, or answered one of its asks. agents (kind session) is one row per session id: the same event actors that are sessions (the Legion daemon’s legion-daemon:<PROJECT> writer left out), merged with every session the listener’s Legion agent registry records for the issue, which adds legion {role, role_token, started_at, replaced_by, replaced_at} and its title. Each row carries reasons (assignee, claimed, posted, answered) and last_at; no running state. legion_registry is unavailable when the listener did not answer, and the rest still answers.
- Who may call: Human or agent
- Path parameters:
key
GET /api/v1/issues/{key}/comments
Section titled “GET /api/v1/issues/{key}/comments”List an issue’s comments; ?artifact= scopes to a document. The answer carries an ETag, which moves for every write the list shows, a delivery attempt that appends no event included; If-None-Match answers 304.
- Who may call: Human or agent
- Path parameters:
key
POST /api/v1/issues/{key}/comments
Section titled “POST /api/v1/issues/{key}/comments”Comment on an issue, a document quote, or an ask. A reply names the comment it answers in reply_to, and one block of it in reply_to_block; with ask_id in place of reply_to, reply_to_block names one part of that ask.
- Who may call: Human or agent
- Path parameters:
key
GET /api/v1/issues/{key}/coverage
Section titled “GET /api/v1/issues/{key}/coverage”What each requirement of the issue’s spec lacks: {issue, spec_artifact_id, has_plan, requirements, gaps}. A requirement is a requirement block of the spec; requirements lists them in document order, each with its reference, its words, and how many check and plan_task blocks of any document of the issue or of any descendant issue, at any status, name it in serves. gaps is one line per gap - <block id> has no check, and <block id> has no plan task only once the issue has a plan document - or the single line the spec marks no requirements for a spec that marks none. Nothing is ever refused for a gap. This is not GET /api/v1/search/coverage, which reports how much of what meaning search ranks carries a vector.
- Who may call: Human or agent
- Path parameters:
key
GET /api/v1/issues/{key}/decisions
Section titled “GET /api/v1/issues/{key}/decisions”The issue’s decision records: {document, open_questions, decisions}. document is the dispatch:// address of its decisions.md, null (with no decisions) when it has none. open_questions are the issue’s open asks whose block is in its spec, oldest first. Each decision is one ## section of decisions.md in document order - a ### section under ## Replaced for a replaced one - with its heading block and markdown, the ask ids its blocks mention (kind person when it has any, agent otherwise), whether a paragraph of it starts Unsure:, the version its text last changed in, the Keep a person recorded, and one entry per link from its blocks to a spec block, each stale when the decision changed after it first linked that block and the block has not changed since.
- Who may call: Human or agent
- Path parameters:
key
PUT /api/v1/issues/{key}/design-gates
Section titled “PUT /api/v1/issues/{key}/design-gates”Publish the documents this issue’s design-gate steps record {gates}: each entry {step, artifact_id}, step one of decisions, criteria and spec and artifact_id the document that step’s gate recorded, or [] for an issue whose gates record none. The deployment’s daemon writes it and replaces the issue’s whole set, appending no issue event and notifying nobody whether the set changed or not. Answers 200 {issue, gates} with the stored set in step order. A body without gates, a step that is none of the three, a step named twice, and an artifact_id that is empty are each 400 INVALID_ISSUE; an issue Dispatch does not know is 404 ISSUE_NOT_FOUND.
- Who may call: Human or agent
- Path parameters:
key
GET /api/v1/issues/{key}/events
Section titled “GET /api/v1/issues/{key}/events”Page an issue’s event log (after, before, ids, order, limit). The answer carries an ETag over the issue’s event sequence and the paging query; If-None-Match answers 304.
- Who may call: Human or agent
- Path parameters:
key
GET /api/v1/issues/{key}/handoffs
Section titled “GET /api/v1/issues/{key}/handoffs”List an issue’s handoffs, newest first, each {id, issue_key, phase, version, author, created_at} without its body; a closed issue’s read too.
- Who may call: Human or agent
- Path parameters:
key
POST /api/v1/issues/{key}/handoffs/{phase}
Section titled “POST /api/v1/issues/{key}/handoffs/{phase}”Record a session’s handoff for one phase of an issue {actor, body}: the session names itself in actor (400 ACTOR_KIND otherwise; a cookie caller is 403 HANDOFF_FORBIDDEN), phase is a lowercase slug (^[a-z][a-z0-9-]{0,31}$, 400 HANDOFF_INPUT otherwise), and body is one JSON object as a string (400 HANDOFF_INPUT otherwise) of at most 262144 bytes (413 CAP_EXCEEDED), stored exactly as sent. The record is the phase’s next version, from 1; a closed issue is 409 ISSUE_CLOSED. Answers 201 {id, issue_key, phase, version, author, created_at} and appends handoff.recorded {handoff_id, phase, version}, which never carries the body.
- Who may call: Agent only
- Path parameters:
key,phase
GET /api/v1/issues/{key}/handoffs/{phase}/latest
Section titled “GET /api/v1/issues/{key}/handoffs/{phase}/latest”The newest handoff of one phase of an issue with its body, the exact text written; 404 HANDOFF_NOT_FOUND when the phase has none. A closed issue’s read too.
- Who may call: Human or agent
- Path parameters:
key,phase
GET /api/v1/issues/{key}/messages
Section titled “GET /api/v1/issues/{key}/messages”List an issue’s message threads: every root message, oldest first, each with its deliveries and its replies.
- Who may call: Human or agent
- Path parameters:
key
POST /api/v1/issues/{key}/messages
Section titled “POST /api/v1/issues/{key}/messages”Post an issue message; target and delivery address a session or role. A reply names the message it answers in in_reply_to, and one block of it in in_reply_to_block.
- Who may call: Human or agent
- Path parameters:
key
GET /api/v1/issues/{key}/messages/{id}
Section titled “GET /api/v1/issues/{key}/messages/{id}”Read one message with its deliveries and replies.
- Who may call: Human or agent
- Path parameters:
key,id
GET /api/v1/issues/{key}/references
Section titled “GET /api/v1/issues/{key}/references”The eight-hop closure of documents the issue’s content references: {members, truncated}, each member a document in the closure with its depth and the node it was reached via, under an ETag (If-None-Match answers 304). ?versions=latest leaves each member’s artifact.versions holding only its latest version, under its own ETag; absent, empty or all is the full answer; another value, or versions given more than once, is 400 INVALID_QUERY.
- Who may call: Human or agent
- Path parameters:
key
GET /api/v1/issues/{key}/results
Section titled “GET /api/v1/issues/{key}/results”The checks of the issue’s Verification document in document order, each with its attributes (when, surface, run, negative_control, ci_check), the version its words last changed in and their digest, and every result recorded against it from any issue, newest first: {issue, verification_artifact_id, checks}. A check’s results come newest first by created_at, the time the recording transaction began, with id descending breaking a tie, and a client reading one result per check reads the first. Two results recorded in transactions that overlapped can come back in either order. Each check carries compared_with, the commits and branches its results are compared with, in the order they are asked about. For a before_merge check that is every pull request head Dispatch holds for the check’s issue, each {kind: pull_request, commit, pull_request, learned_at: {at, kind}}, the kind of time being read_at when Dispatch last read that pull request from GitHub (checked_at) and updated_at when the pull request’s own update time, as its newest webhook event carried it (event_at), is the later of the two. For an after_merge check it is {kind: branch, branch} for the branch that issue’s pull request merged into and for the repository’s default branch, the default branch alone where Dispatch holds no merge of the issue. For a default_branch check it is the repository’s default branch alone. Where that list is empty the check carries compared_nothing with its reason: no_pull_request_or_branch (the issue resolves to no repository, or a before_merge check’s issue holds no pull request), repository_unreadable (Dispatch’s GitHub App cannot read the repository) or read_failed (a read Dispatch makes did not answer). Each check also carries pass_stands {answer, roles}: yes with the roles whose passes stand, no, or unknown where the repository could not be read, and for a check CI proves whose check runs GitHub could not be read (ci state unread). A pass stands when its commit is at one of the compared commits, the check’s words have not moved since it was recorded, and no retraction stands against it. Each result states two facts apart from each other. commit_stands {answer, basis, reason} is where its commit stands: at_basis with the basis it matched (for a check compared with branches, the branch that carries the commit), other_commit, or unknown with the reason (repository_unreadable, read_failed). check_words_moved is whether the check’s words moved after the result was recorded, which the digests decide, and the result’s own check_version stands beside the check’s version. A result a retraction stands against carries retracted {reason, actor, created_at} and counts as no evidence. A check CI proves is one whose ci_check names a GitHub check run, with when before_merge, no run, a negative_control, and no result recorded against it. It carries ci {run, state, case, detail, line}, read from that run at the head of the issue’s open pull request of the highest number, else at the final head of its merged pull request of the highest number: state pass, fail, no_result or unread; case the conclusion that decided it (success, failure, timed_out, action_required, startup_failure, stale, or another conclusion as GitHub wrote it), or neutral, skipped, cancelled, not_completed, absent (no run of that name at the head), no_head (Dispatch holds no open or merged pull request head of the issue) or github_unreadable (state unread); detail one sentence naming the case, with GitHub’s own answer where GitHub could not be read; line the pull-request body line for the check. Its one result is derived from that run on every read, for a pass or a fail alone, and is stored nowhere: id ci-run:<check run id>, role ci, actor {kind: github_app, id: the slug of the App that made the run}, session “”, verdict pass or fail, commit_sha the head read, created_at the run’s completed_at, observed the conclusion, the run’s output title and summary cut to 2,000 runes, and its URL, one a line, check_sha256 the check’s own, and no event_seq. It is compared as a recorded result is. ?at=<commit> replaces compared_with for every check of the issue by that one commit, {kind: named, commit}. The only read of GitHub it leaves is the check runs of a check CI proves, read at the issue’s newest pull request head as on every read, so a derived result stands at the named commit only where that commit is that head. A value that is not 40 lowercase hex characters is 400 INVALID_QUERY. An issue with no Verification document answers no checks.
- Who may call: Human or agent
- Path parameters:
key
POST /api/v1/issues/{key}/results
Section titled “POST /api/v1/issues/{key}/results”Record one result of one check: {check, role, verdict, commit, surface?, run?, observed?, negative_control?, failed?, skipped?, excluded?, screenshots?}. {key} is the issue the result is recorded on; check names a check block of its Verification document or of one of its ancestors’, by block id or, when a block id is not unique in that set, by its canonical reference (409 CHECK_AMBIGUOUS); any other id is 400 UNKNOWN_CHECK. A check CI proves (GET’s definition: ci_check set, when before_merge, no run, a negative_control, and no result recorded against it) is 409 CHECK_PROVED_BY_CI for every role, naming the block and the check run, since its result is read from that run. role is at most 32 characters of a-z and -, verdict is pass or fail, commit is 40 lowercase hex, and the counts default to 0; a pass counting any failed, skipped or excluded case is 400 RESULT_CONTRADICTS. A commit the repository the check’s issue resolves to does not hold is 400 RESULT_COMMIT_UNKNOWN, naming the commit and that owner/repo; a repository Dispatch cannot read, or none at all, stores the result as before. A screenshot names an image document of the recording issue or of the check’s issue. The result is appended as result.recorded on the check’s issue. A closed issue, recording or the check’s, takes a result only for a check whose when is after_merge or default_branch; every other result there is 409 ISSUE_CLOSED. The answer is {result, line}, line being the pull-request body line the result implies, which carries no link to Dispatch.
- Who may call: Human or agent
- Path parameters:
key
GET /api/v1/issues/{key}/subscribers
Section titled “GET /api/v1/issues/{key}/subscribers”Sessions subscribed to an issue’s topics.
- Who may call: Human or agent
- Needs the scope:
person - Path parameters:
key
DELETE /api/v1/issues/{key}/subscribers/{session_id}
Section titled “DELETE /api/v1/issues/{key}/subscribers/{session_id}”Unsubscribe a session from an issue.
- Who may call: Human or agent
- Needs the scope:
person - Path parameters:
key,session_id
GET /api/v1/issues/{key}/summary
Section titled “GET /api/v1/issues/{key}/summary”What a reference to an issue shows, read without the full issue: {key, project, title, status, documents}, documents every artifact of the issue in the order the issue read lists them, each {id, slug, name, kind, latest_version, primary}, primary whether the document is the issue’s spec; 404 NOT_FOUND for no such issue.
- Who may call: Human or agent
- Path parameters:
key
Limits
Section titled “Limits”GET /api/v1/limits
Section titled “GET /api/v1/limits”The bounds a write is held to: how long one block of a body may be and how many blocks a body may hold, how many options an ask may have and how long its question and options are together, how long a body an event carries whole may be, and the bytes a request, a document and a handoff body may hold.
- Who may call: Human or agent
Machine logins
Section titled “Machine logins”GET /api/v1/machine-logins
Section titled “GET /api/v1/machine-logins”List the machine logins the caller may revoke that can still reach a secret: the secrets broker’s launcher credentials for every service’s login, whoever approved it, and for the caller’s own machines, unexpired or expired with a session still running, each naming who approved it.
- Who may call: Human or agent
- Needs the scope:
person
POST /api/v1/machine-logins/{id}/revoke
Section titled “POST /api/v1/machine-logins/{id}/revoke”Revoke a machine login, expired or not, ending every session it enrolled, a service’s pods included: anyone signed in revokes a service’s login, and only the person who approved a person’s machine login revokes that one.
- Who may call: Human or agent
- Needs the scope:
personandapprove - Path parameters:
id
Markdown
Section titled “Markdown”POST /api/v1/markdown/round-trip
Section titled “POST /api/v1/markdown/round-trip”The markdown Dispatch would store for the text a body gives it {markdown}: the parse and the render every write of a whole document makes. The route writes nothing and reads no row. A caller compares the answer with the text it holds, and refuses its own write where the two differ. Text the parse or the render refuses comes back as it was given, so the write that follows refuses it with its own error.
- Who may call: Human or agent
GET /api/v1/me/agent-tokens
Section titled “GET /api/v1/me/agent-tokens”List the caller’s personal agent tokens, each with the scopes it holds.
- Who may call: Human or agent
- Needs the scope:
person
POST /api/v1/me/agent-tokens
Section titled “POST /api/v1/me/agent-tokens”Mint a personal agent token {name, scopes?}: scopes is the set the new token holds, defaulting to the scopes the caller’s own credential holds. A scope this deployment does not know, or approve without person, is 400 TOKEN_SCOPES_INPUT; a scope the caller does not hold itself is 403 OAUTH_SCOPE_REQUIRED. The answer is the one time the token’s plaintext is shown.
- Who may call: Human or agent
- Needs the scope:
personandapprove
DELETE /api/v1/me/agent-tokens/{id}
Section titled “DELETE /api/v1/me/agent-tokens/{id}”Revoke a personal agent token.
- Who may call: Human or agent
- Needs the scope:
personandapprove - Path parameters:
id
PATCH /api/v1/me/agent-tokens/{id}
Section titled “PATCH /api/v1/me/agent-tokens/{id}”Set the scopes of one of the caller’s personal agent tokens {scopes}: the whole set it is to hold, with the mint’s refusals, answering the updated token. A token of another person’s, or none, is 404 NOT_FOUND.
- Who may call: Human or agent
- Needs the scope:
personandapprove - Path parameters:
id
GET /api/v1/me/agents/state
Section titled “GET /api/v1/me/agents/state”The caller’s per-agent conversation state: cleared_before, read_through, unread_replies (the session’s replies to the caller’s direct messages newer than both and not read by id), and pinned_at (when the caller pinned the session, which puts it in their sidebar’s Agents list).
- Who may call: Human or agent
- Needs the scope:
person
PUT /api/v1/me/agents/{session_id}/state
Section titled “PUT /api/v1/me/agents/{session_id}/state”Clear an agent’s conversation for the caller (cleared_before hides exchanges at or before it), mark it read (read_through only moves forward), mark some of its replies read by id (read_replies, the session’s own messages), and/or pin it to the caller’s sidebar (pinned: true pins it, false unpins it, and neither touches the Clear); answers the session’s state.
- Who may call: Human or agent
- Needs the scope:
person - Path parameters:
session_id
GET /api/v1/me/answers
Section titled “GET /api/v1/me/answers”The caller’s own answers and replies on asks across issues and project documents, newest first: each row is an answer (the ask.answered event’s answer, and whether it is still the ask’s current one) or a reply, with the ask’s question, state, owner and deep link; pages with limit (1-200, default 50) and offset; {rows, total, limit, offset}.
- Who may call: Human or agent
- Needs the scope:
person
DELETE /api/v1/me/asks/{id}/snooze
Section titled “DELETE /api/v1/me/asks/{id}/snooze”Un-snooze an inbox row for the caller.
- Who may call: Human or agent
- Needs the scope:
person - Path parameters:
id
PUT /api/v1/me/asks/{id}/snooze
Section titled “PUT /api/v1/me/asks/{id}/snooze”Snooze an inbox row for the caller until snoozed_until.
- Who may call: Human or agent
- Needs the scope:
person - Path parameters:
id
GET /api/v1/me/documents/{id}/read
Section titled “GET /api/v1/me/documents/{id}/read”The caller’s last read version of a document {artifact_id, version, read_at}; version and read_at are null when they have read none.
- Who may call: Human or agent
- Needs the scope:
person - Path parameters:
id
PUT /api/v1/me/documents/{id}/read
Section titled “PUT /api/v1/me/documents/{id}/read”Record the caller’s last read version of a document {version}: the Spec tab marks each block that changed since it. Answers the stored read; a version that is missing or negative is 400 INVALID_STATE.
- Who may call: Human or agent
- Needs the scope:
person - Path parameters:
id
POST /api/v1/me/inbox/dismiss
Section titled “POST /api/v1/me/inbox/dismiss”Take threads out of the caller’s Inbox, or put them back: {threads} (the row ids reply:<thread id> and agent:<root message id>) with {seen_replies}, or the Inbox’s own narrowing - kind, priority, urgency, project, and issue or artifact, as POST /api/v1/me/inbox/read reads them - with {dismiss_through} (RFC3339, required with the narrowing; stored as no later than now; missing, unparsable or more than a minute ahead of the server is 400 INVALID_STATE), never both (400 INVALID_QUERY). seen_replies is what the page drew in each row threads names, keyed by that same row id, and a dismissal naming threads without it is 400 INVALID_QUERY: a reply: row’s entry is {version}, that row’s version as GET /api/v1/me/replies answered it, and an agent: row’s is {newest_reply, unread_replies}, that row’s reply.id and the ids it drew as unread. The mark is written only while the row still holds what the page drew, so a dismissal buries no write nobody has read. A restore takes no seen_replies, and neither does the narrowing, where the server draws the rows itself (each 400 INVALID_QUERY). With the narrowing it changes exactly the threads GET /api/v1/me/replies answers under it whose newest reply, or whose dismissal, is at or before dismiss_through. {restore: true} clears the marks instead of writing them, so the threads are in the Inbox again. Dismissing marks a thread read as well, so a dismissed thread never counts as unread, and a later write by somebody else clears the mark and brings the thread back unread, while the caller’s own reply leaves it dismissed. An Ask row’s id, a row id of neither shape and an id that is not a uuid are 400 INVALID_QUERY. Answers {dismissed}, the number of threads it changed; a request that changes nothing answers 0 and appends no event. It takes the lock order and appends the events POST /api/v1/me/inbox/read does: one user_thread_state.updated per project and one user_agent_state.updated per session it touched.
- Who may call: Human or agent
- Needs the scope:
person
POST /api/v1/me/inbox/read
Section titled “POST /api/v1/me/inbox/read”Mark every unread Inbox thread read in one write: {read_through} (RFC3339, required; stored as no later than now; missing, unparsable or more than a minute ahead of the server is 400 INVALID_STATE), with the Inbox’s own narrowing - kind (reply, agent; both by default), priority (P0, P1, P2, P3 and none), urgency (low, med, high, blocking and none), project (a project key and none), and issue or artifact - each absent list meaning every value. It marks exactly the threads GET /api/v1/me/replies?state=unread answers under that narrowing whose newest reply is at or before read_through: a thread written in since stays unread, and one already read past the mark keeps its later mark. Marking read takes no thread out of the Inbox; POST /api/v1/me/inbox/dismiss does. An Agent row has no issue, document, project, priority or urgency, so any of those narrowings leaves it out; a Reply row has no urgency either, so an urgency list without none marks nothing. An agent reply is marked read by its id, so no session’s own read mark moves and the live conversation keeps what it had. issue with artifact, an unknown kind, priority or urgency value, and an artifact that is not a document id are 400 INVALID_QUERY. Answers {marked}, the number of threads marked; a request that moves nothing answers 0 and appends no event.
- Who may call: Human or agent
- Needs the scope:
person
PUT /api/v1/me/issues/{key}/state
Section titled “PUT /api/v1/me/issues/{key}/state”Update the caller’s read state for an issue.
- Who may call: Human or agent
- Needs the scope:
person - Path parameters:
key
GET /api/v1/me/replies
Section titled “GET /api/v1/me/replies”The caller’s Inbox threads, of the kinds ?kind= names: reply (the default) lists the threads they wrote in - an ask, a comment thread, an issue message thread - that somebody else has written in, less an open ask on an open issue that waits on them, which the Inbox already lists; agent lists their one-off messages to an agent session (a BTW, a broadcast’s copy, or a Send or Aside the session did not take as a turn of its live conversation) holding a reply from that session; reply,agent lists both in one order. ?state= is unread (the default), which lists the rows somebody else has written in since they read them, newest reply first; inbox, every row in their Inbox, read or not, dismissed ones aside, newest reply first; or dismissed, what they have dismissed out of it, newest dismissal first, 200 rows at a time from ?cursor= (the next_cursor of the page before it; limit and offset are 400 INVALID_QUERY there, and cursor is 400 INVALID_QUERY under any other state). Each row names the thread, its owner, its issue’s priority, its project, the thread’s subject, its root’s deep link (an agent row’s is the session’s live view), the newest reply’s author, first line and time, the caller’s read mark, whether it is unread and how many writes in it they have not read, the root’s document anchor when it has one, its dismissal mark when it has been dismissed, and, on an agent row, the session and the ids of its unread replies. ?issue= or ?artifact= narrows to one owner (both is 400 INVALID_QUERY), and an agent row, which has neither, is then in no answer. An absent limit answers every row, as the Inbox reads them; a named limit is 1-200 (limit=0 is 400 INVALID_QUERY) from ?offset=. {rows, total (both kinds, and under state=dismissed the page the read answers rather than the whole pile, which nothing counts), limit (0 when none was named), offset, as_of, next_cursor (a dismissed page that filled)}: as_of is the database time of the one read-only repeatable-read snapshot every row was read in, which is what a mark-all passes back as its read mark.
- Who may call: Human or agent
- Needs the scope:
person
GET /api/v1/me/state
Section titled “GET /api/v1/me/state”The caller’s per-issue read state.
- Who may call: Human or agent
- Needs the scope:
person
PUT /api/v1/me/threads/{id}/read
Section titled “PUT /api/v1/me/threads/{id}/read”Move the caller’s read mark on one thread forward to {read_through} (RFC3339; stored as no later than now; more than a minute ahead of the server is 400 INVALID_STATE). It only moves forward; a thread the caller never wrote in writes nothing; 204 either way. A non-uuid id is 400 THREAD_ID_INPUT.
- Who may call: Human or agent
- Needs the scope:
person - Path parameters:
id
Messages
Section titled “Messages”GET /api/v1/messages/{id}
Section titled “GET /api/v1/messages/{id}”Read the conversation a message belongs to by any message id in it, issue-less or not: the thread root with its deliveries and every reply, oldest first. An issue thread follows the issue’s rule. A direct (issue-less) thread is read whole by a credential holding the person scope, since the conversation is that person’s own; every other credential names its session in ?session= and reads only one whose root targets that session or that it replied in (403 THREAD_FORBIDDEN), a guard against reading another session’s conversation by mistake, not an authorization boundary. Direct-conversation text also reaches every authenticated caller through that caller’s events view on GET /ws/live; nothing in Dispatch restricts it by session.
- Who may call: Human or agent
- Path parameters:
id
GET /api/v1/messages/{id}/blocks
Section titled “GET /api/v1/messages/{id}/blocks”A message’s current blocks in body order: {item, indexed, blocks: [{id, type, text, ref, from, to, embedding}]}, item the message’s graph node, from and to UTF-16 code units into its stored body, ref absent for a direct message’s block (it has no address), embedding where meaning search stands on the block (embedded, pending, or none while no window covers it). Read under GET /api/v1/messages/{id}’s rule: a credential without the person scope reading a direct message names its session in ?session= (400 SESSION_REQUIRED, 403 THREAD_FORBIDDEN). While the message is owed a block index, indexed is false and blocks empty; the read writes nothing.
- Who may call: Human or agent
- Path parameters:
id
POST /api/v1/messages/{id}/deliveries
Section titled “POST /api/v1/messages/{id}/deliveries”Retry delivering a targeted message.
- Who may call: Human or agent
- Path parameters:
id
POST /api/v1/messages/{id}/deliveries/{attempt}/accept
Section titled “POST /api/v1/messages/{id}/deliveries/{attempt}/accept”The attempt’s session records that it took the message as its user’s own turn; the session names itself in actor (403 ACCEPT_FORBIDDEN for another’s attempt). One compare-and-set: only a direct message to that session, on no issue, neither a broadcast’s copy nor a reply in a broadcast’s thread, in a thread whose root targets it (409 ACCEPT_NOT_DIRECT), that a person wrote (409 ACCEPT_NOT_WRITTEN_BY_PERSON), sent as a Send or an Aside (409 ACCEPT_NOT_ASIDE_OR_STEER); only the message’s latest attempt (409 ACCEPT_SUPERSEDED), when no attempt of it was accepted before (409 ACCEPT_ALREADY_ACCEPTED), which a person requested (409 ACCEPT_NOT_REQUESTED_BY_PERSON), that person being the message’s author (409 ACCEPT_NOT_REQUESTED_BY_AUTHOR), which did not fail (409 ACCEPT_FAILED; a pending or sent attempt is taken), within the last minute (409 ACCEPT_STALE). Answers the attempt with accepted_at and accepted_as and the message’s stored body; leaves the attempt’s state to the send, and appends message.accepted.
- Who may call: Agent only
- Path parameters:
id,attempt
POST /api/v1/messages/{id}/reply
Section titled “POST /api/v1/messages/{id}/reply”The targeted session’s reply to a delivery; the session names itself in actor. Once the attempt is answered, ?follow_up=true posts other text as the session’s follow-up, threaded under its first reply; with follow_up=false or absent, or with text the session already posted there, nothing is posted and the stored message comes back marked duplicate. Any other follow_up is 400 MESSAGE_INPUT.
- Who may call: Agent only
- Path parameters:
id
Priorities
Section titled “Priorities”GET /api/v1/priorities
Section titled “GET /api/v1/priorities”Every live priority, in rank order: id, title, description (markdown of any length), owner, rank (its place in the one order), state (live; a deleted priority is gone and is not listed), and when and by whom it was made, each beside the work that advances it (work): by_status, the open issues in each lifecycle status, claimed, open_asks, closed_last_7_days and newest_event_at. The work under a priority is the issues that state the relation and every issue below one of them. Beside the list it answers the two faults: work_under_no_priority, the open issues in flight (in_progress, testing, needs_review, retro) that advance no live priority, and priority_work_parked, the issues that advance one and stand in backlog or icebox, each as a whole count, the same count by project or by priority, and the newest 50 rows. Each row carries advances as the issue read does, one {id, inherited} entry per live priority the issue advances, inherited true for one it takes from an issue above it; a row of work_under_no_priority carries the empty list. A priority belongs to no project, so this read is of the whole deployment and any authenticated caller makes it, the shared token every pod carries included. ?project=<KEY>, repeatable, narrows the work counted and both fault lists to those projects and never the priorities themselves; ?summary=true answers the two fault counts alone, with no priority row and no fault row, which is what the navigation rail reads.
- Who may call: Human or agent
POST /api/v1/priorities
Section titled “POST /api/v1/priorities”Write one priority {id?, title, description?, owner?}: title is required, and a title with nothing in it or over 1000 characters (UTF-16 units, counted as an issue title is) is 400 PRIORITY_INPUT; id is the caller’s and defaults to the title written as a slug, and an id that is no slug of lowercase letters, digits and single hyphens, one over 200 characters, the ids none and any (the issue listing reads advances=none as the work under no priority and advances=any as the work under any), and a title no slug can be written from are each 400 PRIORITY_INPUT, an id derived from the title as much as one given; an id the store already holds is 409 PRIORITY_EXISTS. The new priority takes the last place of the order. A credential scoped to one project is refused, since a priority belongs to none.
- Who may call: Human or agent
DELETE /api/v1/priorities/{id}
Section titled “DELETE /api/v1/priorities/{id}”Delete a priority: its state is written gone and no row is removed, so the listing answers without it while its history still names who ended it and when. An id no priority carries is 404 PRIORITY_NOT_FOUND; 204 otherwise.
- Who may call: Human or agent
- Path parameters:
id
PATCH /api/v1/priorities/{id}
Section titled “PATCH /api/v1/priorities/{id}”Write one priority’s title, description or owner, and move its order {title?, description?, owner?, rank?}: each field named is written and each left out is left alone. rank is {before, after}, the priority this one lands above and the one it lands below, as the issue board’s own drag names them; a rank naming neither, a neighbour that is this priority, and one no live priority carries (an id the store does not hold, or one whose state is gone) are each 400 RANK_INPUT. A title with nothing in it or over 1000 characters (UTF-16 units, counted as an issue title is, so a rename is held to the bound a create is) and a body naming nothing to write are 400 PRIORITY_INPUT, and an id no priority carries is 404 PRIORITY_NOT_FOUND.
- Who may call: Human or agent
- Path parameters:
id
GET /api/v1/priorities/{id}/history
Section titled “GET /api/v1/priorities/{id}/history”One priority’s own history, newest first: every act stated about it as {field, value, issue, asserted, actor, at}. field is what was stated — title, description, owner and rank, the four a person writes, state (live when it was made, gone when it was deleted) and visibility; advances is the relation an issue states on it. value is what an attribute write wrote and is empty on an advances act; issue is the issue at the other end of an advances act and is empty on an attribute write; asserted is false on the act that retracted an advances and true on every other. A priority belongs to no project, so any authenticated caller reads this, the shared token every pod carries included. A deleted priority still answers its history, the delete its newest act; an id no priority carries is 404 PRIORITY_NOT_FOUND.
- Who may call: Human or agent
- Path parameters:
id
Projects
Section titled “Projects”GET /api/v1/projects
Section titled “GET /api/v1/projects”List projects. A project’s default assignee is not listed here: GET /api/v1/settings/project-default-assignees reads it, with the person scope.
- Who may call: Human or agent
POST /api/v1/projects
Section titled “POST /api/v1/projects”Create a project {key, name, default_assignee}: any signed-in person or agent session; a bearer names its session in actor. Every project names the person its unowned work lands on, the last step of the chain that decides who a question reaches: default_assignee is the email of a person who has signed in, and a call that names none takes the person creating the project, else the person its credential acts for. A credential that acts for nobody and names no default_assignee is 400 INVALID_PROJECT; a login nobody has signed in with, the one the call names or the one its credential acts for, is 400 ASSIGNEE_NOT_ALLOWED. A key that is not a project key, an empty name, a name holding a control character or a bidi embedding, override or isolate, or a name with no visible character (a letter, number, punctuation or symbol other than the blank fillers U+115F, U+1160, U+3164, U+FFA0 and U+2800, or a code point Go’s Unicode tables leave unassigned, such as an emoji newer than them, other than a noncharacter (U+FDD0-U+FDEF and each code point ending in FFFE or FFFF) or a reserved default-ignorable (U+2065, U+FFF0-U+FFF8, U+E0000-U+E0FFF); combining marks, format characters, private-use characters and whitespace don’t count) is 400 INVALID_PROJECT; a name over 80 characters is 400 CAP_EXCEEDED; a key that already exists is 409 PROJECT_EXISTS. The answer is the project with the default_assignee the create stored.
- Who may call: Human or agent
GET /api/v1/projects/{key}/architecture
Section titled “GET /api/v1/projects/{key}/architecture”A project’s component tree with the work attached: its sources’ sync bookkeeping, each component with its source repository, per-component counts and issue rows, plus unassigned, not-architectural, and retired-link issues; 404 SOURCE_NOT_FOUND without a source.
- Who may call: Human or agent
- Path parameters:
key
GET /api/v1/projects/{key}/architecture-source
Section titled “GET /api/v1/projects/{key}/architecture-source”Kept for dispatch clients older than this release, logged at warn: the project’s first architecture source, or null when it has none. GET /api/v1/projects/{key}/architecture-sources replaces it.
- Who may call: Human or agent
- Path parameters:
key
POST /api/v1/projects/{key}/architecture-source/sync
Section titled “POST /api/v1/projects/{key}/architecture-source/sync”Kept for dispatch clients older than this release, logged at warn: import every architecture source of a project now and answer one row, the first in id order whose last_error is set or else the first; 404 SOURCE_NOT_FOUND without a source. POST /api/v1/projects/{key}/architecture-sources/sync replaces it.
- Who may call: Human or agent
- Path parameters:
key
GET /api/v1/projects/{key}/architecture-sources
Section titled “GET /api/v1/projects/{key}/architecture-sources”A project’s architecture sources, one per repository, in the order they were added; [] when it has none. The project’s component model is every source’s components together.
- Who may call: Human or agent
- Path parameters:
key
POST /api/v1/projects/{key}/architecture-sources/sync
Section titled “POST /api/v1/projects/{key}/architecture-sources/sync”Import every architecture source of a project now, each whatever another’s import did; 200 carries every updated row in the order the sources were added, each with its own last_error (an access failure included).
- Who may call: Human or agent
- Path parameters:
key
DELETE /api/v1/projects/{key}/architecture-sources/{owner}/{repo}
Section titled “DELETE /api/v1/projects/{key}/architecture-sources/{owner}/{repo}”Remove one architecture source of a project and the components it projected; every other source’s components stay.
- Who may call: Human or agent
- Path parameters:
key,owner,repo
PUT /api/v1/projects/{key}/architecture-sources/{owner}/{repo}
Section titled “PUT /api/v1/projects/{key}/architecture-sources/{owner}/{repo}”Add a repository as an architecture source of a project, or change the branch of the source it is {branch}, after proving the GitHub App can read it and that branch is the name of a branch it has; a missing branch, a commit SHA, a relative ref (main~1, HEAD) or a refs/ path is 409 SOURCE_ACCESS naming the value and the repository, as a credential problem is, and nothing is written. A re-save resets the source’s sync bookkeeping and keeps its components.
- Who may call: Human or agent
- Path parameters:
key,owner,repo
POST /api/v1/projects/{key}/architecture-sources/{owner}/{repo}/sync
Section titled “POST /api/v1/projects/{key}/architecture-sources/{owner}/{repo}/sync”Import one architecture source’s model now; 200 carries its updated row (last_error set when the import failed), 409 SOURCE_ACCESS a credential or branch problem.
- Who may call: Human or agent
- Path parameters:
key,owner,repo
GET /api/v1/projects/{key}/artifacts
Section titled “GET /api/v1/projects/{key}/artifacts”List a project’s documents.
- Who may call: Human or agent
- Path parameters:
key
POST /api/v1/projects/{key}/artifacts
Section titled “POST /api/v1/projects/{key}/artifacts”Upload a project document or file (JSON or multipart); a file’s kind is read from its bytes, and a new artifact’s name held to its bound, as an issue upload’s are.
- Who may call: Human or agent
- Path parameters:
key
GET /api/v1/projects/{key}/artifacts/{slug}
Section titled “GET /api/v1/projects/{key}/artifacts/{slug}”Read a project document by slug or unambiguous filename.
- Who may call: Human or agent
- Path parameters:
key,slug
GET /api/v1/projects/{key}/artifacts/{slug}/blocks
Section titled “GET /api/v1/projects/{key}/artifacts/{slug}/blocks”A project document’s blocks and tokens, by slug or unambiguous filename.
- Who may call: Human or agent
- Path parameters:
key,slug
POST /api/v1/projects/{key}/artifacts/{slug}/edits
Section titled “POST /api/v1/projects/{key}/artifacts/{slug}/edits”Apply edit ops with an optional optimistic-concurrency precondition to a project document, by slug or unambiguous filename.
- Who may call: Human or agent
- Path parameters:
key,slug
GET /api/v1/projects/{key}/artifacts/{slug}/summary
Section titled “GET /api/v1/projects/{key}/artifacts/{slug}/summary”What a reference to a project document shows, read without its versions: {id, project, slug, name, kind, latest_version}, the slug or unambiguous filename resolved as the project document read resolves it (404 ARTIFACT_NOT_FOUND, 400 ARTIFACT_REFERENCE_AMBIGUOUS).
- Who may call: Human or agent
- Path parameters:
key,slug
GET /api/v1/projects/{key}/artifacts/{slug}/text
Section titled “GET /api/v1/projects/{key}/artifacts/{slug}/text”A project document’s canonical markdown, whole-document token and version, by slug or unambiguous filename; ?blocks=true carries its blocks beside them, from the one tree.
- Who may call: Human or agent
- Path parameters:
key,slug
POST /api/v1/projects/{key}/artifacts/{slug}/versions
Section titled “POST /api/v1/projects/{key}/artifacts/{slug}/versions”Name the current version of a project document, by slug or unambiguous filename.
- Who may call: Human or agent
- Path parameters:
key,slug
GET /api/v1/projects/{key}/artifacts/{slug}/versions/{number}
Section titled “GET /api/v1/projects/{key}/artifacts/{slug}/versions/{number}”One version of a project document, by slug or unambiguous filename.
- Who may call: Human or agent
- Path parameters:
key,slug,number
PUT /api/v1/projects/{key}/default-assignee
Section titled “PUT /api/v1/projects/{key}/default-assignee”Move a project’s default assignee to another person {assignee} (the email of a person who has signed in): who an issue created with no assignee, by nobody, under no parent or under a parent with nobody on it, goes to, and who a question on an issue nobody holds reaches. A project always names a person, so an assignee that is absent, null or blank is 400 INVALID_PROJECT and the default cannot be cleared. Needs the person scope, as the default names a person: a credential the deployment configured, the shared token or a pod’s, holds none.
- Who may call: Human or agent
- Needs the scope:
person - Path parameters:
key
PUT /api/v1/projects/{key}/gated-steps
Section titled “PUT /api/v1/projects/{key}/gated-steps”Publish the design-gate steps the Legion deployment that serves this project gates {steps}: a list of decisions, criteria and spec, in the order it gates them, or [] for a deployment that gates none. The deployment’s daemon writes it at every boot; a publish of the list the row already holds changes nothing and appends no event. A body without steps, or one naming anything else, is 400 INVALID_PROJECT, and an unknown project 404 PROJECT_NOT_FOUND. It declares no personal scope: the credential the deployment configured writes it, as it does the issue PATCH.
- Who may call: Human or agent
- Path parameters:
key
GET /api/v1/projects/{key}/related
Section titled “GET /api/v1/projects/{key}/related”Run the related-item check an issue create runs, over ?title= and ?text= (checked as the title, a newline and the text; either alone is enough), without writing anything: no issue, ask, suggestion or related_checks record. ?exclude_issue= leaves that issue of the project and everything it holds out of the results and out of the word statistics (400 EXCLUDE_ISSUE_INPUT when it is not an issue of the project); ?exclude_issues=, repeated, leaves each of up to 50 issues of the project out of the results alone, so a draft can leave out a whole subtree without changing how rare its words are (400 EXCLUDE_ISSUE_INPUT for a longer list or for a key the project does not hold); ?keywords_only=true skips meaning search. It waits at most 5 seconds and answers the create’s advice.suggestions shape: outcome found, none or not_made (a blank text is 200 not_made, the reason in missing), search, search_note, search_query and related, with up to 3 open issues before any item of another kind and everything a done issue owns last. This form answers no other_projects: its caller is the dashboard’s new-issue dialog, which runs the check for each burst of typing and draws the items of the asking project alone. Title and text over 1000 characters together are 400 CAP_EXCEEDED; POST takes a longer text. An unknown project is 404 PROJECT_NOT_FOUND.
- Who may call: Human or agent
- Path parameters:
key
POST /api/v1/projects/{key}/related
Section titled “POST /api/v1/projects/{key}/related”The GET form of this route with its fields in a JSON body {title, text, exclude_issue, exclude_issues, keywords_only}, exclude_issues a list of issue keys: the related-item check over a title and a text as long as an issue’s spec (the body’s 1 MiB bound; a title over 1000 characters is 400 CAP_EXCEEDED), writing nothing and waiting at most 5 seconds. The two forms do not answer alike. This one is the check an agent runs to ask a question, so it answers other_projects beside related: at most 2 answered question asks of other projects, each with its ask state, its answer and the project it stands in (owner.project), ranked by meaning alone and offered with no claim that they answer the question. The GET form answers none.
- Who may call: Human or agent
- Path parameters:
key
References
Section titled “References”GET /api/v1/references
Section titled “GET /api/v1/references”Edges of one node in the reference graph: ?to=<dispatch ref> backlinks or ?from= links; ?kind=, ?since=<events.id> (mentions only, read up to the event log’s safe horizon: every mention committed before the read, unless an open transaction holds the horizon back past 1 s, when the read answers the mentions at or below it and a later read the rest). A caller with no cursor starts from since=0, which lists every mention committed before the read that carries a source_seq, and after that reads again from the highest source_seq an answer showed it. A mention written with no event (a rebuild, a backfill, an older build) carries no source_seq until a later write of its source stamps it with that write’s event, and until then only a read without since lists it. A signed-in page may start from GET /auth/whoami’s event_head instead, which takes a cookie or a trusted header and not a bearer, so a bearer caller starts from 0. Never take a since from max(events.id) read straight from the database: event ids are not commit order, and an id taken that way can stand above an edge that commits later. An empty answer is not proof that nothing is new, so the caller reads again with the id it holds. A ?to= reference whose item id or block id is written short (8 characters or more, never a whole uuid) names the one item of the owner the reference names that carries it: the issue of dispatch://<KEY>/ask/<prefix>, the document of a #b-<prefix>; the node answered carries the whole id, a prefix two of them carry is 409 ID_AMBIGUOUS listing both, and one nothing carries is the 404 the reference as written always gave. A whole id is read whatever owner the reference names, as it always was.
- Who may call: Human or agent
GET /api/v1/references/questions
Section titled “GET /api/v1/references/questions”The question map of one reference source’s document: ?kind= one of artifact, ask, comment or message and ?id= that source’s whole uuid or a unique prefix of 8 characters or more. Answers {document, questions}: document the uuid of the document the source’s text belongs to - its own for a document version, the document an ask or a comment is written on or anchored to, its issue’s spec for an issue message - and questions each :::ask{#id} block that document’s latest version declares, by block id, with the whole id of the ask settlement made for it and that ask’s dispatch:// address, which is what a #id in the source’s text names and the address its edge is stored under (empty for an ask on an agent conversation’s document, which no address names). A block no ask is settled for yet is absent, and a document declaring none answers []. A source whose text belongs to no document - a direct message, an id nothing holds - is 404 NOT_FOUND; a kind that is none of the four is 400 INVALID_KIND; a missing id is 400 INVALID_QUERY; an id prefix two sources carry is 409 ID_AMBIGUOUS.
- Who may call: Human or agent
Related checks
Section titled “Related checks”GET /api/v1/related-checks
Section titled “GET /api/v1/related-checks”Count the related-item checks recorded when an issue, an ask or a new decision block was written, within [from, to) (RFC3339, each optional) and of ?project= and ?source_kind= (issue, ask or decision_block; any other is 400 INVALID_QUERY) when given: {project, source_kind, from, to} echo the filters (null when absent), total, by_source_kind, by_outcome (found, none, not_made), by_search (meaning_and_keywords, keywords_only, not_made), by_reason (each reason a check was not made or searched by keywords only), completed (found or none), skipped_for_time (reason timeout), other_project_offers, and keyword_ms and total_ms each {count, p50, p95, max, over_300ms}: the checks holding that time, its discrete percentiles and largest value (null with no check), and how many took over 300 ms. other_project_offers is how many ask creates of the window offered at least one answered question of another project (other_projects), a count of writes and not of items, read from write_suggestions over the same window and under the same filters, and zero for a source kind no such row carries; the two records are written at different moments and only the check’s own row is retried, so a count at a window’s edge can name one fewer write than the window’s total. The meaning_ms recorded for such a check holds the whole meaning stage, the wait for the other projects’ answers with it, which is at most 300 ms after the asking project’s own query answered.
- Who may call: Human or agent
Results
Section titled “Results”POST /api/v1/results/{id}/retract
Section titled “POST /api/v1/results/{id}/retract”Retract one recorded result, with the reason it is wrong: {reason, actor?}. {id} is the result’s own id. The retraction is a record of its own: the result is neither updated nor deleted, and both records stay readable with the reason and the actor who wrote it. No surface counts a retracted result as evidence. Three callers are admitted and every other is 403 RETRACTION_FORBIDDEN: the session that recorded the result, a session holding the Dispatch claim of the issue the result was recorded on, and a caller whose credential carries a person, which is a personal token or a signed-in person’s own session. A closed issue and a check of any when are retracted here, which is what recording refuses. A blank reason is 400 RETRACTION_INPUT, an id no result has is 404 RESULT_NOT_FOUND, and a result a retraction already stands against is 409 RESULT_RETRACTED. The answer is the retraction: {result_id, reason, session, actor, created_at, event_seq}. It is appended as result.retracted on the check’s issue.
- Who may call: Human or agent
- Path parameters:
id
Schema
Section titled “Schema”GET /api/v1/schema/blocks
Section titled “GET /api/v1/schema/blocks”The typed document block schema as JSON.
- Who may call: Anyone
Search
Section titled “Search”GET /api/v1/search
Section titled “GET /api/v1/search”Full-text search ?q= across issues, documents, asks, comments, and messages, within ?project= when given: every match ranked on one list across the kinds by one score, the keyword leg’s length-normalised rank and the meaning leg’s cosine weighted onto one scale and summed, one page of ?limit= (1-50, default 20) from ?offset=, with the total match count and how many the pages can reach (each leg’s best 100). A q that is one issue key in any form a person writes it - INFRA-1042, infra-1042, INFRA 1042, or the number alone, which names that issue of the project the number is read in, or of every project at once, all of them then heading the list in project order - puts that issue first. The number is read in ?project= when the search is narrowed to one, and otherwise in ?number_project=, the project of the page the search was made from: that parameter narrows nothing at all, so a search carrying it answers every project’s rows, and a request naming both reads the number in the ?project= it filters by. A q is searched as typed, websearch_to_tsquery’s AND of its words, whatever its length; only a q that matches nothing at all is searched a second time for its 5 rarest words ORed, rarest by the document frequency the project’s own term statistics hold. The “phrase”, OR and -word forms are read as always, and the answer’s query {operators, broadened} names the forms it read and whether that second search is what answered. ?status=, ?author= and ?updated_since= narrow every leg of the search beside ?project=: the status of the issue a row belongs to (a row with no issue, such as a project document, matches none), the actor id that wrote the item a row names (a person’s login or a session id; an issue’s and a document’s is their creator), and the RFC3339 time at or after which that item was last written. The answer’s filters echoes those four and never ?number_project=, which narrowed nothing. ?keywords_only=true runs the keyword leg alone and makes no embedding call, whatever embedder the deployment configured; its answer is not stamped degraded, because nothing is unavailable - the caller asked for the words alone. The command bar sends it on every keystroke, so a person typing waits on no model call and no reader of this route should take the bar’s type-ahead for the hybrid path. A request leaving it out searches as it always did, by meaning too wherever an embedder is configured. This route declares ?q=, ?project=, ?limit= and ?offset= alone, and GET /api/v1/search/results declares all nine: a client sends a search carrying ?number_project=, ?status=, ?author=, ?updated_since= or ?keywords_only= there, because a build released before those five answers this route 200 with the member dropped, which is unnarrowed rows under the filters a person chose, where it answers that path 404 no route. This route still reads all nine, so a page served by an earlier release that sends one of the five here is still answered narrowed. A q over 1000 characters is 400 CAP_EXCEEDED, a project or number_project that is not a project key 400 INVALID_PROJECT or INVALID_NUMBER_PROJECT, a repeated, blank or unreadable status, author or updated_since 400 INVALID_STATUS, INVALID_AUTHOR or INVALID_UPDATED_SINCE, and a keywords_only that is neither true nor false 400 INVALID_KEYWORDS_ONLY.
- Who may call: Human or agent
GET /api/v1/search/coverage
Section titled “GET /api/v1/search/coverage”Meaning search’s coverage {identity, place, model, total, kinds, not_indexed}: the configured embedder’s identity, its place (bedrock, azure, or dev for a local harness) and model, all three null with none configured; then, in total and for each kind (issue, document_block, comment_block, message_block, ask_part), the searchable items, how many carry a current vector from that identity, how many lack one, how many failed for good, and how many seconds the oldest lacking item has waited (null when none lacks). not_indexed names every kind of node neither leg of search holds a row of - component, session, document_version, result and handoff - each {kind, items}, items being how many of it the database holds, counted from the table that holds it; such a kind is named rather than left out, so a reader never reads it as a covered kind with nothing lacking, and it is in no total.
- Who may call: Human or agent
GET /api/v1/search/results
Section titled “GET /api/v1/search/results”Full-text search ?q= across issues, documents, asks, comments, and messages, within ?project= when given: every match ranked on one list across the kinds by one score, the keyword leg’s length-normalised rank and the meaning leg’s cosine weighted onto one scale and summed, one page of ?limit= (1-50, default 20) from ?offset=, with the total match count and how many the pages can reach (each leg’s best 100). A q that is one issue key in any form a person writes it - INFRA-1042, infra-1042, INFRA 1042, or the number alone, which names that issue of the project the number is read in, or of every project at once, all of them then heading the list in project order - puts that issue first. The number is read in ?project= when the search is narrowed to one, and otherwise in ?number_project=, the project of the page the search was made from: that parameter narrows nothing at all, so a search carrying it answers every project’s rows, and a request naming both reads the number in the ?project= it filters by. A q is searched as typed, websearch_to_tsquery’s AND of its words, whatever its length; only a q that matches nothing at all is searched a second time for its 5 rarest words ORed, rarest by the document frequency the project’s own term statistics hold. The “phrase”, OR and -word forms are read as always, and the answer’s query {operators, broadened} names the forms it read and whether that second search is what answered. ?status=, ?author= and ?updated_since= narrow every leg of the search beside ?project=: the status of the issue a row belongs to (a row with no issue, such as a project document, matches none), the actor id that wrote the item a row names (a person’s login or a session id; an issue’s and a document’s is their creator), and the RFC3339 time at or after which that item was last written. The answer’s filters echoes those four and never ?number_project=, which narrowed nothing. ?keywords_only=true runs the keyword leg alone and makes no embedding call, whatever embedder the deployment configured; its answer is not stamped degraded, because nothing is unavailable - the caller asked for the words alone. The command bar sends it on every keystroke, so a person typing waits on no model call and no reader of this route should take the bar’s type-ahead for the hybrid path. A request leaving it out searches as it always did, by meaning too wherever an embedder is configured. This route declares all nine members and answers exactly what GET /api/v1/search answers, from the same handler: a client sends a search carrying ?number_project=, ?status=, ?author=, ?updated_since= or ?keywords_only= here, because a build released before those five answers this path 404 no route, where it answers GET /api/v1/search 200 with the member dropped, which is unnarrowed rows under the filters a person chose. GET /api/v1/search still reads all nine, so a page served by an earlier release that sends one of the five there is still answered narrowed. A q over 1000 characters is 400 CAP_EXCEEDED, a project or number_project that is not a project key 400 INVALID_PROJECT or INVALID_NUMBER_PROJECT, a repeated, blank or unreadable status, author or updated_since 400 INVALID_STATUS, INVALID_AUTHOR or INVALID_UPDATED_SINCE, and a keywords_only that is neither true nor false 400 INVALID_KEYWORDS_ONLY.
- Who may call: Human or agent
Settings
Section titled “Settings”GET /api/v1/settings/architecture-sources
Section titled “GET /api/v1/settings/architecture-sources”List every project’s architecture sources, by project and then in the order each project added them.
- Who may call: Human or agent
GET /api/v1/settings/delivery
Section titled “GET /api/v1/settings/delivery”The delivery timeline’s one configuration record (deploy repository, workflow paths, production job names, optional production apply step, population authors, excluded repositories, and the Pipeline page’s optional staging end-to-end, staging deploy and PR-checks roll-up job names), or null when it has not been set.
- Who may call: Human or agent
PUT /api/v1/settings/delivery
Section titled “PUT /api/v1/settings/delivery”Set the delivery timeline’s configuration after proving the GitHub App can read the deploy repository; 409 DELIVERY_SETTINGS_ACCESS a credential or repository problem.
- Who may call: Human or agent
GET /api/v1/settings/new-issue
Section titled “GET /api/v1/settings/new-issue”What the dashboard’s search bar files a person’s unmatched typed words as: {label, session}, the label that new issue carries and the session it is routed to and its first message goes to, as this deployment configured them (DISPATCH_NEW_ISSUE_LABEL, DISPATCH_NEW_ISSUE_SESSION). Each is “” where it configured none: no label on the issue, and no conversation started on it. The bar writes the issue itself, through POST /api/v1/issues and the issue’s own artifact, patch and message routes.
- Who may call: Human or agent
GET /api/v1/settings/project-default-assignees
Section titled “GET /api/v1/settings/project-default-assignees”List every project with the person it defaults to, by project key: every project names one, and that person is the last step of the chain that decides who a question reaches. Needs the person scope, as the default names a person: a credential the deployment configured, the shared token or a pod’s, holds none.
- Who may call: Human or agent
- Needs the scope:
person
GET /api/v1/settings/repo-projects
Section titled “GET /api/v1/settings/repo-projects”List GitHub repository to project mappings, each marked whether it is its project’s default and what repository it was renamed to, null for one that was never renamed.
- Who may call: Human or agent
DELETE /api/v1/settings/repo-projects/{owner}/{repo}
Section titled “DELETE /api/v1/settings/repo-projects/{owner}/{repo}”Remove a repository mapping no issue names as its repo.
- Who may call: Human or agent
- Path parameters:
owner,repo
PUT /api/v1/settings/repo-projects/{owner}/{repo}
Section titled “PUT /api/v1/settings/repo-projects/{owner}/{repo}”Map a GitHub repository to a project {project, default?, renamed_to?}; default true makes it the project’s one default. renamed_to is the owner/name this repository is called today, which every comparison of a repository canonicalises through: absent or null leaves the stored one alone, “” clears it, and a pair naming this repository, one naming a repository that is itself renamed, or one on a repository another mapping is already renamed to is 400 INVALID_REPO_PROJECT.
- Who may call: Human or agent
- Path parameters:
owner,repo
Signins
Section titled “Signins”GET /api/v1/signins
Section titled “GET /api/v1/signins”List the caller’s own sign-ins on their machines (agent-secrets login), one per box key they have not revoked, each naming the machine’s account, the key’s thumbprint, when the person signed in there and when that machine last used it. Nobody lists another person’s: Dispatch names the person from its own session.
- Who may call: Human or agent
- Needs the scope:
person
POST /api/v1/signins/{id}/revoke
Section titled “POST /api/v1/signins/{id}/revoke”End one of the caller’s own sign-ins: that machine trades no token for them again and every access token already minted under it ends, while their sign-ins on other machines keep working. A sign-in of anyone else is an unknown id.
- Who may call: Human or agent
- Needs the scope:
personandapprove - Path parameters:
id
GET /api/v1/users
Section titled “GET /api/v1/users”Everyone who has signed in, by lowercase email: the assignee picker’s options.
- Who may call: Human or agent
- Needs the scope:
person
Whoami
Section titled “Whoami”GET /api/v1/whoami
Section titled “GET /api/v1/whoami”Who the server takes the caller for: {kind: user, login, scopes} (login is the person’s lowercase email) or {kind: agent, owner, service, session, project, scopes} (owner is a personal token’s owner or a broker-issued token’s person by lowercase email, null for the shared token and for a broker-issued token that names a service; service is a verified service-account token’s Kubernetes subject, or the fleet a broker-issued token naming no person belongs to, null for every other credential; session is the session the caller’s credential proved, null for a credential that proves none, which every one but an access token the secrets broker issued is; project is the one project key that credential lets the caller write, null where it names none). scopes is the OAuth scopes the credential holds, an empty array for one that holds none: person acts as the person on every route the dashboard reaches, and approve settles what a person settles.
- Who may call: Human or agent