Weekly content campaigns

The isolated project /o/{orgSlug}/p/{projectSlug}/content-campaigns screen persists a campaign handoff over the existing supplied social week. A week has seven days with three slots per day and one variant per selected configured IG/X/TikTok/YouTube/LinkedIn target. It uses canonical timezone/DST, received-upload authorization, revision approval and calendar services. There is no additional calendar, delivery queue, provider adapter or scheduler. Orchestration receipts use the existing append-only tenant audit store; they contain IDs, requirements, model choices and hashes, never briefs/copy/prompts or inbox bodies.

Explicit source briefs and supplied copy are required. Text agent execution is unavailable in this immutable base: choosing Opus reports agent_step_contract_unavailable and holds review, without substituting another model. Image/video choices do not grant generation or spend authority. Capabilities report configured canonical services, missing Vertex project and settings mismatch/disabled holds; they do not claim provider availability. Generate in the existing authorized Image studio, inspect progress/failure/unknown there, accept the exact image version, then bind it to a campaign variant. Binding verifies tenant/project, exact provider/model and acceptance, then uses the canonical edit service. Received image/video attachments use the same upload authorization path. Canonical media generator records and bound studio version states appear in campaign readbacks. No claim of automatic text/video production or live media availability is made.

Creation rejects multiple configured accounts for the same platform before claiming any campaign receipt. Instagram requires an image or video declaration; TikTok and YouTube require video (image alone is insufficient). X and LinkedIn permit text-only declarations. Declared but missing assets remain visible as holds across all 21 saved slots. Refused declarations leave no campaign or week receipt and may be corrected using the same operation ID.

Missing required media holds weekly review and scheduling. Requesting review checks all 21 exact revisions, creates one canonical issue and in-app notification per revision set, and never approves. A person approves individual exact revisions; a delegated credential cannot approve. Approval transitions preserve the weekly review; copy, media, account or time edits invalidate it. The calendar reads canonical actual/proposed states. Stopped campaigns refuse new handoffs; Stop does not cancel provider work that already started.

Scheduling uses the integrated canonical expected-revision contract under the dispatch lock. Org API keys cannot schedule, matching the canonical credential policy; refusal occurs before a campaign claim. A canonical-eligible signed-in person delegates to the existing canonical provider route after a durable campaign claim. Replaying the same operation only reads. Any attempted channel remains fenced against another campaign dispatch, including under a new revision or operation ID. An attempt without a canonical scheduled/posted result is conservatively provider_outcome_unknown, and the canonical route retains its durable dispatching claim on UNKNOWN. Reconciliation/retry release is intentionally unavailable in this slice; the owning provider service must supply authoritative reconciliation. Do not bypass the fence by scheduling directly elsewhere.

Monitoring is an on-demand recipe admitted at most once per four hours per selected account set, through canonical inbox and current project permissions. It has no timer installation and sends nothing. It reads up to 100 unseen inbound canonical messages using exact provider/account/channel tuples; deduplication spans overlapping selections. Do-not-contact preferences stop work; all other messages create a human review issue and notification because the scoped tone/stop agent-step contract is absent. Comments/mentions that connectors do not ingest are explicitly unavailable. Account-reference and operation conflicts refuse. A changed account set may admit new unseen items immediately. Receipts retain message/thread IDs but no message text.

Parent composition and remaining execution contracts

The parent composition mounts the reviewed REST plugin, records its ten MCP tools through the existing outreach registration wrapper, adds only the eight draft/handoff outreach API-key patterns below, and links the project page through the current permission-aware capability catalogue. These are source integration facts, not a production or connected-execution acceptance claim. Existing creator ceilings, outreach/API flags, current permissions and session-only approval/monitoring remain authoritative.

  1. Applied in apps/api/src/app.ts: import contentCampaignRoutes from ./modules/content-campaigns/routes; register await app.register(contentCampaignRoutes, { prefix: '/v1' }); after auth setup alongside the social week plugin. It relies on existing db, config and authenticate decorators.
  2. Applied in apps/api/src/modules/mcp/tools.ts: import registerContentCampaignTools from ../content-campaigns/mcp; call registerContentCampaignTools(server, ctx) inside the existing curated registration wrapper, using its policy-recording server, alongside registerSocialWeekTools. Preserve all creator ceilings, write confirmations and existing policy recording.
  3. Applied in apps/api/src/plugins/auth.ts: publish the following route patterns in the existing outreach-family API-key allowlist, preserving both outreach flags, read/write checks and runner/user-token restrictions. Approval and monitoring remain session-only in service:
