Developer docs
Start here

Overview

What this is, who it is for, and the rules that do not bend.

docs/PROJECT_BRIEF.md

One-liner

The internal marketing command centre for Verjson: every project, channel, campaign, ad account and KPI in one place, with AI and hired agents doing the work under an approval gate.

The table

FieldValue
Product nameVerjson Marketing Studio
What it isAn internal solution, not a product for sale. No pricing page, no self-serve signup, no marketing funnel. Built for one org (Verjson) plus the agencies it hires.
ProblemMarketing work is scattered across a calendar, a spreadsheet of KPIs, the Google Ads UI, the Meta Ads UI, a Canva folder, and a WhatsApp thread with the agency. Nobody can answer "what is running, what did it cost, what did it return" without half a day of assembly.
Primary userVerjson's marketing lead — owns the plan, approves the work, answers for the numbers.
Secondary users(a) Hired agency staff, who draft and execute but must not see everything; (b) AI agents, which draft, analyse and propose changes through the same API and the same approval gate.
Core job-to-be-done"Plan a quarter across every channel, get the work drafted (by a human or an AI), approve it, ship it, and see what it returned — without leaving one app."
Out of scope (v1)Self-serve signup · billing/subscriptions · white-label resale · native mobile apps · a public-facing marketing site beyond the internal landing page
Success metricA marketing lead can answer "what is running and what is it returning" in under 60 seconds, and no campaign ships without a recorded approval.
Key constraintBackend and frontend are separately deployable and share nothing but a typed contract. Local = Docker Compose; production = Kubernetes.
Auth modelEmail + password → JWT access/refresh. Org-scoped RBAC (owner · admin · member · agency · client). Agency users are scoped to the projects they are assigned, never the whole org. Scoped API keys for agents.
Data residency / complianceSingle Postgres. OAuth tokens and platform credentials encrypted at rest. Append-only audit log for every security-relevant and every agent-initiated action. GDPR export + delete per workspace.
Target platformsResponsive web, desktop-first, evergreen browsers.

Where this comes from

Three inputs were merged into this brief. See PORTING.md for the feature-by-feature provenance.

SourceContributes
requirements.txtThe actual ask: projects, marketing calendar, all channels, ad benchmarks, AI plan generation, one KPI surface, agency logins, approval workflows, agentic layer, docs-first, Compose→k8s, code-reviewed + mock-tested
brightbean-studio/ (Django)The social side: composer, calendar, approvals, publisher, inbox, analytics, media library, notifications, client portal, credential vault, agent API + MCP endpoint
adwords-adsense/ (Next.js)The paid side: Google Ads integration, campaign wizard, ad asset pipeline, conversion-tracking health, CRM/offline conversions, GA4, autopilot optimisation

Non-negotiables

#RuleWhy
1Every query is org-scoped, and an agency user is additionally project-scoped. A cross-tenant or out-of-scope read returns 404, never a partial result.We invite outside agencies into this system. A leak here is a leak to a third party.
2The web app never opens a database connection. It talks to the API over HTTP and shares only @verjson/contracts.The two are separate deployables; coupling them silently is what makes a split impossible later. Enforced by lint, not convention.
3No campaign or post reaches a third-party platform without a recorded approval. Agent-initiated actions are subject to the same gate.An autopilot that can spend money unattended is a liability, not a feature.
4Every agent action is audited with its actor identity — which key, which agent, what it changed."The AI did it" is not an acceptable answer to a spend question.
5OAuth tokens, platform credentials and API keys are encrypted at rest; keys are stored as hashes only.These grant spend authority on our accounts.
6process.env is read in exactly one file per app (apps/api/src/core/config.ts).Every runtime requirement stays discoverable; nothing configures itself from a hidden corner.
7A feature is not ✅ until VERIFICATION.md names the command that proves it.Otherwise the board lies, and a board that lies is worse than no board.