Dashboard
The Dashboard is your morning landing pad. It rolls up the team's current pipeline, the work due today, and how every active sequence is performing. Most actions on the Dashboard are drill-downs -- click any card or stat to open the underlying tab pre-filtered to that slice.
What every widget shows
- Stat cards -- Companies, Leads, Emails Sent, Tasks Due, Pipeline Value, Active Customers. The Emails Sent and Pipeline cards respect the date-range picker and scope (Mine / Team).
- Weekly Performance + Engagement bubbles -- compact view of opens, clicks, replies, and calls plotted across the last 7 days.
- Play bar -- a single-row timeline of the day's outbound activity so you can see at a glance whether the team has been busy.
- Due Tasks (next 6) -- next-up tasks sorted by Priority (overdue first), Due date, or Engagement (warm-lead-first ranking).
- Upcoming Meetings -- the next few items on your connected calendar.
- Activity Feed -- unified call / email / system / meeting / note / sequence events with one-click filter pills.
- Emails Sent analytics + Best Time to Call heatmap -- both honor the date-range picker.
- Monthly Goals, Sequence Performance, Deliverability (30d), Call Activity (30d) -- per-cadence and per-deliverability rollups.
- Conversion Funnel + Lead Sources -- where leads enter the pipeline and where they fall out.
- Team Leaderboard + Your Performance -- per-rep totals so reps can see how they're tracking.
Filters that change what you see
- Scope picker (top of the dashboard) -- "Mine" filters every contact-derived widget to contacts you own; "Team" widens to the whole org.
- Date range picker -- changes the window the Emails Sent / Play bar / heatmap / aggregations compute against. Persists per-session.
- Activity Feed pills -- toggle individual event types in the feed without affecting other widgets.
Setup checklist card
On a fresh account, a Setup Checklist card appears at the top of the dashboard listing the remaining onboarding steps (connect a sheet, connect email, invite a teammate, etc). The card hides itself when every item is done OR when you dismiss it with the "x". You can bring it back from Settings -- Workspace.
Common questions
Leads
The Leads tab is your sales pipeline -- every contact you haven't sold yet lives here. A lead carries a sales-cycle status (New -> Working -> Connected -> Contract -> Closed Won/Lost) plus owner, source, and the tags / custom fields you map from your sheet. Closed-Won leads automatically convert to customers.
Creating leads
- Manual -- "+ Add Lead" button at the top of the tab.
- CSV import -- upload your existing list. The mapper auto-detects common column headers; required fields are flagged with a red banner you can't dismiss until they're mapped. Within-CSV and against-CRM dedupe both run pre-import so you can resolve duplicates before they land.
- Lead Search -- built-in prospect crawler. Pull leads from a list of source URLs or run a Prospect Finder query; matches against your existing CRM are flagged at import time so you can skip, update, or merge before they land.
- Webhooks -- Facebook Lead Ads + LinkedIn Lead Gen flow into the Leads tab automatically once you connect them in Settings -- Integrations.
The status lifecycle
Statuses are not free-text -- they're a controlled list that drives funnel reports + the auto-convert hook. The default states for leads are:
- New -- haven't touched yet
- Working -- in active outreach
- Connected -- had a live conversation
- Contract -- proposal / quote out
- Closed Won -- auto-converts to customer
- Closed Lost / Not Interested / Dead -- removed from active pipeline; surface in reports
Filtering, search, and saved views
- The filter bar above the list combines status, source, owner, tag, company, and any custom field. Toggle Include vs Exclude per filter to write "everyone except churned" queries.
- Saved filters live in your browser (per-device) so the same query you build today is one click away tomorrow.
- Search is full-text across name, email, company, phone, and title.
Bulk actions
Multi-select with the row checkboxes (or "select all visible") then use the sticky action bar to reassign owner, apply a tag, enroll into a sequence, or delete. Bulk owner reassignment respects org permissions -- non-admins can only reassign leads they currently own.
Duplicate detection & merge
The CRM watches your contact list for duplicates and surfaces them in a small Duplicates pill in the sidebar with a count badge. It looks at three signals at once: matching email (case doesn't matter), matching phone (any of the three phone fields, ignoring formatting so "(555) 123-4567" and "5551234567" still match), and matching name + company (exact or close fuzzy match). This catches duplicates that pure email matching would miss — the same person entered twice from different sources.
- Sidebar pill, not a pop-up -- duplicates surface unobtrusively. Click the pill to open the resolver. The count refreshes whenever you add, edit, delete, merge, or import contacts.
- Checked when you add a new contact -- if a possible match exists, you get an "Open existing / Save anyway / Cancel" dialog instead of silently creating the dupe.
- Checked at CSV import + Lead Finder -- the importer flags incoming rows that already match an existing contact and lets you skip, update, or import anyway.
The merge picker
Clicking Merge on a pair opens a side-by-side picker. Every field with a value on either side appears as two radio options. Fields where the values differ are highlighted yellow so you can spot them at a glance.
- Fields covered: name, first/last name, company, title, primary phone, all phones, website, street/city/state/zip individually (so address pieces from the contact you're removing aren't silently dropped), notes, sequence + step + due date, status + status reason, amount, LinkedIn, Facebook, and Created Date.
- The "Keep All from Contact 1 / 2" buttons at the top let you bulk-select one side. You can still override individual fields after.
- For the All Phones row your radio choice is honored — pick Contact 1 to keep only Contact 1's phones, or Contact 2 for theirs. Earlier versions ignored your pick and combined both; that quietly violated what the buttons promised.
What happens to linked records on merge
- Activity logs — combined from both contacts, re-sorted by date, attached to the contact you kept.
- Deals — reassigned to the kept contact automatically.
- Calls, scheduled emails, voicemails, recordings, invoices, subscriptions — cleaned up by a single behind-the-scenes sweep so nothing is left pointing at the contact you removed.
- If the cleanup sweep can't run for any reason, the merge surfaces a warning telling you exactly what might still reference the removed contact — you'll never get a silent partial merge.
Common confusion points
recordType = 'contact'. If the row
didn't move tabs, the status change didn't fire the auto-convert
(e.g. you set it directly in the sheet instead of via the CRM).
Open the contact and re-pick "Closed Won" -- the convert runs.
Customers
The Customers tab is everything after the sale: ongoing
accounts you're nurturing, renewing, or growing. A customer is
a contact with recordType = 'contact' and a
relationship status (Sold, Onboarding, Active / Renewed, At Risk,
Churned). They're the same database row as a lead -- just on
the post-sale side of the lifecycle.
How a lead becomes a customer
Two paths:
- Auto-conversion -- moving a deal to a Won stage flips the linked lead's record type to customer and stamps the contract start date.
- Manual -- on a lead row, set status to Closed Won and the convert hook fires.
Customer-only fields
- Contract start / renewal date -- drives renewal reminders.
- Check-in cadence -- Weekly / Monthly / Quarterly. Surfaces overdue check-ins on the Dashboard + Tasks.
- Last check-in + next check-in -- auto-computed from cadence and last_contacted timestamp; you can override.
- Health score -- computed from engagement, override-able with a manual score that always wins.
- Revenue timeline -- per-customer history of contract value changes.
Filtering + saved views
The customer filter bar is denser than the Leads bar because relationship segments matter more here. Filter by status, cadence, city/state, owner, tag, segment, or saved filter. Saved customer filters persist per-device.
Common confusion points
Companies
The Companies tab is an account-level rollup of every contact
that shares the same company string. Companies are
derived, not stored as their own records -- which keeps the
model simple but has a few sharp edges to watch for.
How grouping works
- Every contact carries a
companytext field. Companies tab groups by exact case-insensitive match on that string. - "Acme Corp" and "acme corp" are the same group; "Acme Corp." (trailing period) is a different group.
- Empty company strings are skipped (you don't get an "Unknown" bucket).
What you see per company
- Roster -- every contact at the company, with their status, last-contact date, and deal value.
- Pipeline rollup -- total open deal value at the account.
- Activity stream -- merged activity across every contact at the company.
- Coverage -- which rep owns which contact (useful when multiple reps split an account).
What the Companies tab can't do
- Add custom fields to a company (custom fields live on the contact)
- Rename a company in one click (rename it on every contact, or use bulk edit)
- Attach a deal directly to a company without a contact
Common confusion points
Deals & Pipeline
A deal is a specific opportunity: one contact, one value, one expected close date, one stage in your pipeline. Deals live in their own tab with a Kanban board (drag between stages) and a sortable list view. Every deal is 1:1 linked to a contact and optionally 1:1 linked to an invoice + a project.
Creating a deal
- From the Deals tab -- "+ New Deal" opens a modal. The contact picker is required; you can't create an unattached deal.
- From a contact -- the contact detail panel has a "+ Deal" shortcut that pre-fills the link.
- From a Quote -- accepting a quote auto-creates a deal pre-filled with the quote total and the contact.
- From a status change -- moving a lead's status to a configured "convert" status can auto-create a deal (toggle in Settings -- Workspace).
The stage lifecycle
Stages are org-configured (Settings -- Team -- Pipeline Stages if you're an admin). Each stage has:
- Key + Name + Color -- the column on Kanban
- Probability default -- auto-applied when a deal lands in this stage (you can still override per-deal)
- Won flag -- moving here triggers the lead-to-customer conversion (if enabled) and bumps probability to 100
- Lost flag -- requires a
lost_reasonto save; bumps probability to 0
Kanban vs List view
- Kanban -- one column per stage, drag-and-drop cards. Each card shows title, contact, amount, days-in-stage badge, and the deal's owner avatar.
- List -- sortable table with every field visible. Better for bulk-edit and CSV export.
The view toggle is at the top-right. Your last-used view persists per device.
Forecasting + weighted pipeline
The Dashboard's Pipeline Value stat sums
amount_cents x (probability / 100) for every open
deal (deals not in a Won or Lost stage). Probability auto-syncs
to the stage's default whenever you change the stage -- so
"Closed Won" deals always count at 100% and "Discovery" stays
at whatever the admin set as the default.
Activities + notes
Every deal carries an internal activity log: created, stage changes, field changes, won/lost, free-form notes. Add a note from the deal modal's inline input -- it saves immediately, no need to click the global Save. The activity log is capped at the most recent 500 entries per deal; older entries are trimmed when a 501st is appended.
Every deal activity also mirrors into the unified Activity Log so the team feed surfaces deal moves alongside calls / emails / meetings.
Linking
- Contact (required, 1:1) -- moves with the deal across stages.
- Invoice (optional, 1:1) -- bidirectional. Linking from either side stamps the FK on the other.
- Project (optional, 1:1) -- attach to track delivery once the deal is won.
Permissions
Common confusion points
stage_changed_at, which
only updates on stage changes. Editing other deal
fields doesn't reset the counter.
Lead Finder
Lead Finder is the prospecting tool built into the CRM -- it's the feature that gave CrawlSpace CRM its name. Two modes work together: Website Crawl pulls contact info out of a specific company's website, and Prospect Finder searches business directories to discover companies you don't have yet. Results are checked for duplicates against your existing CRM at import time so you decide per-row whether to skip, update, or create new before they land in the Leads tab.
When to use each mode
- Website Crawl -- "I know the company; I want their contacts." Point it at a domain (e.g.
acmecorp.com) and it walks the site looking for names, emails, phones, titles, and addresses on contact / about / team pages. - Prospect Finder -- "I want plumbers in Austin, TX." Searches business directories by industry + location and returns a deduplicated list of companies you can import.
Crawl depth
Website Crawl asks you how deep to scan: just the homepage, the homepage + 20 pages, all the way up to 500 pages. Deeper crawls catch contact info hidden in team / about / blog pages but take longer and produce more low-quality hits.
The credit system
Website Crawl is free and unlimited. Prospect Finder uses search credits because directory APIs cost money when Google Places is enabled. The rules:
- 10 free credits per month, rolling reset (not calendar month).
- $0.40 per credit after the free allowance, up to 30/month total.
- Cache hits are free -- if anyone on your team ran the same query in the last 24 hours, you get the same results with zero credits. The "served from cache" badge tells you when this happens.
- Refund on failure -- if a paid search debits a credit but the directory throws before returning results, you get the credit back automatically (with a transaction-log entry).
What counts as a "paid" search
Only Prospect Finder runs that include the Google Maps directory consume credits. DuckDuckGo, BBB, Yellow Pages, and Manta are all free directories -- a Prospect Finder run with Google unchecked is zero-credit.
Result quality + deduplication
- Cross-directory dedup -- if "Acme Plumbing" appears in Google + BBB + DuckDuckGo, you see one row, not three. Matching is by normalized website domain first, then fuzzy name + address.
- CRM dedup -- before any row imports, the CRM checks the existing contact list and flags duplicates so you can skip / update / merge instead of creating doubles.
- Email quality tiers -- personal emails (sarah@acmecorp.com) score higher than generics (info@acmecorp.com, contact@..., support@...). Both import, but the quality tier shows on the row so you know what you're getting.
Saved searches + history
Star a search to save it (give it a name -- "Atlanta plumbers, weekly"). Saved searches re-run with one click. The "Force fresh" toggle bypasses the 24-hour cache when you want to see what's new since the last run.
Lead source tagging
Every imported lead gets a lead_source stamp.
Set it once at the top of the Lead Finder UI ("CrawlSpace -
Prospecting", "Austin plumber blitz", whatever you want)
and every row from that search inherits it. The Source
Performance report uses these tags to compare conversion
rates by channel.
Common confusion points
Tasks
The Tasks tab is your daily action queue: every manual task you've created, every active sequence step that's due, and every overdue customer check-in surface here in one list, sortable + filterable + bulk-actionable. The Dashboard's "Due Tasks" card reads from the same queue, and Play Mode consumes tasks one at a time for focused execution.
Where tasks come from
- Manual tasks -- you create them with "+ Task" on the contact page, the Tasks tab, or from a task template.
- Sequence tasks -- when a contact's active sequence reaches a step that needs human action (Call / Email / Manual Task), the step shows up in the queue. Completion advances the sequence.
- Customer check-ins -- if a customer's
nextCheckInDateis past, a pseudo-task surfaces in the queue. Completion writes the check-in to the contact's activity log + recomputes the next date based on cadence.
Status lifecycle
Tasks are either open or completed. Open tasks split visually into overdue (red), due today (yellow), and upcoming (neutral). Completion is irreversible from the queue -- you can delete a wrongly-completed task from the contact's history if needed, but the activity log entry stays put.
Snooze, defer, reassign, prioritize
- Snooze -- hide the task from your queue until a specific time. Used when "I can't act on this right now, ping me later."
- Reschedule -- change the due date itself (not just snooze). Used when the actual deadline moved.
- Reassign -- hand the task to a teammate. Reassignment now writes an entry to the contact's Activity Log (old assignee -> new assignee + timestamp) so the audit trail isn't silent the way it used to be.
- Priority -- Urgent / High / Normal / Low. Sort by priority to surface what matters first.
Bulk operations
Select multiple tasks with the row checkboxes; the sticky bottom bar exposes:
- Complete -- now reports the actual completion count (e.g. "Completed 11 of 12 tasks (1 skipped -- not in your list)") instead of falsely assuming all-or-nothing
- Reassign -- bulk hand-off to a teammate
- Snooze -- bulk push out
- Delete -- with confirm; permanent
Play Mode
The Play button on the Dashboard's Due Tasks card opens Play Mode: a full-screen overlay that surfaces tasks one at a time with the action button (Call / Email / Text / Mark Complete) right there. Completing one auto-advances to the next without leaving the modal -- the elimination of context-switching is why most reps process 2-3x more tasks per hour in Play Mode than navigating normally.
Templates
Save a checklist of related tasks as a template ("Onboarding", "Post-demo follow-up") and apply it to any contact with one click. The template stamps each task with its relative offset (e.g. "Day 3", "Week 2") so the dates fill in automatically from the application date.
Filters + saved views
Filter by owner (mine / team), priority, status, source (manual / sequence / check-in), and overdue / today / upcoming buckets. Sort by due date, priority, contact name, or last activity. The active filter persists across tab switches within the session.
Activity log integration
Every task completion writes to the contact's activity
log AND to the unified Activity
Log (event_type task_logged). Task
reassignments now also write a system_event
entry so the trail of who-owned-what-when is captured.
What's deferred
- Kanban / calendar view -- only the list view ships today. Use the Projects tab if you want a Kanban for project-scoped items.
- Subtasks / parent_task_id -- the DB supports nesting, but the cards don't render children yet.
- Recurring tasks -- recurrence_rule is on the schema, but the UI to set "every Monday" isn't wired into the task editor yet. Saved templates work fine as a one-time apply.
Common confusion points
My Work (cross-project items)
Different from the Tasks tab. Tasks surfaces engagement work — sequence steps, customer check-ins, manual outreach to-dos. My Work surfaces project items where you're the assignee, rolled up across every Job and Internal Project you're on. Two separate queues, two separate lifecycles, two separate mental modes. Both deserve daily attention; neither replaces the other.
The cross-project query
My Work pulls every row from project_items
where assignee_id = me (scope = Mine,
default), joined with the parent project and the
column's status definition. Scope = All widens to
every item in your org regardless of assignee —
useful when covering for a teammate or auditing the
team's backlog.
Filters + group-by
- Filter: Open / Completed / Overdue / Due this week / Today / Snoozed / All
- Group by: Project (default) / Status / Priority / Due date
- Tag search: surfaces only items carrying a chosen label — useful when a tag spans multiple projects (e.g.
q4-launch,permit-pending)
Today view
Toggle the Today view at the top of the tab to collapse the filter dropdown into one button. Today = open AND (overdue OR due today), minus actively-snoozed items. Your morning triage view — three minutes to walk it and pick what matters today.
Stats strip
The strip at the top of the tab shows:
- Done this week (with percent change vs last week — "+ new" sentinel if last week was zero)
- Done this month
- Open + overdue (red when > 0)
Counts run as parallel COUNT queries against
project_items, scoped to Mine or All to
match the filter strip below. Honest aggregates —
no per-item enumeration, no row-limit caps.
Snooze
Click the snooze icon on any item to hide it from
Today / Open until a chosen time: 1 hour, 4 hours,
tomorrow, 3 days, 1 week, or a custom datetime. The
item keeps its original due date — snooze is purely
a presentation filter that hides the row until
snoozed_until passes. The Snoozed
filter surfaces actively-snoozed items so you can
manage them (un-snooze, edit, complete) without
waiting.
Bulk actions
Tick checkboxes on multiple items to reveal the sticky bulk action bar:
- Mark Done — looks up each item's project's Done column and moves them there (each project can have its own Done column id; one SQL UPDATE per item, all fired in parallel)
- Snooze — same presets as single-item snooze, applied to all selected at once
- Reassign — pick a teammate; updates assignee_id on each selected item
- Delete — permanent, with confirm
Keyboard shortcuts
- J / K — move highlight down / up
- Space — toggle Done on the highlighted item
- S — snooze the highlighted item (opens the snooze picker)
Shortcuts only fire while My Work is the active tab AND you're not typing in an input. Mirrors the Tasks tab's keyboard model.
What's deferred
- Time tracking — when the in-item log-time UI ships, My Work cards will show estimate-vs-logged progress; today the columns exist but no UI writes them.
- Bulk time logging — once time tracking ships, "log 1 hour to all 5 selected items" will join the bulk action bar.
Common confusion points
Email & Inbox
The Inbox tab is your full conversation history with every contact, threaded by Gmail or Outlook depending on which provider you connect. Sends go out from your real address (not a no-reply), opens and clicks are tracked, and replies land back in the contact's activity log automatically. New messages show up in the inbox in real time — you don't have to hit refresh.
Connecting Gmail or Outlook
Go to Settings → Communication → Email Integration. Pick a provider, click Authorize, grant the permissions Google or Microsoft asks for (read + send + modify on your mailbox), and the connection is live. The CRM keeps the connection alive automatically — you don't have to reconnect every hour.
Send-as aliases
Gmail and Outlook both let you send as a different
verified address (e.g. sales@yourcompany.com
instead of your.name@gmail.com). Configure the
alias in your Google / Microsoft account first, then add
it under Settings -- Email Integration -- Send-as
overrides. The CRM uses the override on every
outgoing send.
Composing a message
- Templates + variables -- pull from your saved templates;
{{first_name}}/{{company}}/{{your_name}}tokens auto-substitute from the contact + the sender. - Attachments -- click the paperclip; provider limits apply (25 MB Gmail, 35 MB Outlook).
- Schedule send -- pick a future time; the email queues up and sends itself at that moment, even with the CRM tab closed.
- Signature -- toggle on/off per-send; default comes from Settings -- Account -- Email Signature. Rich-text signatures with inline images are preserved.
- Video -- click the camera icon in the compose toolbar to record / pick from the Video Library and embed a tracked thumbnail.
Threading + conversation view
The Inbox tab defaults to Threaded view — one row per conversation, with reply counts and last-activity timestamps. Flat view shows one row per message. The CRM uses your provider's own threading, so replies from the contact's side stay attached to the right conversation even if the subject changes.
Tracking
- Open tracking -- a tiny invisible image is embedded in every outgoing send. When the contact's mail client loads the image, the message is marked Opened in your inbox.
- Click tracking -- links in the body are rewritten so click-throughs roll up in your reporting.
- Replies -- inbound messages are matched to the original send automatically; matching replies are stamped on the contact's activity log without you doing anything.
Bounces + suppression
Hard bounces (5xx -- invalid mailbox) auto-add the address to your suppression list and skip on future sends; soft bounces (4xx -- temporary) accumulate over multiple sends before triggering suppression. The full bounce log lives in Settings -- Communication -- Email Integration -- Bounce + Suppression, and you can manually lift a suppression from there.
Activity log integration
Every email send writes an entry into the unified
Activity Log (event_type
email_sent) plus the contact's own
logs array. Replies write
email_replied; opens and clicks fire
email_opened / email_clicked.
Filter the Activity Log by event type to see just email
activity for a person, a team, or the whole org.
Common confusion points
Sequences
A sequence is an ordered list of touchpoints -- emails, calls, texts, and manual tasks -- that you assign to a contact. Each step has a relative delay ("3 days after the previous step"), automated steps fire on their own, and manual steps surface in the Tasks queue when they come due. The Sequences tab is where you build + maintain the playbooks; enrollment happens from the contact panel, the Bulk view, or automatically on lead conversion.
Step types
- Email -- subject + body, with
{{first_name}}/{{company}}/{{your_name}}token substitution. Marked Automated sends from your connected Gmail/Outlook on schedule. Left Manual surfaces as a task that pre-fills the compose modal when you click it. - Call -- a call task with an optional script. Opens the built-in dialer pre-dialed to the contact's number on click.
- Text -- an SMS step. Sends from your registered 10DLC or toll-free number; same STOP-keyword + suppression enforcement applies.
- Manual Task -- free-form description ("send LinkedIn connect request", "drop off swag bag"). Marks complete with a checkbox.
Building a sequence
- Click + New Sequence, name it.
- Add steps in order. For each step: pick the type, give it a name, fill in the content, and set the delay from the previous step (days / hours / minutes -- 0 means "send immediately").
- Save. The editor blocks save if any step is missing a name, any Email step is missing a subject, or any delay is negative -- the validations exist because silent saves at those points used to result in failed sends with no clear error.
Variable substitution + HTML safety
Tokens in email bodies and subjects substitute the
contact's data at send time:
{{first_name}}, {{last_name}},
{{company}}, {{email}},
{{phone}}, {{your_name}},
{{your_company}}, plus any custom field by
its key. Both {{token}} and
{token} forms work; matching is
case-insensitive.
Smith & Co
in the source but displays as "Smith & Co" in the
inbox -- safe against <script>
injection from imported data). Plain-text and SMS sends
still substitute raw, since there's nothing to escape.
Enrolling a contact
- From the contact panel -- click the Sequence dropdown, pick a sequence, optionally pick a starting step (mid-sequence enrollment uses that step's own delay as if the previous step had just completed).
- From the Bulk Enrollment view -- filter or search for the cohort, tick the checkboxes for who to enroll, pick the sequence + starting step, click Enroll. The preview chip at the top tracks "X of Y will be enrolled" live as you check + uncheck.
- On lead conversion -- if you've set an auto-onboarding sequence under Settings, every newly-converted lead is enrolled at Step 1 automatically. The Convert modal also offers a one-time sequence picker that overrides the default for this conversion.
How automated steps fire
When a contact is enrolled at a step (Step N) where the next step (Step N+1) is an automated Email, the CRM queues the email for send at the computed time. Every minute, the queue sweeper picks up due emails one at a time, sends them through the rep's connected mailbox, and advances the sequence to the next step (writing the new current step + due date back to the contact and queuing the next automated step if there is one).
How manual steps fire
Manual steps (Email-without-Automated, Call, Text, Manual Task) surface as tasks in the contact owner's queue when the step's due time arrives. The owner sees them in the Tasks tab + on the Dashboard. Completing the task advances the sequence to the next step (and queues the next automated send if applicable). If you ignore a manual task past its due date, the step stays open until you act -- sequences don't auto-skip.
Removing or changing a sequence
Picking "(none)" from the sequence dropdown removes the
contact from the active sequence + deletes any of their
pending scheduled_emails rows so nothing
sends after they're off the playbook. Switching them to
a different sequence does both at once: it clears the
pending rows from the old sequence, then enrolls them
fresh at Step 1 of the new one.
What's deferred
- Auto-pause on reply -- targeted for Q1 2026. Today, if a contact replies to a sequence email, the rest of the steps still send; you need to manually remove them from the sequence to stop.
- Conditional branching -- "if opened, send A; if not, send B" style logic. The reporting infrastructure (open + click events) is in place; the editor UI for branches isn't.
- Status/tag/form-submission triggers -- today's triggers are: manual enrollment (per-contact + bulk), lead-conversion auto-onboarding, and the Convert modal one-time picker. The marketing copy lists more triggers as on-roadmap; those land later.
Common confusion points
pending to sending) within the
second you clicked "remove", it goes out. To kill an
in-flight send you'd have to revoke at the provider --
not the CRM. In practice this is a once-a-year edge case.
scheduled_emails row didn't clear
when you re-enrolled, so the scheduler sent it alongside the
new one. Going forward, the guard's confirm prompt
catches this before the second row is written.
Drip Campaigns
Drip Campaigns are email-only nurture flows aimed at a subscriber list (separate from your CRM contacts). Every list member auto-enrolls into the campaign on the next hourly scheduled run; each step's HTML body fires through the campaign owner's connected Gmail/Outlook on its day offset. Use this when you want marketing nurture to a cohort. Use Sequences instead when you want 1-to-1 multi-channel outreach to individual contacts.
Subscriber lists vs. CRM contacts
Subscribers live in a separate subscribers
table — they came in via a web-form opt-in, manual
addition, or CSV import. They are NOT the same rows as
your CRM contacts, though the send-time personalization
will look up the matching contact (by email) so tokens
like {{company}} and {{title}}
resolve to the same values you'd see on the contact
panel. The Manage Lists button in the Drip Campaigns
header is where you create + edit lists.
Building a campaign
- Click + New Campaign, name it, pick the audience list.
- Add steps. Each step has a day offset (Day 0 = on enrollment; Day 3 = three days after enrollment), a subject, and a rich-text body.
- Steps must be in chronological order. Saving rejects "Step 2 at day 3" sitting after "Step 1 at day 7" — the send-time math is based on day offsets from enrollment, so out-of-order steps would deliver in the wrong sequence.
- Per-step Preview renders the email against a sample contact so you can verify variable substitution + the rendered body before activating.
How sending works
The drip processor runs hourly. Each pass it does two things:
- Auto-enrolls any new list members into every active campaign that targets their list, starting them at Step 1 with the right send time based on the step's day-offset.
- Sends every step that's now past due. Each enrollment is sent exactly once per tick — a temporary blip that re-runs the processor inside the same hour can't double-send.
A/B variants
Per step you can add weighted variants — each variant overrides the step's subject and/or body. At send time the system picks one variant per recipient using weighted random selection, records which one you got, and rolls up the engagement metrics separately so the Variant Performance card can show "Variant A had a 24% open rate, B had 31%" without you exporting anything.
Quiet hours
Each campaign can restrict sends to a specific window — e.g. "weekdays 9–5", "every day 10am–6pm", or "overnight 10pm–6am". Off-window steps defer to the next allowed moment instead of firing at 2 AM. Configured per-campaign under the editor's Quiet Hours toggle. Times are evaluated in UTC, so set your window relative to UTC rather than your local zone.
Stop conditions
- Stop on reply — when a recipient replies to any sent step, their enrollment auto-pauses and subsequent steps are skipped. Toggle is per-campaign, so a "welcome series" can keep going after replies while a "re-engagement" campaign stops.
- Stop on click — same idea but fires on link clicks instead of replies. Useful for re-engagement flows where any signal of life ends the nudges.
- Manual pause — reps can pause individual enrollments from the recipient list at any time. Paused recipients are skipped on every subsequent tick until you unpause.
- Unsubscribe — every send includes an unsubscribe link. A click immediately removes the recipient from future sends across every campaign.
Sender identity overrides
Per campaign you can set a custom From name
(e.g. "Customer Success Team") and Reply-to
address (e.g. cs@yourcompany.com).
Useful when the campaign creator's personal inbox
shouldn't field replies. The save flow validates the
reply-to email format before persisting — a typo here
used to silently ship with every send and bounce every
recipient reply.
Trial vs paid
Reporting
Drip Campaign Performance lives in the Reports tab. It surfaces per-campaign sends, opens, clicks, replies, and (when variants exist) per-variant engagement breakdowns. Drip Campaigns is also a first-class source object in the Report Builder, so you can cross-cut "sends per rep × campaign status × month" or "campaigns owned by trialing accounts" without leaving the product.
What's deferred
- Conditional branching — "if step 1 opened, send A; if not, send B" style logic. The reply/click events are already tracked, but the editor UI for branches isn't built.
- Per-subscriber timezone — quiet hours are evaluated in UTC, not the recipient's local time. A "9-5 in their timezone" feature would require capturing timezone on the subscriber row first.
- Holiday calendar — quiet hours support weekday filtering but not a "skip federal holidays" toggle.
Common confusion points
Scheduled Posts
Scheduled Posts is a reminder-based content library for LinkedIn and Facebook posts. Compose a post in the CRM, set a scheduled time, and when that time arrives the CRM fires an in-app notification ("Time to post on LinkedIn") so you publish manually to your social pages. There's no auto-publish today — the in-app "Library mode" banner at the top of the tab makes this explicit. The OAuth plumbing for direct publishing to LinkedIn Pages and Facebook Pages is on the roadmap.
Composer
Click + New Post to open the composer. The first decision is platform — LinkedIn (3,000-character limit) or Facebook (63,206). The composer enforces the platform's character cap with a live counter that turns yellow at 80% and red over the limit. Toggling platform mid-compose updates the placeholder copy + the audience label + the primary button to match.
- Content — the body that will go on the platform.
- Hashtags — legacy field; hashtags now live inline in the body. Anything you save here renders as a separate row beneath the post body for backward compatibility.
- Link URL — renders as a link preview (LinkedIn / Facebook auto-fetch the OG card when you actually paste it; the preview here is approximate).
- Media — comma-separated image URLs OR uploaded via the upload helper (10 MB cap per file, JPEG/PNG/GIF/WEBP only — SVG is rejected because of the inline-script XSS risk on the public bucket URL).
- Scheduled for — date/time picker. Setting this moves the post into Scheduled status; leaving it empty saves as Draft.
- Notes — private notes visible only to you, never part of the published post. Use for "ask Sarah for approval before posting" or revision history.
- Cross-post toggle (when first creating a post) — when enabled, saves the post as two separate posts (one LinkedIn, one Facebook) linked together so reports can group them. Edits to either side are independent after the cross-post.
Statuses
- Draft — saved but no schedule set.
- Scheduled — has a future scheduled time. Notification fires when the time arrives.
- Posted — you clicked the green check on the card after publishing manually to the platform. Timestamp recorded.
- Archived — hidden from the active grid but kept for history. Use the Archived filter to find them; click the unarchive icon to restore.
How the reminder fires
While you have a CrawlSpace tab open, the CRM checks every 60 seconds for any of your scheduled posts whose time has arrived. When it finds one, it claims the reminder slot (so two browser tabs can't both fire the same alert) and posts a "Time to post on LinkedIn / Facebook" notification with the post preview and a click-through to the Scheduled Posts tab. The card flips to a pulsing "Due now" pill until you mark it posted.
Sidebar badge
The Scheduled Posts nav item shows a red badge with the count of posts that are past their scheduled time but not yet marked as posted. Glance at the sidebar to spot overdue posts without opening the tab.
Mark Posted / Archive / Delete
After publishing the post manually to LinkedIn or Facebook, click the green ✓ on the card. The card flips to Posted and stops triggering reminders. The Posted status unlocks an "add performance metrics" action so you can paste in the actual likes/comments/shares from the platform — useful when reports group sends by author or month.
Archive hides the post from the active grid without deleting it. Delete is permanent and owner-only — teammates without ownership can archive but not delete.
Recurring posts
A scheduled post can be marked recurring with a weekly (day-of-week) or monthly (day-of-month) cadence + a fire time. The recurring the scheduler materializes a child post for each occurrence (banner label "from recurring") so you can edit individual occurrences without breaking the template. Useful for "every Monday morning at 8am" sales-team posts.
Templates
Save any composed post as a template (Save as Template button in the composer). Future composes can start from a template via the Templates button — pre-fills body / hashtags / link / media. Useful for the "Friday wrap-up" or "new-customer welcome" cadence where the structure is consistent and only the content changes.
Org-shared library
Posts are scoped to your org (or to your user if you have no org). Team members can see and edit any post in the org library. The owner gate is per record — only the original author (or an org owner / admin) can permanently delete; teammates can archive instead. Notification reminders only fire to the owner so two reps don't both get pinged for the same post.
Filtering, search, calendar view
The toolbar has search (matches body, hashtags, notes), platform filter (All / LinkedIn / Facebook), status filter (Drafts & Scheduled / Draft only / Scheduled only / Posted / Archived / All), owner filter (Mine / Team), and sort (Soonest first / Newest created / etc). The calendar view shows scheduled posts on a month grid for visual cadence sanity-checking.
What's deferred
- Direct OAuth publishing — V2 will add a "Publish Now" button next to "Mark as Posted" once you OAuth into LinkedIn Pages or Facebook Pages. The reminder model carries over for accounts that aren't connected.
- Server-side reminder firing — today the 60-second notification check runs in your browser, so closing the tab pauses reminders. A future server-side reminder system will fire reminders as email or in-app notifications regardless of whether you have the CRM open.
- Multi-image carousels — the composer allows multiple image URLs but the preview renders the first four as a grid. True carousel support comes with the OAuth publish flow.
- Post-engagement analytics — V2 will fetch engagement metrics back from LinkedIn/Facebook into the contact's engagement score. Today you manually enter likes/comments/shares on the Posted card.
Common confusion points
<script> blocks that execute when
the file is fetched directly from the public bucket
URL (e.g. when a teammate shares the URL as "check
out this image"). Use PNG or JPEG instead — LinkedIn
and Facebook also don't accept SVG post media.
Marketing & Forms
The Marketing tab is your lead-capture hub. It bundles four related surfaces that all feed inbound leads into the same CRM contacts table: the Web Form Builder (drag-and-drop forms embedded on your site), QR Forms (the same forms shared via a scannable QR code), Facebook Lead Ads auto-import, and LinkedIn Lead Gen Forms auto-import. Each source independently checks the incoming email against your existing CRM contacts before creating a row, so a lead who fills out a Web Form, then a Facebook Lead Ad, then a QR Form ends up as one contact with three lead-source labels appended — not three separate rows.
Web Form Builder
Click + Create Form on the Marketing tab to open the builder. Split-view editor (left = field list + settings, right = live preview). Drag / add fields, set per-field labels + types + CRM mappings, style the submit button + success message, and save. The form gets its own UUID-keyed URL the moment you save.
Embed
From the form list, click Embed. A
modal shows you a self-contained HTML+JS snippet (no
external CSS or JS dependencies) — paste it into any
page on any platform (WordPress, Squarespace, Wix,
raw HTML, etc.). The snippet creates the form, handles
client-side validation, POSTs to a public
/.netlify/functions/form-submit endpoint
using only the form's UUID key, and shows the success
message inline (or redirects to your configured
thank-you URL).
QR Forms
From the form list, click the purple QR
Code button. The modal generates a 400x400
PNG via qrserver.com, with copy-link, download, and
print actions. Scanning the QR opens a mobile-friendly
hosted version of the same form. QR submissions are
tagged QR Form: [name] instead of
Web Form: [name] so reports can split
event/trade-show leads from website leads.
Form submission pipeline
- Honeypot check — every form embeds a hidden
_cs_hpfield. Bots fill it; humans don't see it. If filled, the submission silently 200s (bot thinks it worked) and no lead is created. - Rate limit — max 15 submissions per IP per form per hour (returns 429 over the cap).
- reCAPTCHA v3 — optional per-form layer. Enable on the form + paste your Google reCAPTCHA secret key in Settings → Marketing. Scores under 0.5 silently reject.
- Required field validation — server re-validates required fields (the client validation is just a UX hint).
- Dedup by email — if the submitter's email already exists in your CRM (org-scoped if you're a team, user-scoped otherwise), the existing contact is UPDATED instead of duplicated. Empty fields fill, lead-source trail appends, notes append with a timestamp. Status / lead owner / active sequences are preserved — they belong to whoever's been working that contact.
- Write to whichever storage mode the form owner has selected (CrawlSpace native or your connected Google Sheet).
- Post-submission hooks (fire-and-forget so a slow side effect doesn't block the success response): auto-assign owner (round-robin or fixed user), auto-enroll into a Sequence and/or Drip Campaign, send the submitter a receipt email, send the owner a notification email, fire Facebook Pixel / Google Tag / LinkedIn Insight Tag events on the form-page side.
Facebook Lead Ads
Connect under Settings → Integrations → Facebook. You sign in to Facebook, grant CrawlSpace permission to read your Page leads, then pick which Pages to subscribe. For each subscribed Page you set a default Tag and a default Sequence — every inbound lead from that Page lands with the tag attached and the sequence enrolling them at Step 1.
The same email-dedup behavior runs for Facebook leads — if a lead's email is already in your CRM, the existing contact gets updated (with "Facebook Lead Ad" appended to the lead-source trail) instead of creating a duplicate. Every Facebook webhook hit is logged inside the CRM so you can troubleshoot misses without digging through server logs.
LinkedIn Lead Gen Forms
When live, the flow mirrors Facebook: connect, select your ad accounts, set per-account default Tag + Sequence, leads arrive automatically, same dedup + routing as every other source.
Lead routing across all sources
All four sources land in your CRM through the same dedup logic, so cross-channel duplicates resolve to one contact. The lead-source field accumulates a pipe-separated trail (e.g. "Web Form: Contact Us | Facebook Lead Ad | QR Form: Trade Show 2026") so the Source Performance report can attribute touches without losing earlier ones.
Marketing tab dashboard
The top of the Marketing tab shows four stat cards — one per source — with totals + this-month + this-week counts. Each card links into the underlying lead list pre-filtered to that source. The "leads over time" sparkline gives you a quick sense of which channel is accelerating without opening Reports.
What's deferred
- LinkedIn Lead Gen — Connect button — gated on LinkedIn Marketing Developer Platform app approval. Code is live; UI activates on approval.
- Facebook / LinkedIn lead → round-robin assignment — web forms support it; social webhooks fall back to per-page/per-account default routing. A future pass will share the post-submission hook between all three handlers.
- Facebook / LinkedIn lead → submitter receipt + owner notification email — same gap as round-robin. Web-form-only today.
- Form analytics by field — the per-form submission counter is in place, but "which question made people abandon" requires per-field abandonment logging we haven't built.
Common confusion points
SMS & Texting
SMS is a regulated channel in the US -- carriers won't deliver your messages until your business + your message content are registered (10DLC for local numbers, separate verification for toll-free). The CRM walks you through registration in-app, then routes every send + reply through your registered number with full STOP-keyword + suppression enforcement.
The painful part first: 10DLC registration
If you're using a 10-digit local number (your area code, not 800/833/844/etc), US carriers require 10DLC registration -- two steps:
- Brand registration -- your business name, EIN, address, use case. ~1-3 days to approve. The brand identifies who is sending.
- Campaign registration -- describes your messaging program: opt-in language, sample messages, expected volume. ~1-4 days to approve after brand approves. The campaign identifies what you're sending.
Both happen in Settings -- Communication -- Phone System -- SMS Registration. Total cost: $10 ($4 brand + $10 campaign, one-time, via Square). Once approved, the CRM stores the Twilio Messaging Service SID and routes every outbound send through it.
Toll-free numbers (800 / 833 / 844 / etc)
Toll-free numbers have their own verification flow (separate from 10DLC). Submit your verification form in Settings -- Phone System -- Toll-Free Verification: business name, address, opt-in screenshots, sample messages. Approval takes 2-5 business days. Until then, the CRM blocks toll-free sends with a clear status message.
Sending an SMS
- Compose -- click any contact's phone number, or the SMS icon in the dialer / sequence editor. Type your message; the segment counter shows below the textarea (160 GSM chars or 70 Unicode chars per segment).
- Templates + variables -- pull from your saved templates; tokens like
{{first_name}},{{company}},{{your_name}}substitute from the contact + sender. Missing fields render as empty strings. - MMS -- attach up to 10 images per message. MMS counts as 1 segment but charges 4 cents (vs 1 cent for SMS) since Twilio's per-MMS cost is higher.
- Schedule send -- pick a future date/time; the message queues up and sends at the right minute, even with the CRM tab closed. Each scheduled send is guaranteed to fire exactly once — a hiccup that retries the scheduler can't double-send.
- Broadcasts -- send one message to many contacts. The CRM staggers the queue at 5-second intervals to stay within Twilio's per-second long-code throughput.
STOP / START / HELP compliance
US carriers require honoring opt-out keywords. The CRM handles this automatically:
- Inbound "STOP" / "STOPALL" / "UNSUBSCRIBE" / "CANCEL" / "END" / "QUIT" -- auto-inserts the sender into your suppression list. Future sends to that number are blocked at both live-send time AND scheduled-send time (the scheduler re-checks at fire time so a queued broadcast can't blow past an opt-out that arrived after scheduling).
- Inbound "START" / "SUBSCRIBE" / "UNSTOP" -- removes the suppression. The contact can receive messages again.
- Inbound "HELP" / "INFO" -- the CRM doesn't auto-respond; you can reply manually with your contact info.
Suppressions are per-user OR org-wide. Manual lift available from Settings -- Phone System -- Suppressions, with the original inbound message body attached for the audit trail.
Inbound conversation threading
Incoming SMS routes to the matching contact's thread via E.164 phone normalization. If the sender isn't a contact yet, the message appears in the "Unknown number" filter in the SMS tab so you can create a contact from it. MMS attachments are re-hosted into your CRM on receipt (the original media links from the carrier expire after 24 hours; the CRM keeps your copies indefinitely).
Sequences integration
SMS steps inside a sequence respect the same suppression + registration checks as direct sends. If the contact opted out, the SMS step is skipped (not failed) and the sequence advances to the next step.
Contacts also have a Texting Available field (Yes / No / Not Set). When set to No, the SMS button is disabled and sequence SMS steps skip that contact -- useful for explicit opt-in tracking on contacts you haven't gotten STOP from but never got positive opt-in from either.
Costs
- Outbound SMS: 1 cent per segment (passthrough Twilio rates)
- Inbound SMS: 1 cent per segment
- MMS: 4 cents per message
- 10DLC registration: $4 (brand) + $10 (campaign), one-time, paid via Square
- Vetted brand (extended verification, faster throughput): +$45 one-time
- Toll-free verification: free
SMS pulls from the same credit pool as voice calls (Settings -- Billing).
Common confusion points
Calls & Dialer
The CRM ships with a browser-based phone (powered by Twilio) plus a Power Dialer for working through a list of contacts back-to-back. Calls auto-log, dispositions are configurable, recordings + transcripts attach to the contact, and DNC suppression is enforced at the moment of dial. No second softphone tab, no copy-paste numbers.
Setting up Twilio
Settings -- Communication -- Phone System walks you through creating a Twilio subaccount (we create it under our parent account so you don't need a separate Twilio bill), buying a local or toll-free number, and -- for toll-free numbers -- submitting the carrier verification form. Toll-free calling is gated by carrier verification; the CRM watches your verification state and notifies you the moment it approves.
Click-to-call
- Anywhere a phone number appears -- contact card, lead row, search results, deal modal -- clicking it opens the dialer pre-loaded with the contact context.
- Power Dialer queue -- queues a whole list. Auto-advances on disposition save so the rep doesn't have to click between calls.
- Manual -- type any number into the dial pad and hit call.
Recording rules
Call recording is feature-gated at the org level (Settings -- Phone System -- Recording). When it's on, every outbound and inbound call records by default. You can customize:
- Per-call disable -- toggle the mic icon in the dialer to record-this-call or not-this-call before placing.
- Inbound announcement -- toggle the "This call may be recorded..." pre-roll for inbound calls (required in two-party-consent states).
- Outbound announcement -- same toggle for outbound calls.
- Announcement text -- customize the script Twilio reads.
Call dispositions
Every call ends with a disposition picker: Connected, Left Voicemail, No Answer, Busy, Not Interested, Callback, Appointment Set, Wrong Number, Do Not Call, or your own custom labels. Each disposition has:
- Slug -- immutable analytics key (so renaming the label doesn't break reports).
- Label -- what the rep sees; rename freely.
- Category -- drives downstream behavior.
dncauto-adds to the suppression list.callbackschedules a re-dial in the Power Dialer queue.
Admins manage the dispositions list in Settings; reps just
pick from it. The defaults ship with every org via
seed_default_call_dispositions().
DNC (Do Not Call) suppression
The dialer checks the suppression list before placing every call. If the destination number is flagged (the contact requested DNC, was a wrong number, etc.) you get a confirm dialog with the reason -- proceed only if the contact has since explicitly asked you to call back. Suppressions are:
- Per-user -- "this contact asked ME not to call again"
- Org-wide -- "no one at the company should call this number" (set by admins)
Disposition = Do Not Call auto-inserts a per-user suppression with a back-pointer to the call_record so you have an audit trail. Manual lift from Settings -- Phone System -- Suppressions.
Voicemail greetings (inbound)
Record one custom greeting per user. Inbound calls that go to voicemail play your greeting, record up to 2 minutes, drop the recording into the voicemail inbox, and trigger Twilio's speech-to-text. The transcript attaches to the voicemail row within 30-90 seconds of the recording finishing.
Voicemail drops (outbound)
Pre-record up to 5 voicemail templates ("Hi, this is Alex from Acme; give me a call back at..."). When you reach voicemail on an outbound call, click Drop in the dialer + pick a template; Twilio plays your pre-recorded message and ends the call. Cuts the per-call overhead from ~30 seconds of talking to ~5 seconds of clicking.
Power Dialer
- Lists -- create a list, add members (manual, from a filter, from a saved search). Optional daily quota with a confirm-to-override modal when you hit the limit.
- Dial mode -- queues the list, auto-advances on disposition save. Skip pushes the current member to the back of the queue.
- Callbacks -- disposition = Callback schedules the contact for re-dial in N days. The Callbacks view shows everything due today.
- Dispositions view -- per-list rollup of how the queue went (connected count, voicemail count, callback count, DNC count).
Business hours + after-hours forwarding
Settings -- Phone System -- Business Hours lets you configure your org's weekly schedule + holidays (IANA timezone aware). Inbound calls outside hours can:
- Go to voicemail -- default; uses your standard greeting
- Forward to another number -- your cell, an answering service, anyone
- Play a custom message -- "We're closed; please email..."
Call costs
Costs pass through Twilio at-cost (no markup):
- Outbound + inbound calling: ~$0.013-$0.022/min depending on destination
- Local phone number rental: ~$1.15/month per number
- Toll-free number rental: ~$2/month per number
- Recording: ~$0.0025/min
- Transcription: ~$0.05/min (opt-in)
Pre-flight balance check requires at least $0.10 to place a call. Top up from Settings -- Billing.
Common confusion points
/.netlify/functions/twilio-recording
using the recording SID (not the URL), so playback always
works as long as Twilio still has the file (retention is
set in your subaccount settings).
Hold, voicemail drop, and disposition safety
Calendar & Booking
The Calendar tab is your two-way sync with Google Calendar
or Outlook Calendar -- events you create in the CRM push
out to your provider, events you create in your provider
show up in the CRM. On top of sync, you can publish a
public booking page (think Calendly) at
/book/<your-slug> so prospects pick a
slot from your live free-busy without the back-and-forth.
Connecting Google Calendar or Outlook
Calendar connection rides on your email integration -- when you connect Gmail or Outlook in Settings -- Communication -- Email Integration, the calendar OAuth scope comes along. No separate "connect calendar" button. The Calendar tab uses the same access token to read + write events on your primary calendar.
Creating events
- From the Calendar tab -- click a date / time slot to open the new-event modal. Title, attendees (auto-completes from contacts), description, location, optional video call link (Meet for Gmail, Teams for Outlook). Save pushes to the provider, which echoes back into the CRM grid.
- From a contact -- the meeting button on the contact card opens the same modal pre-filled with the contact as the attendee.
- From a booking page -- prospect picks a slot; the CRM creates the event on the assigned rep's calendar + creates a CRM contact if one doesn't exist + schedules reminders.
Booking pages
Settings -- Communication -- Calendar & Booking -- New Page. Configure:
- Slug -- the public URL is
/book/<slug>. Globally unique. - Title + description -- shown on the public page header.
- Meeting types -- which durations the prospect can pick from (15 / 30 / 45 / 60 min, or your own custom types).
- Weekly hours -- which hours each day are bookable. Cross-checked against your live calendar free-busy in real time so a conflicting meeting blocks the slot.
- Buffer -- default 15 minutes before + after each booking. Prevents back-to-back meetings with no breathing room.
- Minimum notice -- the prospect can't book a slot less than N hours out (default 2 hours).
- Maximum advance -- the prospect can't book a slot more than N days out (default 30 days).
- Intake questions -- short answer fields the prospect fills out at booking. Answers are stored on the booking row and shown in the calendar event description.
- Assignment strategy -- for shared team pages: single owner, round-robin (cycles through assigned reps), or first-available.
Reminders
Every booking schedules two CRM-sent email reminders out of the box: 24 hours before and 1 hour before. These come from your real mailbox (Gmail or Outlook), on top of whatever popup notifications your provider fires. Each reminder is guaranteed to fire exactly once even under retry or clock skew.
Cancel + reschedule
- Cancel from the CRM event modal -- removes the event from your provider calendar.
- Reschedule via the booking page reschedule link -- the prospect picks a new slot; the CRM patches the existing event.
- Provider-side delete (you delete the event in Google Calendar directly) -- next sync removes it from the CRM grid.
Spam protection
The public booking endpoint has a hidden honeypot field
that catches bots. Real humans don't fill it; bots do.
Triggered submissions get logged as
status='spam' in the bookings table for
audit, and the bot is silently returned a success
response so it doesn't retry.
Slot race protection
Two prospects clicking Confirm on the same slot at the
same time used to both succeed (a fairly embarrassing
bug). The submit handler now pre-checks the bookings
table for any existing status='created' row
at the same time + same rep, and returns a 409
"that slot was just taken" if found. The window is
narrowed from multi-second (the calendar API roundtrip)
to milliseconds.
Costs
Calendar sync and booking pages are free with your CRM subscription -- no per-booking or per-page fees. Reminder emails go out via your connected mailbox (Gmail / Outlook) and count against your provider's daily send quota, not a CRM credit pool.
Common confusion points
Video Messaging
Record a personalized video right in the browser (or upload
one), embed it into an email or SMS, and the recipient
opens a branded landing page at
/v/<token> with play tracking + a Reply
CTA. Two tabs surface the feature: the Video
Library (your reusable recordings) and
Video Activity (every send + how it
was received).
Recording vs uploading
- Record -- two modes: Camera Only (talking head) or Screen + Camera (share a window/tab with your camera as a small bubble overlaid on the screen). For Screen + Camera you also pick the bubble shape (circle / rounded / square) and which corner it lives in.
- Upload -- drag-drop or pick a file. Accepted formats: MP4, WebM, QuickTime / MOV. Max 3 minutes, 2 GB. The browser extracts a thumbnail at the 1-second mark; if extraction stalls (rare on huge files), the upload still completes with a neutral placeholder you can replace via Customize.
Your library
Up to 25 videos per user. The Library shows a card per video with title, duration, file size, engagement stats (sends / plays / play rate / avg watched), and chips for any customizations (chapters, CTA, custom thumbnail, team-shared). Rename inline. Delete with the trash icon -- that removes the file from storage AND any future sends point at a "video no longer available" page (existing sends keep their tracking history).
Sharing one-to-one
- Copy Link -- mints a fresh tracking token and copies the URL to your clipboard. Paste into any chat, email, SMS, or doc.
- SMS -- pick a contact and send the tracked link as a text directly from the library card.
- Email composer -- the camera icon in the compose toolbar inserts a video thumbnail + tracked link inline. The recipient sees a clickable poster.
Broadcasting to many
The Broadcast button on each library card opens a recipient picker. Two ways to add recipients:
- Search + check from your contacts (live filter on name / email / company)
- Paste a comma / newline / semicolon-separated list of email addresses (validated + deduplicated against your selection)
Each broadcast creates one parent send record plus one per-recipient send with its own unique landing URL, so per-recipient analytics still work. Capped at 500 recipients per broadcast; if your paste exceeds that, the modal asks whether to send the first 500 or cancel and split.
Entity linking
Each send can be tied to a specific contact, deal, or quote so the Activity Log can drill back from the send into the related CRM record. The send composer captures the contact automatically; manual linkage from the broadcast modal lets you tie sends to a specific deal or quote if relevant.
Activity tracking
- Engagement stats -- per-send: opened, played, max watch %, watch progress bar, drop-off histogram (10% buckets).
- Reactions + comments -- recipients can react with an emoji or leave a short comment from the landing page. Rate-limited per IP (30 reqs / 10s) to slow accidental click loops.
- First-play notification -- the sender gets an in-app notification the first time a recipient plays the video. Subsequent plays don't re-notify (avoids spam if the recipient re-watches). The first-play event is guaranteed to fire exactly once even if the recipient hits play twice in quick succession.
- Activity Log entry -- every send writes a "Video sent" event to the unified Activity Log; first play writes a "Video played" event (direction = inbound).
Video Activity tab specifics
The Video Activity tab in the Insights sidebar group is the unified send-by-send timeline (vs the Video Library which is the per-content view, and contact detail which is the per-recipient view).
- 500-send cap. The tab loads the 500 most-recent sends so the page stays snappy. For high-volume reps with more than 500 sends, the KPI tiles surface "Sends shown: 500 of X" plus a yellow caveat banner. Pre-fix, the tab silently treated 500 AS the total and reported wrong play-rate / completion percentages for big senders; the true total now comes from a separate head-only COUNT query.
- Owner vs member scope. Org owners and admins see every send across the org. Regular members see only their own sends — teammates can't see each other's individual sends unless they're owner / admin.
- Filter dropdown -- All sends / Played only / Not yet played / Watched fully. "Watched fully" means the recipient reached 100% of the video; "Played" means any play started.
- 30-second cache. The tab caches engagement data for 30 seconds so navigating between Library / Activity / contact panels is fast. Hit Refresh after you know someone just watched to bypass the cache.
- "Open ↗" link. Opens the recipient's view of the video in a new tab using the same tracking token they received — useful to check what they actually saw. Caveat: clicking your own Open ↗ does generate a view-tracking row on your own send (not a sender-mode preview yet — that's roadmap).
Retention + auto-cleanup
A daily cleanup sweep removes recording files older than 30 days unless they were viewed in the last 14 days OR watched deeply (50%+) at any point. The check looks at the most-recent play on each send, so a recipient who watches at day 25 keeps the file alive until day 39. Reactions + comments on expired sends are cleaned up too, so nothing dangles.
Costs
Recording, storage, and bandwidth are included in your CRM subscription — no per-video or per-play fees. Most teams stay well within the included allotment thanks to the 25-video library cap and 30-day temp-file retention.
Common confusion points
Meeting Recordings
Meeting Recordings is a unified library of every Google Meet and Microsoft Teams recording you've been part of, auto-filed to the matching CRM contact by attendee email. The video files stay in your Google Drive (for Meet) or OneDrive / SharePoint (for Teams) — the CRM stores the metadata + transcript + a fresh signed URL on demand. No third-party recorder (Gong, Otter, Chorus) needed; the integration just imports what your meeting platform already records.
Connecting a provider
Open Settings → Integrations → Meeting Recordings. Click Connect Google Workspace or Connect Microsoft 365. Standard OAuth flow grants the CRM read-only access to:
- Google —
meetings.space.readonly(list recordings + transcripts),drive.readonly(resolve the MP4 file by id). - Microsoft —
OnlineMeetingRecording.Read.All+OnlineMeetingTranscript.Read.All+Files.ReadWritefor the Drive resolve (Microsoft Graph requires the broader scope set).
Connection tokens are stored encrypted on our side. Only the connection owner can disconnect — the pulled recordings + transcripts stay visible to the whole org so a coverage rep can find a former teammate's calls.
How the sync works
Every 15 minutes the CRM checks each connected Google / Microsoft account for new recordings since the last sync. For each one it finds:
- Pulls the recording's metadata (start/end time, attendee list, file location) plus the transcript when one is available.
- Matches attendee emails to your CRM contacts (only your contacts — never a teammate's).
- Files the recording against every matching contact. Re-running the sync within the same 15-minute window is safe; it won't create duplicates.
Auto-attachment by attendee email
Each recording's attendee list is matched against your CRM contacts by email. A meeting with you and two CRM contacts auto-attaches to both contacts — open either contact and the Meeting Recordings card lists all their recordings, newest first. External attendees (no matching contact) are listed by name + email but don't get auto-attached.
Why playback links rotate
The CRM never stores the video file itself. When you click Play, the CRM generates a fresh playback link to your Google Drive / OneDrive / SharePoint file that's valid for about an hour. This keeps the file in your own storage (no duplication, no extra storage cost on your CrawlSpace bill) and prevents anyone from hot-linking the file outside the app.
Public share links
Click Share on any recording to create a public link. Anyone with the link can view the recording + transcript without a CrawlSpace login. Set an optional expiry, and revoke any time — the link stops working immediately.
AI summary
Click Summarize on any recording with a transcript. The CRM passes the transcript (truncated to 200k chars to keep API costs predictable) to Claude Haiku 4.5 and stores the response. Idempotent — re-clicking shows the cached summary unless you check Re-generate. Summary includes a paragraph overview + a bulleted action-items list with suggested assignees (the rep creates the actual tasks manually; auto-creation is on the roadmap).
Transcript search across the org
The search bar at the top of the Meetings tab matches against transcripts org-wide. Find every call where "pricing", "renewal", "competitor", or any objection phrase came up. Real-time picture of which deals are at risk + a coaching feedback loop for the whole team.
Connection status
The header strip shows a per-provider chip:
- green ok — last sync succeeded.
- yellow "Reconnect needed" — your connection was revoked (admin pulled the app, password changed, etc.). Click Reconnect.
- orange admin_consent_required — Microsoft 365 only; an admin needs to approve the app for the tenant before the user-level token works.
- red error — the most recent sync failed. Hover to see the last error message; click Reconnect or wait for the next 15-minute sync to retry.
What's deferred
- Zoom recordings — no API integration today. If Zoom is your primary platform, use the Drive / OneDrive export from Zoom's cloud recordings and attach manually for now.
- Real-time transcription — transcripts come from the provider post-call, not from CrawlSpace mid-call. Use Meet / Teams native transcription to enable.
- Auto-create tasks from action items — the AI summary lists action items with suggested assignees; the rep creates the matching Tasks manually. Auto-creation is on the roadmap.
- Speaker diarization beyond provider output — we use whatever the provider transcribes. Meet labels speakers by attendee; Teams does the same. No additional ML reprocessing.
Common confusion points
Jobs & Projects
Jobs and Internal Projects are two sidebar tabs
pointing at the same underlying project-management
feature, filtered by project_type:
Jobs = customer-facing service work
(linked to a CRM contact + deal); Internal
Projects = internal team initiatives with no
customer attached. Same boards, items, sprints,
custom fields, and views — different audience.
Creating a project
- Click + New Job or + New Project depending on which tab you're on. The created project carries the right
project_typeautomatically. - Required: name. Optional: description, color, customer link (for Jobs), start/end dates, priority, project lead.
- Date sanity check: if end < start, you'll get a confirm dialog ("Target end date is earlier than the start date. Continue anyway?") — sometimes you legitimately mean "delivered last quarter," so it's a warn-not-block.
- On create, the CRM seeds a default column set (Backlog / To Do / In Progress / Done) which you can edit in Project Settings.
Five views per project
- Board — kanban with drag-and-drop between columns. Default view. Group By dropdown swaps the column dimension between status / assignee / priority / type / sprint.
- List — spreadsheet-style table with sortable columns. Faster for 50+ items.
- Timeline — gantt-style bars per item's start-to-due range with a red "today" line. Items without dates don't appear.
- Calendar — monthly grid with items plotted on due date, color-coded by status.
- Dashboard — stat cards (Total / Completed / Overdue / Progress % / Unassigned), status breakdown chart, item-type counts, recently completed list.
Items, subtasks, comments
Items have: title, description, type (Task / Bug / Feature / Software-parent / Milestone), priority (Urgent / High / Medium / Low), assignee, due date, labels, position, effort (story points), and an auto-incrementing item number per project (e.g. #42).
Subtasks are checklist children of an item — flat list, one level deep. Subtask completion count shows on the kanban card (e.g. "2/5"). Moving the parent to a "done" status auto-completes its subtasks.
Comments support @mentions (notifications fire to the mentioned user), are HTML-escaped before render, and URLs auto-linkify.
Watchers + notifications
Click Watch on any item to subscribe to its updates. Watchers receive in-CRM notifications when comments are added, status changes, or @mentions land. The project header bell icon shows all your project-scoped notifications — mark individually or all-at-once.
Sprints + custom fields + automations
- Sprints — time-boxed buckets with name, goal, start, end. Assign items to a sprint, filter the board by sprint to focus on the current iteration.
- Custom fields — per-project: text, number, date, dropdown, checkbox, URL, email, or formula. Appear in the item-detail sidebar.
- Status automations — per-column rules: auto-assign a user, auto-set a due-date offset, auto-notify someone when a card enters a column.
- Recurring items — set a recurrence rule (daily/weekly/monthly); completion materializes the next occurrence.
Quote → Job auto-create
When a lead converts to a customer (closed-won) the CRM can auto-create a Job from a template — columns, items, and subtasks pre-built, linked to the customer + deal contract value. Set up in Project Settings → Templates. Skips the "what did we agree to again?" kickoff conversation.
Cross-project view
The My Work view (sidebar nav, under Outreach) shows every item assigned to you across every project. Filter by Open / Overdue / Due This Week / Completed. Group by project, priority, status, or due date. Your morning starts in one place, not twelve project tabs.
Time-entry validation
When the time-tracking UI ships, the underlying
_pmLogTime helper enforces: hours must
be positive, and a single entry can't exceed 24
hours (longer entries are almost certainly a
forgotten timer or typo like "210" meant "2.10").
The hard cap protects burndown / invoice totals from
nonsense data the moment the UI ships.
What's deferred
- Time tracking — Estimate / Actual hours per item with Dashboard rollup. The Dashboard "Hours Logged" stat card already exists; it stays at 0 until the in-item log-time UI ships. Reports can already filter on logged + estimate hours.
- Dependencies — "Blocked by" / "Blocks" relationships between items. Coming in the next iteration.
- File attachments — URL or native upload on items. Coming in the next iteration.
- Hours → invoice line items — once time tracking ships, logged hours will flow into draft invoice line items on the linked customer (Jobs only).
Common confusion points
Inventory
Inventory is the products + services catalog (pricebook) that feeds Quotes + Invoices. Add a product once, drop it into any deal as a line item, and let the CRM track stock + revenue + margin from quote through invoice paid. Supports physical products, services, and recurring subscriptions in the same catalog. Single quantity-on-hand per product — multi-warehouse / multi-location is not supported.
Product types
- One-Time — service or physical good charged once when sold. Optional fields: cost, vendor, unit of measure (each / hour / day / sq ft / linear ft / cubic ft / lb / oz / gal), track-inventory toggle, quantity on hand, low-stock threshold.
- Recurring — subscription billed on a weekly / monthly / quarterly / annually cadence. Optional fields: setup fee, trial-period days, minimum-contract months, auto-renews toggle. The CRM stores the structure but doesn't auto-bill (that's a Stripe/Square responsibility — see Sync below).
When stock deducts
Per product you pick when stock decrements:
- Sold (default) — when the linked deal closes won.
- Quoted — when a line item is added to a quote (reserves stock optimistically). Risky if you quote heavily without closing — use only when stock is committed at quote time.
- Invoice paid — when the linked invoice is marked paid (automatically via Square / Stripe OR manually via Mark Paid). Matches "stock leaves the warehouse when money arrives" — best for physical retail.
- Manual — never auto-deducts; the rep clicks Adjust on the product card to record the change.
Concurrency-safe stock math
No double-deduction on invoice-paid
Every deduction is recorded in a ledger before applying. If the same invoice triggers the deduction twice (e.g. a manual Mark Paid plus a delayed Square notification for the same invoice arriving moments later), the second attempt detects the existing ledger entry and skips — guaranteeing one deduction per invoice no matter how many times the trigger fires.
Adjustments ledger
Every stock change writes a row to
inventory_adjustments: the product
ID, delta, before/after values, source
(invoice_paid / closed_won / manual / csv_import),
and a related invoice ID when applicable. The
product detail panel shows the full history. Use
the Report Builder to roll up "stock movement by
SKU last 30 days" or "manual adjustments by user."
Low-stock notifications
When stock crosses (down through) the per-product threshold, the CRM fires a notification + tags the product with a red "Low Stock" badge in the catalog grid. The notification is informational only — there's no PO auto-creation or vendor auto-email yet (see Deferred below).
CSV import
Bulk-load via the Import CSV button. 10 MB file
cap. Auto-detects column-name variations (e.g.
"Product Name" / "Item" / "Title" → Name; "Item
Code" / "Part Number" → SKU; "Wholesale" / "Cost"
→ Cost; "Reorder Point" / "Min Quantity" → Low
Stock Threshold). Smart type coercion:
$1,299.95 → 1299.95;
yes/no/true/false/1/0/x/on/off →
boolean.
Stripe + Square sync
- Stripe — two-way sync of product + price. Edits in CrawlSpace push to Stripe instantly; new products in Stripe pull back on the periodic sync.
- Square — one-time catalog import + ongoing stock-count sync. Square is the source of truth for stock; CrawlSpace pulls the current count on every sync.
Closed-won workflow
Marking a lead as Closed Won flips all "quoted"
line items to "sold", updates the contact's Amount
field to the sum of sold items, and decrements
stock for products with deduct_on='sold'.
Reopening a Closed Won deal (status change away
from Closed Won) prompts: "This contact has N sold
items. Reopen them and restore inventory?" Confirm
to flip sold → quoted + restore stock + revert the
Amount field. Cancel to keep items as sold (you'd
do this if you closed a different deal but want to
track the lost one separately).
Line items on quotes
Open any contact → scroll to Quote Items → click + Add Product. Picker shows active products only; inactive products stay available on past quotes but don't clutter the picker. Per line item edit: quantity, unit price override (without changing the master product), discount %. Totals + the contact's Quote Amount update inline with a half-second debounce.
Product image upload
5 MB cap per image. JPEG / PNG / GIF / WEBP only — SVG is rejected because SVGs can carry inline a piece of malicious code that runs when the image is fetched directly. Images are stored under your org's private space, so another org can't pull them even if they had the URL.
Margin reporting
Set Cost on each product to enable margin math.
The Report Builder exposes price,
cost, and a computed
margin = price - cost column for any
custom report. No FIFO / LIFO / weighted-average
cost accounting yet — Cost is the value at the
moment of report run, not the historical cost at
sale time.
What's deferred
- Multi-location / multi-warehouse — single quantity_on_hand per product. If you have multiple physical locations and need per-location stock, a dedicated WMS is a better fit.
- Lot / serial tracking — products are interchangeable units; no lot numbers or expiration dates.
- Reorder-point automation — low-stock crosses fire a notification only. No PO auto-creation, no vendor auto-email.
- Inventory valuation reports (FIFO/LIFO/weighted) — margin uses current Cost, not historical cost-at-sale.
Common confusion points
Quotes
The Quotes tab is a sortable + filterable inbox of every quote across every customer — distinct from the per-contact line-items workflow inside contact detail, which is great for one-offs. Each quote has its own lifecycle (draft → sent → viewed → accepted / declined / expired / cancelled / converted), a public accept link the customer signs without logging in, and one-click conversion into a real Stripe or Square invoice. Customer-facing branding pulls from your org settings; the audit-trail fields (typed name + IP + timestamp) are captured at acceptance.
Creating a quote
Two entry points:
- From contact detail — add line items to the contact's Quote Items panel and the quote auto-creates with the next sequential quote number (
Q-YYYY-NNNNper-org per-year). - From the Quotes tab — click + New Quote, pick a customer, the same line-item editor opens.
Status lifecycle
- Draft — still building. No public link minted yet.
- Sent — clicked Send Quote. A 32-char URL-safe token is minted and your Gmail/Outlook compose modal opens with the link pre-filled.
- Viewed — customer opened the public link at least once. View count bumps on every render.
- Accepted — customer typed their full legal name + checked the agree box. Acceptance time, signer name, and IP are recorded.
- Declined — customer clicked Decline (optional reason captured).
- Expired — auto-flipped once the expiry date passes (whichever comes first: a customer opening it, or the hourly scheduler sweeping).
- Cancelled — rep clicked Cancel from the editor (terminates the public link).
- Converted — accepted quote that was Convert-to-Invoiced. Terminal; can no longer be edited.
Accept / decline can't double-fire
View tracking
Every public-link render writes a row to a per-quote view log (timestamp + IP + user-agent). The editor shows "Opened N times, last on X" with a collapsible per-open table. Useful for "did they read it before declining?" forensics, and for spotting whether the customer forwarded the link (different IP / browser fingerprint on the same quote).
Convert to invoice
Once a customer accepts, the editor surfaces a Convert to Invoice button. One click and the line items snapshot into a real Stripe or Square invoice using whichever payment provider you've connected (Stripe is picked first when both are present). The customer gets an invoice email automatically and the quote flips to Converted with a link to the new invoice.
Generate contract from a quote
Accepted quotes also expose a Generate
Contract action. Click it to open the
Contract editor pre-filled with the customer + a
starter SOW body referencing the quote number. The
contract carries a quote_id back so
both objects show the linkage in their respective
views.
Discount + quantity clamping
Per-line edits — quantity, unit price override, discount % — are debounced (500ms) inline saves. Server-side clamping prevents nonsense values from landing in the DB: discount is clamped to 0-100, quantity and unit price are clamped to ≥ 0. Pre-fix, a tampered DOM or direct DB write could set discount=150 → effective line total goes negative; the convert-to-invoice math then silently clamped the invoice line to $0, losing the rep's pricing intent. The clamp now happens at write time so the quote never holds invalid math.
Public quote URL
The public link lives at
/.netlify/functions/quote-public?token=...
with a 32-char URL-safe token (unguessable). The
page renders the org's name + logo, the line items,
the totals, and a typed-name signature block.
Customer enters their full legal name, checks the
agree box, clicks Sign. No login or account
creation required. The same URL stays valid until
the quote enters a terminal state, at which point
re-visiting it shows the appropriate status banner
("Already accepted by X on Y" / "Declined on Y" /
"Expired on Y" / "Cancelled by the sender").
Acceptance certificate
Accepted quotes can mint a printable PDF / HTML
acceptance certificate via
quote-certificate — captures the
signer's name, IP, timestamp, and the full
line-item snapshot at acceptance time. Useful for
legal / compliance archives or for sending the
customer a "thanks for signing" confirmation with
a hard copy attached.
Lifecycle scheduler
Every hour the system auto-expires quotes whose expiry date has passed and that haven't already been accepted, declined, or cancelled. The same check also runs the moment a customer opens an expired quote, so the editor and the public page never disagree about whether a quote is still live.
Templates
Save any quote as a template via the Templates button — captures the line items + terms + expiry days + tax / shipping defaults. New quotes can start from a template to skip the from-scratch setup for "standard service package" or "annual-renewal proposal" workflows.
Owner gating
Each quote has an owner_user_id
(defaults to whoever created it). A team member who
isn't the owner sees the editor in read-only mode
with a "you don't own this quote" banner. Owners
and org owners / admins can edit / send / convert /
cancel. Status-based locks (accepted / declined /
converted) apply to everyone — including the
owner, since editing a signed quote would invalidate
the customer's acceptance.
What's deferred
- Multi-currency — single currency per org. No per-quote currency override.
- Tax tables / jurisdiction-based tax — tax is a per-quote text field, not auto-computed by ZIP/jurisdiction. For sales-tax compliance use TaxJar or similar and stamp the computed rate in the tax field.
- Quote versioning / amendments — each quote is a single document. Editing overwrites; there's no "v2 of Q-2026-0042" thread. If the customer asks for changes after acceptance, cancel + clone is the workflow.
- Real-time multi-author edits — single-author edits with last-write-wins. Two reps editing the same quote simultaneously will overwrite each other's changes silently.
Common confusion points
Contracts
Contracts is the in-CRM e-signature feature for NDAs, MSAs, SOWs, change orders, and any agreement that needs a signature. Customers sign through a public link — no login, no software install — and the CRM captures a full audit trail (typed name + IP + timestamp + user-agent + the exact body locked at sign time). Multi-recipient sequential signing, inline form fields (signature / initials / text / date / checkbox), reminder cadences, templates, and a one-click handoff from Quotes are all built in.
Creating a contract
From the Contracts tab click + New
Contract, or from a contact detail use
the + New Contract button on the
Contracts card. Title it, pick the customer
("From CRM" pulls from your leads/customers so
{{customer.*}} variables resolve at
sign time), set an optional expiry, and write the
body in the rich-text editor.
Variables + inline form fields
- + Insert variable… pulls CRM data live at render time:
{{customer.full_name}},{{org.address}},{{deal.amount}},{{today}}. - + Insert field… drops a form-field token the recipient fills when signing: Signature (typed name in script font), Initials (boxed), Text, Date, Checkbox. Each field is scoped to a specific recipient — recipient 1 can't accidentally fill recipient 2's slot.
Field tokens render as plain text in the editor
([[signature:customer_sig|1]]). To
remove a field, just delete the token. To reassign,
edit the number after the pipe (the recipient
index) or delete + re-insert.
Sequential multi-recipient signing
A contract can have 1 to N recipients who sign in
order. Recipient 2 doesn't see or sign anything
until Recipient 1 finishes. Each recipient has a
name, email, optional role label ("Customer" /
"Witness" / "Counter-signatory"), and (once
activated) a unique share_token they
receive via email.
Sign atomicity
Public sign URL — per-recipient tokens
Each recipient gets a unique 32-char URL-safe
share_token stored in
contract_recipients.share_token. The
public URL is /c/<token>. There's
also a legacy contracts.share_token
field for single-signer contracts that pre-date the
multi-recipient flow.
share_token on a
multi-recipient contract could sign as a "legacy"
signer, bypassing the per-recipient gate. The
resolver now rejects the legacy path when any
contract_recipients rows exist for
that contract — multi-recipient contracts MUST be
accessed via a per-recipient token.
Objections vs declines
- Raise concerns — recipient types an objection, chain PAUSES, you get a notification. You revise + click Resend on that recipient (fresh token + status reset) and the chain resumes.
- Decline — ends the contract entirely. Status flips to
declined; all remaining recipients' tokens are invalidated. No recovery — create a new contract.
Open tracking
Every public-link render logs to
contract_views with IP + user-agent +
referer. The editor shows "Opened N times, last on
X" plus a collapsible per-open log so you can spot
"did their lawyer open it from a different IP?"
forensics before signing.
Templates
Save any contract as a reusable template via the Templates button. Templates store body + default title + default expiry days + variable + field tokens. New contracts pick a template from the "Start from template" dropdown and inherit its structure as an editable draft.
Reminder cadence
The system automatically nudges pending recipients on a configurable cadence (typical default: day 3, day 7, day 14 after the last touch). Disable per-contract via the Auto Reminders toggle in the editor. The reminder email goes through the contract owner's connected Gmail / Outlook so it looks like a personal follow-up, not a robot.
Lifecycle scheduler
Runs hourly: auto-expires past-due contracts that aren't in a terminal state, fires the webhook to integrated systems on full execution, and handles edge cases where the in-flight chain advance failed and needs a retry.
Certificate PDF (audit trail)
The contract-certificate endpoint
spins up headless Chromium, navigates to the
public contract page with a
?print=1 flag (which suppresses the
sticky sign bar + success overlay), and captures
the rendered page as a PDF. Pixel-perfect parity
with what the recipient signed — same fonts, same
paper background, same signature rendering, no
duplicate renderers to maintain.
?token= (self-authenticating) or a
?id=<contract-uuid> from the
CRM-side button. The ?id= path now
REQUIRES a Bearer token tied to a member of the
contract's org — pre-fix, anyone who learned a
contract UUID (from logs, Slack, git history) could
pull the signed cert + audit trail. Both
CRM-side callers (Signed PDF + Draft PDF buttons)
now use authenticatedFetch + blob
download.
Quote → Contract handoff
Accepted quotes have a Generate
Contract button that opens the contract
editor pre-filled with the customer + a starter
SOW body referencing the quote number. The contract
carries a quote_id back so both
objects show the linkage in their respective views.
What's deferred
- KBA (knowledge-based authentication) — no SSN/credit-history identity questions before signing. Fine for typical B2B contracts; required by some regulated industries (real-estate closings, certain financial agreements).
- Notarization workflow — no remote online notary (RON) integration. For notarized docs use a dedicated RON service.
- Bulk send — no "send this contract to 200 customers in one batch" workflow. Each contract is a one-off.
- Branching / conditional fields — fields are always shown / always required for their assigned recipient. No "if checkbox A is ticked, show field B" logic.
- Native mobile signing app — signing works in mobile browser via the public URL (responsive). No dedicated iOS / Android app.
Common confusion points
Invoicing (Financial tab)
The Financial tab is a Stripe + Square
passthrough. The CRM stores
provider_invoice_id references and
reflects status via webhooks — money flows from the
customer directly to your Stripe / Square balance.
Zero CrawlSpace markup, zero funds held server-side.
Connect either processor (or both — invoices pick
Stripe first when both are connected, matching the
Quote → Invoice convert path).
Connecting a processor
Only org owners and admins can connect or disconnect a payment provider. Standard OAuth flow: click Connect Stripe or Connect Square, authorize on the processor's page, and the encrypted token is stored server-side. Once connected, every team member with Financial = Edit on their permission profile can create invoices using that connection.
Creating an invoice
- From a contact — open the contact, click + Create Invoice on the Financial card. Line items pre-fill if the contact has Quote Items.
- From a Quote — Convert to Invoice on an accepted quote (one-click; line items snapshot from the quote, double-click guarded).
- From the Financial tab — generic + Create button, pick a customer.
- Manually as paid — for cash / advance / wire that didn't flow through Stripe or Square, click Mark Paid in Cash/Advance on the invoice modal. Records the invoice locally + appends the payment to the contact's revenue timeline without touching the processor. Org owner/admin only.
Settings (per org)
- Payment terms — Due on receipt / Net 7 / 15 / 30 / 60 / 90.
- Default note — appears on every invoice.
- Late fee — % of unpaid total, applied after a grace-period window when the lifecycle scheduler runs.
- Customer email — subject line + branded HTML body with placeholders. Sent via your connected Gmail/Outlook account so the customer sees a real reply-to address, not a no-reply.
Forged-event protection
Both Stripe and Square cryptographically sign every payment event before sending it to the CRM. The CRM verifies the signature before acting on the event, so a forged "this invoice was paid" notification from anyone else is rejected automatically.
Mark-paid permissions + double-submit guard
Mark-paid is a money-side action — it triggers revenue updates, deal advancement to Won, stock deduction, and lead auto-conversion. Two guards protect it:
- Role gate — only owners and admins can mark an invoice paid. Members with hidden Financial access can't trigger it even by working around the UI.
- In-flight guard — the confirm dialog disables the button while the action runs, so a rapid second click can't create two duplicate invoice rows.
Late-fee + reminder automation
Every hour, the CRM sweeps overdue invoices: applies your configured late fee after the grace period passes, sends overdue reminder emails through the invoice creator's connected mailbox, and updates the dashboard's "overdue" panel. You don't have to babysit anything — set the late-fee + grace period on the invoice and forget about it.
Recurring invoices
Products with a recurring billing interval (set in the Inventory tab) generate a fresh invoice each period automatically. If you're using Stripe or Square subscriptions, the provider does the actual billing; otherwise the CRM creates the invoice ready for you to send.
Pagination on the invoice list
The Financial tab's invoice list supports pagination, up to 500 invoices per page, with a total count shown ("showing 1–200 of 847"). If your org has a long invoice history, use the page controls at the bottom to navigate beyond the first batch.
Sync from Square
The ↻ Sync from Square button pulls in invoices created directly in Square's dashboard (outside the CRM) and matches them to your CRM contacts where possible. Use this when you started with Square and want historical invoices visible inside the CRM. There's no equivalent Stripe sync — the Stripe webhook handles drift naturally because every Stripe invoice fires an event we capture.
What's deferred
- Multi-currency per invoice — single currency per org, inherited from the Stripe / Square account. No per-invoice currency override.
- Inline tax computation by ZIP / jurisdiction — tax is a numeric field on the invoice. For sales-tax compliance use TaxJar or similar and stamp the computed rate.
- White-label invoice URLs — payment pages are Stripe-hosted / Square-hosted. The customer's email is branded but the payment page itself is the processor's.
- Crypto / ACH / wire orchestration — whatever payment methods Stripe / Square accept in your processor account are what customers see. The CRM doesn't add or restrict methods.
Common confusion points
Document Library
The Document Library is centralized file storage for everything the team reuses or needs to share — proposals, contracts, one-pagers, pricing sheets, case studies, photos, brand assets. Lives in the Library sidebar group alongside Meeting Recordings. Org-shared by default (Team scope) so a coverage rep can find a former teammate's files; switch to Mine scope for just-your-uploads view.
Upload
Click Upload and pick any file (no file-type restriction — upload anything). The file lands in your active folder (or the root if no folder is selected). PDF + image previews render inline in the detail panel; other formats (Word, Excel, PowerPoint, video) download for opening locally.
Auto-categorization
On upload, the CRM picks a category from the file name (case-insensitive substring match):
proposal→ proposalcontractoragreement→ contractbrochure→ brochurepricingorquote→ pricing- everything else → other
Override the category any time from the document detail. Manual overrides are sticky — re-running text extraction doesn't reset them.
Folders
Build a folder hierarchy via the tree on the left. Create / rename / move folders, drag-and-drop documents in and out. Cycle prevention stops you from accidentally moving a folder into one of its own children (would otherwise create an infinite loop in the tree renderer).
Search
Two layers run together: full-text search inside PDFs, Word docs, and plain text (works on files with a real text layer — not scanned-image PDFs without OCR), plus a name-match fallback so you always get something even while content extraction is still running on a fresh upload.
Search spans your whole accessible library — folder filters are temporarily ignored while searching so you don't miss a hit in a different folder.
Filters + sort
- Scope — Mine (just yours) or Team (everything in your org you have access to).
- Folder — current folder or root (cleared during a search).
- Category — pick one of the auto-detected categories.
- Starred — toggle to show only starred docs.
- Contact / Job / Quote — filter to docs linked to a specific record.
- Sort — date / name / size, asc or desc.
Pagination
The list shows up to 500 documents per page. Orgs with thousands of documents can page through the full library instead of hitting an arbitrary cap.
Public share links
Click Share on any document to mint a public URL with an unguessable random token. Default expiry is 30 days (max 365). Anyone with the link can preview / download without a CrawlSpace login. Revoke any time — the link stops working immediately.
Link to records
Set a document's "Linked to" dropdown to a contact, job, or quote and the file shows up on that record's detail panel. Useful for "show me every photo attached to the Acme job" — one filter, every relevant file.
Bulk operations
- Bulk move — multi-select then pick a target folder.
- Bulk delete — multi-select and confirm; the file and the metadata are both removed.
Attach from library (in email composer)
The email compose modal has a From Doc Library button. Browse / search / star / multi-select to pick docs, click Attach. The CRM grabs the file at attach time and ships it inside the outgoing email. Critically: this is a snapshot, not a link — deleting the doc later doesn't break previously-sent emails. The recipient's saved copy is unaffected.
Text extraction
Runs automatically on upload to power the inside-the-file search. Re-triggering on the same doc is safe (no double-processing). If extraction fails on a particular file (encrypted PDF, corrupt file, unsupported format), the file is still downloadable and searchable by name — you just won't get inside-the-file content search for it until you replace the file with a clean copy.
What's deferred
- Inline preview for Office / spreadsheet files — only PDF + images render inline today; Word/Excel/PowerPoint download for local opening.
- Automatic version history per template — uploading a new file with the same name creates a new document, not a version. Use name suffixes ("MSA v2") for now.
- Per-file audit panel (previews + downloads + shares + deletes) — the dedicated per-file activity log + CSV export + access alerts land in the next iteration. Today the org activity log captures broad actions but there's no per-file drill-down.
- OCR for scanned-image PDFs — text extraction works on text-layer PDFs only. Scanned receipts/contracts aren't searchable by content.
Common confusion points
Reports
The Reports tab is a read-only viewing hub for every report — 27+ pre-built templates AND your custom reports — organized in collapsible folders. Building / editing happens in the separate Report Builder tab (Salesforce-style split: consuming vs building are different mental modes). Every report opens to a full-width chart + table viewer with Export CSV / Export to Sheet / Print-to-PDF.
Folder organization
Reports group by folder. Custom folders (My Reports + any user-created) sit at the top, then the seven built-in category folders: Sales & Pipeline, Lead Performance, Activity, Customers, Marketing, Project Management, Products. Each folder shows a count badge and is collapsed by default. Click + New Folder in the Reports header to create a custom folder; pick the "Move to..." dropdown on any custom report to relocate it. Built-in template reports stay in their original category — they can't be moved.
27+ canned reports
- Sales & Pipeline — Pipeline Value, Status Breakdown, Win/Loss, Revenue by Month, Conversion Funnel, Deal Aging
- Lead Performance — Source, Owner, Territory, Tag
- Activity — Status × Source, Owner Leaderboard, Sequence Performance
- Customers — Customer Revenue, Health, Upcoming Renewals, Check-in Cadence, Cohort Analysis, Sales Velocity Modeling, Win Probability Scoring
- Marketing — Newsletter performance, Drip Campaign performance
- Project Management — Items by Status, Workload, Items by Type, Overdue, Cycle Time, Bug Tracker, Story Points, Project Completion (+ Time Tracking when that feature ships)
- Products — Inventory
Viewer layout
Click any report row to open the full-width viewer. Chart on top (when a Group By is set), data table below. Toolbar buttons:
- Edit — opens in Report Builder pre-filled with the config.
- Chart type — dropdown in the chart header (Auto / Bar / Line / Pie / Table only). Auto picks the best fit based on data shape.
- Export CSV — downloads the current view (respecting filters).
- Export to Sheet — creates a new tab in your connected Google Sheet / Excel with the report data; tab named with the report + date for snapshot history.
- Print / PDF — pops a clean popup, captures Chart.js charts as static images, triggers your browser's print dialog. UI chrome (sidebar, headers) excluded.
Grouped detail view
Summary reports show group headers (with subtotals underneath) and individual rows under each group — Salesforce-style "both aggregate AND detail in one view." Click a group header to collapse / expand its rows.
Search + deep links
Use the search box in the Reports header to filter
across every report by name, description, or
category. Matches in any folder. Folders without
matches collapse out of the way. Every report has
a unique URL like
#reports/pipeline-value — bookmark,
share with a teammate, or open in a new tab; the
report opens directly to the viewer.
Custom icons + colors
When saving a custom report, pick from 18 icons (Dollar, Chart, Trophy, Trend, Target, User, Map, Tag, List, Bolt, Mail, Drop, Users, Clock, Timer, Bug, Rocket, Box) and any color from the picker. The icon + color appear in the gallery row for quick visual scanning.
Permissions
Reports tab access is gated by Permission Profiles (Settings → Organization → Permission Profiles): View can open + view but not edit; Edit can create + modify; Hide removes the tab from the sidebar entirely. Custom reports also respect the org boundary — a member from another org can't see your reports, regardless of how they try to reach them.
Open the Reports tabReport Builder
The Report Builder tab is where you create + edit custom reports. Salesforce-style layout: left panel has Outline + Filters sub-tabs; right panel has the chart on top + a data preview below. Live preview updates as you change settings.
Source objects (what you can report on)
Nine source objects in the
SOURCE_OBJECTS registry — each has
its own field set with per-field aggregations,
filterability, and groupability flags:
- Contacts (Leads + Customers) — storage-mode-aware
- Quote Line Items
- Sequence Sends — one row per email queued / sent through a sequence (powers "sends per sequence per month")
- Email Events — opens / clicks / replies on any tracked send
- Products / Inventory
- Newsletters
- Drip Campaigns
- Marketing Sends — unified send table across newsletters + drips
- Project / Job Items
Multi-object joins (Phase 2)
Join related sources via foreign keys registered in
the JOIN_REGISTRY: Contacts ↔ Quote
Line Items, Contacts ↔ Project Items (via
contact_id), etc. Joins use
LEFT-JOIN semantics so parent rows without
children still appear.
Outline tab
- Report Name (required), Description, Folder.
- Report On — pick the source object.
- Columns to Show — defaults pre-selected based on the source object. Override by checking / unchecking; reorder with ↑ / ↓ arrows.
- Group By (optional, but the KEY decision) — empty = flat list of records; set = report becomes a Summary with totals + a chart.
- Show Top N + Sort + Limit.
Filters tab
Tab label shows the active filter count (e.g. "Filters (3)"). Use AND / OR toggles to combine filters. Click + Add Filter — the UI adapts to field type:
- Date fields → date picker + relative date presets ("Last 30 Days", "YTD", "This Quarter"). Presets are stored as tokens (
last_30_days) so the report stays current as time passes. - Currency fields → $ prefix on the input.
- Status / Enum fields → dropdown of existing values pulled from the data.
Values to Calculate (Summary reports only)
When Group By is set, a "Values to Calculate" section appears in a blue highlight box. Add metrics: Count, Sum, Avg, Min, Max — each supports a WHERE clause for conditional aggregation (e.g. "Sum of Amount WHERE status = Won"). The chart auto-adapts to the metrics + data shape.
Date bucketing
bucketDate function used by
Summary aggregation now uses LOCAL time components
for every bucket type (day / week / month /
quarter / year). Pre-fix it mixed UTC + local —
the 'day' bucket called toISOString()
while 'week' / 'month' / 'quarter' / 'year' all
used getDate() / getMonth()
/ getFullYear(). For a user near
month-end in a negative-UTC timezone, the same
timestamp could land in May for the month bucket
but June 1 for the day bucket. Now consistent.
Formula fields (Advanced)
Expand the "Advanced: Formula Fields" accordion at
the bottom of Outline. Examples:
quote_amount - amount (gap to close),
(price - cost) / price * 100 (margin
%), logged_hours / estimate_hours
(effort accuracy ratio). The expression evaluator
is whitelist-validated — only arithmetic +
parentheses + recognized field names. No
function-call syntax, no string concatenation,
no SQL injection vector.
Live preview
The right panel updates in real time as you change settings. Data table shows below; chart shows above (when grouped). What you see while building is exactly what your saved report shows. Use this to sanity-check group cardinality + chart fit before saving.
Save flow
Click Save Report. Modal asks for a name (if not already set), an icon (18 options), a color (color picker), and a folder. Saves to the custom-reports list + navigates to the Reports tab with the new report open in the viewer.
Dashboards
Combine multiple reports into a single tile-view dashboard via the Dashboards section. Each tile renders the report's chart + key metric. Order tiles by drag-and-drop. Useful for "morning glance" or "weekly stand-up" dashboards that surface the 4-6 metrics that matter most.
Public sharing
Click Share on any report to mint a token-keyed public URL. The share captures a snapshot at the moment of share creation (frozen rows + chart config), not a live feed — so the public viewer can't pull updated data over time. Revoke any time. The snapshot-not-live design means a leaked share link can't be used to monitor your business in real-time after revocation.
What's deferred
- Scheduled / emailed reports — no auto-email of report PDFs on a cadence. Run manually + use Print/PDF for now.
- Pivot tables beyond group-by — single dimension at a time, no row × column pivots.
- Raw SQL editor — the builder generates the query; no raw SQL escape hatch.
- ML / predictive analytics — Sales Velocity Modeling and Win Probability Scoring are heuristic / rule-based, not ML-trained.
Common confusion points
Activity Log
The Activity Log is the unified audit trail — every meaningful CRM action lands here in chronological order, scoped to the org. Useful for compliance reviews, customer disputes ("when did we last contact them?"), onboarding new team members ("show me everything we've done with this customer"), and forensics ("who marked this invoice paid manually?").
Two views
- Per-contact — each contact's detail page has an Activity History card scoped to that contact. Type filter + time-range filter built in. This is the view you'll use day-to-day.
- Org-wide — the Activity Log tab in the Insights sidebar group surfaces every event across every contact, filterable by event type, actor (user), contact, and date range. Built for compliance reviews and quick "who did what" lookups across the whole team.
How the two views stay in sync
Every action writes to both views at once. The per-contact panel is the authoritative source — if a temporary network blip drops the org-wide view's copy of an event, the per-contact panel still shows it correctly. Any subsequent action on that contact catches both views back up.
Event types captured
- Email — sent / received, with direction
- Call — inbound / outbound, duration, disposition
- Text — SMS sent / received
- Sequence — enrollment, step completion, removal
- Status / Conversion — lead → customer, status changes, statusReason changes
- Revenue — deal won, invoice paid (manual + Stripe + Square)
- Document — uploaded, attached to record, shared publicly, deleted
- Contract — sent, viewed, signed, declined, voided
- Task — completed, reassigned, snoozed
- System — automated events (sequence auto-advance, scheduled jobs affecting a contact, etc.)
Filtering + search
The org-wide tab exposes:
- Event type dropdown — Email / Call / Text / Sequence / Status / etc.
- Actor — drop down to a specific team member to see only their actions.
- Contact — type-ahead picker to scope to one customer's history (mirrors the per-contact view but in the tab UI).
- Date range — uses your browser's local time so the boundary lines up with what you see in the picker.
- Search — substring match against the entry's details + contact name + actor.
Safe rendering
Email bodies in the detail panel render in a sandboxed preview so any code inside an HTML email can't escape and touch the rest of the CRM. Log text itself is fully sanitized before display so quirky characters in a contact's name or event details never break the UI.
Spreadsheet-mode parallel history
If you're in Spreadsheet storage mode, log entries also append to columns AN onwards on the contact's row in your Google Sheet or Excel file. Format: "YYYY-MM-DD — Contact Name — Type (Direction): Details". Creates a complete history inside your sheet alongside the CRM — useful if you ever leave the product (your audit trail isn't proprietary).
Public share
Click Share on any contact's activity entry (or the contact's full log) to mint a public URL with an unguessable random token. The public viewer serves a snapshot frozen at share-creation time, so revoking the share or editing the entry later doesn't leak through. Set an optional expiry; revoke any time.
Permission boundaries
Activity Log access is gated by the Permission Profile system (Settings → Organization → Permission Profiles). The org-wide tab respects the profile's Activity Log setting (View / Edit / Hide). Per-contact logs always show to anyone who can see the contact. Members of one org can never pull another org's activity — it's blocked at the database level, not just hidden in the UI.
Deletion + immutability
Activity Log entries are append-only from the UI — there's no delete button on individual events. Deleting a contact cascades to their activity entries (clean removal), but individual entries can't be edited or removed once written. This is intentional: the log is the audit trail, and an editable audit trail isn't an audit trail.
What's deferred
- Login / sign-in events — not surfaced in the CRM activity log today. The Members table's "last seen" timestamp is the closest in-app proxy.
- Permission-change events — when an admin changes a member's role or profile, the change isn't currently logged to the activity feed. Roadmap.
Common confusion points
Profile
The Profile tab is your personal landing page — distinct from Settings (which is org-wide). Click the user-circle icon at the bottom of the left sidebar to open it. Profile shows your identity, your appearance pick, your connected email account, your subscription (or permission profile, for non-owners), and a sign-out button. The Danger Zone at the bottom is the account deletion entry point.
Identity
- Avatar — auto-generated from the first two letters of your email, uppercased. No upload UI — the same initials appear next to your name in comments, @mentions, and the activity feed.
- Display name — what teammates see when you comment or get @mentioned. Edit it from Settings → Profile (Settings, not the Profile tab — legacy split).
- Email — the email address you signed in with. Changing it is a behind-the- scenes flow; ask support if you need it.
- Role badge — owner, admin, or member. Owner is unique per org. Admin is assignable.
Appearance — six themes
The theme picker offers six options. Your pick saves to your account and applies instantly across the CRM — no page reload.
- Original — default, blue accents.
- Calm — soft, muted palette for long sessions.
- Dynamic — lighter, vibrant accents.
- Midnight — deep dark for night work.
- Editorial Green / Editorial Blue — magazine-style typography with green or blue accents.
Connected email account
Profile shows which provider (Gmail or Outlook) you have connected and the email address the connection was set up with. This row is read-only here — use Settings → Email Integration to connect or disconnect.
Subscription (or permission profile)
- Owners see plan name, status (active / past due / canceled), next billing date, and amount. The Manage Subscription button opens your billing portal.
- Members see their permission profile here instead — which tabs are editable, which are read-only, which are hidden. See the Permission Profiles topic in Settings for how those are assigned.
Sign-out — signs you out everywhere
The Sign Out button signs you out across every browser tab, every device, and the mobile app (all on the same account). If you only want to sign out the current tab, close the tab without clicking Sign Out.
Email signature — safety on send
The signature editor (lives in Settings → Email Integration → Signature) is a rich-text editor: formatting, links, and inline images all supported. Two safety nets run on every outbound:
- Sanitization before send — the signature is scrubbed of any embedded scripts or sketchy code right before the email ships. Defense in depth for the (rare) case of a signature pasted from a site that snuck in a tracking snippet.
- Image upload allowlist — signature images must be JPG, PNG, GIF, or WebP under 2 MB. Both the file type and the extension are checked, so a renamed file masquerading as an image is rejected.
Danger Zone — account deletion
Scroll to the bottom of Profile. The "Delete My Account" button requires typing your email address exactly — this prevents accidental clicks. Once confirmed, every piece of CRM data tied to your account is removed (contacts, deals, calls, recordings, voicemails, signature images, documents, settings) and your sign-in account itself is deleted.
Common confusion points
Contact Support
CrawlSpace does not have a chatbot, a help-desk
queue, or a self-service ticket portal. The
Contact Support button (in Profile,
in the sidebar Help menu, and at the top of Settings)
opens a modal that emails
joshua@crawlspacecrm.com directly
through your connected Gmail or Outlook account
— you reach a real person, every time.
The form
- Type — Report a Bug, Feature Request, How-To / Help, Billing Question, or Other. Your choice sets a priority tag on the email subject so the inbox can be triaged at a glance — Bug and How-To are tagged High, Feature Request and Billing are Medium, Other is Low. All five types route to the same person.
- Subject — optional but recommended. If you fill it in, it becomes the email subject (capped at 140 characters) so the ticket reads "[High] Calendar sync broken on Outlook reconnect" instead of the generic "[High] Report a Bug - CrawlSpace CRM Support".
- Description — required. Include reproduction steps for bugs, the relevant contact name when applicable, screenshots as attachments.
- Attachments — up to 5 MB per file, 20 MB combined. Allowed types: JPG / PNG / GIF / WebP screenshots, PDFs, and TXT / CSV. Other file types are dropped from the outbound email — share a link to the file in the description if you need to send something else.
What gets sent with your ticket
- Your name and email (so support knows who you are).
- An internal account ID (so support can pull up your data directly without playing 20 questions).
- Submission timestamp + priority tag.
- Your chosen type + subject + description + attachments.
No passwords, no connection tokens, no recent error logs are auto-attached. If you want support to see an error message, paste it into the description yourself.
Replies land in your inbox
When support replies, the response arrives in your normal email inbox — no portal login, no separate thread to track. It's just an email reply.
Provider requirement
Privacy + abuse controls
- Sign-in required — only signed-in users can submit, so the inbox isn't a spam target.
- Site-locked — the form only accepts submissions from the CrawlSpace site, so an attacker's website can't trick a signed-in tab into firing a ticket on your behalf.
- File-type allowlist — attachments outside the JPG / PNG / GIF / WebP / PDF / TXT / CSV set are dropped before the email goes out, so a renamed executable doesn't end up in the support inbox.
What this is NOT
- No live chat. No widget, no bot. The button is a form, the form sends an email.
- No tier-based SLA. Monthly and annual plans reach the same person at the same address.
- No 24/7 staffing. Replies target one business day during Eastern-time business hours.
- No phone support. Email only. If you need a screen-share, ask in the ticket and we'll schedule one.
Common confusion points
Notifications
Click the bell icon in the bottom-left sidebar (above the Settings gear) to open the notification panel. It slides in over the sidebar and shows up to 50 most recent notifications, newest first, with a red unread badge on the bell itself.
How fresh is the badge?
Notifications are in-app only — there's no browser permission prompt, no OS-level pop-ups:
- Every 20 seconds the CRM checks for new notifications and updates the badge and panel list.
- Every 60 seconds three local checks run: overdue tasks, upcoming meetings (within 15 min), and scheduled posts past their fire time.
- No browser permission prompt — nothing to click "Allow" on.
Worst-case lag from event to badge is about a minute. Events that come from outside the CRM (payments, video plays, contract signs) land instantly on the next 20-second refresh.
Notification types fired today
- New lead — fires when a fresh contact appears in your list (most commonly a web form submission). Click jumps to the contact.
- Overdue task — one ping per task the first time it crosses its due date. Click jumps to the Tasks tab with the task highlighted.
- Upcoming meeting — 15-minute heads-up before any calendar event starts. Click jumps to the Calendar tab.
- Voicemail / Missed call — fires when a voicemail lands or an inbound call is missed. Click jumps to the Calls tab and opens that record.
- Deal won / Payment received / Payment failed / Subscription started / Subscription canceled — fires automatically when your payment processor notifies us. Payment events also refresh your contact list so a lead that auto-converted to a customer shows up under Customers without you reloading.
- Video watched — the first time a recipient plays a video you sent them. The notification opens the right contact card even if your CRM has reorganized since the send.
- Time to post — scheduled LinkedIn / Facebook posts ping you the minute their scheduled time arrives.
- Low stock — fires when product stock crosses its threshold after a Closed-Won deal decrements inventory.
- Team join — fires to the org owner when an invited teammate accepts.
- Storage limit — soft warning when your contact count crosses 2,500.
Click-to-navigate behavior
Every notification knows where to take you. Clicking switches to the right tab and opens the relevant record:
- Contacts / Customers — opens the contact detail panel.
- Tasks — scrolls to and highlights the specific task in the list.
- Calls — opens the call detail panel (recording + transcript + notes).
- Products — opens the product detail panel.
- Calendar / Financial / Contracts / Scheduled Posts / Settings — switches to the tab and pops up a toast with the notification title so you know what triggered the click; the tab's own list is your jumping-off point.
Mark all read + per-item read state
- Click any individual notification to mark it read (and navigate).
- The Mark all read button in the panel header flips every row in one click; the badge zeroes out immediately.
- Unread items have a blue left border and bolder title text; read items go quiet.
Snooze persistence (tasks)
When you snooze an overdue-task notification for 1 hr / tomorrow / next week, the snooze sticks — it survives a browser reload and won't re-ping you on the next 60-second check. Earlier versions stored snooze locally only, so a refresh would resurrect every snoozed ping.
Common confusion points
Storage Modes
CrawlSpace stores your contacts in one of two places: Spreadsheet mode (your own Google Sheet or OneDrive Excel file as the source of truth) or CrawlSpace mode (our cloud database). The same CRM features work in both; the difference is where the rows live and which features are available. Set it in Settings → Database Storage.
Spreadsheet mode — what it does
- Reads from and writes to your own Google Sheet or OneDrive Excel file.
- Activity logs append into columns AN+ of the same sheet, so the audit trail travels with the data.
- Cancel CrawlSpace tomorrow and your data is still sitting in your Drive / OneDrive — no export step required.
CrawlSpace mode — what it does
- Contacts live in our cloud database, shared across your whole org so every team member sees the same list.
- Native CSV import, manual add, and the one-way Spreadsheet → CrawlSpace migration tool live in Settings → Database Storage.
- Uses a fixed contact schema (no per-user column mappings), so the Column Mappings nav item is hidden in this mode.
Switching modes — what actually happens
To consolidate, use the Migrate Existing Spreadsheet button in Database Storage settings. It one-way copies all contacts from your sheet into CrawlSpace storage, with email + name+company dedup against the org's existing CrawlSpace rows so re-running is safe.
What gets migrated, what doesn't
- Migrated: contact records (every field the schema covers), in batches of 25. Each migrated contact gets a stable identifier that persists across reloads.
- Not migrated: deals, tasks, sequence enrollments, call history, scheduled emails, invoices. These stay associated with sheet mode and reappear if you switch back. If you've relied on them in sheet mode, plan to either recreate them in CrawlSpace mode or keep the sheet connection alive for read-only history.
Two-way sync (Spreadsheet mode only)
Sync runs in both directions but the cadence is asymmetric:
- CRM → Sheet: instant on save. The moment you change a status, log a call, or edit a phone number in the CRM, the corresponding sheet cell updates.
- Sheet → CRM: on demand. Click the circular-arrow Sync button in the sidebar, or wait for the auto-refresh tick (every 5 minutes if you enabled it in Settings). A row you edit in Sheets lands in the CRM on the next pull, not in real time.
Conflict resolution is last-write-wins. If you and a teammate edit the same row in both places between syncs, whichever write reaches the sheet last is what you'll see — usually the CRM write, since it's instant. To avoid the rare collision, pick one place to edit a given row for a given session.
Feature parity — not 100%
Most features work identically in both modes. A few only work in CrawlSpace mode (or in Spreadsheet mode with Google Sheets specifically):
- Web forms / QR forms: Google Sheets sheet-mode and CrawlSpace mode both accept form submissions. Excel/OneDrive form receivers are coming soon — if you're in Excel sheet mode, form submissions return an error pointing you at Google Sheets or CrawlSpace mode.
- Lead-to-customer auto-conversion on invoice paid: requires CrawlSpace mode. In Spreadsheet mode you set the Closed-Won status by hand.
- Cross-source custom reports: joining contacts with marketing sends, invoice history, or revenue timeline needs CrawlSpace mode (spreadsheet rows can't be combined that way).
- Webhook-fed lead intake: Facebook Lead Ads + LinkedIn Lead Gen drop straight into CrawlSpace storage; Spreadsheet mode silently misses these because the webhooks can't reach your sheet in real time.
Org-shared by default
Common confusion points
Learning Library
The Learning Library is the in-app tutorial surface — not to be confused with this public Help Library, which is the marketing-site walkthrough you're reading now. Click the graduation-cap icon in the left sidebar after signing in to open the Learning Library: a categorized tile grid of step-by-step topics, some with interactive coachmark tours that highlight live UI elements while they explain.
What it covers
- A growing topic library across categories like Getting Started, Sales & Deals, Communication, Automation, Settings & Customization, Team & Collaboration, Project Management, and Notifications.
- Each topic has a written step list. About two-thirds also have an interactive coachmark tour ("Start Walkthrough") that runs inside the live app — spotlight, tooltip, optional action callbacks that switch tabs or open modals for you.
- Topics without a coachmark tour hide the Start Walkthrough button entirely — you don't get a button that fires a "Walkthrough not available" toast.
Search and filter
The search input above the tile grid is a substring match against three fields per topic: title, one-line description, AND the full step body text. So a search like "Jaro Winkler" surfaces the Duplicate Detection topic even though that phrase only appears in the steps, and "voicemail drop" surfaces both the Calls topic and the Settings Voicemail Drops topic.
- Input is debounced ~180 ms — typing a long query doesn't re-render on every keystroke.
- Press Esc in the search input to clear the query.
- Categories that have zero matching topics drop out of the rendered view so you don't see empty section headers.
Progress tracking
Each topic-detail modal has a Mark as Complete button. Completion is saved to your account so it follows you across devices.
Walkthrough engine (coachmark tours)
The coachmark tour engine lives in
tutorial-engine.js and is driven by
a per-topic step list keyed by topic ID. Each step
can include:
- A selector for the live UI element to spotlight.
- A title + text for the tooltip.
- An optional action callback that switches tabs, opens modals, or temporarily replaces a panel's HTML with a mock so the user sees what the feature looks like without affecting their real data.
- An optional waitFor selector so the tour pauses until the target element renders (useful when a step depends on a tab switch settling).
You can Prev / Next between steps, jump to any step via the tooltip header, or close the tour at any time — the engine cleans up the backdrop and any mock-HTML replacements on close.
Public help vs. in-app library
Two different surfaces, by design:
- This Help Library (the page you're reading now on the marketing site) is publicly readable and covers the core feature set section-by-section. Good for evaluation before signup.
- The in-app Learning Library (graduation-cap icon in the sidebar) is for signed-in users. It adds interactive walkthroughs that run against the live app, plus progress tracking and deeper how-tos you won't see in the public version.
Common confusion points
Settings
Settings is the org-wide configuration surface (distinct from Profile, which is per-user). The sidebar inside Settings groups related controls into sections; the visible set depends on your role (owner / admin / member) and your active permission profile. Members see only what their profile allows; owners and admins see everything.
Section map
- Profile — editable form for the Profile tab: display name, business name, mailing address, timezone. Saved fields fan out to anywhere your name appears (sequences, signatures, contact-detail owner badges).
- Database Storage — the Spreadsheet / CrawlSpace mode picker, sheet URL, column mappings, and the one-way migration tool. See the Storage Modes section for the full story on switching.
- Column Mappings — spreadsheet mode only. Per-field column-letter assignments (A through AM). The Save action blocks any two fields pointing at the same letter and surfaces an error toast naming the conflict, so a misconfigured mapping doesn't silently overwrite one column with another.
- Email Integration — connect Gmail or Outlook, edit your signature, set Send-As aliases, manage inbox sync. Signature image uploads cap at 2 MB and accept JPG / PNG / GIF / WebP only.
- Phone & SMS — phone-number purchase, voicemail greeting, voicemail drops, recording settings, 10DLC + toll-free SMS registration. Voicemail audio uploads accept MP3 / M4A / WAV / WebM / OGG up to 3 MB.
- Calendar & Booking — booking page slug, weekly hours, buffer rules, meeting-type list (15/30/45/60 min), round-robin assignment.
- Organization & Team — member list, invites, seat counts, permission profile assignments. Profile changes apply immediately — the panel reloads after each save so two admins racing to change the same member's profile both see the same current state.
- Permission Profiles — five preset profiles (Full Access, View Only, Sales, Project Manager, Marketing) plus custom profiles. A per-tab grid lets you set Edit / Use / View / Hide. Enforced in the UI AND at the database level, so a teammate can't work around the restriction.
- Subscription & Billing — plan, payment method, invoices, cancel/downgrade flow. Owners only.
- Tag Options — the controlled vocabulary for the Tag dropdown.
- Integrations — Square, Stripe, Facebook Lead Ads, LinkedIn Lead Gen, Chrome Extension. Each tile shows connect / disconnect status.
Save semantics
The Save button disables for the duration of the save (it relabels to "Saving...") so a rapid double-click can't fire two concurrent saves and leave the two writes in a half-old, half-new state.
Permission profile changes
When an admin changes a member's permission profile, the panel reloads immediately so both you and any other admin viewing the same panel see the change take effect. The member's own sidebar + tab visibility update on their next page load — we don't hot-swap mid-session. But the database enforces the restriction immediately regardless, so even if they kept the page open, any restricted action they tried would be blocked.
Multi-tab edits
Settings is last-write-wins across tabs. If you open Settings in two tabs and edit different fields in each, the second save overwrites the first (every save sends the full settings object). Reload Settings before editing in a second tab to start from current state.
Common confusion points
Mobile App
The CrawlSpace mobile app is a native Android app — not a phone-shaped view of the website. It has its own dialer, its own inbox-style SMS, its own task and calendar screens, and biometric sign-in. Built for field reps who spend half their day between meetings and need to actually transact from their phone instead of just looking at data. iOS is under evaluation; email support if iOS is critical for your team.
Signing in
- Same email and password as the desktop CRM. No separate registration.
- After your first sign-in, enable biometric login (fingerprint or face scan) for instant access on future opens.
- Mobile and desktop sessions are independent — both can be active at the same time without conflict, and signing out on one doesn't sign you out on the other.
What's on the app
- Dashboard — daily activity stats: calls made, emails sent, tasks completed, deals won, current pipeline.
- Contacts — leads + customers with instant search. Tap any contact to see full activity history, edit fields, or kick off a call / text / email.
- Communications — the full Twilio dialer (mute, hold, keypad, speaker, hang-up), two-way SMS in a chat-style view, voicemail inbox, and email compose with your signature.
- Calendar + Tasks — today / overdue / this week task views, plus calendar event creation that syncs to Google Calendar or Outlook.
- Reports + Settings — read-only access to your saved reports; settings inherited from the desktop CRM so there's nothing duplicated to configure.
Calling from the app
Outbound calls go through your connected Twilio business number, so the recipient sees your company — not your personal cell. After each call a disposition modal pops up so you can tag the outcome (Connected, Left Voicemail, No Answer, etc.) and add notes; the call lands in the same activity log the desktop sees.
How to install
- Direct download — from the desktop CRM, the Mobile App link in the marketing site footer (or the in-app help) points to the latest Android build for direct install.
- Google Play Store — search "CrawlSpace CRM" and install normally. The Play version gets tested releases on a regular cadence; direct downloads get new features first.
Limitations
- Android only today. iOS is being evaluated.
- Needs a network connection — the app doesn't currently work offline. In a dead zone you can browse cached contacts but you can't make calls, send SMS, or sync tasks.
- Settings live on the desktop — email connection, Twilio setup, sequence configuration, and other admin actions happen on desktop. Mobile reads those settings but doesn't duplicate the configuration UI.
Common confusion points
Chrome Extension
The CrawlSpace Chrome extension turns any web page into a one-click lead capture. Install it once, sign in once, then capture LinkedIn profiles, company About pages, conference attendee lists, podcast guest bios — any page with a name and contact info — into your CRM in seconds. Built for SDRs and anyone doing prospecting outside the CRM tab.
What it does
- One-click LinkedIn profile capture — browse LinkedIn, find a promising profile, click the extension icon. The popup extracts name, title, current company, location, LinkedIn URL, and any publicly listed email. Review or edit in the popup, then create the lead in your CRM with one more click.
- Works on any website — not limited to LinkedIn. The generic parser handles arbitrary pages; LinkedIn and company pages have tuned parsers for better field detection.
- Auto-enrich existing contacts — if the email on the page matches a contact you already have, the popup switches to enrich mode and shows you what would change. One click updates the existing contact instead of creating a duplicate.
- Theme-matched popup — the popup picks up your CRM's color theme so it doesn't feel like a separate tool.
Signing in
Install from the Chrome Web Store (or load it directly during early access). Open the popup once and sign in with your CrawlSpace email and password. The extension stays signed in across browser restarts — no per-rep config or activation codes.
What it can read on a page
- LinkedIn profiles — first / last name, current title, current company, location, LinkedIn URL, profile photo, and email if publicly listed (rare on LinkedIn).
- LinkedIn company pages — company name, industry, employee count, website, headquarters location.
- Generic web pages — visible name, email, and company when detectable. The popup always lets you correct fields before saving, so a missed parse never becomes a bad lead.
How leads land
Captures write straight into your CRM's contact list — same database as your deals, calls, sequences, and reports. A lead you capture from LinkedIn is immediately eligible for drip sequences, newsletters, and dashboard reports. No separate sync, no lag.
Common use cases
- LinkedIn prospecting runs — open a LinkedIn search, click through promising profiles, capture 60-80 leads per 2-hour block instead of 15-20 with manual retyping.
- Event / conference attendee capture — scrape attendee lists from event websites and bulk-load them into the CRM the same day.
- Refreshing stale contacts — find a contact on LinkedIn, see their title changed, click the extension, email-match triggers enrich mode, update in one click without duplication.
Limitations
- Email isn't always on the page — LinkedIn profiles rarely show personal emails. The extension captures what's visible; type the email in manually on the confirmation panel if you have it from another source.
- Generic-page parsing is best-effort — LinkedIn and major company pages have tuned parsers; other sites get a generic name/email/company sweep that may miss fields. The confirmation panel always lets you edit before saving.
- Requires an active CrawlSpace subscription — the extension is a companion tool; the data goes into your CRM. No CRM, nothing to capture into.