PrismDraft SDK

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);