PrismDraft SDK

plans

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

Methods

pd.plans.suggest(input, init?)

Suggest a publishing plan

POST /api/projects/{projectId}/plans/suggest

Asks the model for articles for the project, one per slot of the cadence, and sets each on its day. Slots follow the cadence's weekdays from startsOn, read on the wall of timezone, and scheduledFor is each slot as a UTC instant. The project's last 60 titles, as ideas or articles, are not repeated, nor is one title twice. Nothing is stored: confirm the result, edited if need be, with plans.create. Requires articles.create on the project. Costs 1 credit per model call that brought new ideas (at most one call per 12 slots, plus one more when the first came up short): the credits are reserved first, 402 INSUFFICIENT_CREDITS when the balance cannot cover them, and the ones for a call that brought nothing are released. 429 QUOTA_EXCEEDED when the ai_operations quota is used up. 400 when the first slot is already in the past (nothing is reserved or charged). The answer holds fewer suggestions than slots when the model had too few new ideas; estimate is for the suggestions returned and is the most writing them costs, not a charge.

Input

Name In Type Required Description
projectId path string (uuid) yes Id of the project. The workspace is read from the project, never from the request; a caller who is not a member of it gets 404 RESOURCE_NOT_FOUND.
body body object yes
body.postsPerWeek body integer yes Articles per week, 1 to 7. They fall on fixed weekdays: 1 Wednesday; 2 Tuesday and Thursday; 3 Monday, Wednesday and Friday; 4 Monday to Thursday; 5 Monday to Friday; 6 Monday to Saturday; 7 every day.
body.weeks body integer yes How many weeks the plan covers, 1 to 12.
body.startsOn body string yes First day the plan may use, YYYY-MM-DD on the wall of timezone. A start in the middle of a week skips the days already gone. The year is 2000 to 2100.
body.runTime body string yes Time of day of every slot, HH:MM 24-hour, on the wall of timezone.
body.timezone body string yes IANA time zone the dates and runTime are read in, such as Europe/Paris.
init init { signal?: AbortSignal; timeoutMs?: number } no The second argument: signal aborts the call, timeoutMs overrides the client's per-attempt timeout.

Returns 200 OK

Field Type Required Description
suggestions object[] yes One per slot, in date order. Fewer than the slots when the model brought too few new ideas. Nothing is stored.
suggestions[].title string yes Suggested working title.
suggestions[].primaryKeyword string yes Suggested main search phrase.
suggestions[].searchIntent "informational" | "commercial" | "transactional" | "navigational" yes What a reader searching for it is after.
suggestions[].contentCluster string yes Group of related topics the idea belongs to.
suggestions[].localDate string yes Day of the slot, YYYY-MM-DD on the wall of the requested time zone.
suggestions[].scheduledFor string (date-time) yes The slot as a UTC instant: localDate at runTime in the time zone.
estimate object yes What writing every suggested article would cost at most.
estimate.perArticle integer yes Most credits one article costs: its quote, images included, plus a translation for each of the project's other languages.
estimate.articles integer yes Articles in the answer.
estimate.total integer yes perArticle times articles: the most writing and translating them all costs.
credits integer yes Credits charged for the suggestions: 1 per model call that brought new ideas, 0 when none did.

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.plans.suggest({
  projectId: "3f0c5a1e-8b2d-4c6e-9a7f-1d2e3f4a5b6c",
  body: {
    postsPerWeek: 1,
    weeks: 1,
    startsOn: "example",
    runTime: "example",
    timezone: "example",
  },
});
console.log(result);

pd.plans.create(input, init?)

Create a publishing plan

POST /api/projects/{projectId}/plans

Saves a plan: one idea and one calendar item per entry of items, the item set on its localDate at runTime in timezone with its status planned and its runState pending, all or nothing. Requires articles.create on the project. 400 when a slot is already in the past, 409 CONFLICT with details.reason plan_limit when the workspace already has 3 open plans (active or paused; the plans of an archived project are not counted). A plan costs nothing to save. With autopilot on, each article is written, approved and published on its day (see autopilot below); otherwise nothing runs. creditCap and estimatedCredits default to the quote of writing every item. Sending the same idempotencyKey again answers 200 with the plan the first call made, and stores nothing, even when its first slot has passed since; a new plan answers 201. A key already used for a plan of another project answers 409 CONFLICT with details.reason idempotency_key_reused. With autopilot on, 400 INVALID_REQUEST with details.reason autopilot_off when the platform has switched autopilot off.

Input

