You have a waitlist tool with 200 emails. A form builder with 80 survey responses. A chat widget that captured 30 leads. An analytics dashboard showing traffic sources. Four tools, four data silos, and no way to answer the question that actually matters: who are these people, and what do you know about them?

OperatorStack uses a unified Contact model keyed on project_id + email. Every submission endpoint — waitlist, forms, chat, contact messages — calls upsert_contact() to find or create a single record. No duplicates, no manual merging, no data silos. The Audience tab shows everything about every person in one view.

The Fragmentation Problem

Most solo founders end up with contacts scattered across three to five tools within the first week of pre-launch. Mailchimp has the email subscribers. Tally has the survey respondents. Google Analytics knows which traffic sources convert. Intercom or Crisp has the chat leads. None of these systems talk to each other.

The result is predictable. You export CSVs, paste them into a spreadsheet, try to match by email, and give up after twenty minutes. The spreadsheet gets stale within a day. You never actually build the unified picture you wanted.

This is not a minor inconvenience. It means you cannot answer basic questions like: "Did the person who gave that great survey feedback also come from Product Hunt?" or "How many of my waitlist subscribers have actually engaged beyond the initial signup?"

How Unified Contacts Work in OperatorStack

OperatorStack solves this at the data layer, not with integrations or CSV exports. Every person who interacts with your project gets a single Contact record.

1

Someone joins your waitlist. The signup endpoint calls upsert_contact() with their email. If no contact exists for that email in your project, one is created. The WaitlistSignup record links to this contact via contact_id.

2

The same person fills out a survey form. The form submission endpoint calls the same upsert_contact() function. It finds the existing contact by email and links the FormSubmission to it. No duplicate created.

3

They send a message through your contact form. Same pattern. upsert_contact() finds the contact, links the ContactMessage. One person, one record, three interactions all visible in one place.

The key is the upsert pattern. The contact is keyed on project_id + email. Every public submission endpoint runs through the same function before doing anything else. Deduplication happens at write time, not as a cleanup step.

Domain-specific models (WaitlistSignup, FormSubmission, ContactMessage, ChatConversation) still exist with their own data. They each point to a Contact via a foreign key. The Contact is the hub; everything else is a spoke.

The Audience Tab: Your Full Picture

The Audience tab in the OperatorStack dashboard is where unified contacts come together visually. For each contact, you see:

  • Email and name (collected from whichever interaction provided it first)
  • Waitlist status — position, signup date, referral code
  • Referral count — how many people they brought in
  • Form submissions — every survey or feedback form they completed
  • Messages — any contact form or chat messages
  • Source — which traffic source or referral brought them to your page

This is the view that fragmented tools cannot give you. When you open a contact, you are not looking at a Mailchimp subscriber or a Tally respondent. You are looking at a person and everything they have done.

Sort your Audience by referral count to find your most engaged early supporters. These people are already advocating for your product before it exists. Reach out to them personally.

Why This Matters for Pre-Launch Decisions

Unified contacts change the quality of decisions you can make during pre-launch.

Prioritize features based on who is asking. When a waitlist subscriber with 12 referrals submits a feature request through your form, that carries more weight than an anonymous suggestion. With unified contacts, you see the full context instantly.

Identify your best acquisition channels. When you can see which traffic source produced contacts who also filled out your survey and referred friends, you know where to double down. Analytics alone show you clicks. Unified contacts show you quality.

Avoid spamming people. When contacts live in separate tools, it is easy to accidentally email someone from Mailchimp about your waitlist while also sending them a Tally follow-up. One contact list means one communication stream.

What You Do Not Need

Unified contacts replace the need for several tools that founders often adopt too early:

  • CRM software. During pre-launch, your Audience tab is your CRM. You do not need Salesforce or HubSpot to manage 500 early contacts.
  • Data integration tools. No Zapier or Make.com workflows to sync contacts between services. There is nothing to sync when everything writes to the same record.
  • Manual spreadsheets. No CSV exports, no VLOOKUP formulas, no stale data. The Audience tab is always current.

Unified contacts work by email. If someone uses different email addresses across interactions, they will appear as separate contacts. This is a deliberate trade-off — email is the most reliable pre-launch identifier, and false merges are worse than missed merges.

Getting Started

There is no setup required for unified contacts. The moment you create an OperatorStack project, the Contact model is active. Every waitlist signup, form submission, and contact message automatically creates or updates a contact record.

Add the script tag to your landing page, configure your waitlist or forms, and let people interact. The Audience tab populates itself.

Frequently Asked Questions

How does OperatorStack prevent duplicate contacts?

Every contact is keyed on project_id + email. When someone submits a form, joins your waitlist, or sends a message, OperatorStack calls upsert_contact() to find or create a contact record. If the email already exists for your project, the new interaction is linked to the existing contact instead of creating a duplicate.

Can I see all interactions for a single contact?

Yes. The Audience tab shows a unified view for each contact, including their waitlist position, form submissions, chat messages, referral count, and analytics source data. Everything is linked through the contact record.

What happens if someone uses different emails?

OperatorStack deduplicates by email address. If someone uses two different emails, they will appear as two separate contacts. This is intentional — email is the most reliable identifier for pre-launch contact management.

Do I need to manually merge contacts from different features?

No. The unification is automatic. Every submission endpoint — waitlist signup, form submission, contact message, chat lead capture — calls the same upsert_contact() function. Contacts are merged at write time, not after the fact.