HTTP API: links
The 14 operations tagged links, each with its parameters, its body and what it answers.
List what refers to a document
GET /v1/documents/{id_or_ref}/backlinks
Return everything whose prose refers to this item.
A sub-resource rather than §8.5's ?include=backlinks, and the departure is
deliberate. INCLUDABLE's own rule is that every entry promises a bounded number of
queries per page, and backlinks on a page of fifty is either fifty lookups or a join
nobody asked for — the N+1 that parameter exists to remove, moved inside the server.
Every other section subroutine show renders is already a sub-resource: links,
comments and history.
Enveloped like every other collection (§8.4) and returned whole, for the reason the
links listing gives: what refers to an item is bounded by how much somebody wrote, so
has_more is a statement rather than a shrug.
Narrowed in the domain, which is where §6.15's rule belongs — a mention from a project the reader cannot see is omitted entirely, because something you cannot see mentioned this discloses that activity exists and explains nothing.
Parameters
id_or_ref(string, in path, required): The item's ref — a plain integer, as returned inref— or its id.42and019f…are both accepted. Write#42in prose, never in a URL.workspace_id(stringornull, in query): Which workspace.
Responses
200:Collection_Backlink_. Successful Response422:HTTPValidationError. Validation Error
List what governs a document
GET /v1/documents/{id_or_ref}/governing
Return the documents in force that govern this item — what to read before starting.
The workspace-wide what binds you narrowed to a single item. It answers from typed
links alone: a documents or derives_from link is somebody saying that a decision
settles this work, where being filed nearby or being mentioned in passing is a
different claim. Answering the second under the first's name is how a reader learns
not to trust the answer.
In force, not merely of the right type. A superseded decision is not a rule and a draft one is not yet, and the status category decides it — so a workspace that has renamed its statuses still gets an answer.
Titles and refs, never bodies. A document's title states its conclusion, so this is readable on its own and only the one that matters has to be fetched.
Empty until somebody says a document governs something. What an item's own writing suggests, which is usually more, is its proposed links.
Parameters
id_or_ref(string, in path, required): The item's ref — a plain integer, as returned inref— or its id.42and019f…are both accepted. Write#42in prose, never in a URL.workspace_id(stringornull, in query): Which workspace.
Responses
200:Collection_Governing_. Successful Response422:HTTPValidationError. Validation Error
List a document's links
GET /v1/documents/{id_or_ref}/links
Return every link touching this item, labelled from its point of view.
Enveloped like every other collection (§8.4), and returned whole: an item's links are
bounded by how many somebody typed, so there is nothing to page through. has_more
is therefore always false — which is a statement the caller can rely on, and is the
reason this is worth an envelope rather than a bare array. Until 2026-07-30 it was a
bare array, and a caller had no way to tell a complete set from a truncated one.
Parameters
id_or_ref(string, in path, required): The item's ref — a plain integer, as returned inref— or its id.42and019f…are both accepted. Write#42in prose, never in a URL.workspace_id(stringornull, in query): Which workspace.
Responses
200:Collection_Link_. Successful Response422:HTTPValidationError. Validation Error
Link a document to something
POST /v1/documents/{id_or_ref}/links
Join this item to another one.
Parameters
id_or_ref(string, in path, required): The item's ref — a plain integer, as returned inref— or its id.42and019f…are both accepted. Write#42in prose, never in a URL.workspace_id(stringornull, in query): Which workspace.
Request body: LinkRequest as application/json, required.
Responses
201:Link. Successful Response422:HTTPValidationError. Validation Error
Withdraw a link
DELETE /v1/documents/{id_or_ref}/links/{link_id}
Withdraw a link.
Parameters
id_or_ref(string, in path, required): The item's ref — a plain integer, as returned inref— or its id.42and019f…are both accepted. Write#42in prose, never in a URL.link_id(string(uuid), in path, required)workspace_id(stringornull, in query): Which workspace.
Responses
204: Successful Response422:HTTPValidationError. Validation Error
List the links a document's writing suggests
GET /v1/documents/{id_or_ref}/proposed-links
Return the documents this item's writing suggests govern it, and nobody has said do.
A proposal is not a link and is never counted as one. What governs an item is answered from typed links alone, because written about near this and binds this are different claims and answering the second under the first's name is how a feature loses the trust it exists to earn. This is the evidence for a link that is not there yet: somebody cited a decision in their own words, deliberately, and that is worth offering.
Confirming one is an ordinary POST to this item's links, with the link_type and the
other end this names. Nothing here writes anything.
Only a document that governs — a decision, a specification, a design or a dead end — and only where no link of any kind already joins the pair, because a pair somebody has already related is one somebody has already thought about.
Parameters
id_or_ref(string, in path, required): The item's ref — a plain integer, as returned inref— or its id.42and019f…are both accepted. Write#42in prose, never in a URL.workspace_id(stringornull, in query): Which workspace.
Responses
200:Collection_Proposal_. Successful Response422:HTTPValidationError. Validation Error
Walk what has to happen before a document
GET /v1/documents/{id_or_ref}/tree
Return what has to happen before this item can, as a walk in reading order.
The links above answer one level; this walks them, so a plan of twenty-eight items and forty-two links can be read in one call rather than reconstructed from twenty-eight.
Prerequisites rather than dependents, which is what a milestone's contents are: an item whose blockers are its parts. Only the sequencing link types are followed — relates to and documents order nothing, and a tree drawn through them would put a decision under a phase as though the phase were waiting on it.
Flat, with a depth per row, because the shape is a graph rather than a tree: an item
reached twice is drawn once and says stopped: "again" the second time, and one left
unwalked at the limit says stopped: "deeper". A row that says neither has everything
below it on the page.
Parameters
id_or_ref(string, in path, required): The item's ref — a plain integer, as returned inref— or its id.42and019f…are both accepted. Write#42in prose, never in a URL.workspace_id(stringornull, in query): Which workspace.depth(integer, in query): How many levels to walk.
Responses
200:Collection_Beneath_. Successful Response422:HTTPValidationError. Validation Error
List what refers to a task
GET /v1/tasks/{id_or_ref}/backlinks
Return everything whose prose refers to this item.
A sub-resource rather than §8.5's ?include=backlinks, and the departure is
deliberate. INCLUDABLE's own rule is that every entry promises a bounded number of
queries per page, and backlinks on a page of fifty is either fifty lookups or a join
nobody asked for — the N+1 that parameter exists to remove, moved inside the server.
Every other section subroutine show renders is already a sub-resource: links,
comments and history.
Enveloped like every other collection (§8.4) and returned whole, for the reason the
links listing gives: what refers to an item is bounded by how much somebody wrote, so
has_more is a statement rather than a shrug.
Narrowed in the domain, which is where §6.15's rule belongs — a mention from a project the reader cannot see is omitted entirely, because something you cannot see mentioned this discloses that activity exists and explains nothing.
Parameters
id_or_ref(string, in path, required): The item's ref — a plain integer, as returned inref— or its id.42and019f…are both accepted. Write#42in prose, never in a URL.workspace_id(stringornull, in query): Which workspace.
Responses
200:Collection_Backlink_. Successful Response422:HTTPValidationError. Validation Error
List what governs a task
GET /v1/tasks/{id_or_ref}/governing
Return the documents in force that govern this item — what to read before starting.
The workspace-wide what binds you narrowed to a single item. It answers from typed
links alone: a documents or derives_from link is somebody saying that a decision
settles this work, where being filed nearby or being mentioned in passing is a
different claim. Answering the second under the first's name is how a reader learns
not to trust the answer.
In force, not merely of the right type. A superseded decision is not a rule and a draft one is not yet, and the status category decides it — so a workspace that has renamed its statuses still gets an answer.
Titles and refs, never bodies. A document's title states its conclusion, so this is readable on its own and only the one that matters has to be fetched.
Empty until somebody says a document governs something. What an item's own writing suggests, which is usually more, is its proposed links.
Parameters
id_or_ref(string, in path, required): The item's ref — a plain integer, as returned inref— or its id.42and019f…are both accepted. Write#42in prose, never in a URL.workspace_id(stringornull, in query): Which workspace.
Responses
200:Collection_Governing_. Successful Response422:HTTPValidationError. Validation Error
List a task's links
GET /v1/tasks/{id_or_ref}/links
Return every link touching this item, labelled from its point of view.
Enveloped like every other collection (§8.4), and returned whole: an item's links are
bounded by how many somebody typed, so there is nothing to page through. has_more
is therefore always false — which is a statement the caller can rely on, and is the
reason this is worth an envelope rather than a bare array. Until 2026-07-30 it was a
bare array, and a caller had no way to tell a complete set from a truncated one.
Parameters
id_or_ref(string, in path, required): The item's ref — a plain integer, as returned inref— or its id.42and019f…are both accepted. Write#42in prose, never in a URL.workspace_id(stringornull, in query): Which workspace.
Responses
200:Collection_Link_. Successful Response422:HTTPValidationError. Validation Error
Link a task to something
POST /v1/tasks/{id_or_ref}/links
Join this item to another one.
Parameters
id_or_ref(string, in path, required): The item's ref — a plain integer, as returned inref— or its id.42and019f…are both accepted. Write#42in prose, never in a URL.workspace_id(stringornull, in query): Which workspace.
Request body: LinkRequest as application/json, required.
Responses
201:Link. Successful Response422:HTTPValidationError. Validation Error
Withdraw a link
DELETE /v1/tasks/{id_or_ref}/links/{link_id}
Withdraw a link.
Parameters
id_or_ref(string, in path, required): The item's ref — a plain integer, as returned inref— or its id.42and019f…are both accepted. Write#42in prose, never in a URL.link_id(string(uuid), in path, required)workspace_id(stringornull, in query): Which workspace.
Responses
204: Successful Response422:HTTPValidationError. Validation Error
List the links a task's writing suggests
GET /v1/tasks/{id_or_ref}/proposed-links
Return the documents this item's writing suggests govern it, and nobody has said do.
A proposal is not a link and is never counted as one. What governs an item is answered from typed links alone, because written about near this and binds this are different claims and answering the second under the first's name is how a feature loses the trust it exists to earn. This is the evidence for a link that is not there yet: somebody cited a decision in their own words, deliberately, and that is worth offering.
Confirming one is an ordinary POST to this item's links, with the link_type and the
other end this names. Nothing here writes anything.
Only a document that governs — a decision, a specification, a design or a dead end — and only where no link of any kind already joins the pair, because a pair somebody has already related is one somebody has already thought about.
Parameters
id_or_ref(string, in path, required): The item's ref — a plain integer, as returned inref— or its id.42and019f…are both accepted. Write#42in prose, never in a URL.workspace_id(stringornull, in query): Which workspace.
Responses
200:Collection_Proposal_. Successful Response422:HTTPValidationError. Validation Error
Walk what has to happen before a task
GET /v1/tasks/{id_or_ref}/tree
Return what has to happen before this item can, as a walk in reading order.
The links above answer one level; this walks them, so a plan of twenty-eight items and forty-two links can be read in one call rather than reconstructed from twenty-eight.
Prerequisites rather than dependents, which is what a milestone's contents are: an item whose blockers are its parts. Only the sequencing link types are followed — relates to and documents order nothing, and a tree drawn through them would put a decision under a phase as though the phase were waiting on it.
Flat, with a depth per row, because the shape is a graph rather than a tree: an item
reached twice is drawn once and says stopped: "again" the second time, and one left
unwalked at the limit says stopped: "deeper". A row that says neither has everything
below it on the page.
Parameters
id_or_ref(string, in path, required): The item's ref — a plain integer, as returned inref— or its id.42and019f…are both accepted. Write#42in prose, never in a URL.workspace_id(stringornull, in query): Which workspace.depth(integer, in query): How many levels to walk.
Responses
200:Collection_Beneath_. Successful Response422:HTTPValidationError. Validation Error