@vruum/mcp 0.10.1 → 0.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +9 -2
  2. package/package.json +1 -1
  3. package/tools.json +3 -3
package/README.md CHANGED
@@ -135,12 +135,19 @@ Keep the same request UUID and all four inputs after an uncertain acknowledgemen
135
135
  The saved task owns model and delivery recovery; an unknown outcome does not
136
136
  authorize a replacement task or a second send.
137
137
 
138
- Use `manage_assistant action=follow_up` to continue completed private analysis.
138
+ Use `manage_assistant action=follow_up` to continue completed private analysis
139
+ or eligible private email work. A pending unapproved email revision retires the
140
+ old draft atomically. Completed email work prepares a new email, not a mail-thread
141
+ reply. The server retains the original contact and mailbox; a new draft requires
142
+ its own approval. Approved or uncertain delivery keeps its original recovery.
139
143
  Retain the same request UUID, parent Action, displayed revision, and message on
140
144
  an uncertain retry. The server creates one new Action and reconstructs permitted
141
145
  history with fresh company context. Read earlier turns through the
142
146
  `previous_action_id` returned by `fetch assistant_task`; each turn retains its
143
- own complete content references. Opening or reading work never starts analysis.
147
+ own complete content references. Earlier email drafts keep their canonical review
148
+ links. The server retains permitted draft/research context and reconstructs it
149
+ when one model request cannot hold it. Exact current delivery state stays outside
150
+ working notes. Opening or reading work never starts a model purchase.
144
151
 
145
152
  ## Configuration
146
153
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vruum/mcp",
3
- "version": "0.10.1",
3
+ "version": "0.12.1",
4
4
  "mcpName": "ai.vruum/mcp",
5
5
  "description": "Stdio MCP bridge for the hosted Vruum revenue-platform MCP (https://api.vruum.ai/mcp): serves the advertised tool surface for credential-free introspection and proxies tool calls with your Vruum token.",
6
6
  "license": "MIT",
package/tools.json CHANGED
@@ -1140,7 +1140,7 @@
1140
1140
  "readOnlyHint": false,
1141
1141
  "title": "Manage Saved Assistant Tasks"
1142
1142
  },
