Nice, this is exactly the sort of thing that cleans up the whole mental model. Below is a single structured view of flows, split into tight tables and mini text diagrams so you can literally walk them in frontend and API console and tick them off. I am describing how it is intended to work, aligned with current models and views. --- ## 0. Key objects involved | Area | Model / Endpoint | Notes | | ------------------------- | ---------------------------------------------------------------------------------------------------------- | --------------------------------------------------- | | Plans | `Plan` model with `price`, `billing_cycle`, `included_credits`, `stripe_*` fields | Defines included monthly credits and Stripe product | | Accounts | `Account` with `plan`, `credits`, `status` (active, trial, suspended, cancelled) | Tenant root, holds current credits | | Subscription | `Subscription` with `current_period_start`, `current_period_end`, `status`, `cancel_at_period_end` | Mirrors Stripe subscription state | | Auth endpoints | `/api/v1/auth/register/`, `/api/v1/auth/login/`, `/api/v1/auth/plans/` allowed without auth | Used before tenant context exists | | Account/Subscription APIs | `AccountsViewSet`, `SubscriptionsViewSet` under `/api/v1/auth/accounts/` and `/api/v1/auth/subscriptions/` | Manage accounts and subscriptions | ### Payment methods (current state) - Options shown: Stripe (placeholder), PayPal (placeholder), Bank Transfer (active). - Stripe/PayPal: keep UI/API options but mark as "coming soon"; no charge attempts yet. - Bank Transfer: the only active path; capture payer details + plan, mark subscription/account pending payment until confirmed manually. - No country-specific rules for any method; keep global defaults. --- ## Flow F1 - Sign up, plan selection, initial credits ### Text diagram ```text Marketing Site → App Auth 1) User chooses plan on pricing page 2) Frontend loads plans from API 3) User fills register form with plan 4) Backend creates User + Account + (optionally) Subscription 5) Account gets plan and starting credits ``` ### Step table | Step | Frontend surface | Backend call | Backend effect (DB) | What to verify | | ---- | ------------------------------- | ----------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | | 1 | Pricing page (public) | `GET /api/v1/auth/plans/` | Reads active `Plan` records via `PlanSerializer` | Plans visible match Plan rows (name, price, billing_cycle, included_credits) | | 2 | Register page shows chosen plan | no call or same `GET /auth/plans/` | - | Selected plan id matches an active Plan row | | 3 | Register submit | `POST /api/v1/auth/register/` (body contains user + account name + `plan_id`) | Creates `User`, `Account(plan=Plan)` and possibly `Subscription` if Stripe created immediately | In DB check: new User, new Account with `plan_id` set and `status` = `trial` or `active` as per your rule; `account.owner` = user | | 4 | Initial credits seed | same register flow or follow up billing hook | Account.credits should be set to `plan.get_effective_credits_per_month()` (from `included_credits` or legacy `credits_per_month`) | After signup, check `Account.credits` equals the plan credits for this plan | | 5 | Payment method choice | UI shows Stripe (placeholder), PayPal (placeholder), Bank Transfer (active) | Record `payment_method` on Account/Subscription. For now only Bank Transfer can be marked paid; Stripe/PayPal stay "coming soon" | UI labels make clear Stripe/PayPal are not yet active; bank transfer selection stores choice and blocks entry until marked paid | For validation you can drive this entirely with Postman and Django admin plus the app UI. --- ## Flow F2 - Login, tenant context, subscription gate This validates that a user can only enter the app when account and plan are healthy. ### Diagram ```text Login form → POST /auth/login/ → Validate password → Ensure User has Account → Ensure Account has active Plan → Issue JWT with account_id → All subsequent requests: → Middleware sets request.account from token → Permissions enforce tenant access ``` ### Step table | Step | Frontend surface | API | Backend logic | Verify | | ---- | -------------------------- | --------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------ | | 1 | Login form | `POST /api/v1/auth/login/` | Finds user by email, checks password | Wrong password gives 401 with "Invalid credentials" | | 2 | Account gate | same login handler | If `user.account` is missing, returns 403 "Account not configured" | Create a user without account and confirm it cannot log in | | 3 | Plan gate | same | If `account.plan` is missing or inactive, returns 402 "Active subscription required" | Mark a plan inactive, try login, see 402 | | 4 | Token issue | same | Generates access and refresh tokens embedding `user_id` and `account_id` in JWT, and returns them with expiry timestamps | Decode JWT and check `account_id` == Account.id | | 5 | Tenant context per request | Any app request after login | `AccountContextMiddleware` reads JWT, fetches User and Account, sets `request.account` and blocks suspended / invalid accounts | Hit a random endpoint, log `request.account.id` and compare to user.account.id | If this flow passes, your base tenancy gate is sound. --- ## Flow F3 - Normal app workflow and credits consumption Think of this as the day to day life of a tenant. ### Subflow F3a - Site and sector inside an account | Step | UI | API | DB objects | Verify tenancy | | ---- | ----------- | -------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | | 1 | Sites page | `GET /api/v1/auth/sites/` | Returns `Site` filtered by `account` via `AccountModelViewSet` and `SiteSerializer` | Confirm only sites with `site.account_id = request.account.id` show | | 2 | Create site | `POST /api/v1/auth/sites/` | Creates `Site` inheriting `account` from `request.account`, counts against `plan.max_sites` | Try creating more sites than `plan.max_sites` and confirm it fails as per your rule | | 3 | Add sectors | `POST /api/v1/auth/sectors/` or site detail → add sector | Sector belongs to Site, and Site belongs to Account. `Site.get_max_sectors_limit()` uses `account.plan.max_industries` to enforce limit | Adding sectors beyond limit returns validation error; each sector.site.account_id matches account | This sets the container for Planner / Writer. --- ### Subflow F3b - Planner credits usage Based on the Planner technical guide. #### Diagram ```text Keywords → Auto cluster → Ideas Each AI operation: - Calculate credits needed - Call CreditService to deduct from Account.credits - Call AICore with account context - Store results (clusters, ideas, tasks) ``` #### Table | Stage | Frontend action | Endpoint | Backend behavior | Credits check | | ------------------ | ------------------------------- | --------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 1. Attach keyword | Keywords page, "Add keyword" | `POST /v1/planner/keywords/` | Validates seed keyword, site, sector, then creates Keyword using `SiteSectorBaseModel` so `account` is set from site | No or minimal credits used, depending on your rule | | 2. Auto cluster | Select keywords, "Auto Cluster" | `POST /v1/planner/keywords/auto_cluster/` | `auto_cluster` loads keywords, instantiates `ClusteringService`, calls `cluster_keywords(keyword_ids, account, sector_id)` | In `ClusteringService` step 2, it "Check credits - Calculate credits needed and deduct credits from account" . Validate that Account.credits decreases accordingly after clustering | | 3. Idea generation | Clusters page, "Generate ideas" | Usually `POST /v1/planner/clusters/generate_ideas/` | Service collects cluster data, calls AI via `AICore(account=request.account)` and creates `ContentIdeas` | Confirm one AI call consumes the planned credits and created ideas are tied to same site/sector/account | To test: run each step for a known account and watch `Account.credits` in admin before and after. --- ### Subflow F3c - Writer credits usage Pattern mirrors Planner. | Stage | Frontend | Endpoint | Backend behavior | Credits check | | -------------------------- | -------------------------------------- | -------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- | | 1. Create task from idea | Ideas page, "Send to Writer" | `POST /v1/writer/tasks/` | Creates Writer Task linked to idea, site, sector, account | No credits yet or minimal | | 2. Generate draft | Writer task detail, "Generate Article" | `POST /v1/writer/content/generate/` or similar | Calls AI via `AICore(account=request.account)`, passes structured prompt, receives content, stores `Content` row | Check that for each generation, Account.credits decreases by the expected credit cost per 1k tokens | | 3. Regenerate sections etc | Same page | e.g. `POST /v1/writer/content/regenerate_section/` | Same AI pipeline, smaller requests | Verify smaller credit deductions and that usage is logged in any Usage / Billing tables if present | `AICore` always initialises with an account and logs cost per request based on tokens and `MODEL_RATES` . You can cross check AI cost vs credit deduction. --- ## Flow F4 - Subscription renewal and monthly or annual cycle The renewal flow is Stripe driven, mirrored in your `Subscription` and `Account` rows. ### Diagram ```text Stripe billing cycle → Stripe renews subscription (card charged) → Stripe sends webhook to backend → Backend updates Subscription row → Backend resets/adjusts Account.credits based on Plan.included_credits ``` ### Step table | Step | Source | Backend effect | What to verify | | | ---- | ------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------- | | 1 | Stripe billing engine / bank transfer confirmation | Stripe (later) marks subscription renewed; today bank transfer confirmation is recorded manually against the subscription | For bank transfer, admin marks payment received and sets renewal date; Stripe dashboard flows remain future work | | | 2 | Stripe webhook or admin update | Webhook (future) or admin update finds `Subscription` by payment method and updates `status`, `current_period_start`, `current_period_end`, `cancel_at_period_end` | Subscription dates/status match payment confirmation; Stripe webhook path is stubbed until integration | | | 3 | Credits reset or top up | Same handler/job | Sets `Account.credits` to `plan.get_effective_credits_per_month()` or adds included credits, depending on your policy | At renewal/confirmation, credits jump to intended value and usage across the new period subtracts from that | | 4 | Account status update | Same billing service | Ensures `Account.status` switches from `trial` or `past_due` back to `active` | After payment, login is allowed again and middleware does not block due to account status | You can simulate this by editing Subscription dates and status in admin if webhooks are not wired yet, then running the same code path as webhook. --- ## Flow F5 - Cancellation and expiry Short but necessary to validate health. | Scenario | Backend state | Effects to validate | | ---------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | | User cancels at period end | `Subscription.cancel_at_period_end=True` while `status` remains `active` until `current_period_end` | App continues working, credits used until period end; no new credits after end date | | Subscription ends or payment fails | `Subscription.status` becomes `canceled` or `past_due`. Billing job sets `Account.status` to `suspended` or `cancelled` | Login returns 402 or 403 according to your rule, Planner/Writer calls fail with proper error and do not consume credits | --- ## How to actually use this You can turn this into a verification pass like: 1. Pick a clean test user and Stripe test customer. 2. Walk F1 then F2 in sequence and confirm all DB fields line up. 3. Run through F3a, F3b, F3c and log credits before and after each AI-heavy action. 4. Simulate renewal and cancellation via Stripe or DB and walk F4 and F5.