Skip to content

Marketing

When Marketing is included in your purchase, staff plan and publish social posts across connected channels from the admin — campaigns, an approval queue, a calendar, and per-post metrics. See Marketing for what the domain owns underneath.

Open Marketing (admin.your-domain.com/dashboard/marketing).

Marketing → Channels. Each channel — Meta (Facebook page + optional linked Instagram), LinkedIn, or X — connects through its own Connect dialog: it opens that platform’s own authorization screen in a new tab, and you paste the resulting code back to finish the link. A connected channel shows a status pill (connected, pending, disconnected, or error, with a reason); Disconnect unlinks it without deleting any posts already sent through it. Connecting or disconnecting needs marketing.manage_channels.

Someone with access to the deployment’s .env does this once, before any store can connect that channel.

Meta (Facebook + Instagram)

  1. At developers.facebook.com, create an app of type Business.
  2. Add the Facebook Login for Business and Instagram Graph API products.
  3. Under Facebook Login → Settings, add your API host’s callback page as a valid OAuth redirect URI — it must match META_REDIRECT_URI exactly.
  4. Request these scopes: pages_show_list, pages_manage_posts, pages_read_engagement, instagram_basic, instagram_content_publish, business_management. Anything beyond what your own account/team can test with needs Meta’s App Review (Advanced Access) before a real customer’s page can grant it.
  5. Copy the App ID and App Secret into the deployment’s .env as META_APP_ID, META_APP_SECRET, and META_REDIRECT_URI.

LinkedIn

Personal profiles and company pages use separate LinkedIn apps. Keep the personal app’s settings when adding a company app:

Connection type App products Permissions requested
Personal profile Share on LinkedIn and Sign In with LinkedIn using OpenID Connect openid, profile, w_member_social
Company page Community Management API w_organization_social

For personal profiles:

  1. At the LinkedIn Developer Portal, create an app associated with your organization.
  2. Add the Sign In with LinkedIn using OpenID Connect and Share on LinkedIn products.
  3. Add your callback URL as an authorized redirect URL — it must match LINKEDIN_REDIRECT_URI exactly.
  4. Set LINKEDIN_APP_ID, LINKEDIN_APP_SECRET, and LINKEDIN_REDIRECT_URI in the deployment environment.

For company pages:

  1. Create a separate app associated with the company page. A Page super admin must verify the app.
  2. Under Products, request Community Management API access. LinkedIn requires an app without other provisioned products or pending product requests for this Development-tier application. Keep Share on LinkedIn and Sign In with LinkedIn using OpenID Connect on the personal app.
  3. Complete LinkedIn’s access review. Requesting the product does not grant its permissions until LinkedIn approves access. See the official Community Management access rules and app review requirements.
  4. Set the company app’s credentials and callback URL in the deployment environment:
Setting Value
LINKEDIN_COMPANY_APP_ID Client ID from the company app
LINKEDIN_COMPANY_APP_SECRET Client secret from the same company app
LINKEDIN_COMPANY_REDIRECT_URI Authorized callback URL registered on the company app; defaults to LINKEDIN_REDIRECT_URI when unset

For example, the callback can be https://api.your-domain.com/api/v1/marketing/admin/linkedin/callback/. Register the exact URL under the company app’s Auth settings. Enter secrets through the deployment’s environment settings, never in a post or the channel dialog.

The company flow requests only w_organization_social; it does not require OpenID Connect products. The app must have company-posting access and the authorizing member must have an appropriate role on the page. See LinkedIn’s posting permissions. Missing company app configuration shows an error when Company page is selected; it does not use the personal app instead.

X

  1. At developer.x.com, create a project and app with OAuth 2.0 enabled (PKCE — Nudova handles the code challenge/verifier itself).
  2. Add your callback URL, matching X_REDIRECT_URI exactly.
  3. Scopes requested: tweet.read, tweet.write, users.read, offline.access (the last one is what lets Nudova refresh the token without asking a person to reauthorize).
  4. Set X_APP_ID, X_APP_SECRET, X_REDIRECT_URI.

Once the app exists, a store owner or manager with marketing.manage_channels finishes the link from Marketing → Channels:

  1. Open the provider’s Connect dialog. For LinkedIn, choose Connect as and complete the account fields described below before continuing.
  2. Choose Continue to open the platform’s consent screen in a new tab. Approve access there.
  3. The platform redirects to your configured callback URL with a code in the address bar. Paste that value into Nudova’s OAuth code field.
  4. Meta only — also paste the Facebook Page ID, Page name, Instagram Business Account ID, and Instagram username you’re connecting. Nudova doesn’t call the Graph API to list your pages for you; find these in Meta Business Suite → Settings, or the Graph API Explorer.
  5. Choose Complete connection. X needs no additional account fields; it posts as the account that approved the consent screen.

In Connect LinkedIn, Connect as explicitly selects the destination:

