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
| Field | Value |
|---|---|
| Product name | Verjson Marketing Studio |
| What it is | An 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. |
| Problem | Marketing 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 user | Verjson'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 metric | A 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 constraint | Backend and frontend are separately deployable and share nothing but a typed contract. Local = Docker Compose; production = Kubernetes. |
| Auth model | Email + 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 / compliance | Single 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 platforms | Responsive 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.
| Source | Contributes |
|---|---|
requirements.txt | The 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
| # | Rule | Why |
|---|---|---|
| 1 | Every 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. |
| 2 | The 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. |
| 3 | No 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. |
| 4 | Every 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. |
| 5 | OAuth tokens, platform credentials and API keys are encrypted at rest; keys are stored as hashes only. | These grant spend authority on our accounts. |
| 6 | process.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. |
| 7 | A 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. |