PrismDraft SDK

generation

The methods of pd.generation. Inputs and outputs are described in Conventions; every method can also fail as described in Errors.

Methods

pd.generation.estimate(input, init?)

Quote the credit cost of generating this article

GET /api/articles/{articleId}/estimate

Needs articles.read. Prices the article's stored generation configuration (400 when it has none) and compares the quote with the workspace's available credits. Nothing is reserved or charged: estimate.total is the most a run can cost, and the amount POST /api/articles/{articleId}/generate reserves. Research and fact checking are priced only when the article asks for them and the deployment's web_research and fact_checking features are on. What a run finally charges is the delivered part of this quote, never more.

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
estimate object yes The quote for generating this article from its stored configuration.
estimate.total integer yes Credits the run can cost at most: the sum of breakdown. This is the amount reserved when the run starts.
estimate.breakdown object yes The total split by what it pays for.
estimate.breakdown.text integer yes Writing the article: 1 credit per started 1,000 words of wordCount.
estimate.breakdown.images integer yes Images: 1 credit for each of imageCount.
estimate.breakdown.research integer yes Web research: 1 credit when the article asks for it and the deployment's web_research feature is on, otherwise 0.
estimate.breakdown.factCheck integer yes Fact checking: 1 credit when the article asks for research and the deployment's fact_checking feature is on, otherwise 0.
estimate.imageCount integer yes Images the quote prices, cover included: the configured imageCount, or the count derived from wordCount when that is null.
wallet object yes The workspace's credits at this moment.
wallet.balance integer yes Every credit the workspace owns, held ones included.
wallet.reserved integer yes Credits held by runs in progress; they leave the balance when those runs settle.
wallet.available integer yes balance less reserved: what a new run can reserve.
sufficient boolean yes Whether wallet.available covers estimate.total, so that starting now would not answer 402.

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.generation.estimate({
  articleId: "3f0c5a1e-8b2d-4c6e-9a7f-1d2e3f4a5b6c",
});
console.log(result);

pd.generation.getOutline(input, init?)

The outline awaiting review

GET /api/articles/{articleId}/outline

Needs articles.read. Returns the outline stored on the article, null when it has none or the stored one no longer validates, and whether it can still be edited (editable: the article is a DRAFT holding an outline). Every run stores its outline on the article, so it can also be read after an ordinary generation; a run with reviewOutline set stops there for review. Submit the reviewed version to POST /api/articles/{articleId}/outline/approve.

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
outline object | null yes The outline the article holds; null when it has none, or the stored one no longer validates.
outline.title string yes The article title, 1 to 300 characters.
outline.slug string yes The URL slug: lowercase letters and digits separated by hyphens, up to 120 characters.
outline.metaTitle string yes The SEO title, 1 to 70 characters.
outline.metaDescription string yes The SEO description, 1 to 200 characters.
outline.sections object[] yes The sections in reading order, 1 to 40.
outline.sections[].id string yes Identifier of the section inside the outline, 1 to 64 characters.
outline.sections[].heading string yes The section's heading, 1 to 300 characters.
outline.sections[].level 2 | 3 yes Heading rank: 2 for a main section, 3 for a sub-section.
outline.sections[].purpose string yes What the section covers, 1 to 1000 characters.
outline.sections[].keywords string[] yes The keywords attached to the section, at most 25 of 1 to 120 characters. Empty: none. Default: [].
outline.sections[].estimatedWords integer yes The section's length in words, between 20 and 5000.
editable boolean yes true while the article is a DRAFT holding an outline, the window in which POST /api/articles/{articleId}/outline/approve is meaningful; false once writing has started.

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.generation.getOutline({
  articleId: "3f0c5a1e-8b2d-4c6e-9a7f-1d2e3f4a5b6c",
});
console.log(result);

pd.generation.approveOutline(input, init?)

Approve an outline and resume writing

POST /api/articles/{articleId}/outline/approve