Name In Type Required Description
projectId path string (uuid) yes Id of the project. The workspace is read from the project, never from the request; a caller who is not a member of it gets 404 RESOURCE_NOT_FOUND.
body body object yes
body.postsPerWeek body integer yes Articles per week, 1 to 7. They fall on fixed weekdays: 1 Wednesday; 2 Tuesday and Thursday; 3 Monday, Wednesday and Friday; 4 Monday to Thursday; 5 Monday to Friday; 6 Monday to Saturday; 7 every day.
body.weeks body integer yes How many weeks the plan covers, 1 to 12.
body.startsOn body string yes First day the plan may use, YYYY-MM-DD on the wall of timezone. A start in the middle of a week skips the days already gone. The year is 2000 to 2100.
body.runTime body string yes Time of day of every slot, HH:MM 24-hour, on the wall of timezone.
body.timezone body string yes IANA time zone the dates and runTime are read in, such as Europe/Paris.
body.name body string no Name of the plan, up to 120 characters. Absent: the project's name and the start date.
body.items body object[] yes The articles and their days, 1 to 24; usually the edited result of plans.suggest.
body.items[].title body string yes Working title of the article, 1 to 300 characters.
body.items[].primaryKeyword body string no Main search phrase, up to 200 characters. Empty when absent. Default: "".
body.items[].searchIntent body "informational" | "commercial" | "transactional" | "navigational" | null no informational, commercial, transactional or navigational. null or absent: not set.
body.items[].contentCluster body string no Label of the group of related topics, up to 200 characters. Empty when absent. Default: "".
body.items[].localDate body string yes Day of the slot, YYYY-MM-DD on the wall of the plan's timezone; the year is 2000 to 2100.
body.creditCap body integer no Most credits the plan may spend on writing. Absent: the estimate.
body.autopilot body boolean no Whether the plan writes, approves and publishes each article on its day without a person. Needs articles.generate, articles.approve and articles.publish on the project and a person's session (403 for an API key or a member without them); the caller becomes the member whose permissions are re-checked on every run. Default: false.
body.idempotencyKey body string yes A value the caller makes up once per plan, such as a UUID. Sending the same one again returns the plan it created instead of making a second.
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, 201 Created

Field Type Required Description
plan object yes The plan.
plan.id string (uuid) yes Id of the plan.
plan.workspaceId string (uuid) yes Id of the workspace that owns the plan.
plan.projectId string (uuid) yes Id of the project the plan writes for.
plan.name string yes Name of the plan.
plan.status "draft" | "active" | "paused" | "completed" | "cancelled" yes active, paused, completed or cancelled. draft is a state that is never returned yet.
plan.pausedReason string | null yes Why a paused plan stopped; null otherwise.
plan.postsPerWeek integer yes Cadence the plan was made with.
plan.weeks integer yes Weeks the plan was made for.
plan.startsOn string yes First day the plan used, YYYY-MM-DD.
plan.runTime string yes Time of day of every slot, HH:MM.
plan.timezone string yes IANA time zone of the dates and runTime.
plan.autopilot boolean yes Whether the plan writes, approves and publishes on its own, as long as the member who enabled it keeps the permissions.
plan.estimatedCredits integer yes Most credits writing and translating every item costs, as estimated when the plan was made.
plan.creditCap integer yes Most credits the plan may spend on writing.
plan.creditsSpent integer yes Credits the plan has spent on writing.
plan.createdAt string (date-time) yes When the plan was made.
plan.updatedAt string (date-time) yes When the plan last changed.
plan.progress object yes Counts of the plan's items by state.
plan.progress.total integer yes Items of the plan.
plan.progress.pending integer yes Waiting for their day.
plan.progress.running integer yes Being written or published.
plan.progress.done integer yes Published.
plan.progress.parked integer yes Stopped for a person to look at.
plan.progress.skipped integer yes Dropped because the plan was cancelled.
items object[] yes The plan's calendar items, earliest first.
items[].id string (uuid) yes Id of the calendar item.
items[].title string yes Working title of the article.
items[].scheduledFor string (date-time) | null yes The slot as a UTC instant.
items[].localDate string | null yes The slot's day on the wall of the plan's time zone.
items[].runState "pending" | "claimed" | "generating" | "publishing" | "translating" | "done" | "parked" | "skipped" | null yes Where the item is in its run.
items[].parkedReason string | null yes Why a parked item stopped.
items[].articleId string (uuid) | null yes The article written for it, once there is one.
items[].ideaId string (uuid) | null yes The idea it came from.

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.plans.create({
  projectId: "3f0c5a1e-8b2d-4c6e-9a7f-1d2e3f4a5b6c",
  body: {
    postsPerWeek: 1,
    weeks: 1,
    startsOn: "example",
    runTime: "example",
    timezone: "example",
    items: [
      {
        title: "example",
        localDate: "example",
      },
    ],
    idempotencyKey: "example",
  },
});
console.log(result);

