siteAnalyses
The methods of pd.siteAnalyses. Inputs and outputs are described in Conventions; every method can also fail as described in Errors.
Methods
pd.siteAnalyses.start(input, init?)
Analyse the client's website to draft a project
POST /api/workspaces/{workspaceId}/site-analyses
Needs projects.create on the workspace. Records an analysis of the site and answers 202 with it, queued: the service reads the homepage, the sitemap and up to 10 chosen pages in the background and drafts a project, brand voice and article ideas from them. Poll GET /api/site-analyses/{analysisId} until status is ready (or failed), then send the reviewed draft to POST /api/site-analyses/{analysisId}/apply. The run costs no credits; a workspace may start 3 in any 24 hours, and the fourth answers 429 QUOTA_EXCEEDED (metric site_analyses). Every call starts a new analysis and counts toward that cap, even for a URL already analysed. An address that is not a public http or https URL answers 400 before anything is stored.
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. |
body |
body | object |
yes | |
body.url |
body | string |
yes | The client's website, up to 2000 characters, for example https://lumen.fr or just lumen.fr (read as https). It must be a public http or https address: one that carries credentials, does not resolve, or points to a private network answers 400. |
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 |
|---|---|---|---|
analysis |
object |
yes | The analysis as it stands now. |
analysis.id |
string (uuid) |
yes | Id of the analysis. Poll it with siteAnalyses.get. |
analysis.workspaceId |
string (uuid) |
yes | Id of the workspace that started the analysis. |
analysis.url |
string |
yes | The address that was read, after the server added https:// to a scheme-less input and normalised it. |
analysis.status |
"queued" | "crawling" | "analysing" | "ready" | "failed" | "applied" |
yes | Where the run stands: queued (accepted, not started yet), crawling (reading the site), analysing (the model is drafting), ready (result can be reviewed and applied), failed (see errorCode) or applied (a project was created from it; final). A run that stops moving for 5 minutes is reported failed with ANALYSIS_FAILED when it is next read. |
analysis.step |
string | null |
yes | The step the run is at while crawling or analysing: reading_homepage, choosing_pages, reading_pages or understanding_business. null in every other status. |
analysis.pages |
object[] |
yes | The pages that were read to draft the project. Empty until the run is ready. |
analysis.pages[].url |
string |
yes | Address of a page that was read. |
analysis.pages[].title |
string |
no | The page's title; absent when the page has none. |
analysis.result |
object | null |
yes | The draft project, defaults, brand voice and ideas to review and send back to siteAnalyses.apply. null until the run is ready. |
analysis.result.project |
object |
yes | The project to create. description, industry, audience and language are saved as the project's own, and defaults.tone and defaults.wordCount become its default tone and article length. |
analysis.result.project.name |
string |
yes | Name of the project. |
analysis.result.project.description |
string |
yes | Plain-language summary of what the business does and offers, up to 2000 characters. |
analysis.result.project.industry |
string |
yes | Industry or market the business operates in, as a short phrase, up to 200 characters. |
analysis.result.project.audience |
string |
yes | Who the business's content is for. |
analysis.result.project.language |
string |
yes | Language the site is written in, as a language tag. |
analysis.result.defaults |
object |
yes | What new articles of the project start from. |
analysis.result.defaults.tone |
"professional" | "conversational" | "authoritative" | "practical" | "persuasive" |
yes | Tone that new articles of the project start from. |
analysis.result.defaults.wordCount |
integer |
yes | Target length in words that new articles start from. |
analysis.result.brandVoice |
object |
yes | The brand voice to create, as the project's default voice. When the workspace already has a voice of that name, a numeric suffix such as (2) is added to it. |
analysis.result.brandVoice.name |
string |
yes | Name of the brand voice. |
analysis.result.brandVoice.description |
string |
yes | Summary of the voice. |
analysis.result.brandVoice.tone |
string |
yes | How the voice sounds, as free text of up to 500 characters (not one of the fixed article tones). |
analysis.result.brandVoice.writingRules |
string[] |
yes | Rules about how the voice writes. |
analysis.result.brandVoice.preferredWords |
string[] |
yes | Words and phrases the voice favours. |
analysis.result.brandVoice.avoidedWords |
string[] |
yes | Words and phrases the voice avoids. |
analysis.result.ideas |
object[] |
yes | The ideas to keep, up to 15, in priority order; delete the ones you do not want, and send none to create the project without ideas. Each is saved as a content idea (title as its title and topic, cluster as its content cluster), the first three at priority 1, the next three at 2, and so on up to 5. |
analysis.result.ideas[].title |
string |
yes | Working title of the article idea. |
analysis.result.ideas[].primaryKeyword |
string |
yes | Keyword the article idea targets. |
analysis.result.ideas[].searchIntent |
"informational" | "commercial" | "transactional" | "navigational" |
yes | Intent behind searches for the keyword. informational: learning something. commercial: comparing options before buying. transactional: buying or signing up. navigational: reaching a particular site or page. |
analysis.result.ideas[].cluster |
string |
yes | Topic cluster the idea belongs to. |
analysis.iconUrl |
string | null |
yes | The site's icon, stored by PrismDraft. null when none could be read; the icon never makes a run fail. |
analysis.errorCode |
string | null |
yes | Set when status is failed: SITE_UNREACHABLE (the homepage could not be read), SITE_TOO_THIN (the site has too little text to analyse) or ANALYSIS_FAILED (the model call failed, the run took too long or was lost). null otherwise. |
analysis.errorMessage |
string | null |
yes | A short English explanation that goes with errorCode. null when there is no error. |
analysis.projectId |
string (uuid) | null |
yes | Id of the project created from the analysis. null until it is applied. |
analysis.createdAt |
string (date-time) |
yes | When the analysis was started, as an ISO 8601 UTC date-time. |
analysis.updatedAt |
string (date-time) |
yes | When the run last changed, 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.siteAnalyses.start({
workspaceId: "3f0c5a1e-8b2d-4c6e-9a7f-1d2e3f4a5b6c",
body: {
url: "example",
},
});
console.log(result);
pd.siteAnalyses.get(input, init?)
Read a website analysis
GET /api/site-analyses/{analysisId}
Needs projects.create on the workspace the analysis belongs to (reading it takes the same permission as creating the project). Returns the analysis with its progress; once status is ready the draft is in result. A run that has not moved for 5 minutes is marked failed with errorCode ANALYSIS_FAILED when it is read, so a run that is lost never stays queued, crawling or analysing forever. Other failures are SITE_UNREACHABLE (the homepage could not be read) and SITE_TOO_THIN (too little text to analyse). 404 for an unknown id.
Input
| Name | In | Type | Required | Description |
|---|---|---|---|---|
analysisId |
path | string (uuid) |
yes | Id of the website analysis, from POST /api/workspaces/{workspaceId}/site-analyses's answer. |
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 |
|---|---|---|---|
analysis |
object |
yes | The analysis as it stands now. |
analysis.id |
string (uuid) |
yes | Id of the analysis. Poll it with siteAnalyses.get. |
analysis.workspaceId |
string (uuid) |
yes | Id of the workspace that started the analysis. |
analysis.url |
string |
yes | The address that was read, after the server added https:// to a scheme-less input and normalised it. |
analysis.status |
"queued" | "crawling" | "analysing" | "ready" | "failed" | "applied" |
yes | Where the run stands: queued (accepted, not started yet), crawling (reading the site), analysing (the model is drafting), ready (result can be reviewed and applied), failed (see errorCode) or applied (a project was created from it; final). A run that stops moving for 5 minutes is reported failed with ANALYSIS_FAILED when it is next read. |
analysis.step |
string | null |
yes | The step the run is at while crawling or analysing: reading_homepage, choosing_pages, reading_pages or understanding_business. null in every other status. |
analysis.pages |
object[] |
yes | The pages that were read to draft the project. Empty until the run is ready. |
analysis.pages[].url |
string |
yes | Address of a page that was read. |
analysis.pages[].title |
string |
no | The page's title; absent when the page has none. |
analysis.result |
object | null |
yes | The draft project, defaults, brand voice and ideas to review and send back to siteAnalyses.apply. null until the run is ready. |
analysis.result.project |
object |
yes | The project to create. description, industry, audience and language are saved as the project's own, and defaults.tone and defaults.wordCount become its default tone and article length. |
analysis.result.project.name |
string |
yes | Name of the project. |
analysis.result.project.description |
string |
yes | Plain-language summary of what the business does and offers, up to 2000 characters. |
analysis.result.project.industry |
string |
yes | Industry or market the business operates in, as a short phrase, up to 200 characters. |
analysis.result.project.audience |
string |
yes | Who the business's content is for. |
analysis.result.project.language |
string |
yes | Language the site is written in, as a language tag. |
analysis.result.defaults |
object |
yes | What new articles of the project start from. |
analysis.result.defaults.tone |
"professional" | "conversational" | "authoritative" | "practical" | "persuasive" |
yes | Tone that new articles of the project start from. |
analysis.result.defaults.wordCount |
integer |
yes | Target length in words that new articles start from. |
analysis.result.brandVoice |
object |
yes | The brand voice to create, as the project's default voice. When the workspace already has a voice of that name, a numeric suffix such as (2) is added to it. |
analysis.result.brandVoice.name |
string |
yes | Name of the brand voice. |
analysis.result.brandVoice.description |
string |
yes | Summary of the voice. |
analysis.result.brandVoice.tone |
string |
yes | How the voice sounds, as free text of up to 500 characters (not one of the fixed article tones). |
analysis.result.brandVoice.writingRules |
string[] |
yes | Rules about how the voice writes. |
analysis.result.brandVoice.preferredWords |
string[] |
yes | Words and phrases the voice favours. |
analysis.result.brandVoice.avoidedWords |
string[] |
yes | Words and phrases the voice avoids. |
analysis.result.ideas |
object[] |
yes | The ideas to keep, up to 15, in priority order; delete the ones you do not want, and send none to create the project without ideas. Each is saved as a content idea (title as its title and topic, cluster as its content cluster), the first three at priority 1, the next three at 2, and so on up to 5. |
analysis.result.ideas[].title |
string |
yes | Working title of the article idea. |
analysis.result.ideas[].primaryKeyword |
string |
yes | Keyword the article idea targets. |
analysis.result.ideas[].searchIntent |
"informational" | "commercial" | "transactional" | "navigational" |
yes | Intent behind searches for the keyword. informational: learning something. commercial: comparing options before buying. transactional: buying or signing up. navigational: reaching a particular site or page. |
analysis.result.ideas[].cluster |
string |
yes | Topic cluster the idea belongs to. |
analysis.iconUrl |
string | null |
yes | The site's icon, stored by PrismDraft. null when none could be read; the icon never makes a run fail. |
analysis.errorCode |
string | null |
yes | Set when status is failed: SITE_UNREACHABLE (the homepage could not be read), SITE_TOO_THIN (the site has too little text to analyse) or ANALYSIS_FAILED (the model call failed, the run took too long or was lost). null otherwise. |
analysis.errorMessage |
string | null |
yes | A short English explanation that goes with errorCode. null when there is no error. |
analysis.projectId |
string (uuid) | null |
yes | Id of the project created from the analysis. null until it is applied. |
analysis.createdAt |
string (date-time) |
yes | When the analysis was started, as an ISO 8601 UTC date-time. |
analysis.updatedAt |
string (date-time) |
yes | When the run last changed, 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.siteAnalyses.get({
analysisId: "3f0c5a1e-8b2d-4c6e-9a7f-1d2e3f4a5b6c",
});
console.log(result);
pd.siteAnalyses.apply(input, init?)
Create the project a website analysis drafted, as reviewed
POST /api/site-analyses/{analysisId}/apply
Needs projects.create on the workspace the analysis belongs to. Creates, in one transaction, the project from draft.project and draft.defaults (with the analysed URL as its website and the site's icon, when one was read), a brand voice from draft.brandVoice as the project's default voice, and a content idea for each of draft.ideas; the workspace takes the site's icon too when it has none. The analysis becomes applied and its projectId is set. Answers 409 CONFLICT when the analysis is not ready: still running, failed, or already applied (so applying twice creates one project), and 429 QUOTA_EXCEEDED when the plan's projects quota is reached. Costs no credits. Records a projects.created audit entry with source site_analysis. It also starts a detection of the new project's image style from its website (no credits, outside the daily cap of image style runs), which is applied on its own when it succeeds and the project still has no style description.
Input
| Name | In | Type | Required | Description |
|---|---|---|---|---|
analysisId |
path | string (uuid) |
yes | Id of the website analysis, from POST /api/workspaces/{workspaceId}/site-analyses's answer. |
body |
body | object |
yes | |
body.draft |
body | object |
yes | The draft to create the project from: the analysis's result as the client reviewed and edited it. What is sent is what is created; it is not compared with the stored result. |
body.draft.project |
body | object |
yes | The project to create. description, industry, audience and language are saved as the project's own, and defaults.tone and defaults.wordCount become its default tone and article length. |
body.draft.project.name |
body | string |
yes | Name of the project. |
body.draft.project.description |
body | string |
yes | Plain-language summary of what the business does and offers, up to 2000 characters. |
body.draft.project.industry |
body | string |
yes | Industry or market the business operates in, as a short phrase, up to 200 characters. |
body.draft.project.audience |
body | string |
yes | Who the business's content is for. |
body.draft.project.language |
body | string |
yes | Language the site is written in, as a language tag. |
body.draft.defaults |
body | object |
yes | What new articles of the project start from. |
body.draft.defaults.tone |
body | "professional" | "conversational" | "authoritative" | "practical" | "persuasive" |
yes | Tone that new articles of the project start from. |
body.draft.defaults.wordCount |
body | integer |
yes | Target length in words that new articles start from. |
body.draft.brandVoice |
body | object |
yes | The brand voice to create, as the project's default voice. When the workspace already has a voice of that name, a numeric suffix such as (2) is added to it. |
body.draft.brandVoice.name |
body | string |
yes | Name of the brand voice. |
body.draft.brandVoice.description |
body | string |
yes | Summary of the voice. |
body.draft.brandVoice.tone |
body | string |
yes | How the voice sounds, as free text of up to 500 characters (not one of the fixed article tones). |
body.draft.brandVoice.writingRules |
body | string[] |
yes | Rules about how the voice writes. |
body.draft.brandVoice.preferredWords |
body | string[] |
yes | Words and phrases the voice favours. |
body.draft.brandVoice.avoidedWords |
body | string[] |
yes | Words and phrases the voice avoids. |
body.draft.ideas |
body | object[] |
yes | The ideas to keep, up to 15, in priority order; delete the ones you do not want, and send none to create the project without ideas. Each is saved as a content idea (title as its title and topic, cluster as its content cluster), the first three at priority 1, the next three at 2, and so on up to 5. |
body.draft.ideas[].title |
body | string |
yes | Working title of the article idea. |
body.draft.ideas[].primaryKeyword |
body | string |
yes | Keyword the article idea targets. |
body.draft.ideas[].searchIntent |
body | "informational" | "commercial" | "transactional" | "navigational" |
yes | Intent behind searches for the keyword. informational: learning something. commercial: comparing options before buying. transactional: buying or signing up. navigational: reaching a particular site or page. |
body.draft.ideas[].cluster |
body | string |
yes | Topic cluster the idea belongs to. |
init |
init | { signal?: AbortSignal; timeoutMs?: number } |
no | The second argument: signal aborts the call, timeoutMs overrides the client's per-attempt timeout. |
Returns 201 Created
| Field | Type | Required | Description |
|---|---|---|---|
project |
object |
yes | The project created from the reviewed draft, with the analysed address as its website. |
project.id |
string (uuid) |
yes | Id of the project, used in every /projects/{projectId} route. |
project.workspaceId |
string (uuid) |
yes | Id of the workspace that owns the project. |
project.name |
string |
yes | Display name of the project, 1 to 120 characters. |
project.slug |
string |
yes | URL-safe identifier, unique within the workspace. Generated from name when not sent at creation, and never changed afterwards. |
project.description |
string |
yes | Free-text description of the project, up to 2000 characters; empty when unset. |
project.settings |
unknown |
yes | Free-form keys; an object, {} for a new project. |
project.websiteUrl |
string | null |
yes | Address of the website the project writes for; null when none was given. |
project.iconUrl |
string | null |
yes | Address of the site's icon, copied to the media host: found by the website analysis, or fetched when websiteUrl is set or changed and by POST .../icon/refresh. null until one is found. No request sets it directly. |
project.industry |
string |
yes | Free-text industry of the site, up to 200 characters; empty when unset. |
project.audience |
string |
yes | Free-text description of who the articles are written for, up to 500 characters; empty when unset. The app starts a new article's audience from it. |
project.language |
string |
yes | The project's default language, a language tag such as en or pt-BR: where a new article starts. Always one of languages. |
project.languages |
string[] |
yes | Every language the project publishes in, as language tags, with the default language first. Articles are written in one of these. |
project.defaultTone |
string |
yes | One of the article tones; the draft's default. |
project.defaultWordCount |
integer |
yes | Target length in words a new article starts from, from 200 to 10000. Each article can change it. |
project.status |
"active" | "archived" |
yes | active, or archived: read-only until POST /api/workspaces/{workspaceId}/projects/{projectId}/restore makes it active again. |
project.createdAt |
string (date-time) |
yes | When the project was created, as an ISO 8601 timestamp. |
project.updatedAt |
string (date-time) |
yes | When the project row last changed (an update, an archive or a restore), as an ISO 8601 timestamp. |
brandVoice |
object |
yes | The default brand voice created for the project. |
brandVoice.id |
string (uuid) |
yes | Brand voice id. |
brandVoice.workspaceId |
string (uuid) |
yes | Workspace that owns the voice. |
brandVoice.projectId |
string (uuid) | null |
yes | Project the voice is scoped to. Null: available to every project of the workspace. |
brandVoice.name |
string |
yes | Display name; unique within the workspace. |
brandVoice.description |
string |
yes | What the voice is for; empty when none was given. |
brandVoice.tone |
string |
yes | Free-text tone the writer is told to use; empty when none was given. |
brandVoice.vocabulary |
string |
yes | Free-text guidance on the register and vocabulary to use; empty when none was given. |
brandVoice.writingRules |
string[] |
yes | Rules about how to write, each given to the writer as an instruction. |
brandVoice.formattingRules |
string[] |
yes | Rules about layout and formatting, each given to the writer as an instruction. |
brandVoice.preferredWords |
string[] |
yes | Words and phrases the writer is told to prefer. |
brandVoice.avoidedWords |
string[] |
yes | Words and phrases the writer is told to avoid. |
brandVoice.examples |
unknown |
yes | Example passages of the voice. An analysis creates the voice without any, so this is []; no route sets or edits them. |
brandVoice.isDefault |
boolean |
yes | Whether generation uses this voice when none is chosen: the project's default first, then the workspace-wide one. One default per scope (the workspace, or one project). |
brandVoice.createdAt |
string (date-time) |
yes | When the voice was created. |
brandVoice.updatedAt |
string (date-time) |
yes | When the voice was last changed. |
ideas |
object[] |
yes | The ideas of the draft, stored on the new project. priority follows the draft's order: 1 for the first three, 2 for the next three, and so on up to 5. |
ideas[].id |
string (uuid) |
yes | Id of the idea. |
ideas[].workspaceId |
string (uuid) |
yes | Id of the workspace that owns the idea. |
ideas[].projectId |
string (uuid) | null |
yes | Id of the project the idea is planned for. null for an idea that belongs to the workspace rather than to a project. |
ideas[].topic |
string |
yes | The subject to write about, in up to 300 characters. For a suggested idea (ideas.generate, or an onboarding draft) it equals title. |
ideas[].title |
string |
yes | Working headline for the article, up to 300 characters. An empty string when none was given. |
ideas[].primaryKeyword |
string |
yes | The main search phrase the idea targets. An empty string when none was given. |
ideas[].searchIntent |
"informational" | "commercial" | "transactional" | "navigational" | null |
yes | What a reader searching for this is after: informational (to learn), commercial (to compare options before choosing), transactional (to act or buy) or navigational (to reach a specific site or page). null when not set. |
ideas[].contentCluster |
string |
yes | Label of the group of related topics the idea belongs to, used to keep ideas on one theme together. An empty string when none was given. |
ideas[].priority |
integer |
yes | Rank from 1 (write first) to 5 (last). ideas.list returns lower numbers first; 3 when none was given. |
ideas[].articleId |
string (uuid) | null |
yes | Id of the article written from the idea. The server sets it when an article is created with this idea's ideaId (articles.create); it cannot be set here. null while the idea is open, and again if that article is deleted. |
ideas[].createdAt |
string (date-time) |
yes | When the idea was created, as an ISO 8601 UTC date-time. |
ideas[].updatedAt |
string (date-time) |
yes | When the idea was last changed, 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.siteAnalyses.apply({
analysisId: "3f0c5a1e-8b2d-4c6e-9a7f-1d2e3f4a5b6c",
body: {
draft: {
project: {
name: "example",
description: "example",
industry: "example",
audience: "example",
language: "example",
},
defaults: {
tone: "professional",
wordCount: 600,
},
brandVoice: {
name: "example",
description: "example",
tone: "example",
writingRules: ["example"],
preferredWords: ["example"],
avoidedWords: ["example"],
},
ideas: [
{
title: "example",
primaryKeyword: "example",
searchIntent: "informational",
cluster: "example",
},
],
},
},
});
console.log(result);