Choice Fields shown Publishing destination
Personal profile Optional member display name The personal profile that authorizes the connection
Company page Required numeric organization ID and optional organization name The company page identified by that ID

For a company page, enter its positive numeric ID from LinkedIn’s page admin tools before choosing Continue to LinkedIn. A vanity URL such as linkedin.com/company/your-company is not the numeric ID. Sign in with the LinkedIn profile of a member who has permission to post for that page; the connected destination is the page.

Changing the account type or organization ID clears an old authorization code. Start authorization again for the destination you selected. Connecting a company page creates that page’s channel; it does not also create a personal-profile channel.

New connections remember which app issued their credentials so refresh and disconnect use the same app. Existing connections made before separate app support continue using the original app settings. Connect again to create a company grant through the new company app.

  • unauthorized_scope_error for w_organization_social — LinkedIn has not authorized this permission for the selected app. Check Community Management approval and the company app credentials. Requesting this scope on the personal app or retrying consent does not grant access.
  • Company app is not configured — ask the deployment operator to set the company app settings above. Personal connections can still use their original app.
  • Organization ID required — enter a positive numeric page ID in Company page mode. An empty company ID cannot complete as a personal connection.
  • Company-posting permission was not granted — complete a fresh authorization through the approved company app. A personal-only grant cannot complete a company connection.

Marketing → Campaigns. A campaign is a name, an objective (awareness, engagement, traffic, conversions, other), a status, and a start/end window — a folder you group posts under, not a workflow of its own.

Marketing (the root screen) or + New post. A post is a body, an optional link, one channel, an optional campaign, and an optional schedule time. A post can also arrive from another domain — for example a published Blog article promoted to a channel — tagged with where it came from, without Marketing needing to know anything about Blog.

The post’s Channel determines the account that publishes it. For LinkedIn, select the intended personal-profile or company-page channel; setting up a company app or connecting a page does not change existing posts’ channels. Update the channel on unpublished posts before publishing. Previously published posts are not moved between accounts.

If publishing is rejected, the post composer shows Last publish failed with the failure reason. Read that reason before trying again; connection, permission, approval, and attachment problems require different fixes.

For LinkedIn, attached images are read through the deployment’s configured file storage and then uploaded to LinkedIn. Publishing does not require a public CDN download of those attachments. If the stored image cannot be read, reattach it or ask the operator to restore storage access. A CDN access error alone does not establish whether the stored file is missing.

Posts move through a fixed lifecycle; which actions you see depend on your permissions and where the post already is:

draft → submitted → approved → published

  • Save while still drafting (marketing.edit).
  • Submit for review hands it to the approval queue — only a draft or a previously failed post can be submitted (marketing.edit).
  • Approve and Publish are separate buttons, but they run on the same permission, marketing.publish. Approving records who approved it and (for an AI-generated post, when review is required) runs it past a brand guardrail check before the status flips to approved.

Marketing → Approval lists everything waiting on a reviewer, with the channel and current status on each row, and Approve/Publish actions right there so a reviewer doesn’t have to open every post individually.

Marketing → Calendar lays out scheduled and published posts by date — the same posts as the list view, arranged for “what’s going out this week” rather than “what needs my review.”

Once a post is published, its detail page grows a metrics panel: impressions, reach, clicks, engagements, and shares, with the time they were last pulled from the channel. A post with nothing published yet, or a channel that hasn’t reported back, shows empty rather than zeroed — Nudova never invents a number the platform hasn’t sent.

When marketing.autopilot is on the license, Marketing → Autopilot adds scheduled content plans, pipeline history, and Autopilot settings (weekly cap and event-source allowlist). Create a plan with a timezone, visual schedule (repeat + run hours), channel, publish mode, and autonomy. Optional research runs a web search before drafting and reuses Blog Autopilot search keys when enabled. Autonomy chooses the gate outcome: draft, submit for review, or brand-guardrail + schedule. Those posts still use Marketing’s post model and scheduled publish worker; they do not import Blog domain models. Event-driven promotion (for example after a blog article publishes) only runs when the event name is allowlisted and the payload includes an absolute public URL. See Marketing Autopilot.

Six permissions, each gating a different slice of the workflow:

Permission Covers
marketing.view Read posts, campaigns, channels, approval queue, calendar, settings.
marketing.create Create a post or a campaign.
marketing.edit Edit a post or campaign; submit a post for review.
marketing.publish Approve and publish — one permission covers both buttons.
marketing.manage_channels Connect or disconnect Meta, LinkedIn, or X.
marketing.manage_settings Change require_review, the default autonomy level, and the monthly post cap.

There’s no separate “approver” permission distinct from “publisher” — anyone who can approve a post can also publish it directly. If you want a real maker/checker split, give the composer’s role marketing.create + marketing.edit only, and keep marketing.publish on a second role. Grant these through Users & roles.

Related: Marketing · Blog / journal · CRM · Entitlements · Users & roles.