Customer Notes & Internal Annotations: Chat Prompts
Internal notes are how your team remembers what happened with each customer — context, preferences, history, warnings, opportunities — without exposing it to the customer themselves. "Champion is leaving the company in March." "Renewed early because procurement was confused; don't bring up auto-renewal in QBRs." "User reported they hate the new dashboard; don't auto-default them to it."
Most B2B SaaS ship a "Notes" textarea on the customer page in week one and then quietly add structure over time as it becomes obvious that one giant textarea doesn't scale: tagging, mentions, threading, attachments, search, role-based visibility, audit logs. This is the chat-prompt playbook for shipping internal notes that scale from a single CSM to a 50-person customer-success org.
When This Belongs
Use internal notes when:
- Multiple internal team members touch each customer (sales → CS → support → expansion)
- Per-customer context is meaningful and persistent
- Handoffs between team members happen regularly
- Memory of "we tried X with this customer" matters
Don't bother when:
- Pre-revenue (founder remembers everything; no team yet)
- All customers self-serve with no human touch
Data Model
I'm building internal notes on customer records. Help me design the data model.
Schema (Drizzle):
```sql
customer_notes:
id, customer_id (or account_id depending on model),
author_user_id, body (markdown), pinned (boolean),
visibility (enum: 'internal' | 'team:specific_team_id' | 'private:author_only'),
category (enum: 'general' | 'sales' | 'support' | 'cs' | 'risk' | 'opportunity'),
created_at, updated_at, deleted_at (soft-delete),
parent_note_id (nullable; for threading)
note_tags:
note_id, tag (e.g. 'churn-risk', 'expansion-candidate', 'champion-leaving')
note_mentions:
note_id, mentioned_user_id, mentioned_team_id (nullable)
note_attachments:
id, note_id, storage_url, filename, content_type, size_bytes
note_audit_log:
note_id, event_type (created | edited | deleted | visibility_changed),
actor_user_id, payload (JSON), timestamp
Implement:
- The schema + types
- Helper functions: createNote(), editNote(), deleteNote(), pinNote()
- Visibility check:
canUserSeeNote(user, note)based on visibility field + user's team membership - Audit-log writes happen atomically with state changes
Stack: Next.js + Drizzle + Postgres.
## The UI: Notes Panel on Customer Page
Build the notes panel on the customer detail page:
Layout:
- Right sidebar (or tab) titled "Internal Notes"
- New-note composer at top (markdown editor)
- Pinned notes section above scrollable history
- Each note: author + timestamp + body + reactions/replies
- Filter: All / Sales / Support / CS / Risk / Opportunity
- Search across notes
- @-mention auto-complete for users + teams
- Hashtag auto-complete for tags
Composer features:
- Markdown editing (bold, italic, lists, links)
- @mentions trigger user-picker dropdown
- #tags trigger tag-picker dropdown
- Attachments via drag-drop
- Visibility selector (default: internal)
- Pin checkbox
Visual indicators:
- Pinned: pinned icon + sticky to top
- Tagged: small chips below note
- Mentioned: highlight if note mentions current user
- Edited: "edited [time ago]" tooltip
Stack: Next.js + Tailwind + shadcn/ui + TipTap or simple textarea+react-markdown.
## Mentions + Notifications
When @mentioning users or teams, generate notifications:
Implementation:
- Parse the note body for
@usernameor@team-namepatterns - Resolve to user IDs / team IDs
- Insert rows into
note_mentionstable - For each mention, send a notification:
- In-app notification (your existing notification system)
- Optional: email (digest by default; instant for "high priority" mentions)
- Optional: Slack DM (if integrated)
Edge cases:
- @everyone or @all: limit to admins-only to avoid spam
- Editing a note adds new mentions: notify only newly added users
- Self-mention: ignore
- Mention in private note (visibility=private): no notification (recipient can't see anyway)
Stack: Next.js + Drizzle + your notification stack (in-app + email).
## Search Across Notes
For mid-to-large customer bases, search becomes essential.
Build search across all internal notes:
Use cases:
- Find all notes mentioning "churn" across all customers
- Find notes by author (e.g. all my notes from last month)
- Find notes from a specific customer in a date range
- Find all notes tagged "champion-leaving"
Search index options:
- Postgres full-text search (good for <1M notes)
- Algolia / Meilisearch / Typesense for richer + faster
- ElasticSearch for enterprise scale
Implement:
- An index update on note create/edit/delete
- A search endpoint with filters (author, customer, tag, date range, full-text)
- A search UI with result highlights
Permissions:
- Search results filtered by visibility (only show notes user can access)
- Audit search queries for compliance
Stack: Next.js + Postgres FTS (or Meilisearch for >100K notes).
## Customer Health Auto-Notes
The most powerful pattern: system-generated notes when customer behavior changes.
Auto-create internal notes when system events happen:
Event triggers:
- Customer downgrades plan: auto-note "Downgraded from Pro to Free on [date]"
- Customer hits usage anomaly (10x prior week): "Usage spike detected"
- Customer's NPS drops to detractor: "NPS submitted: -3 (detractor)"
- Renewal in 90 days: "Renewal upcoming on [date]; current ARR $X"
- Champion (logged in primary user) hasn't logged in for 30 days: "Champion inactive 30 days"
- Support ticket volume spikes: "5 tickets in past 7 days (vs normal 1/week)"
- Payment failed: "Payment failed; dunning started"
Implementation:
- Webhooks from each system into a "customer events" stream
- Event processor evaluates rules; creates auto-notes with category='auto'
- Auto-notes visually distinguished (system avatar; subtle background)
- Auto-notes are searchable + tagged appropriately
- CSM can pin auto-notes that are persistently relevant
Stack: Next.js + Inngest / Vercel Queues + Drizzle.
## Threaded Replies
For larger team discussions on a single topic:
Add threaded replies to notes:
Schema (already in data model):
- parent_note_id allows threading
- Nested 1-2 levels deep (don't allow infinite nesting; gets messy)
UI:
- Reply button under each note
- Replies indented and grouped under parent
- Collapsible threads (click "X replies" to expand)
- @-mention works in replies same as parent
Notification on replies:
- Author of parent note gets notified
- Anyone @mentioned in the reply gets notified
- Anyone who replied previously stays subscribed (optional)
Stack: Next.js + Drizzle.
## Pinned Notes
Critical context that should stay top-of-mind:
Add pinned notes feature:
Behavior:
- "Pin" toggle on each note
- Pinned notes sort to top of customer's notes panel
- Visually distinct (pin icon; subtle background)
- Cap at 5 pinned notes per customer (force prioritization)
- Pin/unpin actions audit-logged
Use cases:
- "This account uses our beta API; not the standard one"
- "Champion is leaving in March; build relationship with [new contact]"
- "DO NOT discuss pricing changes; sensitive contract negotiation"
Stack: Next.js + Drizzle.
## Visibility / Privacy Controls
Implement visibility controls:
Visibility levels:
- Internal (default): all team members with access to the customer record see it
- Team-specific: only the named team (e.g. only Customer Success team)
- Private: only the author can see (rare; use sparingly)
UI:
- Visibility selector when composing
- Visual indicator on the note showing its visibility
- Filter to show/hide private notes
Why team-specific:
- Sales notes about deal negotiation shouldn't always be visible to support
- Support notes about an irate customer shouldn't always be visible to sales
Why private (rare):
- Personal observations the author isn't ready to commit to a team-shared narrative
- Avoid normalizing private notes — they hide context from the team
Permissions enforcement:
- Server-side filter on every query
- Don't trust client to honor visibility
- Audit-log access patterns
Stack: Next.js + Drizzle + your auth context.
## Audit Log
For compliance + accountability:
Maintain an audit log of all note actions:
Logged events:
- Note created / edited / deleted
- Visibility changed
- Pinned / unpinned
- Search query (high-volume; consider sampling for storage efficiency)
Storage:
- Append-only table
- Retain for 1+ year for SOC 2 / similar
Surfacing:
- Per-note edit history (who edited when; what changed)
- Per-customer activity log
- Per-user activity log (audit a user's behavior if needed)
Stack: Next.js + Drizzle.
## CRM Integration
If you also use Salesforce / HubSpot / etc., notes need to flow.
Sync notes between your in-house customer profile and your CRM:
Patterns:
- In-house notes are source of truth: replicate to CRM via API on write
- CRM notes are source of truth: import from CRM via webhook / poll
- Both directions: tricky; needs conflict resolution
For most B2B SaaS:
- In-house notes for product / support / detailed CS context
- CRM (Salesforce / HubSpot) for sales-driven notes that the rev team manages
- Sync sales notes from CRM into your platform; don't sync the other way (you trust CRM authoritative for sales)
Implementation:
- Webhook from CRM on note creation (if available) or poll every 5 min
- One-way mirror to your in-house notes with author = "CRM Sync (User)"
- Deduplicate via external_id
Stack: Next.js + Drizzle + Salesforce / HubSpot SDK.
## Common Pitfalls
**Single text-blob "notes" field with no structure.** Doesn't scale past 10 notes. Build structured records from start.
**No visibility / privacy controls.** Sensitive sales notes leak to support team. Build visibility from day 1.
**No mentions / notifications.** Notes go unread. Add @-mention + notify.
**No search.** "What did we discuss with this customer last quarter?" → unsearchable. Index notes; expose search.
**No audit log.** Bad actor edits / deletes notes; no record. Append-only audit log.
**No pinning.** Critical context buried under recent activity. Pinning surfaces.
**Auto-notes that overwhelm.** System fires 50 auto-notes per customer per day; signal lost. Tune rules; bundle similar events into digests.
**Mentions that spam.** @everyone abused; users opt out of all mentions. Restrict @everyone; tier mention notifications.
**Notes accessible via API key without scope check.** API token returns all notes regardless of visibility. Apply scope to API too.
**No way to export notes.** GDPR DSAR requests need notes about the user. Plan for export.
**Notes not part of customer-record search.** Searching customer name doesn't surface notes. Index together.
**Editing without history.** Edit erases prior content; no record of what was originally written. Track edits.
**Soft-delete that returns deleted notes in queries.** Forgot to filter `deleted_at IS NULL`. Default-filter at query layer.
**Customer-visible accidentally.** Internal note accidentally shared to customer. UI must never expose internal notes externally.
**No threading.** Long discussions become 20-comment walls in chronological mess. Add reply threading.
**Notes scattered across systems.** Sales notes in Salesforce; support notes in Zendesk; CSM notes in your platform; nobody sees full picture. Centralize where it makes sense.
**Note format that doesn't render markdown.** Plain text only; users want bold + lists. Add markdown rendering.
**Tags free-text without governance.** Every CSM creates their own tags; "churn-risk" / "churnRisk" / "atrisk" all variants. Curated tag list.
**No "system" vs "human" distinction.** Auto-notes look identical to human notes; CSMs can't tell. Visual differentiation.
**Mentions that resolve to wrong people on team change.** Mention @sarah; sarah leaves; @sarah doesn't update. Use stable user IDs.
**Forgetting to surface notes during interactions.** Support agent picks up customer call; doesn't see the "champion is leaving" pinned note. Surface notes prominently in any customer-facing surface.
## See Also
- [Audit Logs](./audit-logs-chat.md)
- [Activity Feed / Timeline Implementation](./activity-feed-timeline-implementation-chat.md)
- [Comments / Threading / Mentions](./comments-threading-mentions-chat.md)
- [In-App Notifications](./in-app-notifications-chat.md)
- [Notification Preferences & Unsubscribe](./notification-preferences-unsubscribe-chat.md)
- [Roles & Permissions](./roles-permissions-chat.md)
- [Multi-Tenancy](./multi-tenancy-chat.md)
- [Internal Admin Tools](./internal-admin-tools-chat.md)
- [Customer Health Scoring](./customer-health-scoring-chat.md)
- [Customer Analytics Dashboards](./customer-analytics-dashboards-chat.md)
- [Settings & Account Pages](./settings-account-pages-chat.md)
- [Tags / Labels System](./tags-labels-system-chat.md)
- [Search & Autocomplete / Typeahead](./search-autocomplete-typeahead-chat.md)
- [Customer Reports & Scheduled Exports](./customer-reports-scheduled-exports-chat.md)
- [Account Deletion & Data Export](./account-deletion-data-export-chat.md) — GDPR exports include notes
- [Account Suspension & Fraud Holds](./account-suspension-fraud-holds-chat.md)
- [Approval Workflows & Multi-Step Routing](./approval-workflows-multi-step-routing-chat.md)
- [Customer Support](./customer-support-chat.md)
- [Microcopy & Product Copy Systems](./microcopy-product-copy-systems-chat.md)
- [Sidebar Navigation Implementation](./sidebar-navigation-implementation-chat.md)
- [In-App Status Banners & System Notifications](./in-app-status-banners-system-notifications-chat.md)
- [Toast Notifications UI](./toast-notifications-ui-chat.md)
- [Tooltip & Hint Systems](./tooltip-hint-systems-chat.md)
- [Strategic Account Planning (LaunchWeek)](https://launchweek.dev/content/4-convert/strategic-account-planning.md) — notes feed account plans
- [Customer Health Scoring Playbook (LaunchWeek)](https://launchweek.dev/content/4-convert/customer-health-scoring-playbook.md)
- [Voice of Customer Program (LaunchWeek)](https://launchweek.dev/content/4-convert/voice-of-customer-program.md)
- [CRM Providers (VibeReference)](https://viberef.dev/marketing-and-seo/crm-providers.md)
- [HubSpot (VibeReference)](https://viberef.dev/marketing-and-seo/hubspot.md)