Publishing
Drafts and scheduling on social networks.
Privacy and analytics
We use necessary technical cookies and, only with your consent, analytics and marketing pixels to improve the website and measure campaigns.
Drafts and scheduling on social networks.
The brand's posts with their real state, derived from their destinations (one per connected account): draft, in_review, approved_unscheduled, scheduled, publishing, published, partial, failed (blocked, failed, missed or uncertain: needs a person), deleted (every published destination was removed from its network since) or cancelled. Filter by status, accountId, source and a from/to window on the planned or actual publish time. Each destination carries its scheduled time, permalink and latest metrics. Use publishing.posts.get for the copy and media of one post.
| Field | Value |
|---|---|
| Kind | Read |
| HTTP method | POST /api/v1/publishing.posts.list |
| MCP tool | publishing_posts_list |
| Scope | publishing.read |
| Minimum role | Viewer |
| Human confirmation | No |
| Doors | Nödo, Voice, MCP, API, Chat, Screens, Apps |
| Status | Available |
| Widget | list |
Input
{
status?: "all" | "draft" | "in_review" | "approved_unscheduled" | "scheduled" | "published" | "failed" | "deleted" | "cancelled" // default "all" · A post matches every state one of its destinations is in; `published` includes partially published posts and excludes destinations deleted from their network, which match `deleted`.
accountId?: string // uuid
source?: "person" | "agent"
from?: string // date-time
to?: string // date-time
cursor?: string // ≤ 512 · Opaque cursor from a previous `nextCursor`.
limit?: integer // 1–50 · default 20
}Output
{
items: Array<{
id: string // uuid
title: string | null
status: "draft" | "in_review" | "approved_unscheduled" | "scheduled" | "publishing" | "published" | "partial" | "failed" | "deleted" | "cancelled"
channels: string[]
scheduledAt: string | null // Earliest planned or proposed time among the destinations.
publishedAt: string | null // Latest confirmed publication among the destinations.
source: "person" | "agent" // `agent` when an agent drafted it for a person.
destinations: Array<{
variantId: string // uuid
accountId: string // uuid · Connected account id, as `publishing.accounts.list` returns it.
network: string // Network key: instagram, facebook_pages, threads, linkedin, tiktok, youtube, x, pinterest, bluesky, google_business_profile, wordpress, mailerlite.
account: string | null
format: string
status: string // Raw destination status: draft, in_review, approved, scheduled, preparing, publishing, processing, published, manual_pending, manual_confirmed, missed, blocked, failed, uncertain or cancelled; `deleted` when it was published and later removed from the network (from Nömad or in the network itself).
scheduledAt: string | null
publishedAt: string | null
permalink: string | null // Public link on the network; null once the post was deleted there.
deletedAt?: string | null // When a published destination was found deleted from its network.
metrics: {
observedAt: string
values: Record<string, number> // Latest counters the network reports (likes, comments, views…); keys vary per network.
}
| null
}>
reviewUrl: string // uri · Where a person reviews, approves or edits the post in Nömad. Show it to them.
updatedAt: string
}>
nextCursor: string | null
}One post in full: base copy, each destination with its own copy, format, media (with an in-app preview link), schedule, review requests and decisions, delivery attempts, permalink and latest metrics. revision is what publishing.posts.draft needs as expectedRevision to edit it.
| Field | Value |
|---|---|
| Kind | Read |
| HTTP method | POST /api/v1/publishing.posts.get |
| MCP tool | publishing_posts_get |
| Scope | publishing.read |
| Minimum role | Viewer |
| Human confirmation | No |
| Doors | Nödo, Voice, MCP, API, Chat, Screens, Apps |
| Status | Available |
| Widget | record |
Input
{
postId: string // uuid
}Output
{
id: string // uuid
title: string | null
status: "draft" | "in_review" | "approved_unscheduled" | "scheduled" | "publishing" | "published" | "partial" | "failed" | "deleted" | "cancelled"
channels: string[]
scheduledAt: string | null // Earliest planned or proposed time among the destinations.
publishedAt: string | null // Latest confirmed publication among the destinations.
source: "person" | "agent" // `agent` when an agent drafted it for a person.
destinations: Array<{
variantId: string // uuid
accountId: string // uuid · Connected account id, as `publishing.accounts.list` returns it.
network: string // Network key: instagram, facebook_pages, threads, linkedin, tiktok, youtube, x, pinterest, bluesky, google_business_profile, wordpress, mailerlite.
account: string | null
format: string
status: string // Raw destination status: draft, in_review, approved, scheduled, preparing, publishing, processing, published, manual_pending, manual_confirmed, missed, blocked, failed, uncertain or cancelled; `deleted` when it was published and later removed from the network (from Nömad or in the network itself).
scheduledAt: string | null
publishedAt: string | null
permalink: string | null // Public link on the network; null once the post was deleted there.
deletedAt?: string | null // When a published destination was found deleted from its network.
metrics: {
observedAt: string
values: Record<string, number> // Latest counters the network reports (likes, comments, views…); keys vary per network.
}
| null
title: string
text: string
inheritsText: boolean
deliveryMethod: "direct" | "manual"
options: Record<string, unknown>
media: Array<{
fileId: string // uuid
name: string | null
mimeType: string | null
role: string
altText: string | null
previewUrl: string | null // In-app thumbnail; opens for people signed in to the brand.
}>
schedule: {
kind: string
at: string | null
timezone: string
status: string
}
| null
delivery: {
jobStatus: string | null
attempts: integer // -9007199254740991–9007199254740991
lastError: string | null
updatedAt: string | null
}
}>
reviewUrl: string // uri · Where a person reviews, approves or edits the post in Nömad. Show it to them.
updatedAt: string
baseText: string
tags: string[]
revision: integer // -9007199254740991–9007199254740991
createdAt: string
createdBy: {
id: string // uuid
name: string | null
}
| null
via: {
door: string
clientId: string | null
model: string | null
}
| null // Which agent drafted it, when one did.
review: {
policy: "none" | "internal" | "internal_client"
agentPolicy: "never" | "review"
requests: Array<{
id: string // uuid
accountId: string | null
stage: "internal" | "client"
status: string
createdAt: string
decisions: Array<{
decision: string
comment: string | null
decidedAt: string | null
}>
}>
}
}Creates, in one call, a draft post for several connected accounts: base copy, optional per-destination copy, format and options, and approved Library files (ids from library.search). It always stays a draft, marked as written by an agent for the person you act for, and returns reviewUrl for them plus, per destination, the format issues (issues) and the brand voice check of its copy (brandCheck). It never publishes or schedules; then call publishing.posts.request_review or publishing.posts.schedule so a person decides. Read the limits first with publishing.formats.describe. Pass postId + expectedRevision to rewrite a draft nobody has been asked to review yet.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/publishing.posts.draft |
| MCP tool | publishing_posts_draft |
| Scope | publishing.write |
| Minimum role | Member |
| Human confirmation | No |
| Doors | Nödo, MCP, API, Chat, Screens, Apps |
| Status | Available |
| Widget | record |
Input
{
postId?: string // uuid
expectedRevision?: integer // –9007199254740991 · Required with `postId`: the `revision` from `publishing.posts.get`.
title: string // ≤ 200 · Name of the post in Nömad. Networks with a title field (YouTube, Pinterest, articles) use it unless a destination sets its own `title`.
text: string // ≤ 10000 · Base copy every destination uses unless it sets its own `text`.
tags?: string[] // ≤ 20 items · default []
fileIds?: string[] // ≤ 10 items · default [] · Approved Library files attached to every destination that does not list its own.
destinations: Array<{
accountId: string // uuid
format: "text" | "image" | "carousel" | "video" | "reel" | "story" | "document" | "poll" | "article" | "event" | "offer" | "campaign"
text?: string // ≤ 10000 · Copy for this network only; omit to use the base copy.
title?: string // ≤ 500 · Title where the network has one (YouTube, Pinterest, articles).
fileIds?: string[] // ≤ 20 items
options?: Record<string, string | number | boolean> // Network options from `publishing.formats.describe`, e.g. { "privacyStatus": "unlisted" }.
}> // ≤ 12 items
idempotencyKey?: string // ≤ 120 · Any unique string you choose per intended draft. Retrying with the same key returns the first result instead of a duplicate.
agentModel?: string // ≤ 80 · Your model name (e.g. claude-opus-4). Recorded as self-reported next to the post, so reviewers know what wrote it.
}Output
{
ok: true
id: string // uuid
title: string
revision: integer // -9007199254740991–9007199254740991
status: "draft"
replayed: boolean // True when `idempotencyKey` matched an earlier call and nothing new was created.
readyForReview: boolean // No blocking issue: it can be sent to review.
issues: Array<{
accountId: string | null
field: string
code: string // Stable key, e.g. text_too_long, media_count_invalid, media_aspect_invalid, format_unavailable, media_check_pending, direct_publishing_unavailable. Account warnings: connection_broken, missing_permissions, connection_paused and expires_before_post (params `cause`, `renewBy`): the account needs reconnecting before the post goes out; tell the person which one and why.
severity: "error" | "warning" // Errors must be fixed before review; warnings only inform.
params?: Record<string, string | number>
}>
brandCheck: Array<{
accountId: string // uuid
ok: boolean // False when the text breaks a blocking brand or sector rule.
findings: Array<{
ruleId: string
source: string // `brand` for the Genome rules, otherwise the sector pack.
severity: "block" | "warn"
match: string
message: {
es: string
en: string
}
suggestion?: string
}>
}>
mentionSuggestions?: Array<{
network: string // Network key the handle belongs to (instagram, threads, x, tiktok, bluesky, facebook_pages, linkedin, youtube).
handle: string // Username without the @.
name: string | null // Public display name, when a lookup found the account.
verified: boolean // An official lookup found this public account recently.
uses: integer // -9007199254740991–9007199254740991 · Times the brand used it in posts, collaborators, tags or its imported history.
lastUsedAt: string | null
reason: "named_in_text" | "handle_in_text" | "used_before" // `named_in_text`: the account's name appears in the text without its @handle; `handle_in_text`: its handle appears without the @; `used_before`: the brand mentioned, tagged or collaborated with it before.
accountId: string // uuid
}> // Accounts the brand used before that the copy names without their @handle, per destination. Only proposals: tell the person and let them add the mention; never rewrite the draft on your own.
reviewUrl: string // uri · Where a person reviews, approves or edits the post in Nömad. Show it to them.
}Sends a draft to review so a person decides: the brand's configured reviewers, or its owners and admins when none are set. It never approves or publishes, even when you act for an owner, and agents can never skip review. If a destination still has blocking issues nothing changes and state is needs_changes. Returns reviewUrl: share it so they approve there. To also propose a time, use publishing.posts.schedule instead.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/publishing.posts.request_review |
| MCP tool | publishing_posts_request_review |
| Scope | publishing.write |
| Minimum role | Member |
| Human confirmation | No |
| Doors | Nödo, MCP, API, Chat, Screens, Apps |
| Status | Available |
| Widget | record |
Input
{
postId: string // uuid
accountIds?: string[] // ≤ 12 items · Account ids of the destinations to send; omit for every destination still in draft.
}Output
{
ok: true
id: string // uuid
title: string
state: "in_review" | "needs_changes" // `needs_changes`: nothing changed; fix `issues` (errors) and ask again.
proposedAt: string | null // Time proposed to the reviewer; it only takes effect if a person approves.
timezone: string | null
reviewers: integer // -9007199254740991–9007199254740991 · People asked to decide.
agentPolicy: "never" | "review" // Brand rule for agent posts. `never` (default): a person always approves an agent post.
issues: Array<{
accountId: string | null
field: string
code: string // Stable key, e.g. text_too_long, media_count_invalid, media_aspect_invalid, format_unavailable, media_check_pending, direct_publishing_unavailable. Account warnings: connection_broken, missing_permissions, connection_paused and expires_before_post (params `cause`, `renewBy`): the account needs reconnecting before the post goes out; tell the person which one and why.
severity: "error" | "warning" // Errors must be fixed before review; warnings only inform.
params?: Record<string, string | number>
}>
reviewUrl: string // uri · Where a person reviews, approves or edits the post in Nömad. Show it to them.
}Proposes a publish time and sends the draft to review in one step. Nothing is scheduled until a person approves it in Nömad; the approval then schedules it at that time (if the time has passed by then, it is marked missed). Agents never schedule or publish directly. publishAt must be in the future; timezone (IANA) defaults to the brand's publishing timezone. Returns reviewUrl for the approver.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/publishing.posts.schedule |
| MCP tool | publishing_posts_schedule |
| Scope | publishing.write |
| Minimum role | Member |
| Human confirmation | No |
| Doors | Nödo, MCP, API, Chat, Screens, Apps |
| Status | Available |
| Widget | record |
Input
{
postId: string // uuid
publishAt: string // date-time
timezone?: string // ≤ 100
accountIds?: string[] // ≤ 12 items · Account ids of the destinations to send; omit for every destination still in draft.
}Output
{
ok: true
id: string // uuid
title: string
state: "in_review" | "needs_changes" // `needs_changes`: nothing changed; fix `issues` (errors) and ask again.
proposedAt: string | null // Time proposed to the reviewer; it only takes effect if a person approves.
timezone: string | null
reviewers: integer // -9007199254740991–9007199254740991 · People asked to decide.
agentPolicy: "never" | "review" // Brand rule for agent posts. `never` (default): a person always approves an agent post.
issues: Array<{
accountId: string | null
field: string
code: string // Stable key, e.g. text_too_long, media_count_invalid, media_aspect_invalid, format_unavailable, media_check_pending, direct_publishing_unavailable. Account warnings: connection_broken, missing_permissions, connection_paused and expires_before_post (params `cause`, `renewBy`): the account needs reconnecting before the post goes out; tell the person which one and why.
severity: "error" | "warning" // Errors must be fixed before review; warnings only inform.
params?: Record<string, string | number>
}>
reviewUrl: string // uri · Where a person reviews, approves or edits the post in Nömad. Show it to them.
}Connected accounts the brand can publish to, with their health: credentials ok, expiring (within 7 days) or expired, whether Nömad may publish to that network by API yet (directPublishing, with reasons when not), the formats each accepts, its timezone and whether it is paused. Also the brand's review rule (approvalPolicy) and agent rule (agentPolicy). Use it to get the accountIds for a draft.
| Field | Value |
|---|---|
| Kind | Read |
| HTTP method | POST /api/v1/publishing.accounts.list |
| MCP tool | publishing_accounts_list |
| Scope | publishing.read |
| Minimum role | Viewer |
| Human confirmation | No |
| Doors | Nödo, Voice, MCP, API, Chat, Screens, Apps |
| Status | Available |
| Widget | list |
Input
{}Output
{
publishingActive: boolean // False while Nömad's publisher is switched off: drafts and reviews still work.
paused: boolean
approvalPolicy: "none" | "internal" | "internal_client"
agentPolicy: "never" | "review"
items: Array<{
accountId: string // uuid
network: string
name: string
handle: string | null
status: "connected" | "needs_attention"
credentials: "ok" | "expiring" | "expired" | "error"
credentialsExpireAt: string | null
gateActive: boolean // Nömad has certified API publishing for this network.
directPublishing: boolean
reasons: string[]
formats: string[]
timezone: string | null
paused: boolean
}>
}What each format accepts on each connected account, from Nömad's catalogue of network rules: text limit and its unit (characters, UTF-16 units, bytes, graphemes…), number and type of files, size, aspect ratio, duration, options, and whether it can be published by API. Read it before drafting so the copy fits; omit both filters for every account.
| Field | Value |
|---|---|
| Kind | Read |
| HTTP method | POST /api/v1/publishing.formats.describe |
| MCP tool | publishing_formats_describe |
| Scope | publishing.read |
| Minimum role | Viewer |
| Human confirmation | No |
| Doors | Nödo, Voice, MCP, API, Chat, Screens, Apps |
| Status | Available |
Input
{
accountId?: string // uuid
format?: "text" | "image" | "carousel" | "video" | "reel" | "story" | "document" | "poll" | "article" | "event" | "offer" | "campaign"
}Output
{
items: Array<{
accountId: string // uuid
network: string
account: string | null
format: "text" | "image" | "carousel" | "video" | "reel" | "story" | "document" | "poll" | "article" | "event" | "offer" | "campaign"
status: "verified" | "candidate" | "unsupported" | "retired" // `verified`: checked against the network documentation; `candidate`: not certified yet.
directPublishing: boolean // True when Nömad can publish this format on this account by API once approved; otherwise a person publishes it by hand.
text: {
required: boolean
max: integer | null
unit: "characters" | "code_points" | "utf16_units" | "utf8_bytes" | "graphemes" | "threads_weighted" | "x_weighted" | null
}
media: {
min: integer // -9007199254740991–9007199254740991
max: integer | null
mimeTypes: string[]
maxBytes: number | null
rules: Record<string, unknown> // Every other media rule as the catalogue states it: aspect [min,max] (width/height), duration [min,max] seconds, width, fps, codecs, cover.
}
options: string[] // Network options a destination may set in `options`.
notes: string[] // Known limits not checked automatically.
}>
}Latest metrics of what was published, per post and summed per account: posts published through Nömad and, where imported, the account's earlier history. Filter by postId, accountId and a from/to publish date. Values are the counters each network reports (keys vary); availability says why one is missing. Use it to say what worked before proposing new posts.
| Field | Value |
|---|---|
| Kind | Read |
| HTTP method | POST /api/v1/publishing.metrics.get |
| MCP tool | publishing_metrics_get |
| Scope | publishing.read |
| Minimum role | Viewer |
| Human confirmation | No |
| Doors | Nödo, Voice, MCP, API, Chat, Screens, Apps |
| Status | Available |
Input
{
postId?: string // uuid
accountId?: string // uuid
from?: string
to?: string
limit?: integer // 1–50 · default 20
}Output
{
items: Array<{
origin: "nomad" | "history" // `history`: published outside Nömad and imported.
postId: string | null
variantId: string | null
accountId: string | null
network: string | null
permalink: string | null
publishedAt: string | null
observedAt: string | null
values: Record<string, number>
availability: Record<string, string>
}>
totals: Array<{
accountId: string | null
network: string | null
posts: integer // -9007199254740991–9007199254740991
values: Record<string, number>
}>
}Saves a post idea (title, text, tags, approved Library files) in the brand's Ideas, marked as proposed by an agent. An idea is a post in its first stage: no networks, no date, no work. A person decides whether to turn it into a post, which keeps the same id. Retrying with the same idempotencyKey returns the first idea.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/publishing.ideas.create |
| MCP tool | publishing_ideas_create |
| Scope | publishing.write |
| Minimum role | Member |
| Human confirmation | No |
| Doors | Nödo, MCP, API, Chat, Screens, Apps |
| Status | Available |
Input
{
title: string // ≤ 200
text?: string // ≤ 10000 · default ""
tags?: string[] // ≤ 20 items · default []
fileIds?: string[] // ≤ 10 items · default []
idempotencyKey?: string // ≤ 120 · Any unique string you choose per intended draft. Retrying with the same key returns the first result instead of a duplicate.
agentModel?: string // ≤ 80 · Your model name (e.g. claude-opus-4). Recorded as self-reported next to the post, so reviewers know what wrote it.
}Output
{
ok: true
id: string // uuid
revision: integer // -9007199254740991–9007199254740991
replayed: boolean
url: string // uri
}Saved post ideas of the brand, newest first, with who proposed them (person or agent). An idea turned into a post leaves this list and keeps its id among the posts (publishing.posts.list). Check both before proposing the same idea twice.
| Field | Value |
|---|---|
| Kind | Read |
| HTTP method | POST /api/v1/publishing.ideas.list |
| MCP tool | publishing_ideas_list |
| Scope | publishing.read |
| Minimum role | Viewer |
| Human confirmation | No |
| Doors | Nödo, Voice, MCP, API, Chat, Screens, Apps |
| Status | Available |
| Widget | list |
Input
{
cursor?: string // ≤ 512 · Opaque cursor from a previous `nextCursor`.
limit?: integer // 1–50 · default 20
}Output
{
items: Array<{
id: string // uuid
title: string
text: string
tags: string[]
source: "person" | "agent"
convertedPostId: string | null // Always null: an idea is a post in status `idea`, and turning it into a post keeps its id (it then appears in `publishing.posts.list`). Kept for compatibility.
createdAt: string
}>
nextCursor: string | null
}Accounts the brand already mentioned, tagged or invited as collaborators (from its Nömad posts and imported history), per network, ranked by how often and how recently. Pass text to get only the accounts that text names without their @handle (e.g. «with Café Vómer» → @cafevomer); pass query to filter by handle or name. No network lets apps list followers, so this is the brand's own directory, not a search of the network. Only suggest: tell the person which mentions fit and let them add them; never insert them into a draft yourself.
| Field | Value |
|---|---|
| Kind | Read |
| HTTP method | POST /api/v1/publishing.mentions.suggest |
| MCP tool | publishing_mentions_suggest |
| Scope | publishing.read |
| Minimum role | Viewer |
| Human confirmation | No |
| Doors | Nödo, Voice, MCP, API, Chat, Screens, Apps |
| Status | Available |
Input
{
network?: string // Only this network (instagram, threads, x, tiktok, bluesky, facebook_pages, linkedin, youtube).
text?: string // ≤ 10000 · Copy to check for names written without their @handle.
query?: string // ≤ 120 · Filter by the start of a handle or name.
limit?: integer // 1–20 · default 8
}Output
{
items: Array<{
network: string // Network key the handle belongs to (instagram, threads, x, tiktok, bluesky, facebook_pages, linkedin, youtube).
handle: string // Username without the @.
name: string | null // Public display name, when a lookup found the account.
verified: boolean // An official lookup found this public account recently.
uses: integer // -9007199254740991–9007199254740991 · Times the brand used it in posts, collaborators, tags or its imported history.
lastUsedAt: string | null
reason: "named_in_text" | "handle_in_text" | "used_before" // `named_in_text`: the account's name appears in the text without its @handle; `handle_in_text`: its handle appears without the @; `used_before`: the brand mentioned, tagged or collaborated with it before.
}>
}What each network Nömad publishes to is, connected or not: its formats, how many files of which kind each takes, the recommended aspect ratio, the text limit and the unit it is counted in (characters, X-weighted, UTF-16 units, bytes, graphemes), and its hashtag policy. For a connected account's certified rules use publishing.formats.describe.
| Field | Value |
|---|---|
| Kind | Read |
| HTTP method | POST /api/v1/publishing.networks.list |
| MCP tool | publishing_networks_list |
| Scope | publishing.read |
| Minimum role | Viewer |
| Human confirmation | No |
| Doors | Nödo, Voice, MCP, API, Chat, Screens, Apps |
| Status | Available |
Input
{
network?: string // Only this network (instagram, facebook_pages, threads, linkedin, x, tiktok, youtube, pinterest, bluesky, google_business_profile…).
}Output
{
items: Array<{
network: string
label: string
family: "social" | "video" | "local" | "web" | "email"
clickableLinks: boolean
mentions: boolean
hashtags: {
max: integer // -9007199254740991–9007199254740991
placement: "end" | "inline" | "none"
}
formats: Array<{
format: "text" | "image" | "carousel" | "video" | "reel" | "story" | "document" | "poll" | "article" | "event" | "offer" | "campaign"
media: {
kind: "none" | "image" | "video" | "image_or_video" | "mixed" | "document"
min: integer // -9007199254740991–9007199254740991
max: integer // -9007199254740991–9007199254740991
maxSeconds?: number
}
recommendedAspect: string // Width:height to create files at, e.g. 4:5 or 9:16.
aspectRange: array // ≤ 2 items · Width / height the network accepts or crops to.
textLimit: integer | null
textUnit: "characters" | "x_weighted" | "threads_weighted" | "utf16" | "utf8_bytes" | "graphemes"
textRequired: boolean
titleLimit: integer | null
}>
}>
}Creates or saves a post draft with its destinations (send id and expectedVersion to edit). Returns the post, the checks scheduling will apply as guidance, and unscheduled when the edit sent scheduled destinations back to draft. Never schedules or publishes.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/publishing.posts.save |
| MCP tool | publishing_posts_save |
| Scope | publishing.write |
| Minimum role | Member |
| Human confirmation | No |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
draft: Record<string, unknown> // The composer draft (`PublishingPostDraft`): id and expectedVersion to edit, title, baseText, tags, media and one variant per destination.
}Output
{
post: {
id: string // uuid
version: integer // -9007199254740991–9007199254740991
variants: Array<{
id: string // uuid
status: string
}>
}
issues: Array<{
code: string
field: string
severity: "error" | "warning"
}>
unscheduled: boolean
}Schedules destinations of a post at a fixed time, in the account queue or right now (mode). Idempotent: the same request again changes nothing. With blocking issues it schedules nothing and answers scheduled: false with them. Publishing reaches the public, so agents never run it.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/publishing.variants.schedule |
| MCP tool | publishing_variants_schedule |
| Scope | publishing.write |
| Minimum role | Member |
| Human confirmation | Yes |
| Doors | API, Screens, Apps |
| Status | Available |
Input
{
schedule: Record<string, unknown> // `PublishingScheduleInput`: postId, variantIds, expectedVersion, mode (fixed | queue | now), scheduledAt, timezone.
}Output
{
scheduled: boolean
post: {
id: string // uuid
version: integer // -9007199254740991–9007199254740991
variants: Array<{
id: string // uuid
status: string
}>
}
| null
issues: Array<{
code: string
field: string
severity: "error" | "warning"
}>
}Sends destinations of a post for approval under the brand policy and notifies the reviewers. With blocking issues it sends nothing and answers requested: false with them. Returns the post reviews.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/publishing.reviews.request |
| MCP tool | publishing_reviews_request |
| Scope | publishing.write |
| Minimum role | Member |
| Human confirmation | No |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
postId: string // uuid
variantIds: string[] // ≤ 50 items
}Output
{
requested: boolean
reviews: Record<string, unknown>[]
issues: Array<{
code: string
field: string
severity: "error" | "warning"
}>
}Approves or rejects one review request with a comment. Only the brand reviewers decide; Nömad staff never approve for the brand. An approval may release a «publish now».
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/publishing.reviews.decide |
| MCP tool | publishing_reviews_decide |
| Scope | publishing.write |
| Minimum role | Viewer |
| Human confirmation | Yes |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
requestId: string // uuid
decision: "approved" | "rejected"
comment: string // ≤ 10000
}Output
{
ok: true
}Adds a comment to a post thread (internal, client-facing or a Nömad-only note) and notifies who follows it. unreached lists mentioned colleagues who cannot open the brand. Composer cards (inserts) are built on the web only.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/publishing.comments.add |
| MCP tool | publishing_comments_add |
| Scope | publishing.write |
| Minimum role | Member |
| Human confirmation | No |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
postId: string // uuid
text: string // ≤ 10000
visibility: "internal" | "client" | "nomad"
mentions?: unknown[] // ≤ 50 items · default []
inserts?: unknown[] // ≤ 20 items · default []
}Output
{
comment: Record<string, unknown>
}Cancels or retries every destination of up to 100 posts. Each destination answers on its own; a refusal never undoes the others. A retry sends to the network again, so agents never run it.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/publishing.posts.mutate |
| MCP tool | publishing_posts_mutate |
| Scope | publishing.write |
| Minimum role | Member |
| Human confirmation | Yes |
| Doors | API, Screens, Apps |
| Status | Available |
Input
{
postIds: string[] // ≤ 100 items
action: "cancel" | "retry"
}Output
{
results: Array<{
variantId: string // uuid
ok: boolean
error?: string
}>
}Cancels, retries or marks as published by hand (manual_complete, with an https permalink) up to 100 destinations, each on its own. A retry sends to the network again, so agents never run it.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/publishing.variants.mutate |
| MCP tool | publishing_variants_mutate |
| Scope | publishing.write |
| Minimum role | Member |
| Human confirmation | Yes |
| Doors | API, Screens, Apps |
| Status | Available |
Input
{
variantIds: string[] // ≤ 100 items
action: "cancel" | "retry" | "manual_complete"
permalink?: string // ≤ 2048
}Output
{
results: Array<{
variantId: string // uuid
ok: boolean
error?: string
}>
}Records a destination published by hand (a manual one, or a handoff of a connected account), with its https permalink and the time it went out when known. The assignee, Nömad on the post or an owner/admin.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/publishing.variants.mark_published |
| MCP tool | publishing_variants_mark_published |
| Scope | publishing.write |
| Minimum role | Member |
| Human confirmation | No |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
postId: string // uuid
variantId: string // uuid
permalink?: string | null
publishedAt?: string | null
}Output
{
ok: true
}Removes up to 100 posts that never reached a network (drafts, ideas, scheduled, cancelled or failed destinations, nothing in flight). A post that went out once stays in the brand history, also after being taken down (publishing_post_published); take it off the network with publishing.remote.delete. One result per post; a refused one does not undo the others.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/publishing.posts.delete |
| MCP tool | publishing_posts_delete |
| Scope | publishing.write |
| Minimum role | Member |
| Human confirmation | Yes |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
postIds: string[] // ≤ 100 items
}Output
{
results: Array<{
postId: string // uuid
ok: boolean
error?: string
newPostId?: string // uuid
}>
}Copies up to 50 posts into new drafts (content, destinations and media; never dates, reviews or delivery), titled «… (copy)» in the caller language. One result per post with the new id.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/publishing.posts.duplicate |
| MCP tool | publishing_posts_duplicate |
| Scope | publishing.write |
| Minimum role | Member |
| Human confirmation | No |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
postIds: string[] // ≤ 50 items
}Output
{
results: Array<{
postId: string // uuid
ok: boolean
error?: string
newPostId?: string // uuid
}>
}Starts a post: adds the free «Post» work with its publication task and answers with the relative path to open (carrying the calendar date). With ideaPostId the idea becomes that post. Brands outside posts as works get the direct composer path.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/publishing.posts.start_work |
| MCP tool | publishing_posts_start_work |
| Scope | publishing.write |
| Minimum role | Member |
| Human confirmation | No |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
at?: string // date-time
tz?: string // ≤ 64
ideaPostId?: string // uuid
attemptId?: string // uuid
}Output
{
href: string
}Binds every planned destination of a network in the brand to one of its connected accounts («¿Con qué cuenta publicamos?»). Idempotent. Returns how many were bound, scheduled, past their date or skipped.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/publishing.planned.bind |
| MCP tool | publishing_planned_bind |
| Scope | publishing.write |
| Minimum role | Member |
| Human confirmation | No |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
network: string // ≤ 60
connectionId: string // uuid
}Output
{
bound: integer // -9007199254740991–9007199254740991
scheduled: integer // -9007199254740991–9007199254740991
datePassed: integer // -9007199254740991–9007199254740991
skipped: integer // -9007199254740991–9007199254740991
}Deletes one published destination from its network, then records it. Owners and admins delete any destination, members the posts they own. Only a confirmed deletion (or one the network no longer has) is written. Never an agent's to do.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/publishing.remote.delete |
| MCP tool | publishing_remote_delete |
| Scope | publishing.write |
| Minimum role | Member |
| Human confirmation | Yes |
| Doors | API, Screens, Apps |
| Status | Available |
Input
{
variantId: string // uuid
}Output
{
variantId: string // uuid
postId: string // uuid
outcome: "deleted" | "already_gone" | "already_deleted"
deletedAt: string | null
}Reads attached Library files the way delivery will (type, size, dimensions, duration) and, unless start is false, starts the automatic check for files that have none. Returns each file with its state: ready, running or failed with the reason.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/publishing.media.prepare |
| MCP tool | publishing_media_prepare |
| Scope | publishing.write |
| Minimum role | Viewer |
| Human confirmation | No |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
fileIds: string[] // ≤ 50 items
start?: boolean
retry?: boolean
}Output
{
media: Record<string, unknown>[]
}Starts the check of one file or, with a recipe (width, height, fit, format, fps, maxBytes), a derived version for a network. The original is never overwritten. Returns the queued job.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/publishing.media.start_job |
| MCP tool | publishing_media_start_job |
| Scope | publishing.write |
| Minimum role | Member |
| Human confirmation | No |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
fileId: string // uuid
recipe?: {
width?: integer // -9007199254740991–9007199254740991
height?: integer // -9007199254740991–9007199254740991
fit?: "cover" | "contain"
format?: "jpeg" | "mp4"
fps?: integer // -9007199254740991–9007199254740991
maxBytes?: integer // -9007199254740991–9007199254740991
}
}Output
{
job: Record<string, unknown>
}Accepts the derived version of a file (made by publishing.media.start_job) for publishing. Acceptance is explicit and brand-scoped; the original stays as it was. Returns the file as a post uses it.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/publishing.media.accept_derivative |
| MCP tool | publishing_media_accept_derivative |
| Scope | publishing.write |
| Minimum role | Member |
| Human confirmation | No |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
derivativeId: string // uuid
}Output
{
media: Record<string, unknown>
}Saves the brand approval policy (none, internal, internal and client), its rules, reviewers and time zone, fenced by version. Owners and admins only. Returns the policy as stored.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/publishing.settings.configure_policy |
| MCP tool | publishing_settings_configure_policy |
| Scope | publishing.write |
| Minimum role | Member |
| Human confirmation | Yes |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
policy: Record<string, unknown> // `PublishingPolicy` as settings › Publicación edits it.
}Output
{
policy: Record<string, unknown>
}Saves the weekly slots, time zone and ambiguous-time rule of one connected account queue, fenced by version. Owners and admins only. Returns the queue as stored.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/publishing.settings.configure_queue |
| MCP tool | publishing_settings_configure_queue |
| Scope | publishing.write |
| Minimum role | Member |
| Human confirmation | No |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
queue: Record<string, unknown> // `PublishingQueue`: connectionId, timezone, slots, version, ambiguousTime.
}Output
{
queue: Record<string, unknown>
}Pauses or resumes publishing for the whole brand or, with connectionId, one account. While paused nothing goes out. Owners and admins only. Returns the policy and queues as they stand.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/publishing.settings.pause |
| MCP tool | publishing_settings_pause |
| Scope | publishing.write |
| Minimum role | Member |
| Human confirmation | Yes |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
paused: boolean
connectionId?: string // uuid
}Output
{
policy: Record<string, unknown>
queues: Record<string, unknown>[]
}Applies a new order to one account queue exactly as previewed (expectedPlan); if anything moved since, nothing changes and it fails with publishing_conflict. Owners and admins only.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/publishing.queue.reorder |
| MCP tool | publishing_queue_reorder |
| Scope | publishing.write |
| Minimum role | Member |
| Human confirmation | No |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
connectionId: string // uuid
variantIds: string[] // ≤ 100 items
expectedPlan: Record<string, unknown>[] // ≤ 100 items
}Output
{
ok: true
}Issues one review link, valid seven days, for each configured client email that has not decided yet on a review request (policy internal and client). Only the token hash is stored. Owners and admins only.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/publishing.external_reviews.issue_links |
| MCP tool | publishing_external_reviews_issue_links |
| Scope | publishing.write |
| Minimum role | Member |
| Human confirmation | No |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
requestId: string // uuid
}Output
{
links: Array<{
email: string
url: string
expiresAt: string
}>
}Revokes the review link of one client email on a review request; the link stops working at once. Owners and admins only.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/publishing.external_reviews.revoke_link |
| MCP tool | publishing_external_reviews_revoke_link |
| Scope | publishing.write |
| Minimum role | Member |
| Human confirmation | Yes |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
requestId: string // uuid
email: string // email · ≤ 254
}Output
{
ok: true
}Re-imports the last 90 days of posts of an Instagram, Facebook Page or Threads connection. Answers whether the run was queued or is already running.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/publishing.history.rerun_import |
| MCP tool | publishing_history_rerun_import |
| Scope | publishing.write |
| Minimum role | Admin |
| Human confirmation | No |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
connectionId: string // uuid
}Output
{
status: "queued" | "running"
runId: string | null
}Moves destinations of one post to a new instant, exactly as the composer would. When the move costs an approval under a review policy, it asks for it again. Returns each destination status and whether approval was requested.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/publishing.calendar.reschedule |
| MCP tool | publishing_calendar_reschedule |
| Scope | publishing.write |
| Minimum role | Member |
| Human confirmation | No |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
variantIds: string[] // ≤ 50 items
scheduledAt: string // date-time
timeZone: string // ≤ 100
}Output
{
variants: Array<{
variantId: string // uuid
status: string
scheduledAt: string | null
}>
reviewRequested: boolean
needsApproval: boolean
}Exchanges the times of two scheduled destinations (the Instagram grid drag). If one move fails, the ones already made are undone. Returns each destination status and whether approval was requested.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/publishing.calendar.swap |
| MCP tool | publishing_calendar_swap |
| Scope | publishing.write |
| Minimum role | Member |
| Human confirmation | No |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
firstVariantId: string // uuid
secondVariantId: string // uuid
}Output
{
variants: Array<{
variantId: string // uuid
status: string
scheduledAt: string | null
}>
reviewRequested: boolean
needsApproval: boolean
}Creates a new draft with the same copy, media and destinations (accounts still connected) and no schedule, under the given title. Returns the new post id.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/publishing.calendar.duplicate |
| MCP tool | publishing_calendar_duplicate |
| Scope | publishing.write |
| Minimum role | Member |
| Human confirmation | No |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
postId: string // uuid
title: string // ≤ 500
}Output
{
postId: string // uuid
}The brand approves every destination Nömad prepared on a post, with an optional note; they go out at their time. Only whoever decides Nömad posts for the brand. Returns the post approval state.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/publishing.agency.accept |
| MCP tool | publishing_agency_accept |
| Scope | publishing.write |
| Minimum role | Viewer |
| Human confirmation | Yes |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
postId: string // uuid
note?: string // ≤ 10000 · default ""
}Output
{
state: string
}The brand saves its own edit of a post Nömad prepared and approves it in one step (recorded as approved with changes); schedule can move its time. Returns the approval state.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/publishing.agency.accept_with_changes |
| MCP tool | publishing_agency_accept_with_changes |
| Scope | publishing.write |
| Minimum role | Member |
| Human confirmation | Yes |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
draft: Record<string, unknown> // The composer draft (`PublishingPostDraft`): id and expectedVersion to edit, title, baseText, tags, media and one variant per destination.
schedule: {
mode: "fixed" | "now"
scheduledAt?: string // date-time
timezone: string // ≤ 100
}
| null
note?: string // ≤ 10000 · default ""
}Output
{
state: string
}The brand asks for changes on a post Nömad prepared, always saying what to change; Nömad's task opens again with it. Returns the approval state.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/publishing.agency.request_changes |
| MCP tool | publishing_agency_request_changes |
| Scope | publishing.write |
| Minimum role | Viewer |
| Human confirmation | No |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
postId: string // uuid
note: string // ≤ 10000
}Output
{
state: string
}Nömad records what the client said off the app (who, through which channel, when): approved, or rejected with what to change. Nömad staff on the post only. Returns the approval state.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/publishing.agency.record_approval |
| MCP tool | publishing_agency_record_approval |
| Scope | publishing.write |
| Minimum role | Member |
| Human confirmation | Yes |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
postId: string // uuid
decision: "approved" | "rejected"
channel: string // ≤ 40
approverName: string // ≤ 200
decidedAt: string // date-time
note: string // ≤ 10000
}Output
{
state: string
}Creates an approval link to one post for one contact. By email Nömad sends it; for WhatsApp or to copy, the caller shares the returned url. Nömad on the post or an owner/admin. Fails with publishing_email_not_sent when the email could not go.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/publishing.approval_links.create |
| MCP tool | publishing_approval_links_create |
| Scope | publishing.write |
| Minimum role | Member |
| Human confirmation | Yes |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
postId: string // uuid
channel: "email" | "whatsapp" | "link"
email?: string | null
label?: string | null
}Output
{
url: string
expiresAt: string
emailed: boolean
}Revokes one approval link of a post; it stops working at once. Nömad on the post or an owner/admin.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/publishing.approval_links.revoke |
| MCP tool | publishing_approval_links_revoke |
| Scope | publishing.write |
| Minimum role | Member |
| Human confirmation | Yes |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
postId: string // uuid
linkId: string // uuid
}Output
{
ok: true
}Nödo proposes topics for a post from the editorial plan, briefing and what was published. Saves nothing. Spends brand AI budget; when it is off or spent, the ideas come without Nödo angles and usage says why.
Consumes the brand AI budget.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/publishing.writer.topics |
| MCP tool | publishing_writer_topics |
| Scope | publishing.write |
| Minimum role | Member |
| Human confirmation | No |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
taskId?: string | null
postId?: string | null
exclude?: string[] // ≤ 30 items
like?: {
title: string // ≤ 200
angle: string | null
source: string // ≤ 40
pillar: string | null
date: string | null
}
| null
}Output
{
result: Record<string, unknown>
}Nödo writes a draft for the chosen networks in the brand voice, from a prompt, topic, template, link, briefing or text. Saves nothing: the person picks and saves. Spends brand AI budget.
Consumes the brand AI budget.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/publishing.writer.compose |
| MCP tool | publishing_writer_compose |
| Scope | publishing.write |
| Minimum role | Member |
| Human confirmation | No |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
taskId?: string | null
postId?: string | null
prompt: string // ≤ 2000
topic?: {
title: string // ≤ 200
angle: string | null
source: string // ≤ 40
pillar: string | null
date: string | null
}
| null
template?: string | null
goal?: string | null
length: string // ≤ 20
cta: boolean
source?: {
kind: "link"
url: string // ≤ 2048
}
| {
kind: "briefing"
}
| {
kind: "text"
text: string // ≤ 20000
}
| null
without?: string[] // ≤ 20 items · default []
connectionId: string | null
targets: Array<{
connectionId: string // ≤ 80
format: string // ≤ 40
}> // ≤ 20 items
}Output
{
result: Record<string, unknown>
}Nödo rewrites a text (shorter, another tone, translated, with an instruction…) keeping the brand rules; kept lists rules the instruction asked to break. Saves nothing. Spends brand AI budget.
Consumes the brand AI budget.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/publishing.writer.refine |
| MCP tool | publishing_writer_refine |
| Scope | publishing.write |
| Minimum role | Member |
| Human confirmation | No |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
taskId?: string | null
postId?: string | null
text: string // ≤ 10000
action: string // ≤ 40
instruction?: string | null
language?: string | null
subject?: string | null
connectionId: string | null
targets: Array<{
connectionId: string // ≤ 80
format: string // ≤ 40
}> // ≤ 20 items
hashtags: string[] // ≤ 20 items
}Output
{
result: Record<string, unknown>
}Nödo adapts one text to each chosen network with its rules and the brand voice for that network. Saves nothing. Spends brand AI budget.
Consumes the brand AI budget.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/publishing.writer.adapt |
| MCP tool | publishing_writer_adapt |
| Scope | publishing.write |
| Minimum role | Member |
| Human confirmation | No |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
taskId?: string | null
postId?: string | null
text: string // ≤ 10000
targets: Array<{
connectionId: string // ≤ 80
format: string // ≤ 40
}> // ≤ 20 items
}Output
{
result: Record<string, unknown>
}After a save, records on the post that Nödo helped write it, with the sources and networks it used. Answers whether it was recorded.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/publishing.writer.mark_assisted |
| MCP tool | publishing_writer_mark_assisted |
| Scope | publishing.write |
| Minimum role | Member |
| Human confirmation | No |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
postId: string // uuid
sources: string[] // ≤ 20 items
networks: string[] // ≤ 20 items
}Output
{
recorded: boolean
}Saves the weekly rhythm (slots per network and pillar) of an editorial-plan task, fenced by expectedRevision. Returns the board.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/publishing.editorial_plan.save_rhythm |
| MCP tool | publishing_editorial_plan_save_rhythm |
| Scope | publishing.write |
| Minimum role | Member |
| Human confirmation | No |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
taskId: string // uuid
slots: unknown[] // ≤ 42 items
expectedRevision: integer // 0–9007199254740991
}Output
{
board: Record<string, unknown>
}Saves the month plan (ideas per date) of an editorial-plan task, fenced by expectedRevision. Returns the board.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/publishing.editorial_plan.save_month |
| MCP tool | publishing_editorial_plan_save_month |
| Scope | publishing.write |
| Minimum role | Member |
| Human confirmation | No |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
taskId: string // uuid
plan: unknown
expectedRevision: integer // 0–9007199254740991
}Output
{
board: Record<string, unknown>
}Nödo replaces a default first rhythm and default ideas of an editorial-plan task with ones written for the brand. Spends brand AI budget. Returns the board.
Consumes the brand AI budget.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/publishing.editorial_plan.improve |
| MCP tool | publishing_editorial_plan_improve |
| Scope | publishing.write |
| Minimum role | Member |
| Human confirmation | No |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
taskId: string // uuid
locale?: string // default "es"
}Output
{
board: Record<string, unknown>
}Nödo proposes a more balanced month plan, shown as a diff, with the sources it read. Nothing is saved until the brand accepts it. Spends brand AI budget.
Consumes the brand AI budget.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/publishing.editorial_plan.propose_rebalance |
| MCP tool | publishing_editorial_plan_propose_rebalance |
| Scope | publishing.write |
| Minimum role | Member |
| Human confirmation | No |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
taskId: string // uuid
locale?: string // default "es"
}Output
{
plan: Record<string, unknown>
sources: Record<string, unknown>[]
}Approves the rhythm or the month of an editorial-plan task from its board (or retries a failed step), fenced by expectedRevision. A brand person must be the work approver; Nömad staff approve on the brand behalf. Returns the board.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/publishing.editorial_plan.approve |
| MCP tool | publishing_editorial_plan_approve |
| Scope | publishing.write |
| Minimum role | Member |
| Human confirmation | Yes |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
taskId: string // uuid
part: "rhythm" | "month" | "retry"
expectedRevision: integer // 0–9007199254740991
}Output
{
board: Record<string, unknown>
}Edits one theme of the brand theme bank (theme, description, key message, hook, category, funnel stage, network, content type, production notes). Fields left out keep their value; the edit is added to the theme history.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/publishing.themes.update |
| MCP tool | publishing_themes_update |
| Scope | publishing.write |
| Minimum role | Member |
| Human confirmation | No |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
themeId: string // uuid
changes: {
theme?: string // ≤ 500
description?: string | null
key_message?: string | null
hook_idea?: string | null
category?: string // ≤ 80
target_stage?: string // ≤ 80
recommended_network?: string | null
content_type?: string | null
production_notes?: string | null
}
}Output
{
ok: true
changed: string[]
}Takes an active theme out of the brand theme bank for everyone. It is archived, so publishing.themes.restore puts it back.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/publishing.themes.delete |
| MCP tool | publishing_themes_delete |
| Scope | publishing.write |
| Minimum role | Admin |
| Human confirmation | Yes |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
themeId: string // uuid
}Output
{
ok: true
}Puts a deleted (archived) theme back in the brand theme bank. Fails with not_found when the theme is not deleted.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/publishing.themes.restore |
| MCP tool | publishing_themes_restore |
| Scope | publishing.write |
| Minimum role | Admin |
| Human confirmation | No |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
themeId: string // uuid
}Output
{
ok: true
}Adds a theme written by a person to the brand theme bank (category defaults to educational, stage to awareness). Returns the new theme row.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/publishing.themes.create |
| MCP tool | publishing_themes_create |
| Scope | publishing.write |
| Minimum role | Member |
| Human confirmation | No |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
theme: string // ≤ 500
description?: string // ≤ 4000
category?: string // ≤ 80
target_stage?: string // ≤ 80
key_message?: string // ≤ 2000
hook_idea?: string // ≤ 2000
}Output
{
theme: {
id: string // uuid
theme: string
}
}Starts an AI generation of the brand theme bank in the background: full rebuilds it, append adds about 60 themes keeping the current ones. Returns the generation id; the bank fills in when it finishes.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/publishing.themes.generate |
| MCP tool | publishing_themes_generate |
| Scope | publishing.write |
| Minimum role | Admin |
| Human confirmation | Yes |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
mode: "full" | "append"
}Output
{
generationId: string // uuid
}Marks the latest theme bank generation stuck in generating as failed, so a new one can start. Does nothing when none is running.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/publishing.themes.reset_status |
| MCP tool | publishing_themes_reset_status |
| Scope | publishing.write |
| Minimum role | Admin |
| Human confirmation | No |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{}Output
{
ok: true
reset: boolean
}What Nömad prepared and waits for the brand's approval, newest request first, leaving out posts whose work names another approver. total counts them all; savedTimeZone is the zone the person saved for the brand calendar, if any.
| Field | Value |
|---|---|
| Kind | Read |
| HTTP method | POST /api/v1/publishing.posts.awaiting |
| MCP tool | publishing_posts_awaiting |
| Scope | publishing.read |
| Minimum role | Viewer |
| Human confirmation | No |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
limit?: integer // 1–20 · default 4
}Output
{
items: Record<string, unknown>[]
total: integer // 0–9007199254740991
savedTimeZone: string | null
}The board of an editorial-plan task: rhythm, month, whether the reader can edit or approve it and who approves it. Whoever can edit sees the first rhythm or month as a preview (revision 0) until the first change stores it.
| Field | Value |
|---|---|
| Kind | Read |
| HTTP method | POST /api/v1/publishing.editorial_plan.board |
| MCP tool | publishing_editorial_plan_board |
| Scope | publishing.read |
| Minimum role | Viewer |
| Human confirmation | No |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
taskId: string // uuid
fallbackPillars?: string[] // ≤ 6 items · default []
preview?: boolean // Staff previewing the brand as a client: edit controls are left out.
}Output
{
board: {
canEdit: boolean
canApprove: boolean
approverNames: string[]
}
}Updated on January 1, 1970