| Layer | Technology | Detail |
|---|---|---|
| Web Framework | FastAPI (Python 3.11) | Async HTTP + Jinja2 templating |
| ORM | SQLAlchemy 2.x | Sync sessions via get_db() dependency |
| Database | PostgreSQL | Accessed via DATABASE_URL env var (Replit managed) |
| AI Model | OpenAI GPT-4.1-mini | All reports & emails — key: OPENAI_API_KEY |
| CRM Integration | GoHighLevel API v2 | Contact sync — keys: GHL_API_KEY, GHL_LOCATION_ID |
| Twilio API | WhatsApp Business messaging — keys: TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN, TWILIO_WHATSAPP_NUMBER | |
| Scheduling | APScheduler (BackgroundScheduler) | Pipeline tick (1h), backfill (6h), swing scanner (6h), legacy maintenance (6h) |
| Static Files | FastAPI StaticFiles | Mounted at /static |
| Templates | Jinja2 | Directory: templates/ |
| Hosting | Replit → Custom Domain | Auto-run via python run.py; portal at app.thepcma.uk |
| Session Auth | Cookie-based (SESSION_SECRET) | Admin dashboard only |
| File | Purpose |
|---|---|
run.py | Entrypoint — imports FastAPI app and starts uvicorn |
app/main.py | FastAPI app factory — mounts routers, static files, CORS, startup events |
app/database.py | SQLAlchemy engine + get_db() session dependency + Base |
app/models.py | All SQLAlchemy ORM models (20+ tables) |
app/config.py | Environment variable loading, constants (AUTO_SEND_MODE, etc.) |
app/portal_brain.py | All 16 tool engines, ICP detection, CTA selection, GPT prompts, swing functions |
app/portal_swing_scanner.py | APScheduler job — checks every 6 h for swing up/down on all active portal leads |
app/pipeline_engine.py | 7-tier autonomous pipeline: queuing, tier advancement, multi-channel delivery (email+WhatsApp), conversion detection |
app/pipeline_templates.py | ICP-specific email/SMS templates for all 7 tiers, TIER_WAIT_DAYS, ICP_TOOL_MAP |
app/twilio_whatsapp.py | Twilio WhatsApp send module — phone normalisation, message delivery |
app/short_links.py | Short link system — generates human-readable codes (/portal/go/strategy-wasim), click tracking |
app/safety.py | Rate limiting — 48h cooldown, daily caps, opt-out checks, message logging |
app/scoring.py | Contact scoring engine — profile + engagement points → Bronze/Silver/Gold/Platinum tiers |
app/activity_log.py | Centralised event logging — all engines write to activity_log table |
app/scheduler.py | APScheduler — pipeline tick (1h), backfill (6h), portal swing (6h), legacy maintenance (6h) |
app/nurture_engine.py | 8-step nurture sequence engine — auto-enrollment, step advancement, multi-channel delivery |
app/nurture_content.py | 71-row nurture content library — ICP-segmented templates for email/SMS/WhatsApp |
app/ascension_engine.py | Ascension sequence — post-conversion Elite Partner engagement |
app/ascension_content.py | Ascension content seeds — 9-row config |
app/path_e_engine.py | Path E dormant nurture — 4-phase reactivation (nudge→drip→social proof→quarterly) |
app/wave_engine.py | Bulk marketing waves — batch scheduling, delivery rate tracking |
app/wave_config.py | Wave config — 14 rows: batch sizes, thresholds, auto-scaling rules |
app/ai.py | LLM interface — GPT-4.1-mini content generation for emails and reports |
app/ghl.py | GoHighLevel API layer — contact CRUD, email sends, tag management |
app/audit_generator.py | THIS FILE — generates the live technical audit document at request time |
app/routers/portal.py | All public portal routes: registration, dashboard, 16 tool endpoints, one-click entry, offer doc, VSL, webinar, apply |
app/routers/dashboard.py | Admin dashboard routes: main view, stats API, portal registrations, portal metrics, audit download |
app/routers/webhook.py | Webhook receiver — GHL events, pipeline intake, Twilio status/inbound callbacks |
app/routers/contacts.py | Internal CRM contact CRUD, tagging, scoring |
app/routers/messages.py | Message send/receive endpoints (email, SMS, WhatsApp) |
app/routers/nurture.py | Nurture sequence management endpoints |
app/routers/ascension.py | Ascension sequence — signal detection, score-triggered emails |
app/routers/sync.py | GHL sync triggers and monitoring |
app/routers/waves.py | Wave campaign configuration and approval |
app/routers/ops.py | Operational tools, system health, maintenance |
app/routers/invoices.py | Invoice CRUD, payment recording, PDF export |
app/routers/test_harness.py | E2E simulation and test harness |
app/routers/journey_pdf.py | Contact journey PDF report generator |
| Template | Route | Notes |
|---|---|---|
templates/dashboard.html | GET / | Admin dashboard main view |
templates/portal_metrics.html | GET /portal-metrics | Admin portal KPI/funnel metrics |
templates/portal/register.html | GET /portal/register | Public registration form |
templates/portal/enter_confirm.html | GET /portal/enter/{{token}} | Pipeline one-click entry — confirmation page (anti-bot: GET→POST) |
templates/portal/dashboard.html | GET /portal/dashboard | Lead's personalised portal dashboard |
templates/portal/tools/[slug].html | GET /portal/tools/{{slug}} | Individual tool form pages (×16) |
templates/portal/tool_result.html | GET /portal/tools/{{slug}}/result/{{id}} | Tool report + CTA panel |
templates/portal/offer_doc.html | GET /portal/offer-doc | GPT-generated personalised offer document |
templates/portal/vsl.html | GET /portal/vsl | ICP-personalised VSL page (6 variants) |
templates/portal/webinar.html | GET /portal/webinar | Evergreen webinar embed page |
templates/portal/apply.html | GET /portal/apply | Strategy call booking / application page |
templates/portal/live_webinar.html | GET /portal/live-webinar | Monthly live webinar registration page |
templates/portal/stage2_panel.html | Partial | Stage 2 dashboard panel |
templates/portal/stage3_panel.html | Partial | Stage 3 dashboard panel |
templates/portal/stage4_panel.html | Partial | Stage 4 dashboard panel (offer viewed) |
templates/portal/contact.html | GET /portal/contact | Contact/enquiry form |
| Table | Key Columns | Purpose |
|---|---|---|
portal_leads | id, token (unique 64-char), name, email (unique), phone, icp_type, source, path (A–E), path_state, stage (1–4), contact_id (FK), ghl_contact_id, offer_viewed, offer_view_count, application_clicked, vsl_shown, webinar_registered, live_webinar_registered, offer_doc_generated, last_active_at, swing_last_checked_at | Primary portal lead record — one per registrant |
tool_completions | id, portal_lead_id (FK), tool_id (T01–T16), tool_name, inputs (JSON), report_html, report_summary, recommended_strategy, deal_viable, profit_margin_pct, cta_primary_label/url, cta_alt_label/url, email_sent, completed_at | One row per tool submission per lead |
portal_events | id, portal_lead_id (FK), event_type (register/tool_start/tool_complete/cta_click/offer_view), tool_id, data (JSON), created_at | Append-only event log for every lead action |
| Table | Key Columns | Purpose |
|---|---|---|
contacts | id, name, email, phone, icp_type, score, tier, status, ghl_contact_id, portal_registered, offer_viewed, last_message_at | Main CRM contact record synced from GHL |
messages | id, contact_id (FK), channel, subject, body, sent_at, opened, replied, pending_approval | All messages sent/received per contact |
sequences | id, contact_id (FK, unique), current_wave, current_angle, next_send_date | Wave/angle position per contact |
daily_stats | id, date, sms_sent, emails_sent, whatsapp_sent, replies_received | Daily send volume counters |
human_queue | id, contact_id (FK), incoming_message, suggested_reply, objection_type, resolved | Escalated conversations |
wave_approvals | id, wave_number, status, total_messages, approved_by, approved_at, sent_at | Wave campaign approval workflow |
nurture_sequences | id, contact_id (FK), source, icp, status, current_step, total_steps, next_send_at | 8-step nurture journey |
ascension_sequences | id, contact_id (FK), trigger_score, status, current_step (max 3), step_1/2/3_sent_at | High-intent ascension journey |
activity_log | id, event_type, engine, contact_id, detail, metadata (JSON), created_at (indexed) | System-wide activity stream |
invoices | id, invoice_number (unique), contact_id, pod, status, subtotal, vat_amount, total_amount, amount_paid | Invoice CRUD |
app_settings | id, key (unique), value | Key-value runtime config (auto_send_mode, webinar settings, etc.) |
contacts table)Pipeline state is stored directly on the contacts table (not a separate table):
| Column | Type | Purpose |
|---|---|---|
pipeline_tier | Integer (1–7), nullable, indexed | Current tier in the 7-tier journey |
pipeline_status | String(30), nullable, indexed | Status: queued, sent, registered, converted, paused, send_failed |
pipeline_entered_at | DateTime, nullable | When contact was enrolled in pipeline |
pipeline_last_tier_change | DateTime, nullable | Last tier advancement timestamp |
pipeline_icp | String(50), nullable | Assigned ICP for pipeline template selection |
pipeline_assigned_tool | String(10), nullable | Tool ID assigned for Tier 1 |
pipeline_source | String(50), nullable | Source tracking (e.g., ghl_webhook) |
pipeline_campaign_source | String(100), nullable | Campaign source tracking |
pipeline_contact_token | String(64), unique, indexed | 32-char urlsafe token for one-click URLs |
| Column | Type | Purpose |
|---|---|---|
id | Integer PK | Auto-increment |
code | String(80), unique, indexed | Human-readable short code (e.g., strategy-wasim) |
destination_url | Text | Full target URL to redirect to |
contact_id | Integer FK → contacts | Contact this link was generated for |
created_at | DateTime | Link creation timestamp |
clicked_at | DateTime, nullable | Timestamp of first click (null = never clicked) |
| Method | Path | Auth | Template / Response | Description |
|---|---|---|---|---|
| GET | /portal/register | No | portal/register.html | Shows registration form with profile/source fields |
| POST | /portal/register | No | Redirect → /portal/dashboard?token=… | Creates PortalLead, assigns ICP+path, syncs to GHL, fires welcome email |
| GET | /portal/enter/{{token}} | Pipeline token | portal/enter_confirm.html OR auto-redirect | One-click pipeline entry — already-registered leads redirect to tier-correct destination; new leads see bot-proof confirmation page |
| POST | /portal/enter/{{token}} | Pipeline token | Redirect → tool / VSL / webinar / apply / dashboard | Performs actual registration and redirects based on next or tool param (T1→tool, T3→VSL, T4→webinar, T5→apply) |
| GET | /portal/go/{{code}} | Short link code | Redirect 302 → destination URL | Short link redirect — records first click timestamp in clicked_at |
| GET | /portal/dashboard | Token (query) | portal/dashboard.html | Personalised portal hub — tool cards, stage panels, path badge, progress |
| GET | /portal/tools/{{slug}} | Token (query) | portal/tools/{{slug}}.html | Tool input form for one of the current tools |
| POST | /portal/tools/{{slug}}/submit | Token (query) | Redirect → result page | Processes tool, saves ToolCompletion, fires email, logs event, updates stage/path |
| GET | /portal/tools/{{slug}}/result/{{id}} | Token (query) | portal/tool_result.html | Shows GPT report, structured calc data, 3-CTA panel |
| GET | /portal/offer-doc | Token (query) | portal/offer_doc.html | GPT-generated personalised offer document; sets offer_viewed=True |
| GET | /portal/vsl | Token (query) | portal/vsl.html | ICP-personalised VSL page; sets vsl_shown field |
| GET | /portal/webinar | Token (query) | portal/webinar.html | Evergreen webinar embed; sets webinar_registered=True |
| GET | /portal/apply | Token (query) | portal/apply.html | Strategy call / application page |
| GET | /portal/live-webinar | Token (query) | portal/live_webinar.html | Monthly live webinar registration page |
| POST | /portal/live-webinar/register | Token (query) | JSON {success} | Sets live_webinar_registered=True, fires GHL webinar confirmation email |
| POST | /portal/event | Token (query) | JSON {ok} | Records a PortalEvent, updates last_active_at |
| GET | /portal/contact | Token (query) | portal/contact.html | Contact/enquiry form |
| Method | Path | Auth | Template / Response | Description |
|---|---|---|---|---|
| GET | / | Session | dashboard.html | Admin dashboard main view with CRM stats |
| GET | /api/stats | Session | JSON | Live stats JSON for dashboard polling |
| GET | /portal-registrations | Session | HTML fragment | List of portal leads with stage/path/tools |
| GET | /portal-metrics | Session | portal_metrics.html | 6 KPI cards, 7-step funnel, path distribution, ICP breakdown, top tools chart, activity feed |
| GET | /portal-audit/download | Session | text/html (download) | Downloads THIS dynamically generated technical audit document |
| POST | /settings/auto-send | Session | JSON | Toggle auto_send_mode on/off |
| POST | /sync-ghl | Session | JSON | Trigger manual GHL contact sync |
| GET/POST | /wave/* | Session | Various | Wave preview, approval, and send |
| GET/POST | /api/portal/live-webinar | Session | JSON | Save live webinar settings to AppSettings |
| GET | /api/pipeline-stats | Session | JSON | Pipeline funnel KPIs: queued/sent/registered/converted counts, tier breakdown, ICP stats |
| POST | /api/pipeline/backfill | Session | JSON | Trigger manual pipeline backfill of existing GHL contacts |
| Method | Path | Auth | Description |
|---|---|---|---|
| POST | /webhook/ghl | None | GHL general events — InboundMessage, ContactUpdated, OfferViewed, DiscoveryCallClicked, bounce |
| POST | /webhook/ghl/new-contact | GHL_WEBHOOK_SECRET | Pipeline intake — creates/updates contact, enrolls in pipeline engine |
| POST | /webhook/new-lead | None | New lead processing — sets nurture eligibility, detects ICP/Source from tags |
| POST | /webhook/bridge-to-portal | None | Marks contact as portal_registered, logs if during active nurture |
| POST | /webhook/twilio/status | None | Twilio delivery status callbacks (sent, delivered, failed, undelivered) |
| POST | /webhook/twilio/inbound | None | Twilio inbound WhatsApp messages — logs activity, marks contact replied |
ICP is assigned at registration from a single dropdown (profile field) and is immutable thereafter.
There are currently 4 ICP types.
| Form Value | ICP Code | ICP Label | Core Pain | PCMA Promise |
|---|---|---|---|---|
new | Asp-Dev | Aspiring Developer | I don't know what I don't know about property development. | We will guide you through your first project with a full professional team behind you. |
landlord | Sc-LL | Scaling Landlord | My portfolio is working hard but not growing fast enough. | Your existing properties are worth more than you think — if you convert instead of refurbish. |
busy | TP-Pro | Time-Poor Professional | I have the capital but not the time to manage a development. | We do everything. You approve the decisions. The project runs without you. |
experienced | Ac-Proj | Active Project | I need a professional team I can trust with my capital. | We become your outsourced development department. |
On registration the Brain selects the first recommended tool based solely on ICP:
| ICP Code | ICP Label | First Tool ID | Tool Name | Tool Tagline |
|---|---|---|---|---|
| Asp-Dev | Aspiring Developer | T02 | Strategy Selector | Find Your Best Development Strategy |
| Sc-LL | Scaling Landlord | T01 | RRA Compliance Checker | Know Your Renters' Rights Act Obligations |
| TP-Pro | Time-Poor Professional | T05 | Deal Analyser | Get Your Deal Feasibility Report |
| Ac-Proj | Active Project | T05 | Deal Analyser | Get Your Deal Feasibility Report |
After every tool result, three CTAs are shown: Primary, Alternative, and Soft Exit.
The function select_cta(icp, tools_done, source) selects them:
| Field | Type | Triggers Change |
|---|---|---|
stage | Integer 1–4 | Stage 1: registration. Stage 2: 1 tool. Stage 3: 2+ tools. Stage 4: offer viewed or 4+ tools. |
path | A–E | Set on registration by assign_path(); updated by swing scanner every 6 h. 5 paths defined. |
offer_viewed | Boolean | Set when lead visits /portal/offer-doc (triggers Stage 4 and Swing Up to Path A). |
vsl_shown | String (icp key) | Set when lead visits /portal/vsl/{{icp_key}}. |
webinar_registered | Boolean | Set when lead visits /portal/evergreen-webinar. |
live_webinar_registered | Boolean | Set when lead POSTs to /portal/live-webinar/register. |
last_active_at | DateTime | Updated on every portal event (tool submit, CTA click, page visit). |
swing_last_checked_at | DateTime | Updated by swing scanner after each check run. |
email_sent (ToolCompletion) | Boolean | Set after tool completion follow-up email is dispatched (fires once per completion). |
There are currently 16 tools in the portal. This section is generated from the live TOOLS dictionary and the narrative data in app/audit_generator.py.
Know Your Renters' Rights Act Obligations
Answer 8 questions about your rental property and get a personalised compliance action plan — traffic-light status, estimated costs, and a clear timeline of what to do by when.
| Field | Type | Options / Notes |
|---|---|---|
| Property type | Select | HMO | Single let | Flat block | Mixed use | Holiday let | Student let |
| How many tenants/rooms? | Select | 1–2 | 3–4 | 5–6 | 7+ |
| When was it built? | Select | Pre-1930 | 1930–1960 | 1960–1990 | 1990–2000 | Post-2000 |
| Current EPC rating | Select | A | B | C | D | E | F | G | Unknown |
| Is it currently let? | Select | Yes | No | Partially |
| Deposit scheme registered? | Select | Yes | No | Not sure |
| Gas Safety cert current? | Select | Yes | No | Exempt |
| EICR in last 5 years? | Select | Yes | No | Not sure |
Traffic-light scoring engine — each answer adds risk points. score≥70→RED (HIGH RISK), 40–69→AMBER, <40→GREEN. Hardcoded 10-area compliance check covering Gas Safety, EICR, EPC E, Smoke/CO alarms, Deposit Protection, Tenancy Structure, PRS Database, Ombudsman, EPC C by 2030, Decent Homes Standard by 2035. Cross-pollination: score≥70→T03 | score 40–69→T08 | score<40→T02.
Find Your Best Development Strategy
Answer 5 quick questions and get a personalised strategy recommendation backed by your specific goals, budget, and situation.
| Field | Type | Options / Notes |
|---|---|---|
| Available capital | Select | Under £30k | £30k–£75k | £75k–£150k | £150k–£300k | £300k+ |
| Development experience | Select | Beginner | Landlord | Some experience (1–3) | Experienced (4+) | Professional |
| Primary goal | Select | Generate capital | Build cashflow | Replace income | Build a business | Optimise portfolio |
| Time availability | Select | Under 5 hrs/week | 5–15 hrs | 15–30 hrs | Full-time |
| Risk appetite | Select | Conservative | Moderate | Aggressive | Analytical |
6-strategy scoring matrix with 5 weighted dimensions: Capital(30%), Experience(25%), Goal(20%), Time(15%), Risk(10%). Strategies: C2R, HMO, Title Split, House→Flats, Heritage, New Build. Each strategy scored 0–100 per dimension from hardcoded matrices. Top 3 strategies shown. Cross-pollination: all strategies→T05 (Deal Analyser).
See Your Energy Savings Breakdown
Enter 6 details about your property and get a personalised EPC upgrade plan — specific measures, estimated costs, EPC band improvements, and the financial ROI.
| Field | Type | Options / Notes |
|---|---|---|
| Property type | Select | Semi-detached | Terraced | Detached | Flat (PB) | Flat (converted) | Bungalow | HMO | Commercial | Mixed use |
| Built date | Select | Pre-1930 | 1930–1960 | 1960–1990 | Post-1990 | Post-2000 |
| Current EPC rating | Select | A–G |
| Target EPC rating | Select | A–C |
| Monthly rent (£) | Number | |
| Property size | Select | Small (<60sqm) | Medium (60–90) | Large (90–150) | Very large (>150) |
36-measure EPC database with band-conditional eligibility filter. Measures sorted by points_gain DESC (greedy selection) until target band reached. total_cost = sum of selected measure cost_mid values. ROI = annual_savings / total_cost. Cross-pollination: EPC≤D + cost>£15k→T01 | commercial/mixed→T15 | default→T12.
Get Your Project Cost Breakdown
Enter 7 details about your project and get a comprehensive cost breakdown across 6 categories — acquisition, construction, professional fees, statutory costs, finance, and contingency.
| Field | Type | Options / Notes |
|---|---|---|
| Project type | Select | Light refurb | Heavy refurb | HMO | House to flats | C2R | Barn | Listed | New build (single) | New build (multi) | Loft/Extension |
| Gross floor area (sqm) | Number | |
| Number of units | Number | 1–20 |
| Location type | Select | Prime London | Central London | South East | Midlands/North/Wales | Scotland/NI |
| Specification level | Select | Budget | Mid-range | High spec | Luxury |
| Purchase price (£) | Number | |
| Finance type | Select | Cash | Bridging | Development finance | JV/investor |
6-category cost breakdown: Acquisition, Construction (build_cost × sqm × location_mult × spec_mult), Professional Fees (12%), Statutory Costs, Finance, Contingency (listed/barn=15%, c2r/new_build=12%, others=10%). Location multipliers: prime_london=1.8×, central=1.5×, south_east=1.2×, midlands=1.0×, scotland=0.9×. Spec multipliers: budget=0.85×, mid=1.0×, high=1.15×, luxury=1.35×. Cross-pollination: cost>£1m or new_build→T10 | htf/c2r→T06 | default→T05.
Get Your Deal Feasibility Report
Enter your deal numbers and get an instant profit/loss analysis, ROI assessment, and viability verdict — the same analysis our QS runs on every deal.
| Field | Type | Options / Notes |
|---|---|---|
| Purchase price (£) | Number | |
| Total build cost (£) | Number | |
| Professional fees (£) | Number | 0 if unknown |
| Finance costs (£) | Number | 0 if unknown |
| Expected GDV (£) | Number | |
| Number of units | Number | Default 1 |
| Strategy type | Select | HMO | House to flats | C2R | New build | Refurb | Title split | Other |
profit = gdv - total_costs. margin_pct = profit/gdv×100. roi_pct = profit/total_costs×100. Verdict: margin≥20%→STRONG, 15–20%→VIABLE, 10–15%→MARGINAL, 5–10%→WEAK, <5%→NOT VIABLE. Stores: deal_viable = margin≥15, profit_margin_pct = margin_pct. Cross-pollination: gdv>£500k→T06 | margin<15%→T04 | default→T02.
See Your Best Unit Mix
Enter your building's size and location and see the top 3 unit mix options ranked by GDV — using NDSS space standards so you know every option is buildable.
| Field | Type | Options / Notes |
|---|---|---|
| Gross floor area (sqm) | Number | |
| Number of floors | Select | 1–6 | 7+ |
| Location | Select | Prime city | City centre | Town centre | Suburban (good) | Suburban (avg) | Rural town |
| Goal | Select | Maximise sale value | Maximise rental income | Mix of both |
| Build type | Select | House to flats | C2R | New build | Barn conversion |
NDSS minimum sizes enforced (studio=37sqm, 1bed_2p=50sqm, 2bed_4p=70sqm, 3bed_5p=86sqm). NLA = gross_area × 0.85. Regional GDV values (£/sqm): prime_city=6500, city=4500, town=3200, suburban_good=3800, suburban_avg=3000, rural=2500. 3 unit mix candidates generated, ranked by GDV. Cross-pollination: total_cost>£500k→T10 | default→T05.
Get Your Site Development Potential
Enter a postcode and answer 7 questions about any property. Get a development potential report covering all applicable strategies, planning routes, and value uplift estimates.
| Field | Type | Options / Notes |
|---|---|---|
| Postcode | Text | UK postcode for region detection |
| Property type | Select | House (detached) | House (semi) | House (terraced) | Bungalow | Flat | Commercial | Mixed use | Agricultural |
| Building age | Select | Pre-1930 | 1930–1960 | 1960–1990 | Post-1990 |
| Floor area (sqm) | Number | |
| Number of floors | Select | 1 | 2 | 3 | 4+ |
| Planning constraints | Multi-select | Conservation area | Listed | Flood zone | Article 4 | Green belt | None |
| Owner goal | Select | Maximise profit | Create income | Planning uplift | Explore options |
8 development strategies assessed (HMO, House→Flats, C2R, New build, Loft, Extension, Title split, Barn). Each scored 0–100 from property type, floor area, constraint penalties, age bonuses. Conservation:-20, listed:-40, flood:-15, article_4 HMO:-50. eligible = strategies with score≥40, sorted DESC. Cross-pollination: top>70 + old→T14 | top=c2r→T15 | default→T04.
Get Your Portfolio Optimisation Plan
Enter details for up to 5 properties. Get a performance ranking by yield and equity efficiency, plus a strategic recommendation for each property — hold, improve, convert, or sell.
| Field | Type | Options / Notes |
|---|---|---|
| Property name/label | Text | Per property (up to 5) |
| Property type | Select | HMO | Commercial | Detached | Semi | Terraced | Flat (PB) | Flat (converted) | Mixed |
| Current value (£) | Number | Per property |
| Outstanding mortgage (£) | Number | Per property |
| Monthly rent (£) | Number | Per property |
| Annual costs (£) | Number | Auto 25% of rent if blank |
| EPC rating | Select | A–G | Per property |
| Building age | Select | Pre-1930 | 1930–1960 | Post-1960 |
Per property: gross_yield=rent/value, net_yield=net_income/value, equity_eff=net_income/equity. Compliance risk: EPC A/B=0, C=1, D=2, E=3, F/G=5 + age +1. Conversion potential 0–10: commercial=8, detached=7, semi=6, terraced=5, HMO=2, flat_pb=1. Recommendation: yield≥7%+risk≤2→HOLD | conv≥6+equity>50k→CONVERT | risk≥4→IMPROVE | yield<3%→SELL. Cross-pollination: worst=CONVERT→T07 | IMPROVE/EPC-F/G→T03 | SELL→T12 | default→T01.
See Your Project Timeline
Answer 6 questions about your project. Get a phase-by-phase timeline with best/likely/worst-case scenarios and the financial cost of each month of delay.
| Field | Type | Options / Notes |
|---|---|---|
| Project type | Select | Light refurb | Heavy refurb | HMO | House to flats | C2R | Barn | Listed | New build (single/multi) | Loft/Extension |
| Current status | Select | Just an idea | Owned no design | Design done | Planning submitted | Planning approved | Construction started |
| Planning needed | Select | Permitted development | Prior approval | Full planning | None |
| Complications | Multi-select | Asbestos | Party wall | Listed | Contamination | Structural |
| Finance type | Select | Cash | Bridging (£4,500/month) | Dev finance (£6,000/month) |
7-phase timeline engine: Pre-Design, Design, Planning, BC&Tender, Construction, Completion, Sale/Let. Base months per project type. PD→planning-4m, prior_approval→planning-2m. Complication adds: asbestos+2m, party_wall+1m, listed+4m, contamination+3m, structural+2m. Per phase: best=base×0.8, likely=base+extras, worst=base×1.35+extras. Overrun cost = (worst-likely) × monthly_finance. Cross-pollination: planning_months>6→T16 | construction>8m→T14 | default→T04.
Check Your Funding Readiness
Answer 8 questions that mirror what development finance lenders assess. Get a readiness score across 5 categories and specific actions to close each gap.
| Field | Type | Options / Notes |
|---|---|---|
| Development experience | Select | None | 1 project | 2–3 | 4–5 | 6+ |
| Qualifications held | Multi-select | Formal qualification | Construction exp | Investment exp | Business exp | Finance exp |
| Expected profit margin | Select | Not calculated | Under 10% | 10–15% | 15–20% | 20%+ |
| Deal size | Select | Under £500k | £500k–£1m | £1m–£2m | £2m+ |
| Professional team in place | Multi-select | Architect | QS | PM | Solicitor | Planning Consultant |
| Exit strategy | Select | Uncertain | Refinance | Let | Sell (open) | Sell (agreed) |
| Equity contribution | Select | Under 20% | 20–25% | 25–35% | 35%+ |
| Credit history | Select | Clean | Minor issues | CCJ | Previous bankruptcy |
5 categories: Experience(25pts), Deal Quality(25pts), Team(20pts), Exit(15pts), Finances(15pts). Verdict: 80+→READY, 60–79→NEARLY READY, 40–59→WORK TO DO, <40→NOT YET. Biggest gap (largest pts shortfall) drives cross-pollination: Deal Quality→T05 | Team→T02 | Exit→T07 | score>80→T05 | default→T04.
See Your Lease Extension Savings
Enter 6 details about your leasehold property. Get an estimated extension premium under current law and the incoming 2024 reforms — and see what acting now could save you.
| Field | Type | Options / Notes |
|---|---|---|
| Years remaining on lease | Number | 1–999 |
| Ground rent (£/year) | Number | |
| Ground rent escalation | Select | Fixed | Doubles every 10 years | Doubles every 25 years |
| Property type | Select | Detached | Semi | Terraced | Flat (PB) | Flat (converted) |
| Current property value (£) | Number | |
| Goal | Select | Extend lease | Enfranchise | Understand options |
1993 Act: premium = term_value + reversion_value + marriage_value (if <80 years). Deferment rate = 5%. Relativity table: 100yr=97%, 80yr=88%, 60yr=68%, 40yr=47%, 20yr=28%. 2024 Reform: marriage value abolished → premium = term + reversion only. new_lease = lease_years + 90. value_uplift = after_ext - current. Cross-pollination: premium>30k + house→T13 | premium>30k (other)→T07 | default→T08.
Should You Renovate or Convert?
Enter 8 details about a property. Get a side-by-side comparison of renovation vs conversion — cost, timeline, end value, and profit for each option.
| Field | Type | Options / Notes |
|---|---|---|
| Property type | Select | Terraced | Semi | Detached | Bungalow | Commercial | Mixed use | Flat (converted) |
| Floor area (sqm) | Number | |
| Number of floors | Select | 1 | 2 | 3 | 4+ |
| Current condition | Select | Excellent | Good | Fair | Poor | Very poor |
| Current value (£) | Number | |
| Goal | Select | Maximise profit | Create income | Minimise hassle |
| Constraints | Multi-select | Listed | Leasehold | Conservation | Flood zone |
| Finance | Select | Cash | Bridging | Dev finance |
Renovation: condition_mult (excellent=0.5 → very_poor=1.5) × refurb_cost_per_sqm. Uplift %: very_poor=25%, poor=20%, fair=15%, good=10%, excellent=5%. Conversion viable if: floor_area≥80 AND floors≥2 AND not flat AND not listed. Conv costs/sqm: terraced=£1,200–£2,000, semi=£1,100–£1,900, detached=£1,000–£1,800. units=max(2, area//60). unit_value = current×0.7. conv_value = units × unit_value × 1.05. winner = convert if conv_roi > refurb_roi else renovate. Cross-pollination: winner=convert→T04 | listed→T16 | default→T03.
Get Your Title Split Viability Report
Answer 7 questions about your property. Get a 5-criteria viability assessment, estimated value uplift from splitting, and exactly what needs to happen to make it work.
| Field | Type | Options / Notes |
|---|---|---|
| Property type | Select | Terraced | Semi | Detached | Bungalow | Commercial | Flat (converted) |
| Ownership type | Select | Freehold | Leasehold | Share of freehold |
| Current configuration | Select | Single dwelling | Already converted | Informally split |
| Floor area (sqm) | Number | |
| Current value (£) | Number | |
| Planning status | Select | None | Full planning | LDC issued | PD confirmed |
| Building regs status | Select | Unknown | Full sign-off | Regularised cert |
5 viability criteria: Freehold | Physical Separation (≥100sqm or already converted) | Planning Consent | Building Regs | Separate Council Tax. Verdict: 5/5→FULLY VIABLE | 3+/5+freehold→VIABLE WITH WORK | 2+/5+freehold→CONDITIONAL | else→NOT YET. Uplift: units=max(2, area//65), unit_value=current×0.65, legal_costs=3000+(units×1000). Cross-pollination: no planning→T16 | not freehold→T11 | viable→T06 | default→T05.
Scan Your Project Risks
Answer 7 questions about your project. Get a prioritised risk register filtered from 120+ development risks, a heat map, mitigation actions, and a recommended contingency budget.
| Field | Type | Options / Notes |
|---|---|---|
| Project type | Select | House to flats | HMO | C2R | Barn | Listed | New build | Refurb (light/heavy) |
| Building age | Select | Pre-1930 | 1930–1960 | 1960–1990 | Post-1990 |
| Location constraints | Multi-select | Conservation | Listed | Flood zone | None |
| Planning status | Select | Not applied | Applied | Approved | PD confirmed |
| Finance type | Select | Cash | Bridging | Dev finance |
| Project manager in place | Select | Yes | No |
| Previous issues | Multi-select | Asbestos | Party wall | Structural | Contractor issue | Planning refusal |
30-risk database (R01–R30) across 6 categories: Planning, Design, Construction, Financial, Legal, Market. Each risk: severity (deal_killer/major/moderate/minor) × likelihood (high/medium/low) = risk_score. SEV: deal_killer=4, major=3, moderate=2, minor=1. LIKE: high=3, medium=2, low=1. has_pm AND Construction/Financial → downgrade likelihood. planning_approved AND Planning → downgrade likelihood. Contingency: avg_score≥8→20%, ≥6→15%, ≥4→12%, else 10%. Cross-pollination: dominant_cat=Financial→T10 | Planning→T16 | default→T09.
Score Your C2R Conversion Potential
Answer 8 questions about your commercial building. Get a Class MA eligibility assessment, estimated GDV, conversion score, and a full profit/loss analysis.
| Field | Type | Options / Notes |
|---|---|---|
| Duration in Class E use | Select | Under 2 years | Over 2 years |
| Use class | Select | Class E (offices/retail/café) | Class B2/B8 (industrial) | Other |
| Constraints | Multi-select | Listed | Protected area (AONB/NP) | Article 4 | Conservation | Flood | None |
| Floor area (sqm) | Number | |
| Number of floors | Select | 1 | 2–3 | 4–6 | 7+ |
| Building condition | Select | Good | Fair | Poor | Very poor |
| Location type | Select | Prime city | City centre | Town centre | Suburban (good/avg) | Rural town |
| Purchase price (£) | Number |
Class MA eligibility: under_2yrs/listed/protected_area/article_4 → NOT ELIGIBLE. Penalties: conservation-20, flood-15, not_class_e-30, 7+floors-20. NLA=area×0.85. build_cost=1500×condition_mult×NLA. prof_fees=12%. contingency=12%. GDV=res_per_sqm×NLA (regional: prime_city=6500, city=4500, town=3200). conv_score = eligibility_score + margin bonus (≥20%→+20, ≥10%→+10, <0%→-30). Cross-pollination: eligibility=0→T12 | margin≥15%→T06 | default→T04.
Decode Your Planning Route
Answer 7 questions about your project. Get a clear recommendation on which planning route applies (PD/Prior Approval/Full Planning/LDC), with timeline, cost, and success rate.
| Field | Type | Options / Notes |
|---|---|---|
| Project type | Select | House to flats | HMO | C2R | Barn/agricultural | New build | Extension | Refurb | Listed building works |
| Property type | Select | House (det/semi/terraced) | Commercial | Mixed use | Agricultural | Flat |
| Planning constraints | Multi-select | Listed | Conservation | Article 4 | AONB/NP | Flood | None |
| Change of use required? | Select | Yes | No | Not sure |
| Planning history | Select | None | Previous refusal | Previous approval | LDC issued |
| Target use | Select | Residential | HMO | Mixed | Commercial | Agricultural |
Decision tree recommends up to 3 planning routes from 7 available: PD (free), Householder (£258–2k, 83% success), Prior Approval Class MA/Q (£120, 78%), Full Planning (£578–15k, 74%), LDC (£258–500, 90%), Class Q (£120, 65%), LBC (free, 55%). listed→always LBC | barn→Class Q | c2r not constrained→Prior Approval + Full Planning | htf/hmo/change_of_use→Full Planning | extension→Householder or PD. Cross-pollination: listed→T14 | change_of_use→T15 | default→T09.
| Event Type | Trigger | Data Captured | What Happens Next |
|---|---|---|---|
register | POST /portal/register | name, email, phone, icp, source, path | GHL contact created, welcome email sent, stage=1 set |
tool_start | GET /portal/tools/{{slug}} with token | tool_id, tool_name | last_active_at updated |
tool_complete | POST /portal/tools/{{slug}}/submit | tool_id, tool_name, report_summary | ToolCompletion created, stage updated, email sent (once), swing check triggered |
cta_click | POST /portal/event | cta_type (primary/alternative/soft_exit), url | last_active_at updated, GHL tag applied if offer URL clicked |
offer_view | GET /portal/offer-doc | offer_view_count++ | offer_viewed=True, stage set to 4, swing up to Path A triggered |
vsl_view | GET /portal/vsl/{{icp_key}} | icp_key | vsl_shown set, path recalculated |
webinar_register | GET /portal/evergreen-webinar | — | webinar_registered=True |
live_webinar_register | POST /portal/live-webinar/register | — | live_webinar_registered=True, GHL email sent |
| Sequence | Trigger | GPT? | Key Content |
|---|---|---|---|
| Welcome email | Registration | No | Fixed template via GHL API — from Zoya Abid <resources@thepcma.uk> |
| Tool completion follow-up | Each tool (fires once — email_sent flag) | Yes | 4 paragraphs referencing actual result summary, recommends next tool. Max 200 words. |
| Swing Up email | Swing Up event (path → A) | Yes | 3 paragraphs, CTA: apply for fit check at OFFER_URL. Max 160 words. |
| Swing Down / Re-engagement | 7-day inactivity detected by scanner | Yes | 3 paragraphs, low-friction CTA. Max 130 words. |
| Live Webinar Registration | POST /portal/live-webinar/register | No | Fixed GHL template with webinar date/link |
| Offer Doc | GET /portal/offer-doc (first visit) | Yes | 6-section HTML document, ICP-personalised, ~450 words, £897/month |
| Stage | Condition | Dashboard Panel Unlocked |
|---|---|---|
| 1 | Registration complete, 0 tools done | Tool recommendation card only |
| 2 | 1 tool completed | Stage 2 panel: second tool recommendation, VSL prompt |
| 3 | 2+ tools completed | Stage 3 panel: Offer Doc CTA, webinar prompt, partnership benefits |
| 4 | Offer viewed OR 4+ tools completed | Stage 4 panel: direct application CTA, discovery call button |
| Path | Label | Emoji | Momentum | Description Shown to Lead |
|---|---|---|---|---|
| A | Fast Track | ⚡ | accelerating | You're ready — the next step is the mutual fit check. |
| B | Video First | 🎬 | active | A strategy deep-dive video is your next recommended step. |
| C | Masterclass First | 🎓 | active | The Integration Masterclass is your next recommended step. |
| D | Diagnosis First | 🔍 | active | Complete one more diagnostic tool to deepen your profile. |
| E | Re-engagement | 🔔 | stalled | Pick up where you left off — your results are still saved. |
| Constant | Value | Meaning |
|---|---|---|
STALL_DAYS | 7 | Lead inactive for 7 days triggers Swing Down |
FAST_TRACK_TOOLS | 3 | Completing 3+ tools qualifies high-intent ICPs for Swing Up to Path A |
OFFER_URL | https://thepcma.uk/elitepartners | URL used in all CTA primary buttons and emails |
Dark Bootstrap theme. Background #0f1117, card background #1a1d2e.
Renders templates/dashboard.html. Live stats polled from /api/stats.
| Card | Metric | Data Source |
|---|---|---|
| Total Contacts | Count all contacts | contacts table |
| Tier Breakdown | Platinum / Gold / Silver / Bronze counts | contacts.tier |
| Portal Registrations | Today / This week / Total | portal_leads.created_at |
| Active Deal Owners | Count status=Active Deal Owner | contacts.status |
| Messages Today | SMS + Email + WhatsApp sent today | daily_stats |
| Human Queue | Unresolved escalated conversations | human_queue.resolved=False |
| Auto-Send Mode | On/Off toggle (persisted) | app_settings key=auto_send_mode |
6 KPI cards: Total Leads, Offer Views, Tools Completed, Path A count, Live Webinar Registrations, Stage 4 Leads.
7-step conversion funnel: Portal Registered → First Tool → Second Tool → Webinar → Offer Viewed → Stage 4 → Application Clicked.
Additional panels: Path distribution (A–E), ICP breakdown, Top 10 tools by completion count, 25-item activity feed, Live Webinar admin settings.
app/twilio_whatsapp.py, wired into pipeline engine,
webhooks at /webhook/twilio/status and /webhook/twilio/inbound.
Gated (WHATSAPP_ENABLED = False) pending Meta Business API approval for +447378665987.
app.thepcma.uk.
Bot-proof GET→POST entry prevents email scanner false registrations.
/portal/vsl configured in VSL_URLS.
Pipeline Tier 3 emails link directly with one-click login.
/portal/webinar has YouTube embed via WEBINAR_URL.
Pipeline Tier 4 emails link directly with one-click login.
FA2qo7uaAiGv5UEy9oo7. Tier 5 emails link to strategy call.
discovery_call_clicked triggers pipeline conversion detection.
app/short_links.py generates human-readable codes at /portal/go/{code}.
Click count and last_clicked_at tracked per link per contact.
The following values are currently read directly from portal_brain.py at audit generation time.
Any future change to these constants will be reflected automatically in the next download of this document.
| Constant | File | Current Value | Purpose |
|---|---|---|---|
STALL_DAYS | portal_brain.py | 7 | Days before Swing Down |
FAST_TRACK_TOOLS | portal_brain.py | 3 | Tools to qualify for Swing Up to Path A |
OFFER_URL | portal_brain.py | https://thepcma.uk/elitepartners | Offer page URL used in all CTAs |
| Partnership price | GPT prompt in generate_offer_doc() | £897/month | The price stated in the offer document |
| Deferment rate (T11) | portal_brain.py | 5% | DEFERMENT_RATE for lease extension calc |
| Finance cost/month (T09) | portal_brain.py | Bridging=£4,500, Dev=£6,000 | Monthly finance charges used in overrun cost calc |
| Contingency rates (T04) | portal_brain.py | Listed/Barn=15%, C2R/New=12%, Others=10% | Contingency % per project type |
| Sender email | GHL template | resources@thepcma.uk | From address on all outbound emails |
| Swing scanner freq. | portal_swing_scanner.py | Every 6 hours | APScheduler interval |
| PORTAL_BASE | short_links.py / pipeline_templates.py | https://app.thepcma.uk/portal | Base URL for all one-click links and short links |
| MAX_PIPELINE_SENDS_PER_DAY | pipeline_engine.py | 200 | Daily pipeline send cap |
| PIPELINE_BATCH_SIZE | pipeline_engine.py | 50 | Contacts per tick batch |
| SEND_DELAY_SECONDS | pipeline_engine.py | 2 | Seconds between pipeline sends |
| MIN_HOURS_BETWEEN_MESSAGES | safety.py | 48 | Cooldown between any messages to same contact |
| GHL Calendar ID | pipeline_templates.py | FA2qo7uaAiGv5UEy9oo7 | Strategy call booking calendar |
| Layer | Handling Present |
|---|---|
| GPT calls (all 16 tools) | try/except on every OpenAI call — falls back to minimal HTML. Errors logged via Python logger. |
| GHL API calls | try/except — failures logged; portal lead still created/updated locally. |
| Tool form submission | Missing required fields → HTTP 422 (FastAPI validation). No custom user-facing error page. |
| Invalid/expired token | Returns HTTP 404 if token not in portal_leads. No redirect to register. |
| Database errors | SQLAlchemy session rollback on exception; error logged. |
| Swing scanner | Per-lead try/except — one lead failure does not halt the rest. |
| Pipeline engine | Per-contact try/except in tick loop; MAX_CONSECUTIVE_FAILURES=5 stops batch. send_failed status on persistent errors. |
| Twilio WhatsApp | try/except with error code logging (63007, 63031). Errors recorded in last_send_error. Gated by WHATSAPP_ENABLED flag. |
| Activity logging | Robust try/except with DB rollback — logging failures never crash primary execution threads. |
email has a unique constraint on portal_leads.
A second registration attempt with the same email raises a database IntegrityError (no user-friendly message).completion_id belongs to the
requesting token. A lead accessing another lead's result ID will see their report.The pipeline engine (app/pipeline_engine.py) is the primary contact conversion system.
It processes 6,077 GHL contacts through a 7-tier progression, delivering ICP-specific content
via email and WhatsApp on an automated schedule. The scheduler runs a pipeline tick every 1 hour
and a backfill every 6 hours (batch of 200).
https://app.thepcma.uk/portal/enter/{token}app/short_links.py and app/pipeline_templates.py
| Tier | Content Type | Wait Before Next Tier |
|---|---|---|
| 1 | Diagnostic Tool — ICP-specific free tool (e.g., Strategy Selector, Compliance Checker) | 5 days |
| 2 | Case Study — Social proof relevant to ICP | 6 days |
| 3 | Video Walkthrough — 12-minute VSL | 7 days |
| 4 | Live Masterclass — Free training registration | 7 days |
| 5 | Strategy Call — 15-minute 1-on-1 offer | 7 days |
| 6 | Partnership Summary — Detailed offer document (£897/month) | 7 days |
| 7 | Quarterly Nurture — Long-term retention for unconverted contacts (90-day loop) | 90 days |
When a Tier 1 message is sent, the tool CTA is personalised per ICP:
| ICP Code | ICP Label | Recommended Tool Slug |
|---|---|---|
| Asp-Dev | Aspiring Developer | strategy-selector (Strategy Selector) |
| Sc-LL | Scaling Landlord | compliance-checker (Compliance Checker) |
| TP-Pro | Time-Poor Professional | deal-analyser (Deal Analyser) |
| Ac-Proj | Active Project | deal-analyser (Deal Analyser) |
| Parameter | Value | Purpose |
|---|---|---|
| MAX_PIPELINE_SENDS_PER_DAY | 200 | Total messages sent across all contacts per day |
| PIPELINE_BATCH_SIZE | 50 | Contacts processed per tick |
| SEND_DELAY_SECONDS | 2 | Pause between individual sends to avoid throttling |
| MAX_CONSECUTIVE_FAILURES | 5 | Stops batch if 5 sends fail in a row |
| MIN_HOURS_BETWEEN_MESSAGES | 48 | Cooldown between any messages to same contact (safety.py) |
The /portal/enter/{token} endpoint uses a GET→POST anti-bot flow:
next or tool query param.detect_pipeline_conversions() runs every tick before sends. It checks all pipeline contacts
with status sent or queued for:
contact.portal_registered == True → status = registeredcontact.status == "Active Deal Owner" → status = convertedcontact.discovery_call_clicked == True → status = convertedpcma-elite-member present → status = convertedapp/twilio_whatsapp.pyProvides WhatsApp message delivery via the Twilio API. Currently gated off
(WHATSAPP_ENABLED = False) pending WhatsApp Business API approval.
UK phone numbers are normalised before sending:
07xxx → +447xxx (strip leading 0, prepend +44)447xxx → +447xxx (prepend +)+447xxx → kept as-is| Endpoint | Purpose |
|---|---|
POST /webhook/twilio/status | Delivery status callbacks (queued, sent, delivered, undelivered, failed). Updates activity log. |
POST /webhook/twilio/inbound | Incoming WhatsApp messages. Logs to activity_log, marks contact as replied. |
| Secret | Purpose |
|---|---|
TWILIO_ACCOUNT_SID | Twilio Account SID |
TWILIO_AUTH_TOKEN | Twilio Auth Token |
TWILIO_WHATSAPP_NUMBER | WhatsApp-enabled Twilio number (currently +447378665987, not yet approved) |
app/safety.py)Enforces rate limits and opt-out checks before any message is sent:
| Check | Detail |
|---|---|
| Cooldown | MIN_HOURS_BETWEEN_MESSAGES = 48 hours. Enforced via contact.last_message_at. |
| Daily Caps | MAX_SMS_PER_DAY, MAX_EMAIL_PER_DAY, MAX_WHATSAPP_PER_DAY — tracked in daily_stats table. |
| Opt-Out | Checks contact.unsubscribed flag and GHL tags (e.g., "stop", "unsubscribe", "pcma-unsubscribed"). |
| Channel Validation | Verifies contact has at least one valid delivery channel (email, phone, or whatsapp) before attempting send. |
| Message Logging | Every outgoing message recorded in messages table; updates contact.last_message_at. |
app/scoring.py)| Signal | Points |
|---|---|
| Email present | +10 |
| Phone present | +10 |
| WhatsApp present | +10 |
| Portal registered | +10 |
| Lessons completed (1–3) | +5 to +15 |
| Model downloads | +10 |
| Offer viewed | +15 |
| Offer viewed 2+ times | +10 (bonus) |
| Discovery call clicked | +20 |
Max achievable score: 130.
| Tier | Score Range |
|---|---|
| Platinum | 70+ |
| Gold | 45–69 |
| Silver | 20–44 |
| Bronze | 0–19 |
Used to determine pipeline next param routing:
| Score Range | Pipeline Path |
|---|---|
| ≤20 | path_e (dormant nurture) |
| ≤40 | path_e_phase2 (content drip) |
| ≤60 | path_b_c (moderate engagement) |
| ≤80 | offer_doc (ready for offer) |
| 81+ | application (direct conversion) |
app/activity_log.py)Centralised log_activity(event_type, engine, contact_id, detail, metadata) function
used by all engines. Writes to activity_log table with JSON metadata.
Includes robust error handling with DB rollback to prevent logging failures from crashing primary threads.
app/scheduler.py)| Job ID | Interval | Function | Description |
|---|---|---|---|
pipeline_tick | 1 hour | run_pipeline_tick() | Runs detect_pipeline_conversions() then pipeline_tick() — advances tiers, sends messages |
pipeline_backfill | 6 hours | run_pipeline_backfill() | Enrolls up to 200 existing GHL contacts not yet in pipeline |
portal_swing_scan | 6 hours | run_portal_swing_scan() | Checks all active portal leads for swing up/down conditions |
legacy_maintenance | 6 hours | run_legacy_maintenance() | Legacy outbound path (retired) — maintenance only |
start_scheduler() is called from app/main.py on FastAPI startup event.
All jobs use BackgroundScheduler and run in background threads. Each job wraps its logic
in try/except to prevent one failure from stopping subsequent runs.
PCMA Brain — Technical Audit | Confidential | For Strategic Advisor Review Only
Generated: 06 April 2026 at 09:30 UTC | Zoya Abid, PCMA Elite Partners | resources@thepcma.uk
Partnership: £897/month | Apply: https://thepcma.uk/elitepartners