pd.plans.list(input, init?)

List publishing plans

GET /api/workspaces/{workspaceId}/plans

Returns the workspace's plans, newest first, each with its progress counts; the list is not paginated. Requires articles.read. Plans of projects the caller cannot access are left out.

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.
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
plans object[] yes The workspace's plans, newest first; the list is not paginated.
plans[].id string (uuid) yes Id of the plan.
plans[].workspaceId string (uuid) yes Id of the workspace that owns the plan.
plans[].projectId string (uuid) yes Id of the project the plan writes for.
plans[].name string yes Name of the plan.
plans[].status "draft" | "active" | "paused" | "completed" | "cancelled" yes active, paused, completed or cancelled. draft is a state that is never returned yet.
plans[].pausedReason string | null yes Why a paused plan stopped; null otherwise.
plans[].postsPerWeek integer yes Cadence the plan was made with.
plans[].weeks integer yes Weeks the plan was made for.
plans[].startsOn string yes First day the plan used, YYYY-MM-DD.
plans[].runTime string yes Time of day of every slot, HH:MM.
plans[].timezone string yes IANA time zone of the dates and runTime.
plans[].autopilot boolean yes Whether the plan writes, approves and publishes on its own, as long as the member who enabled it keeps the permissions.
plans[].estimatedCredits integer yes Most credits writing and translating every item costs, as estimated when the plan was made.
plans[].creditCap integer yes Most credits the plan may spend on writing.
plans[].creditsSpent integer yes Credits the plan has spent on writing.
plans[].createdAt string (date-time) yes When the plan was made.
plans[].updatedAt string (date-time) yes When the plan last changed.
plans[].progress object yes Counts of the plan's items by state.
plans[].progress.total integer yes Items of the plan.
plans[].progress.pending integer yes Waiting for their day.
plans[].progress.running integer yes Being written or published.
plans[].progress.done integer yes Published.
plans[].progress.parked integer yes Stopped for a person to look at.
plans[].progress.skipped integer yes Dropped because the plan was cancelled.

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

pd.plans.get(input, init?)

Read a publishing plan

GET /api/workspaces/{workspaceId}/plans/{planId}

Returns the plan and its calendar items, earliest first. Requires articles.read on the plan's project; 403 for a caller who is not a member of a restricted project, 404 when the plan is not in the workspace.

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.
planId path string (uuid) yes Id of the plan, in the workspace workspaceId. 404 when it is not.
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
plan object yes The plan.
plan.id string (uuid) yes Id of the plan.
plan.workspaceId string (uuid) yes Id of the workspace that owns the plan.
plan.projectId string (uuid) yes Id of the project the plan writes for.
plan.name string yes Name of the plan.
plan.status "draft" | "active" | "paused" | "completed" | "cancelled" yes active, paused, completed or cancelled. draft is a state that is never returned yet.
plan.pausedReason string | null yes Why a paused plan stopped; null otherwise.
plan.postsPerWeek integer yes Cadence the plan was made with.
plan.weeks integer yes Weeks the plan was made for.
plan.startsOn string yes First day the plan used, YYYY-MM-DD.
plan.runTime string yes Time of day of every slot, HH:MM.
plan.timezone string yes IANA time zone of the dates and runTime.
plan.autopilot boolean yes Whether the plan writes, approves and publishes on its own, as long as the member who enabled it keeps the permissions.
plan.estimatedCredits integer yes Most credits writing and translating every item costs, as estimated when the plan was made.
plan.creditCap integer yes Most credits the plan may spend on writing.
plan.creditsSpent integer yes Credits the plan has spent on writing.
plan.createdAt string (date-time) yes When the plan was made.
plan.updatedAt string (date-time) yes When the plan last changed.
plan.progress object yes Counts of the plan's items by state.
plan.progress.total integer yes Items of the plan.
plan.progress.pending integer yes Waiting for their day.
plan.progress.running integer yes Being written or published.
plan.progress.done integer yes Published.
plan.progress.parked integer yes Stopped for a person to look at.
plan.progress.skipped integer yes Dropped because the plan was cancelled.
items object[] yes The plan's calendar items, earliest first.
items[].id string (uuid) yes Id of the calendar item.
items[].title string yes Working title of the article.
items[].scheduledFor string (date-time) | null yes The slot as a UTC instant.
items[].localDate string | null yes The slot's day on the wall of the plan's time zone.
items[].runState "pending" | "claimed" | "generating" | "publishing" | "translating" | "done" | "parked" | "skipped" | null yes Where the item is in its run.
items[].parkedReason string | null yes Why a parked item stopped.
items[].articleId string (uuid) | null yes The article written for it, once there is one.
items[].ideaId string (uuid) | null yes The idea it came from.

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

