The complete source of truth for the MageTech Lead Intelligence platform: system architecture, technology stack, every module and function, every API input & output, role-permission matrix, key business flows and full configuration.
Who it is for, how it is organized, and why reading it saves hours of code archaeology.
Everything needed to understand, run, extend and maintain the platform: architecture, stack, modules, endpoints with request/response shapes, business rules, security model and configuration.
Developers (onboarding, integration, debugging), architects (design review, extension), QA (expected behaviors & status catalogs), product managers (feature map & capabilities), sysadmins (deployment & configuration).
Read Architecture and Stack first. Jump to API for endpoint contracts, Modules for per-feature function detail, Flows for how data moves, and Reference for enums, security and configuration.
One canonical reference instead of digging through source. It answers βwhat should happen here?β, βwhich endpoint does that?β, βwho is allowed?β, and βhow is this configured?β in seconds.
Three clean tiers. The browser talks only to the Spring Boot API; the API owns all rules; PostgreSQL (with PGVector for AI search) is the single source of truth. Every record belongs to an organization (tenant).
React 19 SPA on Next 16 App Router (port 3000). Dark corporate theme, inline forms, βK command palette, pure-CSS charts.
Java 21, REST under /api/v1 (port 8081). JWT security, RBAC enforced in services, Flyway migrations, scheduled campaign engine.
Transactional core (JPA/Hibernate) plus vector embeddings for knowledge-base similarity search. Multi-tenant via organization_id.
Animated shuttle = one authenticated API call (browser β API β DB β back)
One end-to-end round trip, from keyboard to database.
Frontend builds the URL via lib/api.ts β http://localhost:8081/api/v1/β¦ and attaches Authorization: Bearer <accessToken> (from localStorage).
Spring Security (stateless, CSRF off) permits public paths (/auth/register|login|refresh, /webhooks/**, Swagger, Actuator health) and authenticates everything else.
JwtAuthenticationFilter parses the Bearer token, checks signature + expiry, and loads authorities: ROLE_<ROLE> plus each granted PERM_<PERMISSION>.
The @RestController maps path, binds and validates the DTO, then forwards to the service β controllers stay thin.
Services explicitly check permissions (e.g. LEADS_WRITE). All domain rules live here: role promotions, campaign guards, reply auto-suppression.
Spring Data JPA repositories, scoped by organization_id, hit PostgreSQL; @Transactional keeps operations atomic and audit events are recorded.
DTOs (never entities) return as JSON. Errors use one shape: { error, message, timestamp, status }. On a 401, lib/api.ts transparently refreshes and retries once.
Exact versions as declared in backend/pom.xml and frontend/package.json.
mvnw.cmd@base-ui/react primitiveshttp://localhost:8081 (SERVER_PORT)http://localhost:30008080 (leave running)localhost:3000DB_PASSWORD, JWT_SECRET, NEXT_PUBLIC_API_URLmvnw.cmd spring-boot:runAll endpoints live under /api/v1. Paginated lists return PageResponse<T> = { items, totalItems, totalPages, currentPage, pageSize }; plain collections return T[]. Errors always come as { error, message, timestamp, status }.
| Method | Endpoint | Purpose | Input β Output |
|---|---|---|---|
| POST | /auth/register | Create org + first user | {organizationName, firstName, lastName, email, password} β 201 AuthResponse. Creates the workspace Owner. |
| POST | /auth/login | Sign in | {email, password} β AuthResponse (user, accessToken, refreshToken, expiresInSeconds, tokenType). |
| POST | /auth/refresh | Rotate tokens | {refreshToken} β new AuthResponse. Refresh TTL 7 days. |
| GET | /auth/me | Current profile | β UserDto. |
| GET | /users | List members | β UserDto[]. |
| POST | /users | Create member | CreateUserRequest β 201 UserDto. OWNER/ADMIN only; only OWNER may grant OWNER/ADMIN. |
| PUT | /users/{id} | Update member | UpdateUserRequest (incl. role) β UserDto. Cannot change your own role/active. |
| DELETE | /users/{id} | Deactivate member | β 204. Guards: no self-deactivation, last owner protected. |
| Method | Endpoint | Purpose | Input β Output |
|---|---|---|---|
| GET | /leads | Paged lead list | ?page,size,q,country,industryId,status,emailStatus,tagId,segmentId,companyId,leadSourceId,fromDate,toDate β PageResponse<LeadDto> (38 fields, sort createdAt DESC). |
| GET | /leads/{id} | Lead detail | β LeadDto (technologies, tags, segments included). |
| POST | /leads | Create lead | CreateLeadRequest (needs email OR companyName; duplicate email β 400) β 201 LeadDto. |
| PUT | /leads/{id} | Update lead | UpdateLeadRequest (all optional) β LeadDto. |
| DELETE | /leads/{id} | Delete lead | β 204. Blocked 409 while enrolled in active campaigns. |
| POST | /leads/{id}/tags/{tagId} | Add tag | β LeadDto. DELETE variant removes. |
| POST | /leads/{id}/segments/{segmentId} | Add to segment | β LeadDto. DELETE variant removes. |
| GET | /leads/{id}/raw | Raw import payloads | β LeadRawDataDto[]. |
| GET | /leads/{id}/campaigns | Campaign membership | β CampaignRefDto[] (drives the delete-guard UI). |
| ALL | /tags, /lead-sources, /segments | Tags & segments CRUD | GET list / POST create / PUT (tags) / DELETE β respective DTOs. |
| Method | Endpoint | Purpose | Input β Output |
|---|---|---|---|
| GET | /companies | Paged companies | ?page,size,q β PageResponse<CompanyDto>. |
| GET | /companies/{id} | Company detail | β CompanyDetailDto = company + domains[] + technologies[] + leadCount. |
| POST | /companies | Upsert company | CompanyUpsertRequest β 201 CompanyDto (find-or-create by domain β name). |
| PUT | /companies/{id} | Update company | CompanyUpsertRequest β CompanyDto. |
| DELETE | /companies/{id} | Delete company | β 204. Blocked 409 while leads are linked. |
| GET | /companies/{id}/campaigns | Campaign membership | β CampaignRefDto[]. |
| GET | /meta/industries Β· /meta/technologies Β· /meta/countries | Reference data | β industries/technologies as {id,name[,category]}, countries as strings. |
| GET | /meta/statuses | Enum catalog | β {leadStatuses[], emailStatuses[]} β powers filter dropdowns. |
| Method | Endpoint | Purpose | Input β Output |
|---|---|---|---|
| POST | /imports/upload | Upload CSV/XLSX | multipart file (xlsx/csv, β€50 MB) β ImportBatchDto (status PARSED). Max 200k rows. |
| GET | /imports | Import history | β PageResponse<ImportBatchDto>. |
| GET | /imports/{id}/mapping | Get column mapping | β MappingResponse (sourceColumns, targetFields, suggestedMapping, unmappedSourceColumns). |
| PUT | /imports/{id}/mapping | Save mapping | {columnβfield} β MappingResponse; batch β MAPPED. |
| GET | /imports/{id}/preview | Preview rows | ?offset,limit (limit β€ 500) β PreviewResponse (columns + mapped rows). |
| GET | /imports/{id}/duplicates | Duplicate review | β DuplicateResponse β groups keyed by DB lead with suggested action; batch β DUPLICATE_REVIEW. |
| POST | /imports/{id}/confirm | Commit import | {mapping, duplicateDecisions} with actions IMPORT / MERGE / IGNORE β ImportResult (imported, merged, ignored, error, invalidEmail counts). |
| GET | /imports/{id}/errors | Row errors | β PageResponse<ImportErrorDto>. |
| DELETE | /imports/{id} | Cancel import | β ImportBatchDto (CANCELLED). |
| Method | Endpoint | Purpose | Input β Output |
|---|---|---|---|
| GET | /campaigns | List campaigns | ?page,size,status β PageResponse<CampaignDto> (sort updatedAt DESC). |
| POST | /campaigns | Create campaign | CampaignRequest {name, description, sequence[], targetFilter} β CampaignDto (DRAFT). |
| PUT | /campaigns/{id} | Update campaign | CampaignRequest β CampaignDto. Blocked while ACTIVE. |
| DELETE | /campaigns/{id} | Delete campaign | β 204. Blocked while ACTIVE. |
| POST | /campaigns/{id}/start | Activate | Requires β₯ 1 step; DRAFT β ACTIVE + startedAt. |
| POST | /campaigns/{id}/pause | Pause | ACTIVE β PAUSED. |
| POST | /campaigns/{id}/resume | Resume | PAUSED β ACTIVE. |
| POST | /campaigns/{id}/complete | Complete | ACTIVE β COMPLETED + completedAt. |
| POST | /campaigns/{id}/leads | Add leads | {leadIds[], filter} β {added, skipped}. Leave ACTIVE to edit. |
| GET | /campaigns/{id}/leads | Enrolled leads | β PageResponse<CampaignLeadDto> (per-lead step + status). |
| GET | /campaigns/{id}/metrics | Health metrics | β CampaignMetrics {totalLeads, pending, sent, opened, replied, bounced, unsubscribed, failed, skipped}. |
| DELETE | /campaigns/{id}/leads/{cid} | Remove enrollee | β 204. |
| ALL | /templates | Email template CRUD | GET/POST/PUT/DELETE + /duplicate + /preview (missing variables) + /generate (AI draft from a lead). |
| Method | Endpoint | Purpose | Input β Output |
|---|---|---|---|
| GET | /messages | Outbound messages | ?campaignId,leadId β PageResponse<EmailMessageDto> (sort sentAt DESC). |
| GET | /replies | Inbound replies | β PageResponse<ReplyDto>. |
| POST | /replies | Record reply (manual) | ReplyCreateRequest β ReplyDto. Triggers suppression + campaign/lead updates + maybe opportunity. |
| POST | /webhooks/emails | Email webhook (reply) | WebhookEmailRequest matched to an outbound message by providerMessageId / inReplyTo β ReplyDto. 400 if unmatched. |
| POST | /webhooks/emails/open | Open event | {providerMessageId} β {ok, messageId}; marks OPENED. |
| POST | /webhooks/emails/bounce | Bounce event | {providerMessageId} β {ok, messageId}; marks BOUNCED + suppresses. |
| GET | /suppression | Suppression list | ?page,size β PageResponse<SuppressionDto>. |
| POST | /suppression | Add suppression | {emails[], reason} β SuppressionDto. |
| DELETE | /suppression/{id} | Remove entry | β 204. |
| ALL | /kb/documents | Knowledge docs CRUD | GET/POST/PUT/DELETE β KnowledgeDocumentDto; embedded on create/update. |
| POST | /kb/search | Vector search | {query, limit} β KnowledgeDocumentDto[], ordered by embedding cosine distance. |
| POST | /kb/ask | Grounded Q&A | {question, leadId?, companyId?} β AskResponse {answer, sources[], model}. |
| Method | Endpoint | Purpose | Input β Output |
|---|---|---|---|
| GET | /crm/opportunities | Opportunity list | ?stage β PageResponse<OpportunityDto>. |
| GET | /crm/opportunities/pipeline | Pipeline summary | β PipelineSummary (per-stage count + value + totals). |
| ALL | /crm/opportunities[/{id}] | Opportunity CRUD | POST/PUT/DELETE β OpportunityDto; stage changes write events + sync lead status. |
| GET | /crm/opportunities/{id}/events | Recorded events | β OpportunityEventDto[] (CREATED / STAGE_CHANGE / NOTE). |
| ALL | /crm/meetings[/{id}] | Meeting CRUD | GET + /upcoming; POST/PUT/DELETE β MeetingDto (PENDING/CONFIRMED/CANCELLED/DONE). |
| ALL | /crm/proposals[/{id}] | Proposal CRUD | ProposalRequest incl. line items. Editable only while DRAFT; edits bump version. |
| POST | /crm/proposals/{id}/status | Proposal status | {status} β SENT β opp PROPOSAL_SENT; ACCEPTED β opp WON 100%; REJECTED β opp LOST 0%. |
| ALL | /abm/accounts[/{id}] | ABM targets CRUD | POST/PUT/DELETE β AbmAccountDto (tier TARGET / KEY / STRATEGIC). |
| GET | /abm/suggestions | Scored suggestions | ?limit=N β AbmSuggestionDto[] ranked by fitScore. |
| Method | Endpoint | Purpose | Input β Output |
|---|---|---|---|
| POST | /ai/leads/{leadId}/intelligence | Run AI analysis | β LeadIntelligenceResult: score (0β100), summary, signals[], recommendedServices[], suggestedAngle, riskNotes[], model, analyzedAt. |
| GET | /ai/leads/{leadId}/intelligence | Latest analysis | β newest result, or computes one on demand. |
| GET | /ai/analyses | Analysis history | β PageResponse<AiAnalysis>. |
| GET | /analytics/dashboard | Dashboard KPIs | β DashboardStats: KPIs + byStatus/byCountry/byIndustry/byTechnology + recentImports. |
| GET | /integrations/status | Connector status | β {provider β boolean} (HUBSPOT / SALESFORCE / SIMULATED). |
| POST | /integrations/{provider}/sync | Push lead(s) | {leadIds?} (default top 100 by org) β {provider, pushedLeads}. |
| GET | /organizations/me | Workspace profile | β org details. PUT {name} β renamed org (ORG_WRITE). |
| ALL | /magentech-services[/{id}] | Services catalog | GET ?activeOnly=true; POST/PUT/DELETE β MageServiceDto. |
| ALL | /playbooks[/{id}] | Sales playbooks | PlaybookRequest with steps [{title, guidance, templateId?, waitDays}] β PlaybookDto. |
Per-module purpose, features and explicit input β output contracts. This is the βin and outβ reference for features.
Single aggregation endpoint powering the home screen, computed live from lead/company tables. Returns an empty structure when there is no data.
Registration bootstraps one organization plus the founding Owner. Login issues a JWT access token (60 min) and a 7-day refresh token. Deactivated users cannot sign in.
sub=userId, org, roleMember lifecycle: invite, change role, deactivate. Hard rules: only OWNER creates/promotes OWNER/ADMIN; no self-deactivation; the last active owner is always protected.
The central record: a 38-field DTO spanning contact, employment, company, location, enrichment (technologies, SEO), tags, segments and pipeline status. Delete is guarded against active-campaign enrollment.
Account profiles de-duplicated by domain, enriched with technology signals and lead counts. Cannot be deleted while leads still reference it.
Guided 5-stage wizard: upload β map β preview β review duplicates β confirm. Parses xlsx/csv (β€50 MB, β€200k rows), auto-suggests the column mapping, and produces a complete audit trail of imported / merged / ignored / errored rows.
Multi-step email campaigns with template sequences and wait days. A 60-second scheduled poll advances each active campaign's due pending leads. Editing / deleting / adding leads is locked while ACTIVE (pause first).
Outbound sends run through a provider abstraction (default SIMULATED). Inbound replies and open/bounce events arrive via webhooks and update the whole graph: reply β suppress sender, mark campaign/lead REPLIED, and auto-create an opportunity.
Per-lead analysis: an LLM path (gpt-4o-mini, temperature 0.3, strict JSON) with a deterministic rule-based fallback so scoring always works even without a key. Results are persisted as AiAnalysis (JSONB) and drive the score badges (0β39 low, 40β69 medium, 70β84 good, 85β100 excellent).
Documents are embedded (OpenAI text-embedding-3-small, 1536 dims; deterministic hash fallback) into PGVector. Search runs cosine similarity (ORDER BY embedding <=> vector); ask retrieves top-4 docs and returns a grounded answer with sources.
Full pipeline: opportunities with stage / value / probability / expected close, activity events, meetings (PENDING β CONFIRMED β DONE), and proposals with line items. Stage and proposal changes cascade into lead-status synchronization.
Account-based marketing: target accounts tiered TARGET / KEY / STRATEGIC with budgets, owners, and AI suggestions ranked by the same fit-scoring signals.
SPI-based CrmConnector with three providers. HUBSPOT and SALESFORCE activate when their tokens are configured; SIMULATED is always available for demos. Sync pushes lead data out to the CRM.
Reusable content layer: email templates with {{variables}} and AI generation, sales playbooks with guided steps and wait days, and the services catalog the AI uses to recommend offerings.
End-to-end behaviors: import, lead lifecycle, campaign execution, replies and AI scoring.
Duplicate detection order: email β LinkedIn URL β company domain β company + contact. Parsers: Apache POI (xlsx) + Commons CSV; maximum 200k rows per file.
Status is advanced automatically: import β IMPORTED; campaign send β CONTACTED; reply β REPLIED; CRM stages sync QUALIFIED / PROPOSAL / NEGOTIATION / WON / LOST back to the lead.
@Scheduled(fixedDelayString="${app.campaign.poll-millis:60000}", initialDelay=15000) scans ACTIVE campaigns.
Pending CampaignLeads whose nextStepAt <= now and current step index is below the sequence length.
Missing template or email β FAILED; suppressed or empty recipient β SKIPPED; success β SENT, step + 1, nextStepAt = now + waitDays Β· 86400s.
After the last step is sent, nextStepAt is cleared and the enrollee becomes idle.
Auto-opportunity only when enabled (app.crm.auto-create-opportunity-on-reply=true) and no open opportunity exists for the lead yet.
| Opportunity stage | Default probability | Lead status sync |
|---|---|---|
| NEW | 10% | β |
| QUALIFIED | 20% | β QUALIFIED |
| PROPOSAL_SENT | 40% | β PROPOSAL |
| NEGOTIATION | 60% | β NEGOTIATION |
| WON | 100% | β WON |
| LOST | 0% | β LOST |
The canonical value catalog, role-permission matrix, data model and run configuration.
| Enum | Values |
|---|---|
| LeadStatus | IMPORTED Β· NEW Β· VALIDATED Β· CONTACTED Β· REPLIED Β· QUALIFIED Β· MEETING Β· PROPOSAL Β· NEGOTIATION Β· WON Β· LOST Β· ARCHIVED |
| EmailStatus | UNKNOWN Β· VALID Β· INVALID Β· RISK Β· SUPPRESSED Β· DUPLICATE Β· BOUNCED Β· UNSUBSCRIBED |
| CampaignStatus | DRAFT Β· ACTIVE Β· PAUSED Β· COMPLETED |
| CampaignLeadStatus | PENDING Β· SENT Β· OPENED Β· REPLIED Β· BOUNCED Β· UNSUBSCRIBED Β· FAILED Β· SKIPPED |
| MessageStatus | SENT Β· DELIVERED Β· OPENED Β· CLICKED Β· BOUNCED Β· FAILED |
| ImportStatus | UPLOADED Β· PARSED Β· MAPPED Β· VALIDATED Β· DUPLICATE_REVIEW Β· CONFIRMED Β· COMPLETED Β· FAILED Β· CANCELLED |
| OpportunityStage | NEW Β· QUALIFIED Β· PROPOSAL_SENT Β· NEGOTIATION Β· WON Β· LOST |
| ProposalStatus | DRAFT Β· SENT Β· ACCEPTED Β· REJECTED |
| MeetingStatus | PENDING Β· CONFIRMED Β· CANCELLED Β· DONE |
| SuppressionSource | MANUAL Β· BOUNCE Β· COMPLAINT Β· UNSUBSCRIBE Β· IMPORT |
| AbmTier | TARGET Β· KEY Β· STRATEGIC |
| Role | OWNER Β· ADMIN Β· SALES_MANAGER Β· SALES_USER Β· VIEWER |
5 roles Γ 14 permission constants. JWT authorities: ROLE_<ROLE> + one PERM_<PERMISSION> each.
| Role | Granted permissions |
|---|---|
| OWNER | All (incl. USERS_WRITE, ORG_WRITE, promotes admins) |
| ADMIN | All permissions |
| SALES_MANAGER | LEADS_R/W/D Β· IMPORTS_R/W Β· COMPANIES_R/W Β· SEGMENTS_R/W Β· USERS_READ Β· ORG_READ |
| SALES_USER | LEADS_R/W Β· IMPORTS_READ Β· COMPANIES_READ Β· SEGMENTS_R/W Β· USERS_READ |
| VIEWER | LEADS_READ Β· IMPORTS_READ Β· COMPANIES_READ Β· SEGMENTS_READ Β· USERS_READ Β· ORG_READ |
Constants: LEADS_READ Β· LEADS_WRITE Β· LEADS_DELETE Β· IMPORTS_READ Β· IMPORTS_WRITE Β· COMPANIES_READ Β· COMPANIES_WRITE Β· SEGMENTS_READ Β· SEGMENTS_WRITE Β· USERS_READ Β· USERS_WRITE Β· ORG_READ Β· ORG_WRITE Β· AUDIO_READ.
Multi-tenant JPA: every table carries organization_id. Base hierarchy: BaseEntity (id, created_at, updated_at) β BaseOrgEntity (+ organization_id). Embeddings live in a PGvector(1536) column; AI results and campaign steps use JSONB.
| Migration | Content |
|---|---|
V1__core.sql | organizations, users, leads, tags, companies, raw data, reference data |
V2__phase2_services_ai.sql | mage services + AI analysis tables |
V3__phase3_campaigns.sql | templates, campaigns, campaign_leads, messages, replies, suppression |
V4__phase4_crm.sql | opportunities, events, meetings, proposals |
V5__phase5_ai_kb_abm.sql | knowledge documents + PGVector, ABM accounts |
V6__fix_opportunity_event_updated_at.sql | adds updated_at to opportunity_event |
| Key | Default / value |
|---|---|
SERVER_PORT | 8081 |
DB_PASSWORD | PostgreSQL password (env) |
JWT_SECRET | β₯ 32 bytes (env); access 60 min, refresh 7 days |
app.campaign.poll-millis | 60000 (initial delay 15000) |
app.crm.auto-create-opportunity-on-reply | true |
app.integrations.hubspot-token / salesforce-* | enable real connectors |
app.uploads.dir | per-org import file storage |
ai.* (OpenAI) | gpt-4o-mini Β· text-embedding-3-small |
NEXT_PUBLIC_API_URL | http://localhost:8081 |
Run backend: mvnw.cmd spring-boot:run with JAVA_HOME (JDK 21), DB_PASSWORD, JWT_SECRET and SERVER_PORT=8081 set.
Pricing is modeled as a workspace that bundles capacity β leads, AI analyses (scoring, recommendations, email generation, KB Q&A), enrichment and campaign contacts β rather than purely per-seat licensing. Users are simply a secondary constraint. This section documents the proposed plan tiers, the intended billing & subscription module, and migration approach.
Prices in USD. Annual plans billed once per year. CTA buttons are placeholders pending the Billing module.
Starter
$49/mo Β· annual $39/mo
Import pipeline + enrichment (limited)
Growth
$149/mo Β· annual $119/mo
Full AI outreach + campaigns + CRM + HubSpot sync
Professional
$399/mo Β· annual $319/mo
Adds Salesforce + API + ABM
Enterprise
Custom pricing
SSO Β· dedicated tenant Β· SLA Β· custom limits
| Capability | Starter | Growth | Professional | Enterprise |
|---|---|---|---|---|
| Users | 2 | 5 | 15 | Unlimited |
| Lead capacity | 2,500 | 15,000 | 50,000 | Custom |
| AI analyses / mo | 250 | 2,000 | 10,000 | Custom |
| AI emails / mo | 100 | 1,000 | 5,000 | Custom |
| Active campaigns | 3 | 20 | Unlimited | Unlimited |
| Campaign leads | 1,000 | 10,000 | 50,000 | Custom |
| Lead imports / CSV / Excel | β | β | β | β |
| Email validation | β | β | β | β |
| Company enrichment | Limited | β | β | β |
| AI recommendations | β | β | β | β |
| Knowledge base | 10 docs | 100 docs | 1,000 docs | Custom |
| AI knowledge Q&A | β | β | β | β |
| CRM β opportunities / meetings / proposals | β | β | β | β |
| ABM | β | β | β | β |
| HubSpot integration | β | β | β | β |
| Salesforce | β | β | β | β |
| API access | β | β | β | β |
| Advanced analytics | β | β | β | β |
| Audit trail | β | β | β | β |
| Priority support | β | β | β | Dedicated |
| SSO | β | β | β | β |
| Dedicated tenant | β | β | β | β |
| Add-on | Tier 1 | Tier 2 | Tier 3 |
|---|---|---|---|
| Additional leads | +10,000 Β· $29 | +50,000 Β· $99 | +100,000 Β· $169 |
| AI credits | +1,000 analyses Β· $19 | +5,000 analyses Β· $69 | +10,000 analyses Β· $119 |
| Enrichment | +5,000 enrichments Β· $29 | +25,000 enrichments Β· $99 | +100,000 enrichments Β· $299 |
| Campaign contacts | +10,000 contacts Β· $19 | +50,000 contacts Β· $69 | +100,000 contacts Β· $119 |
GST extra where applicable.
| Plan | Monthly (INR) | Annual (INR) |
|---|---|---|
| Starter | βΉ3,999 | βΉ39,990 / year |
| Growth | βΉ11,999 | βΉ119,990 / year |
| Professional | βΉ31,999 | βΉ319,990 / year |
| Enterprise | Custom | Custom |
No credit card required. Seeded with a fixed quota, no plan selected.
Not present in the current codebase. Recommended future module so the documented plans can be enforced and invoiced. The current system has no plan limits β enrichment, AI quotas and campaign allowances are not yet gated.
organization_id β plan tier (STARTER / GROWTH / PROFESSIONAL / ENTERPRISE), billing period (monthly / annual), start & renewal dates, soft limit overrides for add-ons.
Counters per organization + period: AI analyses (scoring, recommendations, emails, KB Q&A), imported leads, enrichment calls, campaign contacts. Incremented at write-time in existing services.
Records invoice, add-on line items (override allowances) and amount. Optional webhook to a payment provider; invoice stores provider reference.
Checked inside campaign scheduler, AI services, import pipeline and CRM actions. Hard vs soft limits: soft β warn, hard β block with 402-style error payload.
Leads (import + enrichment) Β· AI (scoring / email / Q&A) Β· Campaigns (contacts, sends). Enforcement surfaces in the API as QUOTA_EXCEEDED.
V7__billing.sql β subscriptions, usage_counters, invoices, plan_tiers (seeded reference data).BaseOrgEntity); plan changes restricted to OWNER.