Needs articles.generate. Stores the reviewed outline (or approves the stored one when the body has none), then starts the writing leg from it: writing resumes from the reviewer's version, not the generated one. Research is not repeated. Answers 400 when the article has no generation configuration or no outline, 409 when a run is already active or the article's status cannot move to QUEUED, 429 QUOTA_EXCEEDED when the plan's articles or images quota would be passed, and 402 INSUFFICIENT_CREDITS when the available credits do not cover the writing leg. The credits reserved are the full quote less research, which the first leg already spent; on completion only the delivered work is charged and the rest is released. Answers 202 with the QUEUED job (poll GET /api/articles/{articleId}/status, or listen for article.generation.completed); a repeated idempotencyKey answers 200 with the original job and reused: true, once that job has ended. Records an articles.outline_approved audit entry.

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.
body body object no
body.outline body object no The reviewed outline. It replaces the stored one as a whole, sections included, and its title, metaTitle and metaDescription become the article's. Absent: the stored outline is approved as it is, and the request answers 400 when the article has none.
body.outline.title body string yes The article title, 1 to 300 characters.
body.outline.slug body string yes The URL slug: lowercase letters and digits separated by hyphens, up to 120 characters.
body.outline.metaTitle body string yes The SEO title, 1 to 70 characters.
body.outline.metaDescription body string yes The SEO description, 1 to 200 characters.
body.outline.sections body object[] yes The sections in reading order, 1 to 40.
body.outline.sections[].id body string yes Identifier of the section inside the outline, 1 to 64 characters.
body.outline.sections[].heading body string yes The section's heading, 1 to 300 characters.
body.outline.sections[].level body 2 | 3 yes Heading rank: 2 for a main section, 3 for a sub-section.
body.outline.sections[].purpose body string yes What the section covers, 1 to 1000 characters.
body.outline.sections[].keywords body string[] no The keywords attached to the section, at most 25 of 1 to 120 characters. Empty: none. Default: [].
body.outline.sections[].estimatedWords body integer yes The section's length in words, between 20 and 5000.
body.idempotencyKey body string no Makes the request safe to repeat, 8 to 200 characters; unique across all runs. A key that already started a writing run returns it (200, reused: true) once it has ended, and answers 409 while it is still running. Absent: a fresh key is generated, so such a request always starts a new run.
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, 202 Accepted

Field Type Required Description
job object yes The writing run, QUEUED on a 202. On a 200 replay it is the run the idempotency key started, in its current state.
job.id string (uuid) yes Id of the run. Cancelling and settling credits are tracked against it.
job.articleId string (uuid) yes Id of the article the run works on.
job.workspaceId string (uuid) yes Id of the workspace that owns the article.
job.projectId string (uuid) yes Id of the project the article belongs to.
job.status "QUEUED" | "TRANSLATING" | "VALIDATING" | "RESEARCHING" | "OUTLINING" | "WRITING" | "GENERATING_IMAGE_PROMPTS" | "GENERATING_IMAGES" | "PROCESSING_MEDIA" | "FINALIZING" | "COMPLETED" | "FAILED" | "CANCELLED" yes Where the run is. QUEUED: accepted, not started yet. VALIDATING, RESEARCHING, OUTLINING, WRITING, GENERATING_IMAGE_PROMPTS, GENERATING_IMAGES, PROCESSING_MEDIA, FINALIZING: the pipeline is in that step, in this order (research only when requested; the outline step is skipped when resuming an approved outline). TRANSLATING: a translation run is working. COMPLETED, FAILED and CANCELLED are final.
job.kind "generation" | "translation" yes generation writes an article from its configuration; translation translates an article into another language.
job.currentStep "validate" | "research" | "outline" | "write" | "image-prompts" | "images" | "media-processing" | "translate" | "finalize" | null yes The step the run last entered; null while it is still QUEUED.
job.progress integer yes Percent of the run completed, 0 to 100. It moves to the step's value on entering each step and is 100 when the run is COMPLETED; a failed or cancelled run keeps the value it reached.
job.idempotencyKey string yes The key the run was started with: the one the client sent, or one the server generated. Unique across all runs; a request that repeats it gets this run back.
job.estimatedCredits integer yes Credits quoted when the run was started, the most it can cost: the full quote for POST /api/articles/{articleId}/generate, the writing leg's share for POST /api/articles/{articleId}/outline/approve.
job.reservedCredits integer yes Credits held against the wallet when the run was queued, equal to estimatedCredits. The hold ends when the run settles, but this value is not reset; read consumedCredits for what was charged.
job.consumedCredits integer yes Credits charged for the work delivered, set when the run settles and never above reservedCredits; 0 until then, and 0 for a run that failed before the article was saved or was cancelled.
job.errorCode string | null yes GENERATION_FAILED or TRANSLATION_FAILED when status is FAILED; otherwise null.
job.errorMessage string | null yes The message of the error that failed the run; null unless status is FAILED.
job.createdAt string (date-time) yes When the run was accepted, ISO 8601.
outline object yes The outline writing resumes from: the one sent in the request, else the one the article held.
outline.title string yes The article title, 1 to 300 characters.
outline.slug string yes The URL slug: lowercase letters and digits separated by hyphens, up to 120 characters.
outline.metaTitle string yes The SEO title, 1 to 70 characters.
outline.metaDescription string yes The SEO description, 1 to 200 characters.
outline.sections object[] yes The sections in reading order, 1 to 40.
outline.sections[].id string yes Identifier of the section inside the outline, 1 to 64 characters.
outline.sections[].heading string yes The section's heading, 1 to 300 characters.
outline.sections[].level 2 | 3 yes Heading rank: 2 for a main section, 3 for a sub-section.
outline.sections[].purpose string yes What the section covers, 1 to 1000 characters.
outline.sections[].keywords string[] yes The keywords attached to the section, at most 25 of 1 to 120 characters. Empty: none. Default: [].
outline.sections[].estimatedWords integer yes The section's length in words, between 20 and 5000.
estimate object no Present on a 202; absent on a 200 replay.
estimate.total integer yes Credits reserved for the writing leg: the full quote less research, which ran before the outline was reviewed.
reused true no Present, and true, only on a 200 replay: the idempotency key had already started a run.

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.generation.approveOutline({
  articleId: "3f0c5a1e-8b2d-4c6e-9a7f-1d2e3f4a5b6c",
});
console.log(result);

