links
The methods of pd.links. Inputs and outputs are described in Conventions; every method can also fail as described in Errors.
Methods
pd.links.list(input, init?)
Internal link suggestions for an article
GET /api/articles/{articleId}/links
Returns every link suggestion generated for the article, whatever its status (pending, accepted, rejected), newest first, each with the title and slug of the article it points at. Requires articles.read. It does not depend on the internal_linking_automation flag.
Input
| Name | In | Type | Required | Description |
|---|---|---|---|---|
articleId |
path | string (uuid) |
yes | Id of an article. The workspace and project it belongs to are read from the article, never from the request; the caller must be a member of that workspace (and of the project, when it has members). For anyone else the answer is 404 RESOURCE_NOT_FOUND, the same as for an id that does not exist. |
init |
init | { signal?: AbortSignal; timeoutMs?: number } |
no | The second argument: signal aborts the call, timeoutMs overrides the client's per-attempt timeout. |
Returns 200 OK
| Field | Type | Required | Description |
|---|---|---|---|
suggestions |
object[] |
yes | Every suggestion for the article whatever its status, newest first. |
suggestions[].id |
string (uuid) |
yes | Id of the link suggestion. |
suggestions[].workspaceId |
string (uuid) |
yes | Id of the workspace that owns the suggestion. |
suggestions[].sourceArticleId |
string (uuid) |
yes | Id of the article the link would be written into: the one that was analysed. |
suggestions[].targetArticleId |
string (uuid) |
yes | Id of the article the link would point to. |
suggestions[].anchorText |
string |
yes | The words in the source article that would become the link. Accepting looks for this text, exactly as written, in a paragraph or quote. |
suggestions[].reason |
string |
yes | Why the model suggested this link. An empty string when none. |
suggestions[].status |
"pending" | "accepted" | "rejected" |
yes | Where the suggestion stands: pending (not decided; nothing has been changed in the article), accepted (the link was written into the source article) or rejected (declined; the article was not touched). |
suggestions[].createdAt |
string (date-time) |
yes | When the suggestion was generated, as an ISO 8601 UTC date-time. |
suggestions[].updatedAt |
string (date-time) |
yes | When the suggestion was last decided or edited, as an ISO 8601 UTC date-time. |
suggestions[].target |
object |
yes | The article the link would point to. |
suggestions[].target.id |
string (uuid) |
yes | Id of the target article; the same as targetArticleId. |
suggestions[].target.title |
string |
yes | Current title of the target article. |
suggestions[].target.slug |
string |
yes | Current slug of the target article, unique within its project and language. |
Errors 400 invalid input, 401 missing or invalid key, 403 the key's roles lack the permission, 404 not found, 429 rate limited or out of quota. See Errors.
Example
import { PrismDraft } from "@prism-draft/sdk";
const pd = new PrismDraft({ apiKey: process.env.PRISMDRAFT_API_KEY! });
const result = await pd.links.list({
articleId: "3f0c5a1e-8b2d-4c6e-9a7f-1d2e3f4a5b6c",
});
console.log(result);
pd.links.generate(input, init?)
Generate internal link suggestions
POST /api/articles/{articleId}/links
Asks the model which of the project's other published articles in the same language (at most 50 candidates) the article could link to, from the first 6,000 characters of its text, and stores up to 10 suggestions as pending; none is applied until links.decide. Requires articles.update; behind the internal_linking_automation feature flag, and 400 INVALID_REQUEST when the flag is off or the article has no text. With no candidate it answers at once with an empty list and no model call; otherwise it costs no credits but counts against the ai_operations quota (429 QUOTA_EXCEEDED). A pair of source and target already suggested, whatever its status, is not added again, so a rejected pair never comes back. The anchor is requested to appear in the article but is not checked: accepting a suggestion whose anchor does not appear answers 409.
Input
| Name | In | Type | Required | Description |
|---|---|---|---|---|
articleId |
path | string (uuid) |
yes | Id of an article. The workspace and project it belongs to are read from the article, never from the request; the caller must be a member of that workspace (and of the project, when it has members). For anyone else the answer is 404 RESOURCE_NOT_FOUND, the same as for an id that does not exist. |
init |
init | { signal?: AbortSignal; timeoutMs?: number } |
no | The second argument: signal aborts the call, timeoutMs overrides the client's per-attempt timeout. |
Returns 200 OK
| Field | Type | Required | Description |
|---|---|---|---|
suggestions |
object[] |
yes | Every suggestion for the article whatever its status, newest first. |
suggestions[].id |
string (uuid) |
yes | Id of the link suggestion. |
suggestions[].workspaceId |
string (uuid) |
yes | Id of the workspace that owns the suggestion. |
suggestions[].sourceArticleId |
string (uuid) |
yes | Id of the article the link would be written into: the one that was analysed. |
suggestions[].targetArticleId |
string (uuid) |
yes | Id of the article the link would point to. |
suggestions[].anchorText |
string |
yes | The words in the source article that would become the link. Accepting looks for this text, exactly as written, in a paragraph or quote. |
suggestions[].reason |
string |
yes | Why the model suggested this link. An empty string when none. |
suggestions[].status |
"pending" | "accepted" | "rejected" |
yes | Where the suggestion stands: pending (not decided; nothing has been changed in the article), accepted (the link was written into the source article) or rejected (declined; the article was not touched). |
suggestions[].createdAt |
string (date-time) |
yes | When the suggestion was generated, as an ISO 8601 UTC date-time. |
suggestions[].updatedAt |
string (date-time) |
yes | When the suggestion was last decided or edited, as an ISO 8601 UTC date-time. |
suggestions[].target |
object |
yes | The article the link would point to. |
suggestions[].target.id |
string (uuid) |
yes | Id of the target article; the same as targetArticleId. |
suggestions[].target.title |
string |
yes | Current title of the target article. |
suggestions[].target.slug |
string |
yes | Current slug of the target article, unique within its project and language. |
created |
integer |
yes | How many suggestions this call added. A source and target pair that was suggested before, whatever its status, is not added again. |
Errors 400 invalid input, 401 missing or invalid key, 403 the key's roles lack the permission, 404 not found, 429 rate limited or out of quota. See Errors.
Example
import { PrismDraft } from "@prism-draft/sdk";
const pd = new PrismDraft({ apiKey: process.env.PRISMDRAFT_API_KEY! });
const result = await pd.links.generate({
articleId: "3f0c5a1e-8b2d-4c6e-9a7f-1d2e3f4a5b6c",
});
console.log(result);
pd.links.decide(input, init?)
Accept, edit or reject a link suggestion
POST /api/workspaces/{workspaceId}/links/{suggestionId}
Decides one suggestion; requires articles.update on the source article's project, and 404 when the suggestion is not in the workspace. rejected only records the refusal. accepted writes the link into the source article and sets the status in one transaction: the anchor is looked up, exactly as written, in the first plain-text paragraph or quote that contains it, and the article gets a new version; 409 CONFLICT (anchor_not_found) when no block does, so edit it first with anchorText. Accepting again with the same anchor changes nothing, a different anchor adds a second link, and rejecting afterwards does not remove a written link. An acceptance records an articles.updated audit entry and, when the article is published, emits the article.updated webhook event.
Input
| Name | In | Type | Required | Description |
|---|---|---|---|---|
workspaceId |
path | string (uuid) |
yes | Id of the workspace the request works in. The caller must be a member of it, or hold an API key issued by it. For anyone else, an unknown id and an id they do not belong to both answer 404 RESOURCE_NOT_FOUND, so membership cannot be probed. |
suggestionId |
path | string (uuid) |
yes | Id of the link suggestion, in the workspace workspaceId; read from links.list. 404 when it is not. |
body |
body | object |
yes | |
body.decision |
body | "accepted" | "rejected" |
yes | accepted writes the link into the source article; rejected only records the refusal and leaves the article as it is. |
body.anchorText |
body | string |
no | The words to turn into the link, 1 to 300 characters, replacing the suggestion's own anchorText; the new value is stored on the suggestion. Absent: the suggested anchor is used. |
init |
init | { signal?: AbortSignal; timeoutMs?: number } |
no | The second argument: signal aborts the call, timeoutMs overrides the client's per-attempt timeout. |
Returns 200 OK
| Field | Type | Required | Description |
|---|---|---|---|
suggestion |
object |
yes | The suggestion after the decision. |
suggestion.id |
string (uuid) |
yes | Id of the link suggestion. |
suggestion.workspaceId |
string (uuid) |
yes | Id of the workspace that owns the suggestion. |
suggestion.sourceArticleId |
string (uuid) |
yes | Id of the article the link would be written into: the one that was analysed. |
suggestion.targetArticleId |
string (uuid) |
yes | Id of the article the link would point to. |
suggestion.anchorText |
string |
yes | The words in the source article that would become the link. Accepting looks for this text, exactly as written, in a paragraph or quote. |
suggestion.reason |
string |
yes | Why the model suggested this link. An empty string when none. |
suggestion.status |
"pending" | "accepted" | "rejected" |
yes | Where the suggestion stands: pending (not decided; nothing has been changed in the article), accepted (the link was written into the source article) or rejected (declined; the article was not touched). |
suggestion.createdAt |
string (date-time) |
yes | When the suggestion was generated, as an ISO 8601 UTC date-time. |
suggestion.updatedAt |
string (date-time) |
yes | When the suggestion was last decided or edited, as an ISO 8601 UTC date-time. |
Errors 400 invalid input, 401 missing or invalid key, 403 the key's roles lack the permission, 404 not found, 429 rate limited or out of quota. See Errors.
Example
import { PrismDraft } from "@prism-draft/sdk";
const pd = new PrismDraft({ apiKey: process.env.PRISMDRAFT_API_KEY! });
const result = await pd.links.decide({
workspaceId: "3f0c5a1e-8b2d-4c6e-9a7f-1d2e3f4a5b6c",
suggestionId: "3f0c5a1e-8b2d-4c6e-9a7f-1d2e3f4a5b6c",
body: {
decision: "accepted",
},
});
console.log(result);