1143
- "description": "Manage Assistant\n\nStart private saved analysis or one reviewed email, or cancel saved work.\n\n- start_analysis: no id; payload={request_id (UUID), message}. Call only\n after the user requests this analysis. This call explicitly grants\n revenue_operator.read_analysis.v1: paid model work using permitted reads,\n without tool writes or external effects. Keep the exact request UUID and\n message on retries; the saved task journal owns request identity and\n recovery. An uncertain response never authorizes a replacement task.\n- start_email: no id; payload={request_id (UUID), message, person_id,\n sender_config_id}. Call only after the user requests this email work.\n Authorizes paid model work, permitted reads and one retained email draft\n for that workspace contact and an eligible mailbox available to the\n current actor. It does not approve or send. Read the returned email_scope\n and linked review_message_id. Review the exact sender, recipient, subject\n and complete body through the existing message review before approving.\n Keep the same request UUID, message, person and mailbox on retries;\n uncertain acknowledgement never authorizes another task or delivery.\n- cancel: id=the saved task's Action UUID; payload={expected_version} from\n a fresh fetch assistant_task. The original current actor or a current\n workspace owner/operator may cancel future continuation. An in-flight\n effect or provider call may still finish; cancellation cannot recall it.\n- follow_up: id=the completed analysis Action UUID; payload={request_id,\n expected_version, message}. Only its current original actor/login can\n continue the latest turn. This explicitly buys a new analysis Action in\n the same private conversation. Keep the exact UUID, parent, revision and\n message on retries, including an uncertain acknowledgement. The server\n reconstructs permitted history and refreshes company context. Historical\n answers are not current facts or approval for effects.\n\nAll actions are scalar: no id arrays or items. No caller history, imported\nthread, email-scope edits, additional effects, actor transfer, or worker\ncontrol fields are accepted. This facade adds no authority to its target.\nThe task is private to its original current actor and login; owner/operator\noversight gives metadata and cancellation, never another member's content.\nExisting retained email work uses its exact linked review without changing\nthe sender. Inspect fetch assistant_task, page fetch assistant_task_events,\nand retrieve full content with fetch assistant_task_content. Read prior\nturns by following previous_action_id from fetch assistant_task until it is\nnull, then order by conversation_turn. Follow next_action_id to newer work. There is no steering of active work or shared conversation. Public chat\ncompletion limits remain active.\n\n### Responses:\n\n**200**: Successful Response (Success Response)\nContent-Type: application/json\n\nPrivate saved email support (assistant_saved_email):\nProduct support is not permission. Inspect current work and follow the domain's authorization and review rules.\n- start_email: manage_assistant action=start_email. An explicit request grants paid model work, permitted reads and one retained draft for the exact workspace person and eligible mailbox. Keep the request UUID and all inputs unchanged on retry. Inspect email_scope and review_message_id on the saved task. The exact sender, recipient, subject and body require separate human review before sending. Approval does not prove delivery.\n- scope_edit: unsupported. No sender, recipient, or task-instruction changes after start. Cancel unused work and create a new request for changed scope.\n- delivery_replacement: unsupported. An unknown model or delivery outcome never authorizes replacement work. The original saved task owns recovery and receipt checks.\n\nPrivate saved conversations support (assistant_saved_conversations):\nProduct support is not permission. Inspect current work and follow the domain's authorization and review rules.\n- follow_up: manage_assistant action=follow_up. Continue the latest completed private analysis at its current revision. Exact request UUID retries return the same child Action. Use existing fetch assistant_task links to read every permitted prior turn. Historical answers are not current facts or execution authority.\n- active_steering: unsupported. A running task cannot accept new messages. Complete it before following up.\n- sharing: unsupported. No shared participants, source publication, or actor transfer.\n\nSaved assistant tasks support (assistant_saved_tasks):\nProduct support is not permission. Inspect current work and follow the domain's authorization and review rules.\n- start: manage_assistant action=start_analysis. An explicit request grants paid read_analysis.v1 only. Exact request UUID retries recover the same task.\n- list: fetch type=assistant_tasks. Page own work or authorized workspace metadata with next_cursor.\n- inspect: fetch type=assistant_task. Read current state, Action version and exact review links.\n- events: fetch type=assistant_task_events. Page journal events by sequence; no raw worker state or hidden reasoning.\n- content: fetch type=assistant_task_content. Read complete immutable content pages under the original current actor and login.\n- cancel: manage_assistant action=cancel. Cancel at the current Action version; in-flight effects may still finish.\n- conversation_import: unsupported. No caller history, thread import, or appended task messages.\n- email_activation: unsupported. This analysis capability cannot start email work. Use the separate assistant_saved_email start_email capability for one reviewed email.\n- executor_transfer: unsupported. No actor, sender, or harness/worker authority transfer.",
1143
+ "description": "Manage Assistant\n\nStart, continue, or cancel private analysis and reviewed email work.\n\n- start_analysis: no id; payload={request_id (UUID), message}. Call only\n after the user requests this analysis. This call explicitly grants\n revenue_operator.read_analysis.v1: paid model work using permitted reads,\n without tool writes or external effects. Keep the exact request UUID and\n message on retries; the saved task journal owns request identity and\n recovery. An uncertain response never authorizes a replacement task.\n- start_email: no id; payload={request_id (UUID), message, person_id,\n sender_config_id}. Call only after the user requests this email work.\n Authorizes paid model work, permitted reads and one retained email draft\n for that workspace contact and an eligible mailbox available to the\n current actor. It does not approve or send. Read the returned email_scope\n and linked review_message_id. Review the exact sender, recipient, subject\n and complete body through the existing message review before approving.\n Keep the same request UUID, message, person and mailbox on retries;\n uncertain acknowledgement never authorizes another task or delivery.\n- cancel: id=the saved task's Action UUID; payload={expected_version} from\n a fresh fetch assistant_task. The original current actor or a current\n workspace owner/operator may cancel future continuation. An in-flight\n effect or provider call may still finish; cancellation cannot recall it.\n- follow_up: id=the latest eligible saved Action UUID; payload={request_id,\n expected_version, message}. Only its current original actor/login can\n continue. This buys a new Action in the same private conversation. Completed\n analysis or email can continue. Revising an unapproved email atomically\n retires that draft; the new draft requires its own exact approval. Contact,\n sender and the complete email envelope stay fixed. Approved, dispatching,\n unresolved, cancelled or blocked work cannot continue. Keep the exact UUID,\n parent, revision and message on retries, including an uncertain response.\n The server reconstructs permitted history and refreshes company context.\n Historical answers and delivery receipts never authorize another send.\n\nAll actions are scalar: no id arrays or items. No caller history, imported\nthread, email-scope edits, additional effects, actor transfer, or worker\ncontrol fields are accepted. This facade adds no authority to its target.\nThe task is private to its original current actor and login; owner/operator\noversight gives metadata and cancellation, never another member's content.\nExisting retained email work uses its exact linked review without changing\nthe sender. Inspect fetch assistant_task, page fetch assistant_task_events,\nand retrieve full content with fetch assistant_task_content. Read prior\nturns by following previous_action_id from fetch assistant_task until it is\nnull, then order by conversation_turn. Follow next_action_id to newer work. There is no steering of running work or shared conversation. Public chat\ncompletion limits remain active.\n\n### Responses:\n\n**200**: Successful Response (Success Response)\nContent-Type: application/json\n\nPrivate saved email support (assistant_saved_email):\nProduct support is not permission. Inspect current work and follow the domain's authorization and review rules.\n- start_email: manage_assistant action=start_email. An explicit request grants paid model work, permitted reads and one retained draft for the exact workspace person and eligible mailbox. Keep the request UUID and all inputs unchanged on retry. Inspect email_scope and review_message_id on the saved task. The exact sender, recipient, subject and body require separate human review before sending. Approval does not prove delivery.\n- scope_edit: unsupported. No sender or recipient changes after start. Cancel unused work and create a new request for changed scope.\n- delivery_replacement: unsupported. An unknown model or delivery outcome never authorizes replacement work. The original saved task owns recovery and receipt checks.\n\nPrivate saved conversations support (assistant_saved_conversations):\nProduct support is not permission. Inspect current work and follow the domain's authorization and review rules.\n- follow_up: manage_assistant action=follow_up. Continue completed private analysis or email, or revise an unapproved email. Its contact and full envelope stay fixed. A revision retires the old draft and requires a new approval. Exact request UUID retries recover the same child Action. Historical receipts never approve a send.\n- active_steering: unsupported. A running task cannot accept new messages. Complete it before following up.\n- sharing: unsupported. No shared participants, source publication, or actor transfer.\n\nSaved assistant tasks support (assistant_saved_tasks):\nProduct support is not permission. Inspect current work and follow the domain's authorization and review rules.\n- start: manage_assistant action=start_analysis. An explicit request grants paid read_analysis.v1 only. Exact request UUID retries recover the same task.\n- list: fetch type=assistant_tasks. Page own work or authorized workspace metadata with next_cursor.\n- inspect: fetch type=assistant_task. Read current state, Action version and exact review links.\n- events: fetch type=assistant_task_events. Page journal events by sequence; no raw worker state or hidden reasoning.\n- content: fetch type=assistant_task_content. Read complete immutable content pages under the original current actor and login.\n- cancel: manage_assistant action=cancel. Cancel at the current Action version; in-flight effects may still finish.\n- conversation_import: unsupported. No caller history, thread import, or appended task messages.\n- email_activation: unsupported. This analysis capability cannot start email work. Use the separate assistant_saved_email start_email capability for one reviewed email.\n- executor_transfer: unsupported. No actor, sender, or harness/worker authority transfer.",
1144
1144
  "inputSchema": {
1145
1145
  "properties": {
1146
1146
  "action": {
@@ -1211,7 +1211,7 @@
1211
1211
  "readOnlyHint": false,
1212
1212
  "title": "Manage Content"
1213
1213
  },
1214
- "description": "Manage Content\n\nDraft, schedule and publish LinkedIn content — the consolidated content writer.\n\nContent actions (default kind; id = post UUID unless noted):\n- draft: create a post draft (no id; `payload` = {content, topic_tags?,\n scheduled_at?, author_user_id?, author_identity?, external_link?,\n attachment_creative_id?}); bulk via `items`.\n- edit: update a draft's content/tags/identity/link/attachment\n (`payload` = the fields to change: content?, topic_tags?,\n author_identity?, external_link? — empty-string external_link clears\n it; attachment_creative_id? — explicit null DETACHES, absent key\n leaves it untouched).\n- schedule: set the publish time (`payload` = {scheduled_at}). A\n row-persisted attachment publishes with the scheduled post.\n- publish: publish the post now (no payload; the retired publish-time\n creative_id param is rejected with the pointer to edit-attach).\n- delete: remove a draft from the calendar (drafts only — published\n posts are not deletable here).\n- draft_post: never generates prose — backend generation is removed\n (VRU-676, permanently). Always returns author-in-harness guidance:\n write the post yourself and submit via draft, then schedule/publish\n (no id; `payload` = {topic?, content_type?, author_user_id?}).\n\nAttachments (VRU-726) — document (PDF carousel), image, or video, ONE\ncontract, persisted on the row (schedulable + reviewable). Golden path\nfor a PDF, entirely via MCP:\n 1. manage_content kind='ad' action='store_creative'\n payload={filename:'my-deck.pdf', size_bytes:<n>,\n content_type:'application/pdf'} → returns upload_url\n 2. curl --fail-with-body -T my-deck.pdf '<upload_url>'\n 3. store_creative again with payload={creative_id:'<id>'} → validates\n (small assets can skip 1-3: payload={asset_base64:'<b64>'})\n 4. manage_content action='draft' payload={content:'<caption>',\n attachment_creative_id:'<id>'}\n 5. get_content_review post_ids=['<post_id>'] → OPEN attachment_url and\n review the actual file (it publishes under the author's identity)\n 6. manage_content action='publish' id='<post_id>'\nThe stored filename becomes the rendered LinkedIn document title\n(override at store time via `filename`). Rules: one attachment per post;\nnot on repost_commentary; caption text required; stored assets are\npublic-at-upload (public bucket, unguessable URL). Media transfers\ninline at publish — expect tens of seconds for large files. Attachment\nfailures revert the post to draft with a structured {code, message, fix}\n422 and never burn post budget on a failed fetch; an ambiguous provider\ntimeout stays terminal `failed` (`publish_outcome_unknown`) — check\nLinkedIn before retrying, a blind retry can double-post.\n\nIdentities (VRU-659): `author_identity` is 'member' (the author's\npersonal profile — default) or 'organization' (the Company Page).\nOrganization publish requires a resolvable Company Page on the author's\nLinkedIn account: exactly one Page on the account, or a selected Page\n(manage_content kind='ad' action='set_page'); the member account still\nauthenticates and must hold admin rights on the Page.\n\n`external_link` (http/https, <=2000 chars) renders as the LinkedIn\npreview card; UTM params are stamped when the link is SAVED (missing\nutm_* added, existing ones never clobbered), so the approved draft is\nexactly what publishes. Combines fine with an attachment.\n\nBulk: edit/schedule/publish/delete take id arrays (server-side loop,\nper-item status — partial failure reported per item, never silent; max\n100). Payloads are validated by the underlying route's schema.\n\nProse gate: draft/edit lint the content and schedule/publish re-lint it\nserver-side; blocked calls return error_code='prose_gate_blocked' with\nstructured `failures[].fix`. `payload` accepts `override_reason` (taste\noverride — proceeds despite block failures, logged to the corpus;\nhonored only for owner/operator roles on the tenant, otherwise ignored\nand the gate blocks normally) and `client_rules_version` (responses\nflag `rules_changed` when the server's rules differ). Pre-check drafts\ncheaply with check_prose.\n\nAd actions (kind='ad'; id = ad-campaign UUID): approve, reject, pause,\nresume, edit_budget — `payload` carries the action's fields (e.g.\n{daily_budget_cents} for edit_budget, {rejection_reason} for reject).\nboost (no id — there's no campaign yet): run a paid LinkedIn campaign.\n`payload` carries {content_post_id (sponsor an EXISTING published post)\nXOR creative_id (a stored VIDEO creative → Direct Sponsored Content,\nMETRICS-ONLY — no engager bridge), vehicle?, objective?,\nbudget:{daily_budget_cents/total_budget_cents}, audience (see below),\nduration_days?, approval_mode?('draft'|'auto'), integration_id?}.\n\nBoost audience — exactly one of:\n- {facets}: the PRIMARY form. LinkedIn-native firmographic targeting: a\n map of facet key (titles, seniorities, industries, locations,\n staff_count_ranges) → list of LinkedIn entity URNs. 'locations' is\n REQUIRED (LinkedIn rejects campaigns without location targeting).\n Values must be entity URNs, never display names — resolve names FIRST\n via `fetch type='ads' subtype='targeting_entities'\n filters={facet, q}` (that resolve-then-boost flow is the happy path).\n- {matched_audience_id}: a PRE-EXISTING uploaded matched audience only.\n- {criteria}: partner-gated — rejected 400 with code\n matched_audience_partner_gated (LinkedIn's audience-member API needs\n partner access); the error's `fix` shows the facets form to use instead.\n\nVehicle/objective/format legality (enforced server-side BEFORE any spend;\nillegal combos 400 with the legal alternatives enumerated):\n| vehicle | objectives | source | destination CTA |\n|------------------|--------------------------------------|-------------------|-------------------------|\n| thought_leader | BRAND_AWARENESS, ENGAGEMENT only | member post only | NOT allowed |\n| page_sponsored | all (BRAND_AWARENESS, ENGAGEMENT, | org post or video | required for |\n| | WEBSITE_VISIT, WEBSITE_CONVERSION, | creative_id (DSC) | WEBSITE_VISIT / |\n| | LEAD_GENERATION, VIDEO_VIEW) | | WEBSITE_CONVERSION |\nDestination = the post's external_link (set at draft/edit via this\naction's content kind) or the video creative's cta_destination_url.\nVehicle is inferred from the post's publish identity when omitted\n(member ⇒ thought_leader, organization ⇒ page_sponsored). Objectives use\nLinkedIn's enum verbatim — do not lowercase.\n\nstore_creative (no id — there's no campaign yet): store a harness creative\nas a DRAFT (zero spend; approval is separate). Stored assets serve ads AND\norganic post attachments (attach via this action's content kind\ndraft/edit payload.attachment_creative_id — VRU-726). BASE64 `payload`\n(small PNG/JPEG/PDF; format decided by server-side sniff):\n{asset_base64 (raw base64, no data: URL — renamed from image_base64),\ngeneration_prompt (required for images only), filename?, campaign_id?\n(owned draft warming campaign to attach), generation_provenance? ({model,\ntool, generated_at, notes} allow-list, string values)}. PRESIGNED\n`payload` (the primary path for real files — mp4 ≤200MB 3s–30min, or PDF\n≤25MB): {filename, size_bytes, content_type ('video/mp4' |\n'application/pdf')} → returns a presigned upload_url; `curl\n--fail-with-body -T <file> '<upload_url>'`, then call store_creative\nagain with {creative_id} to validate/finalize (idempotent; a validation\nfailure returns the failed row with ok=false and the restart step;\nunfinalized rows are swept after 24h). VIDEO also accepts {media_url\n(public https — fetched + validated in the background)} and optional\n{thumbnail_base64}. `filename` becomes the stored file_name — for\ndocuments it is the rendered LinkedIn carousel TITLE. media_url video\nstores are ASYNC — poll `fetch type='ads' subtype='creative'\nid=<creative_id>` until upload_status leaves 'uploading' ('failed' +\nerror_message starting with a probe code means re-export/re-store;\notherwise retriable).\n\nIdentity setup (kind='ad', no id — VRU-659):\n- set_page: `payload` = {organization_urn? (urn:li:organization:<digits>),\n integration_id?}. Omit organization_urn to DISCOVER the candidate Pages\n from the connected LinkedIn accounts, then call again with one. Required\n before page_sponsored campaigns and video ads.\n- authorize_author: `payload` = {author_user_id, auto_approve?,\n integration_id?} — records the operator ATTESTATION that this team\n member consented to Thought Leader Ad sponsorship (required before a\n thought_leader boost; LinkedIn still enforces its own permission).\n Response lists the currently authorized authors.\n- revoke_author: `payload` = {author_user_id, integration_id?}.\n\nAd bulk: `id` arrays loop with per-item status (partial failure is\nreported per item, never silent; max 100). Payloads are validated by the\nunderlying route's own schema.\n\n### Responses:\n\n**200**: Successful Response (Success Response)\nContent-Type: application/json",
1214
+ "description": "Manage Content\n\nDraft, schedule and publish LinkedIn content — the consolidated content writer.\n\nContent actions (default kind; id = post UUID unless noted):\n- draft: create a post draft (no id; `payload` = {content, topic_tags?,\n scheduled_at?, author_user_id?, author_identity?, external_link?,\n attachment_creative_id?}); bulk via `items`.\n- edit: update a draft's content/tags/identity/link/attachment\n (`payload` = the fields to change: content?, topic_tags?,\n author_identity?, external_link? — empty-string external_link clears\n it; attachment_creative_id? — explicit null DETACHES, absent key\n leaves it untouched).\n- schedule: set the publish time (`payload` = {scheduled_at}). A\n row-persisted attachment publishes with the scheduled post.\n- publish: publish the post now (no payload; the retired publish-time\n creative_id param is rejected with the pointer to edit-attach).\n- delete: remove a draft from the calendar (drafts only — published\n posts are not deletable here).\n- draft_post: never generates prose — backend generation is removed\n (VRU-676, permanently). Always returns author-in-harness guidance:\n write the post yourself and submit via draft, then schedule/publish\n (no id; `payload` = {topic?, content_type?, author_user_id?}).\n\nAttachments (VRU-726) — document (PDF carousel), image, or video, ONE\ncontract, persisted on the row (schedulable + reviewable). Golden path\nfor a PDF, entirely via MCP:\n 1. manage_content kind='ad' action='store_creative'\n payload={filename:'my-deck.pdf', size_bytes:<n>,\n content_type:'application/pdf'} → returns upload_url\n 2. curl --fail-with-body -T my-deck.pdf '<upload_url>'\n 3. store_creative again with payload={creative_id:'<id>'} → validates\n (small assets can skip 1-3: payload={asset_base64:'<b64>'})\n 4. manage_content action='draft' payload={content:'<caption>',\n attachment_creative_id:'<id>'}\n 5. get_content_review post_ids=['<post_id>'] → OPEN attachment_url and\n review the actual file (it publishes under the author's identity)\n 6. manage_content action='publish' id='<post_id>'\nThe stored filename becomes the rendered LinkedIn document title\n(override at store time via `filename`). Rules: one attachment per post;\nnot on repost_commentary; caption text required; stored assets are\npublic-at-upload (public bucket, unguessable URL). Media transfers\ninline at publish — expect tens of seconds for large files. Attachment\nfailures revert the post to draft with a structured {code, message, fix}\n422 and never burn post budget on a failed fetch; an ambiguous provider\ntimeout stays terminal `failed` (`publish_outcome_unknown`) — check\nLinkedIn before retrying, a blind retry can double-post.\n\nIdentities (VRU-659): `author_identity` is 'member' (the author's\npersonal profile — default) or 'organization' (the Company Page).\nOrganization publish requires a resolvable Company Page on the author's\nLinkedIn account: exactly one Page on the account, or a selected Page\n(manage_content kind='ad' action='set_page'); the member account still\nauthenticates and must hold admin rights on the Page.\n\n`external_link` (http/https, <=2000 chars) renders as the LinkedIn\npreview card; UTM params are stamped when the link is SAVED (missing\nutm_* added, existing ones never clobbered), so the approved draft is\nexactly what publishes. Combines fine with an attachment.\n\nBulk: edit/schedule/publish/delete take id arrays (server-side loop,\nper-item status — partial failure reported per item, never silent; max\n100). Payloads are validated by the underlying route's schema.\n\nProse gate: draft/edit lint the content and schedule/publish re-lint it\nserver-side; blocked calls return error_code='prose_gate_blocked' with\nstructured `failures[].fix`. `payload` accepts `override_reason` (taste\noverride — proceeds despite block failures, logged to the corpus;\nhonored only for owner/operator roles on the tenant, otherwise ignored\nand the gate blocks normally) and `client_rules_version` (responses\nflag `rules_changed` when the server's rules differ). Pre-check drafts\ncheaply with check_prose.\n\nAd actions (kind='ad'; id = ad-campaign UUID): reject, pause — `payload`\ncarries the action's fields (e.g. {rejection_reason} for reject). This\ntool never starts, resumes or raises paid spend: a workspace owner or\noperator approves a drafted boost in the Vruum app's Work list, or\napproves, resumes or re-budgets it with the Vruum CLI (vruum ad approve |\nresume | budget).\nboost (no id — there's no campaign yet): prepare a paid LinkedIn campaign\nas a DRAFT; it pushes nothing and spends nothing, and it appears in the\nWork list for an owner to approve or decline. `payload` carries {content_post_id (sponsor an EXISTING\npublished post) XOR creative_id (a stored VIDEO creative → Direct\nSponsored Content, METRICS-ONLY — no engager bridge), vehicle?,\nobjective?, budget:{daily_budget_cents/total_budget_cents}, audience (see\nbelow), duration_days?, integration_id?}.\n\nBoost audience — exactly one of:\n- {facets}: the PRIMARY form. LinkedIn-native firmographic targeting: a\n map of facet key (titles, seniorities, industries, locations,\n staff_count_ranges) → list of LinkedIn entity URNs. 'locations' is\n REQUIRED (LinkedIn rejects campaigns without location targeting).\n Values must be entity URNs, never display names — resolve names FIRST\n via `fetch type='ads' subtype='targeting_entities'\n filters={facet, q}` (that resolve-then-boost flow is the happy path).\n- {matched_audience_id}: a PRE-EXISTING uploaded matched audience only.\n- {criteria}: partner-gated — rejected 400 with code\n matched_audience_partner_gated (LinkedIn's audience-member API needs\n partner access); the error's `fix` shows the facets form to use instead.\n\nVehicle/objective/format legality (enforced server-side BEFORE any spend;\nillegal combos 400 with the legal alternatives enumerated):\n| vehicle | objectives | source | destination CTA |\n|------------------|--------------------------------------|-------------------|-------------------------|\n| thought_leader | BRAND_AWARENESS, ENGAGEMENT only | member post only | NOT allowed |\n| page_sponsored | all (BRAND_AWARENESS, ENGAGEMENT, | org post or video | required for |\n| | WEBSITE_VISIT, WEBSITE_CONVERSION, | creative_id (DSC) | WEBSITE_VISIT / |\n| | LEAD_GENERATION, VIDEO_VIEW) | | WEBSITE_CONVERSION |\nDestination = the post's external_link (set at draft/edit via this\naction's content kind) or the video creative's cta_destination_url.\nVehicle is inferred from the post's publish identity when omitted\n(member ⇒ thought_leader, organization ⇒ page_sponsored). Objectives use\nLinkedIn's enum verbatim — do not lowercase.\n\nstore_creative (no id — there's no campaign yet): store a harness creative\nas a DRAFT (zero spend; approval is separate). Stored assets serve ads AND\norganic post attachments (attach via this action's content kind\ndraft/edit payload.attachment_creative_id — VRU-726). BASE64 `payload`\n(small PNG/JPEG/PDF; format decided by server-side sniff):\n{asset_base64 (raw base64, no data: URL — renamed from image_base64),\ngeneration_prompt (required for images only), filename?, campaign_id?\n(owned draft warming campaign to attach), generation_provenance? ({model,\ntool, generated_at, notes} allow-list, string values)}. PRESIGNED\n`payload` (the primary path for real files — mp4 ≤200MB 3s–30min, or PDF\n≤25MB): {filename, size_bytes, content_type ('video/mp4' |\n'application/pdf')} → returns a presigned upload_url; `curl\n--fail-with-body -T <file> '<upload_url>'`, then call store_creative\nagain with {creative_id} to validate/finalize (idempotent; a validation\nfailure returns the failed row with ok=false and the restart step;\nunfinalized rows are swept after 24h). VIDEO also accepts {media_url\n(public https — fetched + validated in the background)} and optional\n{thumbnail_base64}. `filename` becomes the stored file_name — for\ndocuments it is the rendered LinkedIn carousel TITLE. media_url video\nstores are ASYNC — poll `fetch type='ads' subtype='creative'\nid=<creative_id>` until upload_status leaves 'uploading' ('failed' +\nerror_message starting with a probe code means re-export/re-store;\notherwise retriable).\n\nIdentity setup (kind='ad', no id — VRU-659):\n- set_page: `payload` = {organization_urn? (urn:li:organization:<digits>),\n integration_id?}. Omit organization_urn to DISCOVER the candidate Pages\n from the connected LinkedIn accounts, then call again with one. Required\n before page_sponsored campaigns and video ads.\n- authorize_author: `payload` = {author_user_id, auto_approve?,\n integration_id?} — records the operator ATTESTATION that this team\n member consented to Thought Leader Ad sponsorship (required before a\n thought_leader boost; LinkedIn still enforces its own permission).\n Response lists the currently authorized authors.\n- revoke_author: `payload` = {author_user_id, integration_id?}.\n\nAd bulk: `id` arrays loop with per-item status (partial failure is\nreported per item, never silent; max 100). Payloads are validated by the\nunderlying route's own schema.\n\n### Responses:\n\n**200**: Successful Response (Success Response)\nContent-Type: application/json",
1215
1215
  "inputSchema": {
1216
1216
  "properties": {
1217
1217
  "action": {
@@ -1261,7 +1261,7 @@
1261
1261
  }
1262
1262
  ],
1263
1263
  "default": "content",
1264
- "description": "Content family: 'content' (default) for LinkedIn posts, or 'ad' to manage ad campaigns (actions: approve, reject, pause, resume, edit_budget, boost, store_creative, set_page, authorize_author, revoke_author).",
1264
+ "description": "Content family: 'content' (default) for LinkedIn posts, or 'ad' to manage ad campaigns (actions: reject, pause, boost (always a draft), store_creative, set_page, authorize_author, revoke_author).",
1265
1265
  "title": "kind",
1266
1266
  "type": "string"
1267
1267
  },