We don't have a sales team. There's no marketing ops manager and no SDR running outbound. The lead funnel for our waitlist - signup, AI lead qualification, enrichment, access management - runs on Claude calls and a database we wrote ourselves.
Some numbers from where we are right now:
- 2,090 waitlist signups
- 585 access codes granted
- 196 MSPs actively using OpenFrame in production
So roughly 28% of waitlist signups qualify, and 34% of access codes get used. Nobody on the team touches anything until we manually pick which cohorts get codes for the next batch.
What follows is how we wired it up.
Why HubSpot isn't the entry point
At a previous company, traffic tracking and form fills piped everything into HubSpot. Within months the contacts table had tens of thousands of rows, most of them spam or scrapers. Once a CRM gets to that state, deduping it is a six-month project nobody wants to own.
So at Flamingo we do this different. Every waitlist signup lands in our own Postgres table. Claude runs the AI lead qualification pass on it. If the row clears the threshold, it gets pushed to HubSpot. Otherwise it stays put.
Our AI lead qualification pipeline
- Signup writes to our database first. The waitlist form asks for email, business address, and phone. The phone field was added a few months in, after a chunk of our outreach kept landing in spam folders – having a number to call or text turned out to matter.

-
Claude does the enrichment. A structured prompt with the lead's data goes to a Claude call. Web search runs against the company domain. The output is a small JSON object: company name, industry, rough headcount, MSP confidence score from 0 to 100, and the model's written reasoning for the score.
-
HubSpot only sees qualified contacts. If the MSP confidence clears our threshold, we create the HubSpot contact and company objects with all of the enrichment attached, plus UTM params and custom events. Below the threshold, the row stays in our database with the rejection reason on it. No HubSpot record gets created.
-
Access codes are tied to cohorts. Joining the waitlist doesn't give you a code. We reach out to the qualified ones over LinkedIn or email, ask if they actually want to deploy in production, and only then generate a code. Each code belongs to a cohort, so we can compare activation rates across batches.
-
Daily usage telemetry comes back to HubSpot. Once an MSP installs OpenFrame, the tenant reports its device count back every 24 hours. That data drives three things: account health (tenants idle for more than 10 days get a warning email), Stripe billing readiness, and the metrics we share with investors.
What Claude does at the front door
The enrichment pass is where most of the work happens. Each signup gets a structured prompt with whatever fields the form captured. Claude runs web search against the domain and returns the JSON described above.

We store everything Claude returns, including the rationale and the model version. That means we can come back later, compare our AI lead scoring methodology against actual outcomes, and re-score the historical waitlist if the threshold changes. We've done this twice already.
The other piece is Clay. When a HubSpot contact gets created without a LinkedIn URL, a webhook hits Clay async to backfill it. That keeps the enrichment flow fast on the user-facing side while still getting us LinkedIn coverage for outreach later.
The whole synchronous loop - form submit to enriched DB record - runs in a few seconds.
One canonical record per entity
Our Postgres holds the canonical row for each waitlist signup, tenant, and device count. HubSpot stores CRM-relevant fields.
![]()
Stripe handles billing. They're stitched together by external reference IDs we store on our side – HubSpot company_id, Stripe customer_id, and so on – rather than by running a sync job that tries to keep two databases in agreement.
That means if HubSpot's reporting falls short for something we need (and it does, often – the full waitlist → access code → active tenant funnel doesn't exist as a native HubSpot report), we just build the report off our own database. The investor data room runs off the same data the customer health dashboard does.
Why we cared about this so early
This isn't infrastructure most pre-revenue startups build. Most teams we've talked to wait until the CRM is unusable, then hire a marketing ops person to dig out of it. By that point the historical data is gone – you can't recover what a signup's MSP confidence was in 2024 if you never scored it.
The trick is that doing it upfront isn't expensive. The whole thing is a Postgres table and a Claude call, plumbed into HubSpot via webhook. Whatever automation we layer on top later inherits clean inputs.
Follow the show on YouTube and our website.

Michael Assraf
Serial tech entrepreneur with over 15 years of experience and deep knowledge of MSP partnerships and operations. A decade ago he founded a cybersecurity company that continues to protect and support MSPs today, sharpening his insight into the challenges service providers face.
