Glossary & Data Model
Terms
Asset (Connected Asset) — a platform-side object owned by a clinic: a Facebook Page, an Instagram account, an ad account, a lead form, a Google Business Profile location, or a WhatsApp Business account. Discovered on connect; individually enable/disable and map to a stage/branch.
Attribution — the trail linking a lead (and the revenue it produces) back to the exact ad / ad set / campaign that generated it. Stored as raw platform ids so late hierarchy syncs don't break historic attribution.
Booked Spend vs Platform Spend — Booked Spend is ad cost posted to the accounting general ledger; Platform Spend is the raw figure the ad platform reports. ROI prefers Booked Spend.
Channel — the medium of a conversation: WhatsApp, Messenger, Instagram DM, FB comment, IG comment, or Google Business message.
Cooldown — the minimum number of days before the same patient can receive another review request (default 90).
CPL / CPA (CAC) / ROAS — Cost per Lead (spend ÷ CRM leads), Cost per Acquisition a.k.a. Customer Acquisition Cost (spend ÷ won leads), Return on Ad Spend (collected revenue ÷ spend).
Dedupe key — a deterministic identifier that makes an operation idempotent: webhook events dedupe on (platform, externalEventId); automation runs on (ruleId, dedupeKey); leads on (companyId, platform, externalLeadId) and by normalized phone.
Depth cap — the automation safety limit that stops an action-triggered rule from chaining into an infinite loop. Events start at depth: 0.
Feature flag (FEATURE_CRM_*) — a per-tenant switch deciding whether a CRM capability exists for a clinic at all. Distinct from a permission (which decides who may use it).
First-touch — attribution credits the campaign that first produced a lead; a returning person's existing lead wins over a new capture.
LTV / LTV:CAC — patient lifetime value, and the ratio of lifetime value to acquisition cost (≥ 3 flagged healthy).
SLA (first-response / resolution) — the response-time targets on a conversation (defaults: 15 min first response, 1440 min resolution). A cron flips a conversation to "breached" when a target is missed.
Webhook inbox — the WebhookEvent table: every inbound platform event is signature-verified, persisted here, and only then processed — making ingestion durable and replayable.
24-hour window — Meta's rule that a business may send a free-form message only within 24h of the customer's last message. Enforced by the gateway (authoritative) with a client-side pre-check.
Data model map
Enums live in packages/prisma/schema.prisma:8877+ (and mirrored, where GraphQL-exposed, in enums.graphql:1550+).
Ingestion & attribution
| Model | schema.prisma | Purpose | Key uniqueness |
|---|---|---|---|
SocialIntegration | :8958 | one connection per platform | [companyId, platform] |
ConnectedAsset | :8991 | pages / IG / ad accts / lead forms | [companyId, platform, type, externalId] |
WebhookEvent | :9032 | durable webhook inbox | [platform, externalEventId] |
LeadCaptureEvent | :9486 | per-lead attribution spine | [companyId, platform, externalLeadId], leadId @unique |
Inbox
| Model | schema.prisma | Purpose | Key uniqueness |
|---|---|---|---|
OnlineConversation | :4755 | channel-agnostic conversation + SLA | [companyId, channel, contactKey] |
ConversationMessage | :4822 | messages | [conversationId, messageId] |
InboxConfig | :9461 | SLA + auto-create-lead settings | per company |
ConversationTemplate | :4888 | named WA templates | [companyId, name] |
Marketing
| Model | schema.prisma | Purpose |
|---|---|---|
AdCampaign / AdSet / Ad | :9234 / :9267 / :9288 | synced ad hierarchy |
AdInsight | :9316 | daily per-entity metrics + glPostedSpend |
CampaignAttributionSnapshot | :9344 | nightly ROI (leads, won, revenue) |
MarketingPostingConfig | :9383 | GL posting config (VAT, account, funding) |
AdCreditTopUp / AdPlatformInvoice | :9406 / :9435 | prepaid credit / platform bills |
Automation & reviews
| Model | schema.prisma | Purpose | Key uniqueness |
|---|---|---|---|
AutomationRule | :9177 | trigger/condition/action rule | [companyId, name] |
AutomationRun | :9208 | execution audit + once-per-event guard | [ruleId, dedupeKey] |
ReviewConfig | :9094 | review link, template, cooldown | per company |
ReviewRequest | :9113 | tracked ask to a patient | shortCode @unique |
Review | :9146 | ingested GBP review | [assetId, externalReviewId] |
Lead / Patient cutover
A lead's contact/PII is being moved onto the linked Patient (Patient.type = 2 means "Lead"). Deprecated Lead columns are tagged inline // Remove (e.g. Lead.email/phoneNumber/fullName/branch, schema.prisma:5906+). The physical column drop is deferred to a post-soak follow-up PR; a CI tripwire (scripts/audit-deprecated-lead-fields.sh) fails the build if any code reads or writes those fields at the Lead level. See Scope, Gaps & Honest Notes.