Walk every marketing-plan request through this conversation, in this order. Skip a step only when the user's own words in this conversation already answered it.
GET .../marketing-settings). Tell the user what the plan is shaped by and what they can change: goal, marketing tools, content pillars (topics), tone of voice, point of view, business profile, and per-tool cadence. Say which inputs are site-derived and read-only: language, business/target locations, industry — those change in the site's Language & Region / SEO business-location settings, never through marketing settings (details in STEP 1).GET .../marketing-settings/plan-goal-options, GET .../marketing-settings/defaults) and propose suggestions for them to confirm. Write only values the user gave or confirmed; leave everything else to the site defaults. Never fabricate a goal, tone, or topic list on their behalf, and never put language, targetLocations, or businessLocation in the request or claim the call changed them.fieldMask as a comma-separated string matching exactly what you send), then offer to generate the plan — or, if a plan already exists, to regenerate it so the new settings take effect (settings are read only at generation time; use the keyword-research variant when topics changed). Poll until ACTIVE (STEPs 2-3).DRAFT. Compare the returned SCHEDULED ids against the ids you sent, and tell the user which drafts didn't schedule and which channel needs connecting (STEP 4).TWITTER drafts dated after July 31, 2026. FAILED_PRECONDITION on Schedule Drafts means the plan lacks the premium scheduling feature: say so once and stop; it is not a transient error to retry.The rest of this recipe is the reference for executing each step.
This recipe generates a site's AI marketing plan — a schedule of marketing activities (social campaigns, blog posts, emails) from today through the end of next month — and schedules the social posts it generates. Generation is asynchronous and non-deterministic: you fire it, poll until it's ready, then act on the results.
Base URL for all endpoints: https://www.wixapis.com/promote/marketing-plan-service/v1.
Prerequisites:
FAILED plan or an ACTIVE plan with no posts. Verify before generating.For a first-time plan, configure marketing settings before generating (STEP 2), so the first plan reflects the brand's goal, voice, and cadence. Settings are optional — the service applies per-site defaults if you skip this — but they're read only at generation time, so a plan generated with defaults won't change until you regenerate. After a plan exists, changing settings requires a regenerate (STEP 2) to take effect.
API Endpoint: POST https://www.wixapis.com/promote/marketing-plan-service/v1/marketing-settings
Both marketingSettings and fieldMask are required. fieldMask is a single comma-separated string of the paths to write (proto3 field-mask form) — not a JSON array. Sending it as an array (["settings.marketingPlanGoal", …]) fails with 400 "Failed to parse request message". Every path in fieldMask must also be present in marketingSettings, or the call fails with INVALID_FIELD_MASK_ERROR.
Language and location cannot be set here. If the request targets a language or a place ("posts in Spanish", "customers near Berlin"), those are site-derived, read-only fields on marketing settings — never put language, targetLocations, or businessLocation in the request, and never tell the user this call changed them. Set the fields that do belong here (goal, tone, audience description), and direct the user to the site-level settings for the rest — see What you can't set here below.
What each field shapes. Generation has two stages, and fields feed different stages:
marketingPlanGoal (goalKey ∈ DRIVE_TRAFFIC, BRAND_AWARENESS, BOOST_SALES, COLLECT_LEADS, or customGoal free text), topics.included/topics.excluded (content pillars), businessProfile (valueProposition, targetAudience, businessGoal), calendar.ids (which calendars feed the plan), and per-tool cadence overrides.<socialMarketing|emailMarketing|blogs>.frequency ({ interval: WEEK | MONTH | YEAR, count: 1–30 }).toneOfVoice (free text), pointOfView (FIRST_PERSON/SECOND_PERSON/THIRD_PERSON), customContentGuidelines (free text), imageGuidelines (free text), and businessProfile.targetAudience.marketingTools must include SOCIAL_MARKETING for social post drafts to be generated (also BLOGS, EMAIL_MARKETING).socialChannels does not limit channels. The plan generates post drafts for all supported channels; settings.socialChannels is only a hint to the caption generator, not a filter. To avoid publishing to a channel, simply don't schedule (or connect) it in STEP 4 — don't rely on socialChannels to exclude it. Supported channels: FACEBOOK, INSTAGRAM, LINKEDIN, PINTEREST, GBP, TIKTOK, TWITTER (X, sunset — no longer functional after July 31, 2026).
What you can't set here (site-derived, read-only). Several inputs that shape the plan are not part of marketing settings and can't be changed through this endpoint — they come from the site, and setting them via Upsert Marketing Settings has no effect:
settings.language is read-only here). The plan's activities and captions are generated in the site language, so "write my posts in French" means changing the site language, not marketing settings.settings.businessLocation / settings.targetLocations are read-only here).To change any of these, edit them at the site level (or tell the user where: Language & Region for language, SEO business-location settings for locations), then (re)generate the plan (STEP 2) so it picks them up.
To fetch selectable values at runtime, call GET .../marketing-settings/plan-goal-options (goals) and GET .../marketing-settings/defaults (the site's default goal, channels, and frequency); point-of-view values are the fixed enum above. (Two fields are accepted but currently ignored by generation: settings.imageGenerationSettings and topics.coreTopic.)
Note — settings only take effect on (re)generation. Editing settings never changes an existing plan on its own. To apply changes to an existing plan, regenerate it (STEP 2, regenerate-marketing-plan). If you changed topics, use RegenerateMarketingPlanWithKeywordResearch instead (POST .../marketing-plan/regenerate-marketing-plan-with-keyword-research) so keyword research reruns for the new content pillars.
API Endpoint: POST https://www.wixapis.com/promote/marketing-plan-service/v1/marketing-plan/generate-marketing-plan
No body is required:
Expected response — generation runs in the background:
If the site already has a plan and you want to refresh it, call POST .../marketing-plan/regenerate-marketing-plan instead (same response shape).
Poll this endpoint until status is ACTIVE (ready) or FAILED. Poll about every 5 seconds, and stop after ~15 minutes as a safety timeout.
API Endpoint: GET https://www.wixapis.com/promote/marketing-plan-service/v1/marketing-plan?timeframe.startDate=2026-07-01T00:00:00.000Z&timeframe.endDate=2026-08-31T23:59:59.999Z (example dates — compute the timeframe from today's date through the end of next month)
timeframe is optional; omit it to return all activities from today onward.
Expected response when ready:
PlanStatus values: NEVER_CREATED, GENERATING (keep polling), ACTIVE (ready), INACTIVE (auto-deactivated after ~6 weeks idle — and calling this endpoint on a stale plan can itself trigger the transition; regenerate to reactivate), FAILED (retry with STEP 2).
Posts are generated automatically only for the near-term activities (how far ahead depends on the site's premium plan). Later activities have no items yet — see STEP 5 to generate them.
Collect the id of every item whose status is DRAFT from the activities you want to publish. Before scheduling, show the user what will be published — each draft's caption and media (render the image inline if the surface supports it, otherwise post its URL as a clickable link) — and get their approval. The drafts are AI-generated and scheduling publishes them, so never schedule content the user hasn't reviewed. Skip TWITTER drafts whose activity date falls after July 31, 2026 — X is sunset then, and a post scheduled past the cutoff will never publish.
API Endpoint: POST https://www.wixapis.com/promote/marketing-plan-service/v1/marketing-plan/schedule-drafts
draftIds are item IDs; 1–20 per call:
Expected response: the scheduled items (status now SCHEDULED):
Check the response — scheduling can partially and silently succeed:
DRAFT items are scheduled; non-draft IDs are silently ignored.DRAFT — with no error. A 200 does not mean everything was scheduled. Diff the returned items against the draftIds you sent: any id not returned as SCHEDULED is still a draft. Tell the user which channels those drafts belong to and that the channel needs connecting — connect it via the Create and Publish a Social Media Post recipe's connect flow, then reschedule those ids.FAILED_PRECONDITION (advise upgrading the social media marketing plan).The scheduled posts are managed by the Publisher and appear on the site's Social Media Marketing page.
For activities that have no items yet, generate their posts, then schedule them.
Generate — POST https://www.wixapis.com/promote/marketing-plan-service/v1/marketing-plan/generate-social-posts (1–20 activity IDs; returns {} immediately, async):
Note: this generates drafts for all supported channels (it ignores socialChannels) and doesn't re-check SOCIAL_MARKETING in marketingTools.
Poll — POST https://www.wixapis.com/promote/marketing-plan-service/v1/marketing-activity-posts with the same activity IDs until posts appear:
Response: { "marketingActivities": [ { "marketingActivityId": "...", "items": [ { "id": "...", "status": "DRAFT", ... } ] } ] }.
Schedule — collect the new DRAFT item IDs and call schedule-drafts as in STEP 4.
| Symptom | Cause | Fix |
|---|---|---|
Plan status: FAILED, or ACTIVE with no posts | Site not published, or SOCIAL_MARKETING not enabled in settings | Publish the site; ensure settings.marketingTools includes SOCIAL_MARKETING, then regenerate (STEP 2) |
400 "Failed to parse request message" on Upsert Marketing Settings | fieldMask sent as a JSON array (or { "paths": [...] }) instead of a comma-separated string | Send fieldMask as a single comma-separated string, e.g. "settings.marketingPlanGoal,settings.toneOfVoice" |
INVALID_FIELD_MASK_ERROR on Upsert Marketing Settings | A fieldMask path is missing from marketingSettings | Include every fieldMask path in the marketingSettings body |
| Language, region, or location "won't change" via Upsert Marketing Settings | Those fields are read-only here (site-derived) | Change them in the site's Language & Region / SEO business-location settings, then regenerate |
FAILED_PRECONDITION on Schedule Drafts | Plan lacks the schedule-posts premium feature | Advise upgrading the social media marketing plan |
FAILED_PRECONDITION / PLAN_ALREADY_GENERATING_ERROR on Regenerate | A generation is already in progress | Wait for STEP 3 to reach ACTIVE or FAILED before regenerating |
Drafts remain DRAFT after Schedule Drafts | Their channels aren't connected through the Publisher | Connect those channels (Publisher Accounts API), then reschedule |
Polling never reaches ACTIVE | Generation stuck or very slow | Stop after ~15 minutes and retry generation |
GetSocialMarketingPlan returns INACTIVE with no activities | Plan auto-deactivated after long inactivity | Call generate/regenerate (STEP 2) to reactivate |
Last updated: 16 July 2026