pd.generation.aiEdit(input, init?)

Expand, shorten, rewrite, simplify or change the tone of a passage

POST /api/articles/{articleId}/ai-edit

Needs articles.update. Runs synchronously, with no job: the edited passage comes back in the 200 and the article is not changed, so the caller decides whether to apply it. It costs no credits, but each call counts toward the plan's monthly ai_operations quota (429 QUOTA_EXCEEDED once reached). The text is written in the article's generation language, or its primaryLanguage when it has no configuration, and follows the project's default brand voice. change_tone without a tone answers 400.

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.
body body object yes
body.action body "expand" | "shorten" | "rewrite" | "simplify" | "change_tone" yes What to do with the selection. expand: develop it with more substance, about half again as long. shorten: cut it to about two thirds, keeping every claim. rewrite: new wording at about the same length. simplify: plainer words and shorter sentences. change_tone: rewrite in the requested tone.
body.selection body string yes The passage to edit, 1 to 20,000 characters. It is not matched against the article's text.
body.context body string no Text around the selection, up to 20,000 characters, so the edit fits where it sits. It is read for fit only and is not edited or returned.
body.tone body string no The tone to rewrite in, up to 120 characters, for example warmer or more formal. Required when action is change_tone (400 otherwise); ignored by the other actions.
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
text string yes The edited passage, to show the user and replace the selection with if accepted. The article is not changed.
action "expand" | "shorten" | "rewrite" | "simplify" | "change_tone" yes The action that was applied, as sent.

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.generation.aiEdit({
  articleId: "3f0c5a1e-8b2d-4c6e-9a7f-1d2e3f4a5b6c",
  body: {
    action: "expand",
    selection: "example",
  },
});
console.log(result);

pd.generation.start(input, init?)

Start generating an article

POST /api/articles/{articleId}/generate