pd.plans.update(input, init?)

Change a publishing plan

PATCH /api/workspaces/{workspaceId}/plans/{planId}

Renames the plan, changes its credit cap, changes the time of day of its slots (runTime, see the field), turns autopilot on or off (see the field), pauses it (paused, reason manual), resumes it (active) or cancels it (cancelled). Cancelling is final: items that have not started become skipped. An item whose run is already going becomes skipped when that run ends: its article is left as it is for a person, and nothing is approved or published for a cancelled plan (an article already live stays live). A cancelled or completed plan answers 409 CONFLICT with details.reason plan_closed to any status change. A request that changes nothing answers 200 with the plan as it is and records no audit entry. Requires articles.update on the plan's project. The plan of an archived project can only be cancelled, with a body of { "status": "cancelled" } and nothing else; any other change to it answers 409 CONFLICT. 400 when the body names no field, and 400 INVALID_REQUEST with details.reason autopilot_off when it turns autopilot on, or resumes an autopilot plan, while the platform has autopilot switched off.

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.
planId path string (uuid) yes Id of the plan, in the workspace workspaceId. 404 when it is not.
body body object no
body.name body string no New name, up to 120 characters.
body.creditCap body integer no New cap on the credits the plan may spend on writing.
body.runTime body string no New time of day of every slot, HH:MM 24-hour, on the wall of the plan's time zone. Every item that has not started and still lies ahead moves to that time on its own day; items already running, finished, parked or skipped, and items already overdue, stay where they are. 400 when the new time would put an item in the past.
body.status body "active" | "paused" | "cancelled" no paused stops the plan, active resumes it, cancelled closes it for good and skips every item that has not started. A cancelled or completed plan cannot change status.
body.autopilot body boolean no Turns unattended writing, approving and publishing on or off. Turning it on, or resuming a plan that has it on, needs articles.generate, articles.approve and articles.publish on the project and a person's session, and makes the caller the member whose permissions are re-checked on every 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

Field Type Required Description
plan object yes The plan.
plan.id string (uuid) yes Id of the plan.
plan.workspaceId string (uuid) yes Id of the workspace that owns the plan.
plan.projectId string (uuid) yes Id of the project the plan writes for.
plan.name string yes Name of the plan.
plan.status "draft" | "active" | "paused" | "completed" | "cancelled" yes active, paused, completed or cancelled. draft is a state that is never returned yet.
plan.pausedReason string | null yes Why a paused plan stopped; null otherwise.
plan.postsPerWeek integer yes Cadence the plan was made with.
plan.weeks integer yes Weeks the plan was made for.
plan.startsOn string yes First day the plan used, YYYY-MM-DD.
plan.runTime string yes Time of day of every slot, HH:MM.
plan.timezone string yes IANA time zone of the dates and runTime.
plan.autopilot boolean yes Whether the plan writes, approves and publishes on its own, as long as the member who enabled it keeps the permissions.
plan.estimatedCredits integer yes Most credits writing and translating every item costs, as estimated when the plan was made.
plan.creditCap integer yes Most credits the plan may spend on writing.
plan.creditsSpent integer yes Credits the plan has spent on writing.
plan.createdAt string (date-time) yes When the plan was made.
plan.updatedAt string (date-time) yes When the plan last changed.
plan.progress object yes Counts of the plan's items by state.
plan.progress.total integer yes Items of the plan.
plan.progress.pending integer yes Waiting for their day.
plan.progress.running integer yes Being written or published.
plan.progress.done integer yes Published.
plan.progress.parked integer yes Stopped for a person to look at.
plan.progress.skipped integer yes Dropped because the plan was cancelled.
items object[] yes The plan's calendar items, earliest first.
items[].id string (uuid) yes Id of the calendar item.
items[].title string yes Working title of the article.
items[].scheduledFor string (date-time) | null yes The slot as a UTC instant.
items[].localDate string | null yes The slot's day on the wall of the plan's time zone.
items[].runState "pending" | "claimed" | "generating" | "publishing" | "translating" | "done" | "parked" | "skipped" | null yes Where the item is in its run.
items[].parkedReason string | null yes Why a parked item stopped.
items[].articleId string (uuid) | null yes The article written for it, once there is one.
items[].ideaId string (uuid) | null yes The idea it came from.

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