A CRM is the perfect Coda capstone: five interconnected tables, relation columns, rollup formulas, a pipeline board, automations, and AI columns — all woven into a system your whole team will actually use.
A proper CRM isn't a single contacts list — it's a graph of related objects. In Coda, each object type becomes a table, and relation columns connect them. Before building any table, sketch the data model so every connection is intentional.
Companies are accounts. Contacts belong to companies via a relation column. One company can have many contacts.
Each deal links to a company. The deal moves through pipeline stages and tracks value, probability, and close date.
Every call, email, or meeting is an Activity row linked to a contact and optionally a deal. Notes live as a Canvas column on Contacts.
Every contact, deal, and activity ultimately traces back to a company. Build the Companies table first with a clean set of core columns, then add the derived columns that roll up data from child tables.
| Column | Type | Notes |
|---|---|---|
| Name | Text | Display column — shown in relation lookups |
| Industry | Select | SaaS · E-commerce · Healthcare · Finance · Other |
| ARR | Number | Format: currency. Annual Recurring Revenue. |
| Owner | Person | The account executive responsible |
| Stage | Select | Prospect Customer Churned |
| Website | URL | Opens as a link in the row detail panel |
| Notes | Canvas | Rich-text account notes, embedded docs |
| Contact Count | Formula | Contacts.Filter([Company]=thisRow).Count() |
| Open Pipeline | Formula | Deals.Filter([Company]=thisRow).Filter([Stage]!="Lost").Sum([Value]) |
The two formula columns — Contact Count and Open Pipeline — are derived from child tables using Filter() and Sum(). They update automatically as contacts and deals are added. Add these columns after creating the Contacts and Deals tables.
The Contacts table holds the individual people you work with. The most important column is the relation to Companies — it connects each person to their account and makes company attributes available as lookup columns in Contacts.
| Column | Type | Notes |
|---|---|---|
| Name | Text | Display column |
| Company | Relation | → Companies table |
| Title | Text | Job title / role |
| Text | Format: Email | |
| Phone | Text | Format: Phone |
| Last Activity | Date | Updated by automation when an Activity is logged |
| Owner | Person | The rep who owns this relationship |
| Interaction History | Canvas | Free-form notes, email snippets, meeting summaries |
| Company Industry | Lookup | [Company].Industry — inherited from the relation |
| Company Owner | Lookup | [Company].Owner |
[First Name] & " " & [Last Name] and set it as the display column. This keeps lookups readable while keeping the data structured.
The Deals table is the heart of the CRM's commercial layer. It tracks every sales opportunity from first qualification through to won or lost. The Kanban board view — grouped by Stage — turns the Deals table into a visual pipeline your whole sales team can manage.
| Column | Type | Notes |
|---|---|---|
| Name | Text | Deal name — display column |
| Company | Relation | → Companies |
| Value | Number | Format: currency. Expected deal size. |
| Stage | Select | Prospect Qualified Proposal Negotiation Won Lost |
| Close Date | Date | Expected close or actual close |
| Owner | Person | Sales rep responsible |
| Probability | Number | Format: percent. 0–100%. |
| Weighted Value | Formula | thisRow.[Value] * thisRow.[Probability] |
In the Deals table, click + New view → Board. Set group-by to Stage. Set sort to Close Date ascending. This gives you a visual pipeline where cards move between columns as deals progress. Display Value and Owner as card fields for quick scanning.
The Activities table is your interaction log. Every call, email, meeting, or follow-up note becomes a row. Each activity links to a contact and optionally to a deal — so you can filter "all activities for Deal X" or "all activities for Contact Y" at a glance.
| Column | Type | Notes |
|---|---|---|
| Title | Text | Display column — e.g. "Discovery call with Sarah" |
| Type | Select | Call Email Meeting Note |
| Contact | Relation | → Contacts |
| Deal | Relation | → Deals (optional) |
| Date | Date | When the activity occurred |
| Notes | Text | Summary of the interaction |
| Owner | Person | Who conducted the activity |
Add a Calendar view of the Activities table with the Date column as the calendar field. This gives your team a visual week-by-week schedule of all customer touchpoints. Filter by Owner to see each rep's activity calendar.
When a new Activity row is created: Trigger = row added to Activities. Action = modify a row in Contacts where Contacts = this Activity's Contact. Set Last Activity = Today(). This keeps the Contact table's Last Activity column current without manual updates.
A Coda page acts as a dashboard by embedding multiple filtered views of the five tables in one place. The home page of your CRM doc is the central command center — no separate BI tool needed.
Embed the Deals Board view filtered to Stage != Won and Stage != Lost. Reps see their whole pipeline at a glance. Sort by Close Date to keep urgent deals visible.
Embed a filtered Calendar view: Date is this week. Grouped by day. Lets the team see what's scheduled and what was logged without switching tables.
Embed a Deals table filtered to: Stage = "Proposal" AND Close Date < DateAdd(Today(), 14, "days"). These are the deals closing soon that still haven't moved — flag them for immediate action.
Embed the Companies table sorted descending by Open Pipeline. Instantly see which accounts have the most revenue potential in play.
Automations are what turn a Coda CRM from a fancy spreadsheet into an active system. Each automation below handles something that reps would otherwise do manually or forget.
Trigger: row added to Deals. Action: send Slack message to #sales. Message: "New deal: [Name] — $[Value] for [Company]. Owner: [Owner]." Keeps the whole team aware of new opportunities.
Trigger: Deals.[Stage] changes to "Won". Action: create row in Projects table (from Lesson 21) with Name = "Implementation: " & [Deal Name], Owner = Deal Owner, Start Date = Today(). Bridges CRM to PM.
Trigger: scheduled, daily at 9am. Condition: Contact.[Last Activity] < DateAdd(Today(), -30, "days") AND Contact.[Stage] = "Active". Action: Coda notification to Contact.[Owner]. Prevent contacts from going cold.
Trigger: scheduled, daily. Condition: Deal.[Close Date] < Today() AND Deal.[Stage] is not Won or Lost. Action: Coda notification to Deal.[Owner]. Ensures deals don't silently sit past their close date.
Now that the CRM is built, layer in AI columns. These columns read actual row data — deal stages, activity notes, contact history — and generate summaries and recommendations per row. This is where Lesson 18's AI skills pay off at scale.