GET /v1/orgs/:p/projects/:p/content-campaigns
GET /v1/orgs/:p/projects/:p/content-campaigns/capabilities
POST /v1/orgs/:p/projects/:p/content-campaigns
GET /v1/orgs/:p/projects/:p/content-campaigns/:p
POST /v1/orgs/:p/projects/:p/content-campaigns/:p/stop
POST /v1/orgs/:p/projects/:p/content-campaigns/:p/request-review
POST /v1/orgs/:p/projects/:p/content-campaigns/:p/assets
PATCH /v1/orgs/:p/projects/:p/content-campaigns/:p/posts/:p/channels/:p
  1. The project capability catalogue links /content-campaigns using outreach.enabled visibility and current crm:read permission. The isolated page retains project slug conventions; the existing marketing/social page is unchanged.
  2. No DDL, new grant, migration ID or schema allocator changes. Existing canonical tables and append-only audit storage suffice. Shared OpenAPI owner should publish route response contracts after registration.
  3. The integrated canonical scheduling contract accepts optional expectedRevision for compatibility and compares supplied revisions under its post/channel claim lock before dispatch. Campaign scheduling requires and forwards it. The 503 campaign_schedule_contract_unavailable guard remains fail-closed if that contract is absent. MCP content_campaign_schedule is a thin forward with the same credential restrictions; discovery is not permission to dispatch. content_campaign_variant_edit forwards the canonical strict draft-edit schema and invalidates approval. No approval or grant is added.
  4. Existing workflow owner: expose maintained scoped agent/model/media actions and monitor admission through existing deterministic nodes. This base's routine actions and automations do not offer a campaign generator/agent-step action. Do not install a competing workflow executor or pretend this on-demand handoff is recurring execution.

Maintained proof

apps/api/test/content-campaigns.test.ts uses production plugin registration against the real application and approved PostgreSQL fixture. The actual public HTTP MCP SDK checks ten-tool discovery, current API-key draft edits and stale/foreign/revoked/read-only refusal. API-key schedule refusal records no campaign claim and makes no provider call. A separate signed-in-person SDK context forwards through real authenticated REST to the canonical loopback provider and proves expected-revision dispatch plus UNKNOWN/no-resend fencing. This in-process person context is not evidence of public API-key scheduling. No provider is contacted outside the loopback fixture. CAMPAIGN_BROWSER=1 enables the production Next/Chromium journey; its loopback HTTP proxy forwards built-client requests to the real API. It checks all 21 saved slots, missing-media hold, weekly review, human approval, edited revision invalidation and cold-page unknown/no-resend readback. No test contacts paid/live providers.

The web owning tests retain the real shared api() client and record only the outgoing fetch edge. They assert object payloads for campaign creation, exact weekly review revisions and edits, and retain the same creation identity on replay. The shared client owns JSON serialization. This guards against a double-encoded body that an internal API stub previously concealed. The browser additionally checks the review POST's actual status before persisted readback.

Browser harness references: Playwright route fetch/fulfill and Next production CLI. These describe the local forwarding and production-server mechanisms; they are not evidence of live provider availability.

For the disposable browser proof, build with NEXT_PUBLIC_API_URL='' pnpm --filter @bugtracker/web build, then run the owning API suite with CAMPAIGN_BROWSER=1 through the approved PostgreSQL helper. The fixture uses http://127.0.0.1:43288 as its web origin and forwards same-origin /v1 requests to its actual API on port 43287. Next's public environment variables are frozen at build time; setting an API base only on next start does not repair an already-built browser bundle. Ports must be free; no external API, provider or account credentials are used.

We use essential cookies to keep you signed in. See our Privacy Policy for details.