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