How to set up each template base in your Airtable workspace, import seed data, and configure automations.
1. Open airtable.com and go to your workspace
2. Click "+ Add a base" → "Start from scratch"
3. Name the base to match the template (e.g., "CRM", "Project Tracker", "Inventory Management")
4. Delete the default table — you'll create tables from the schema
Each schema file in schemas/ defines the complete structure of one base. Here's how to read them:
schemas/01-crm.json
├── tables[]
│ ├── name: "Contacts"
│ ├── fields[]
│ │ ├── name: "Email"
│ │ ├── type: "email"
│ │ └── description: "Primary contact email"
│ └── views[]
│ ├── name: "All Contacts"
│ └── type: "grid"
Field type mapping — Airtable field types used in schemas:
| Schema Type | Airtable Field | Setup Notes |
|---|---|---|
singleLineText | Single line text | Default field type |
email | Validates email format | |
phoneNumber | Phone | Formats automatically |
number | Number | Set decimal precision as noted |
currency | Currency | Set currency symbol in field config |
percent | Percent | Displays as percentage |
date | Date | Use ISO format (YYYY-MM-DD) |
dateTime | Date with time | Includes time component |
singleSelect | Single select | Create options as listed in schema |
multipleSelects | Multiple select | Create options as listed in schema |
multilineText | Long text | Supports rich text if enabled |
checkbox | Checkbox | Boolean true/false |
url | URL | Validates URL format |
rating | Rating | Set max stars as noted |
duration | Duration | Set format (h:mm or h:mm:ss) |
linkedRecord | Link to another record | Links between tables |
formula | Formula | Enter formula from schema |
rollup | Rollup | Configure linked field + aggregation |
lookup | Lookup | Pull field from linked record |
autoNumber | Auto number | Automatic sequential ID |
attachment | Attachment | File uploads |
For each table in the schema file:
1. Create the table: Click "+" next to your last table tab
2. Rename the default field: The first field is always the primary field — rename it to match the schema's first field
3. Add fields in order: Click "+" at the end of the field headers
4. Set up linked record fields: These create relationships between tables
5. Create views: After all fields are set up, create the views listed in the schema
Seed data CSVs are in the seed-data/ folder. Each CSV corresponds to one table in a base.
| CSV File | Target Base | Target Table |
|---|---|---|
crm-contacts.csv | CRM | Contacts |
crm-deals.csv | CRM | Deals |
project-tasks.csv | Project Tracker | Tasks |
inventory-items.csv | Inventory Management | Products |
content-calendar-posts.csv | Content Calendar | Posts |
hiring-candidates.csv | Hiring Pipeline | Candidates |
1. Open the target table in your base
2. Click the down arrow next to the table name → "Import data" → "CSV file"
3. Select the matching CSV file
4. Airtable will preview the import — verify:
5. Click "Import"
After importing, you may need to:
1. Make sure both tables exist with data
2. Create the linked record field in the source table
3. Manually link the first few records, or use Airtable's "Suggested links" feature
See automations/automation-blueprints.md for step-by-step automation setup instructions for each base.
Quick reference for the most common automations:
| Automation | Trigger | Action |
|---|---|---|
| New lead notification | Record created in Contacts | Send email to sales team |
| Overdue task alert | Date passes on Due Date field | Send Slack/email notification |
| Low stock warning | Quantity falls below Reorder Point | Create record in Orders table |
| Stage change update | Status field changes | Send notification + log activity |
1. Click "Share" in the top-right of the base
2. Choose permission level:
3. Send invite link or enter email addresses
The CSV has values that don't match the field type. Fix: Import as text first, then convert the field type afterward.
Linked record fields require both tables to exist first. Create all tables before setting up links, then import data.
Formulas depend on other fields existing and having data. Verify the referenced fields exist with the exact names used in the formula.
Option names are case-sensitive. "In Progress" and "in progress" are different options. Standardize casing before import.
Free Airtable accounts allow 1,200 records per base and 2GB attachments. The seed data CSVs are well within this limit, but your production data may not be. Consider Airtable Plus or Pro for larger datasets.
If you're setting up multiple bases, this order minimizes rework:
1. CRM (standalone, no dependencies)
2. Project Tracker (standalone)
3. Inventory Management (standalone)
4. Content Calendar (standalone, or link to CRM contacts as authors)
5. Hiring Pipeline (standalone)
6. Expense Tracker (standalone, or link to Project Tracker for project-based expenses)
7. Product Roadmap (optionally link to Project Tracker)
8. Customer Feedback (optionally link to CRM)
9. Event Planner (standalone)
10. Knowledge Base (standalone, benefits from all other bases being populated)
*Need help? Contact support@datanest.dev*
A quick reference for every formula used in the business templates, plus general-purpose formulas you'll use constantly.
Airtable formulas are similar to Excel/Sheets formulas but with some key differences:
{Field Name}&: {First} & " " & {Last}Full Name — Combines first and last name fields:
{First Name} & " " & {Last Name}
Days Since Last Contact — How stale is this relationship:
DATETIME_DIFF(NOW(), {Last Contact}, 'days')
Deal Aging — Days since deal was created:
DATETIME_DIFF(NOW(), {Created Date}, 'days')
Weighted Pipeline Value — Deal value adjusted by close probability:
{Value} * {Probability} / 100
Contact Health Score — Simple engagement indicator:
IF(
DATETIME_DIFF(NOW(), {Last Contact}, 'days') < 7, "Hot",
IF(
DATETIME_DIFF(NOW(), {Last Contact}, 'days') < 30, "Warm",
IF(
DATETIME_DIFF(NOW(), {Last Contact}, 'days') < 90, "Cool",
"Cold"
)
)
)
Days Until Due — Countdown to deadline:
DATETIME_DIFF({Due Date}, NOW(), 'days')
Is Overdue — Boolean flag for overdue tasks:
IF(
AND({Status} != "Complete", IS_BEFORE({Due Date}, NOW())),
"Overdue",
""
)
Time Estimation Accuracy — How close was the estimate:
IF(
{Actual Hours} > 0,
ROUND(({Actual Hours} - {Estimated Hours}) / {Estimated Hours} * 100, 1) & "%",
""
)
Progress Summary — Compact status for dashboards:
{Status} & " | " & IF({Days Until Due} > 0, {Days Until Due} & "d left", ABS({Days Until Due}) & "d overdue")
Stock Value — Total value of current inventory:
{Quantity On Hand} * {Unit Cost}
Margin Percentage — Profit margin per item:
ROUND(({Sell Price} - {Unit Cost}) / {Sell Price} * 100, 1)
Reorder Status — Should you reorder:
IF(
{Quantity On Hand} = 0, "OUT OF STOCK",
IF(
{Quantity On Hand} <= {Reorder Point}, "REORDER NOW",
IF(
{Quantity On Hand} <= {Reorder Point} * 1.5, "Running Low",
"OK"
)
)
)
Days of Stock Remaining — Based on average daily sales (set {Avg Daily Sales} manually or via rollup):
IF({Avg Daily Sales} > 0, ROUND({Quantity On Hand} / {Avg Daily Sales}, 0), 999)
Days in Current Stage — Time spent in current hiring stage:
DATETIME_DIFF(NOW(), {Stage Changed Date}, 'days')
Pipeline Velocity — Days from application to current stage:
DATETIME_DIFF(NOW(), {Applied Date}, 'days')
Compensation Status — Is the ask within budget:
IF(
{Salary Expectation} <= {Budget Max},
IF({Salary Expectation} >= {Budget Min}, "Within Range", "Below Range"),
"Over Budget by " & DOLLAR({Salary Expectation} - {Budget Max})
)
Monthly Total — Formatted monthly spend:
DOLLAR({Amount})
Budget Remaining — Percentage of budget used:
IF(
{Budget} > 0,
ROUND((1 - {Spent} / {Budget}) * 100, 1) & "% remaining",
"No budget set"
)
Over Budget Flag — Visual indicator:
IF({Spent} > {Budget}, "OVER by " & DOLLAR({Spent} - {Budget}), "OK")
Days Until Publish — Countdown to publish date:
IF(
{Status} = "Published", "Published",
IF(
IS_BEFORE({Publish Date}, NOW()), "Overdue",
DATETIME_DIFF({Publish Date}, NOW(), 'days') & " days"
)
)
Content Length Category — Bucketing by character count:
IF(
{Character Count} > 3000, "Long Form",
IF({Character Count} > 1000, "Standard", "Short")
)
These work in any Airtable base. Copy-paste and replace field names.
Capitalize first letter:
UPPER(LEFT({Name}, 1)) & MID({Name}, 2, LEN({Name}))
Extract domain from email:
MID({Email}, FIND("@", {Email}) + 1, LEN({Email}))
Create initials:
UPPER(LEFT({First Name}, 1)) & UPPER(LEFT({Last Name}, 1))
Truncate with ellipsis (for dashboard views):
IF(LEN({Description}) > 50, LEFT({Description}, 47) & "...", {Description})
Clean phone number (remove non-digits):
SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE({Phone}, "-", ""), "(", ""), ")", ""), " ", "")
Percentage with one decimal:
ROUND({Value} * 100, 1) & "%"
Currency formatting:
DOLLAR({Amount})
Thousands separator (workaround — Airtable doesn't have FORMAT_NUMBER):
IF(
{Value} >= 1000000, ROUND({Value}/1000000, 1) & "M",
IF({Value} >= 1000, ROUND({Value}/1000, 1) & "K", {Value} & "")
)
Quarter from date:
"Q" & CEILING(MONTH({Date}) / 3) & " " & YEAR({Date})
Day of week:
SWITCH(
WEEKDAY({Date}),
0, "Sunday",
1, "Monday",
2, "Tuesday",
3, "Wednesday",
4, "Thursday",
5, "Friday",
6, "Saturday"
)
Business days between dates (approximate — excludes weekends):
ROUND(DATETIME_DIFF({End Date}, {Start Date}, 'days') * 5 / 7, 0)
Next Monday:
DATEADD(NOW(), 7 - WEEKDAY(NOW(), 'monday'), 'days')
Start of current month:
DATETIME_PARSE(YEAR(NOW()) & "-" & MONTH(NOW()) & "-01", 'YYYY-M-DD')
Multi-level IF (status color coding):
SWITCH(
{Status},
"Active", "🟢",
"Pending", "🟡",
"At Risk", "🟠",
"Inactive", "🔴",
"⚪"
)
Null/empty handling:
IF({Field} = BLANK(), "Not set", {Field})
Multiple conditions (AND/OR):
IF(
AND(
{Priority} = "High",
IS_BEFORE({Due Date}, DATEADD(NOW(), 3, 'days'))
),
"URGENT",
IF(
OR(
{Priority} = "High",
IS_BEFORE({Due Date}, DATEADD(NOW(), 7, 'days'))
),
"Attention Needed",
"On Track"
)
)
When configuring rollup fields, use these aggregation formulas:
| Goal | Aggregation Formula |
|---|---|
| Count linked records | COUNTALL(values) |
| Count non-empty | COUNTA(values) |
| Sum values | SUM(values) |
| Average | AVERAGE(values) |
| Min/Max | MIN(values) / MAX(values) |
| Comma-separated list | ARRAYJOIN(values, ", ") |
| Unique count | COUNTALL(ARRAYUNIQUE(values)) |
| Percentage complete | COUNTA(values) / COUNTALL(values) * 100 |
1. Start simple: Build complex formulas incrementally. Get the inner expression working first, then wrap it
2. Check field names: Names are case-sensitive and must match exactly, including spaces
3. Watch for blanks: Many formulas break on empty fields. Wrap in IF({Field} != BLANK(), ..., "N/A")
4. Test with known values: Create a test record with predictable values to verify formula output
5. Use SWITCH over nested IFs: More than 3 levels of IF nesting? SWITCH is cleaner and easier to maintain
6. Date timezone issues: NOW() uses your account timezone. TODAY() returns date without time. Pick the right one
| Error | Cause | Fix |
|---|---|---|
#ERROR! | Syntax error in formula | Check parentheses and commas |
#VALUE! | Wrong data type | Ensure field types match operations (e.g., don't do math on text) |
#REF! | Referenced field doesn't exist | Check field name spelling (case-sensitive) |
NaN | Division by zero | Add IF({Divisor} > 0, ...) guard |
| Blank output | Formula returns empty string | Check your IF conditions and BLANK() handling |
*Need help? Contact support@datanest.dev*