Needs articles.generate. Queues a run of the pipeline that researches (when asked), outlines, writes, illustrates and finalizes the article from its generation configuration, which config can set first. The body may be omitted. Credits. The quote (estimate.total, the most the run can cost) is reserved against the wallet before any provider is called; 402 INSUFFICIENT_CREDITS when the available credits are lower. When the run ends only what was delivered is charged and the rest of the hold is released: an image the provider refused, research that found no source or a fact check that checked nothing is not charged. A run that fails before the article is saved, or is cancelled, charges nothing. With reviewOutline the run stops after the outline and charges only research. consumedCredits on the job reports the charge. Refusals. 400 when no configuration exists; 409 when a run is already active for the article or its status cannot move to QUEUED (PUBLISHED, QUEUED, PROCESSING); 429 QUOTA_EXCEEDED when the plan's articles or images quota would be passed, or ai_operations for a reviewOutline run. Asynchronous and idempotent. A new run answers 202 with the QUEUED job; follow it with GET /api/articles/{articleId}/status or the SSE stream, and see article.generation.started, image.generated, credits.consumed, article.generation.completed and article.generation.failed on the webhook. The key comes from idempotencyKey, else the Idempotency-Key header; repeating it after the run has ended answers 200 with the original job and reused: true, and while it is running answers 409. On success the article becomes NEEDS_REVIEW with a new document; on failure, cancellation or an outline-only run it returns to DRAFT. Emits article.generation.started and records an articles.generation_started audit entry.

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.
body body object no
body.config body object no Sets the article's generation configuration before the run, replacing the stored one as a whole: a field left out takes its default, not its previous value. Absent: the stored configuration is used, and the request answers 400 when the article has none.
body.config.topic body string yes What the article is about (1 to 500 characters). The outline step plans the article from it, and with primaryKeyword it is the query for web research and for the project's knowledge base. The article's title is not copied from it: the outline's title, written by the model, becomes the title.
body.config.prompt body string no Free-form direction for the writer, up to 5000 characters (angle, points to cover, things to avoid). Sent to the outline step as 'Additional direction'; the section writers follow the outline it produces.
body.config.primaryKeyword body string no The keyword the article targets, up to 200 characters. The outline step is told to use it where it reads naturally, and it joins topic in the research and knowledge-base queries. It is not copied into the article's targetKeywords.
body.config.secondaryKeywords body string[] no Supporting keywords, up to 25 of 1 to 200 characters. Sent to the outline step, which spreads them over the sections' keywords. Empty by default. Default: [].
body.config.audience body string no Who the article is for, up to 500 characters. Sent to the outline step, to every section writer, to the image-prompt writers and to the knowledge-base query.
body.config.tone body string no The tone to write in, as free text up to 200 characters; the web app offers professional, conversational, authoritative, practical and persuasive. Sent to the outline step as 'Tone'. The section writers read no separate tone: they follow the outline and the brand voice.
body.config.language body string no Language the article is written in, as a language tag such as en or pt-BR (default en). The outline, section, research and image-prompt steps use it, so the text and the images' alt text come out in it. A run does not change the article's own primaryLanguage. Default: "en".
body.config.wordCount body integer no Target length of the whole article in words, 200 to 10000 (default 1200). The outline's sections are planned to add up to roughly this. It also sets the text price per started 1,000 words: 2 credits with writingQuality premium, 1 with standard (1200 costs 4 or 2), and the image count when imageCount is null. Default: 1200.
body.config.imageStyle body "photorealistic" | "editorial" | "minimalist-vector" | "3d-render" | "watercolor" | "corporate-tech" | "cinematic" | "illustration" no Style of the article's images (default editorial). It applies to the cover and the inline images unless the project's imageSettings set a hero.style or inline.style, which take precedence. Values: photorealistic (natural-light documentary photograph, realistic textures, no stylisation), editorial (modern editorial illustration or photograph, clean composition, restrained palette), minimalist-vector (flat vector illustration, few shapes, solid colours, plenty of empty space), 3d-render (soft studio 3D render, smooth matte materials, simple forms), watercolor (loose watercolour painting, soft edges, muted washes), corporate-tech (clean technology visual in cool blues and greys, geometric shapes), cinematic (cinematic still, dramatic lighting, strong atmosphere, wide framing), illustration (hand-drawn illustration, confident linework, warm friendly palette). Default: "editorial".
body.config.imageCount body integer | null no How many images to make, cover included, 0 to 20. 0 makes none, 1 only the cover, more add one inline image per section up to the number of outline sections. null (the default) derives it from wordCount: 2 under 1,000 words, 3 under 2,000, 4 under 3,000, otherwise 5. Each image delivered costs 1 credit, or 3 when imageQuality is hd; the quote and the reservation count the full number. Default: null.
body.config.writingQuality body "standard" | "premium" no Which model writes the article (default premium). premium uses the deployment's article model and costs 2 credits per started 1,000 words; standard uses a lighter, cheaper model and costs 1 credit per started 1,000 words. Default: "premium".
body.config.imageQuality body "standard" | "hd" no Quality of the article's images (default standard). standard costs 1 credit per image; hd asks the image model for its highest quality and costs 3 credits per image, for the run's images and for each regeneration of an image of the article. Default: "standard".
body.config.brandVoiceId body string (uuid) no Id of the brand voice to write in. Absent, or an id that is not one of the workspace's voices, the project's default voice applies, then the workspace's default voice; with neither, no voice is applied.
body.config.contentTemplateId body string (uuid) no Id of the content template to follow: the outline must contain its sections, and the last section ends on its call to action. Only built-in templates and the workspace's own count; any other id, or a deleted template, is ignored without an error.
body.config.sourceUrl body string (uri) no A reference URL for the article. It is stored with the configuration; no generation step reads or fetches it.
body.config.researchEnabled body boolean no Whether to research and fact-check (default false). Research looks up sources before the outline (needs the deployment's web_research feature); fact checking verifies the finished article's claims (needs fact_checking). Research adds 1 credit and fact checking 3 to the quote, each only when its feature is on, and is charged only if it delivered: research found a source, fact checking checked a claim. Default: false.
body.config.reviewOutline body boolean no When true the run stops after the outline (default false): the job ends COMPLETED, the article stays DRAFT with the outline stored, and only research, if any, is charged. Read it with GET /api/articles/{articleId}/outline, edit it, then POST /api/articles/{articleId}/outline/approve to write the article. Default: false.
body.config.faq body boolean no When true (default false) the article ends with a "Frequently asked questions" section of 3 to 5 questions a reader still has after the body, each answered in two or three sentences, in the article's language. It is written after the sections as one more model call, and its words come out of wordCount: the outline is planned for 250 words fewer, so the price does not change. The section is ordinary content (an H2, then an H3 and a paragraph per question) that can be edited like any other. Default: false.
body.idempotencyKey body string no Makes the request safe to repeat, 8 to 200 characters; unique across all runs. A key not seen before starts a run (202). A key that already started one returns that run (200, reused: true) and reserves nothing, once it has ended; while it is still running the repeat answers 409. Absent: the Idempotency-Key header is used, and failing that a fresh key is generated, so such a request always starts a new run.
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, 202 Accepted

Field Type Required Description
job object yes The run, QUEUED on a 202 with reservedCredits set. On a 200 replay it is the run the idempotency key started, in its current state.
job.id string (uuid) yes Id of the run. Cancelling and settling credits are tracked against it.
job.articleId string (uuid) yes Id of the article the run works on.
job.workspaceId string (uuid) yes Id of the workspace that owns the article.
job.projectId string (uuid) yes Id of the project the article belongs to.
job.status "QUEUED" | "TRANSLATING" | "VALIDATING" | "RESEARCHING" | "OUTLINING" | "WRITING" | "GENERATING_IMAGE_PROMPTS" | "GENERATING_IMAGES" | "PROCESSING_MEDIA" | "FINALIZING" | "COMPLETED" | "FAILED" | "CANCELLED" yes Where the run is. QUEUED: accepted, not started yet. VALIDATING, RESEARCHING, OUTLINING, WRITING, GENERATING_IMAGE_PROMPTS, GENERATING_IMAGES, PROCESSING_MEDIA, FINALIZING: the pipeline is in that step, in this order (research only when requested; the outline step is skipped when resuming an approved outline). TRANSLATING: a translation run is working. COMPLETED, FAILED and CANCELLED are final.
job.kind "generation" | "translation" yes generation writes an article from its configuration; translation translates an article into another language.
job.currentStep "validate" | "research" | "outline" | "write" | "image-prompts" | "images" | "media-processing" | "translate" | "finalize" | null yes The step the run last entered; null while it is still QUEUED.
job.progress integer yes Percent of the run completed, 0 to 100. It moves to the step's value on entering each step and is 100 when the run is COMPLETED; a failed or cancelled run keeps the value it reached.
job.idempotencyKey string yes The key the run was started with: the one the client sent, or one the server generated. Unique across all runs; a request that repeats it gets this run back.
job.estimatedCredits integer yes Credits quoted when the run was started, the most it can cost: the full quote for POST /api/articles/{articleId}/generate, the writing leg's share for POST /api/articles/{articleId}/outline/approve.
job.reservedCredits integer yes Credits held against the wallet when the run was queued, equal to estimatedCredits. The hold ends when the run settles, but this value is not reset; read consumedCredits for what was charged.
job.consumedCredits integer yes Credits charged for the work delivered, set when the run settles and never above reservedCredits; 0 until then, and 0 for a run that failed before the article was saved or was cancelled.
job.errorCode string | null yes GENERATION_FAILED or TRANSLATION_FAILED when status is FAILED; otherwise null.
job.errorMessage string | null yes The message of the error that failed the run; null unless status is FAILED.
job.createdAt string (date-time) yes When the run was accepted, ISO 8601.
estimate object yes The quote for the article's current configuration. On a 202 its total is what was reserved; on a replay it is computed afresh, not read back from the run.
estimate.total integer yes Credits the run can cost at most: the sum of breakdown. This is the amount reserved when the run starts.
estimate.breakdown object yes The total split by what it pays for.
estimate.breakdown.text integer yes Writing the article: 1 credit per started 1,000 words of wordCount.
estimate.breakdown.images integer yes Images: 1 credit for each of imageCount.
estimate.breakdown.research integer yes Web research: 1 credit when the article asks for it and the deployment's web_research feature is on, otherwise 0.
estimate.breakdown.factCheck integer yes Fact checking: 1 credit when the article asks for research and the deployment's fact_checking feature is on, otherwise 0.
estimate.imageCount integer yes Images the quote prices, cover included: the configured imageCount, or the count derived from wordCount when that is null.
reused boolean yes false on a 202, a new run. true on a 200: the idempotency key had already started this run, and nothing was reserved 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.generation.start({
  articleId: "3f0c5a1e-8b2d-4c6e-9a7f-1d2e3f4a5b6c",
});
console.log(result);

pd.generation.getStatus(input, init?)

Generation progress for an article

GET /api/articles/{articleId}/status

Needs articles.read. Returns the article's workflow status and its most recent run of any kind (generation or translation), null when none was ever started: status, currentStep and progress say where it is, and a FAILED run carries errorCode and errorMessage. Poll this, or listen to GET /api/articles/{articleId}/events, after POST /api/articles/{articleId}/generate. A run that stopped at an outline for review reads COMPLETED while the article is back to DRAFT. An image regeneration has no run to read here.

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
articleStatus "DRAFT" | "QUEUED" | "PROCESSING" | "NEEDS_REVIEW" | "CHANGES_REQUESTED" | "APPROVED" | "PUBLISHED" yes The article's workflow status now: QUEUED and PROCESSING while a run owns it, then NEEDS_REVIEW after a completed generation, or DRAFT after a failed or cancelled one and after an outline-only run.
job object | null yes The article's most recent run of any kind; null when none was ever started.
job.id string (uuid) yes Id of the run. Cancelling and settling credits are tracked against it.
job.articleId string (uuid) yes Id of the article the run works on.
job.workspaceId string (uuid) yes Id of the workspace that owns the article.
job.projectId string (uuid) yes Id of the project the article belongs to.
job.status "QUEUED" | "TRANSLATING" | "VALIDATING" | "RESEARCHING" | "OUTLINING" | "WRITING" | "GENERATING_IMAGE_PROMPTS" | "GENERATING_IMAGES" | "PROCESSING_MEDIA" | "FINALIZING" | "COMPLETED" | "FAILED" | "CANCELLED" yes Where the run is. QUEUED: accepted, not started yet. VALIDATING, RESEARCHING, OUTLINING, WRITING, GENERATING_IMAGE_PROMPTS, GENERATING_IMAGES, PROCESSING_MEDIA, FINALIZING: the pipeline is in that step, in this order (research only when requested; the outline step is skipped when resuming an approved outline). TRANSLATING: a translation run is working. COMPLETED, FAILED and CANCELLED are final.
job.kind "generation" | "translation" yes generation writes an article from its configuration; translation translates an article into another language.
job.currentStep "validate" | "research" | "outline" | "write" | "image-prompts" | "images" | "media-processing" | "translate" | "finalize" | null yes The step the run last entered; null while it is still QUEUED.
job.progress integer yes Percent of the run completed, 0 to 100. It moves to the step's value on entering each step and is 100 when the run is COMPLETED; a failed or cancelled run keeps the value it reached.
job.idempotencyKey string yes The key the run was started with: the one the client sent, or one the server generated. Unique across all runs; a request that repeats it gets this run back.
job.estimatedCredits integer yes Credits quoted when the run was started, the most it can cost: the full quote for POST /api/articles/{articleId}/generate, the writing leg's share for POST /api/articles/{articleId}/outline/approve.
job.reservedCredits integer yes Credits held against the wallet when the run was queued, equal to estimatedCredits. The hold ends when the run settles, but this value is not reset; read consumedCredits for what was charged.
job.consumedCredits integer yes Credits charged for the work delivered, set when the run settles and never above reservedCredits; 0 until then, and 0 for a run that failed before the article was saved or was cancelled.
job.errorCode string | null yes GENERATION_FAILED or TRANSLATION_FAILED when status is FAILED; otherwise null.
job.errorMessage string | null yes The message of the error that failed the run; null unless status is FAILED.
job.createdAt string (date-time) yes When the run was accepted, ISO 8601.

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.generation.getStatus({
  articleId: "3f0c5a1e-8b2d-4c6e-9a7f-1d2e3f4a5b6c",
});
console.log(result);

pd.generation.cancel(input, init?)

Cancel a running generation

POST /api/articles/{articleId}/cancel

Needs articles.generate. Cancels the article's latest run; 409 when there is none or it has already ended (COMPLETED, FAILED, CANCELLED). The answer is immediate and says how: removed means the run had not started and is cancelled now (its reservation is released, the job is CANCELLED and the article returns to DRAFT); signalled means it is running and stops at its next checkpoint, which come before the outline, before each section and before each image, not during a provider call. Poll GET /api/articles/{articleId}/status for CANCELLED. A cancelled run is charged nothing, whatever it had finished. A cancel that arrives after the last image checkpoint does not stop the run, which completes and is charged normally. No webhook event is sent for a cancellation.

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
cancellation "removed" | "signalled" yes removed: the job had not started and is cancelled now. signalled: it is running and stops at its next checkpoint.
jobId string (uuid) yes Id of the run the cancellation applies to.

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.generation.cancel({
  articleId: "3f0c5a1e-8b2d-4c6e-9a7f-1d2e3f4a5b6c",
});
console.log(result);

pd.generation.regenerateImage(input, init?)

Regenerate an image, optionally with a new prompt or style

POST /api/articles/{articleId}/regenerate-image

Needs images.regenerate. Starts replacing the picture of one of the article's images in the background (404 when assetId is not this article's). The asset keeps its id and its place in the body; the new picture is stored under a new URL, so read publicUrl again once it is done. Always answers 202 and returns no job: completion is signalled by the image.regenerated webhook event (and article.updated when the article is published), or seen by reading the image again. Credits. 1 credit (3 when the article's imageQuality is hd) is reserved at once (402 INSUFFICIENT_CREDITS when unavailable) and charged when the new image is stored; a failed regeneration releases it. The plan's images quota applies (429 QUOTA_EXCEEDED). The idempotencyKey guards against double charging: a key used before, while its earlier request is still on record, reserves nothing, starts no second regeneration and still answers 202. The prompt and style rules are on prompt and style; the shape of the image comes from the project's image settings. Records an images.regenerated audit entry when the new image is stored.

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.
body body object yes
body.assetId body string (uuid) yes Id of the image to regenerate, the cover or an inline image of this article; 404 when it belongs to another article. It keeps this id.
body.prompt body string no The image prompt to use as written, 10 to 1200 characters. The project's style description and never-depict list are still appended, and the image's alt text is kept. Absent: a new prompt is written from the article's current text, and the alt text is rewritten to match.
body.style body "photorealistic" | "editorial" | "minimalist-vector" | "3d-render" | "watercolor" | "corporate-tech" | "cinematic" | "illustration" no Style for this image, stored on it so later regenerations keep it. Absent: the project's style for covers or inline images, else the style the image already has. Values: photorealistic (natural-light documentary photograph, realistic textures, no stylisation), editorial (modern editorial illustration or photograph, clean composition, restrained palette), minimalist-vector (flat vector illustration, few shapes, solid colours, plenty of empty space), 3d-render (soft studio 3D render, smooth matte materials, simple forms), watercolor (loose watercolour painting, soft edges, muted washes), corporate-tech (clean technology visual in cool blues and greys, geometric shapes), cinematic (cinematic still, dramatic lighting, strong atmosphere, wide framing), illustration (hand-drawn illustration, confident linework, warm friendly palette).
body.idempotencyKey body string no Makes the request safe to repeat, 8 to 200 characters. A key used before, while its earlier request is still on record, reserves nothing and starts no second regeneration, and still answers 202; use a new key for each regeneration. Absent: a fresh key is generated.
init init { signal?: AbortSignal; timeoutMs?: number } no The second argument: signal aborts the call, timeoutMs overrides the client's per-attempt timeout.

Returns 202 Accepted

Field Type Required Description
reservedCredits integer yes Credits held for the regeneration, 1. They are charged when the new image has replaced the old one and returned if the regeneration fails.
assetId string (uuid) yes Id of the image being regenerated. It keeps this id; its publicUrl changes when the new picture is stored.

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.generation.regenerateImage({
  articleId: "3f0c5a1e-8b2d-4c6e-9a7f-1d2e3f4a5b6c",
  body: {
    assetId: "3f0c5a1e-8b2d-4c6e-9a7f-1d2e3f4a5b6c",
  },
});
console.log(result);

pd.generation.updateAsset(input, init?)

Edit an image's alt text, or make it the hero or an inline image

PATCH /api/articles/{articleId}/assets/{assetId}

Send altText, type, or both (neither: 400). altText is copied to every body block showing the image. type: "hero" makes an image of the article its cover and removes every body block showing it (a new version; contentHtml and contentMarkdown are re-rendered); bodyBlocksRemoved says how many. The article has one hero: while another image is the hero the answer is 409 CONFLICT, details: { reason: "hero_exists", heroAssetId }, and nothing changes; make that one inline first. type: "inline" only changes the type: no block is put back, so the image stays unused until placed. The type the image already has changes nothing. A PUBLISHED article sends article.updated when the edit changes what it shows: a promote or demote, or an alt text that appears in the rendered content (the alt text of a cover, which no block shows, sends nothing). Needs images.update.

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.
assetId path string (uuid) yes Id of an image asset of the article articleId, as returned in its assets. An asset that belongs to another article answers 404 RESOURCE_NOT_FOUND.
body body object no
body.altText body string no The image's alt text (1 to 300 characters); every body block showing it follows.
body.type body "hero" | "inline" no hero makes the image the article's cover and removes every body block showing it; inline makes the hero an ordinary image again, without putting a block back.
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
asset object yes The asset after the edit.
asset.id string (uuid) yes Id of the asset.
asset.altText string yes The asset's alternative text after the edit.
asset.type "hero" | "inline" yes hero: the article's cover. inline: an image of the body.
bodyBlocksRemoved integer yes Image blocks of the body that showed the asset and were removed because it became the hero (a new version of the document); 0 for any other edit.

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.generation.updateAsset({
  articleId: "3f0c5a1e-8b2d-4c6e-9a7f-1d2e3f4a5b6c",
  assetId: "3f0c5a1e-8b2d-4c6e-9a7f-1d2e3f4a5b6c",
});
console.log(result);

pd.generation.deleteAsset(input, init?)

Delete an image

DELETE /api/articles/{articleId}/assets/{assetId}

Needs images.delete. Deletes one of the article's images, the cover or an inline one (404 when assetId is not this article's), and removes every body block that showed it, saving a new version of the document (and, for a published article, sending article.updated). The stored file is removed afterwards unless another row still uses it. Nothing is refunded: the credits the image cost were spent when it was made. Records an images.deleted audit entry.

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.
assetId path string (uuid) yes Id of an image asset of the article articleId, as returned in its assets. An asset that belongs to another article answers 404 RESOURCE_NOT_FOUND.
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
ok true yes Always true: the image was deleted.

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.generation.deleteAsset({
  articleId: "3f0c5a1e-8b2d-4c6e-9a7f-1d2e3f4a5b6c",
  assetId: "3f0c5a1e-8b2d-4c6e-9a7f-1d2e3f4a5b6c",
});
console.log(result);