Simbe Platform — Write-up (Harbor)
1. What I heard from Erin, and what I decided to build
"Measure qualified surrogates, not top-of-funnel."
"Where do I put my money? The facebook spend isn't paying off"
"Where are people getting stuck in the funnel?"
"Is there something the coordinators are doing that is losing potential qualified candidates?"
Three main variables: campaigns → funnel → coordinators
So, how can we put money into facebook/paid channels to receive more qualified candidates? What parts of our funnel and coordinator process are we potentially losing qualified candidates and how can we remediate that?
The app I've built in response is an analytics tool allowing for a broad, shallow sweep across all three areas. The main goal is to allow a user to answer questions about the campaign economics that are rooted in the data. I've taken the learnings from using that app and compiled a report inline here with the major findings and action items.
2. The data model, and why
Two stores, two jobs. Postgres is the source of truth containing all the entities (leads, stage changes, contact events, etc.) and the metric views, with row-level security so one agency can never see another's data (the lake side is isolated structurally instead: each tenant gets its own catalog and storage prefix). The lake (parquet + embedded DuckDB) holds the raw web/event grain that Postgres doesn't: page views, sessions, events. Explore joins the two at query time through a read-only attachment. The charts in this write-up query the same Postgres database the app reads, through the same views where one exists.
Generic on purpose. Nothing in the schema is surrogacy-specific. Funnel stages and the definition of "qualified" are per-tenant configuration, not code; drop reasons come through as data from the agency's own export. The same code path runs Harbor and Lakeside today; agency #3 is a config row, not a schema change. (For Harbor, "qualified" = attended a consult.)
A hard PHI line. Health-related screener answers are never stored, only a derived pass/fail flag. Exactly two non-health answers are kept verbatim by an explicit allowlist: when they want to start, and how they heard about us. The lake never sees the screener at all, and nothing health-shaped leaves the system toward ad platforms.
A table-by-table reference lives in the app under Data model in the nav.
3. What I'd tell Erin today, in plain English
Executive summary:
- Within Facebook, the compensation hook isn't the right thing to lead with.
real-surrogate-storiesconverts at about twice the rate ofearn-up-to-80kand is the better starting point for the next Facebook ad. That said, our strongest campaigns are outside Facebook entirely (brand search, referral), so the bigger move is leaning into those while Facebook gets retooled. - Try a few different ads and audiences at a time, starting with what the rejection reasons already tell us: aim at parents of young kids, and only in the regions we serve. Judge each test in two weeks by who passes screening and starts the application, not by who clicks. Keep what works, cut what doesn't, and fold what we learn into the next round.
- On our site, the application is where we lose people, and most of the unfinished ones stop at the pregnancy history step. We'd review the questionnaire, change the form in small steps, measure completion per section, and keep what moves the number.
- Once coordinators are involved, we need to make sure they are not overloaded. One coordinator is carrying nearly two-thirds of the workload, and when the time to first contact is too long we are likely losing qualified leads. Share the load, then set a reply rule: within 4 hours as the target, never past 12.
First, I'll start with an overview of media buying to make sure we are on the same page for when I make my recommendations.
Media Buying, in brief
Facebook's ad platform will make guesses about who to show your ad to based on a description you give it of who should see the ad: age, location, and so on. Especially in this industry, we can't control for all of our criteria at this level, due to a potential misuse of health information and privacy around that. As Facebook learns more about who interacts with the ad, it may expand this to show to other people.
The actual ad that you made is shown to an individual user, and in this interaction lives the core of what we can control, and is our very first interaction with them. Just getting more clicks is not necessarily a good thing, as you've seen. The key is keeping the people we actually want in mind, so that they want to click and we get a chance to work them as a lead. But if the claims sound intriguing to people outside that group, we end up building a campaign that is targeting those people instead.
Now that the user is on your site, we are in your part of the funnel, and we can better measure and understand the flow. First we screen candidates out by the most common disqualifiers. This is our real first feedback about the health of the funnel above it. Every time there is a large spread between the input and output of a step in the funnel, we have to ask if there is a way to prevent those people from entering categorically, or if we can move the needle in any way towards those that do make it through.
Everything beyond this stage is still important, but for now, let's keep the focus on the Facebook sources. So, we want to improve the quality of the individuals in the top of funnel. This is now where we have an opportunity to change our strategy. We can show a different ad that draws a different audience, or update our current ad to try to shape who in our audience is engaging.
There's a trick other industries use: telling Facebook which visitors turned out to be good ones, so it goes and finds more people like them. In our industry that runs straight into health privacy, and since early 2025 Facebook itself restricts which of these signals it will accept from businesses like ours. The one version I'd explore is a signal from the landing page itself, before we've asked a single health question, but even that needs a legal opinion first. Don't count on this lever.
If we can't help Facebook know what a good lead looks like, we still have the ability to adjust the audience and the ads themselves to shape our leads. We can run structured tests of different hypotheses around what settings lead to the best qualified group of people, and measure success. (A small number at a time: Facebook needs about 50 sign-ups a week from a campaign before it figures out who to show the ad to, so at our volume a few well-fed tests beat many starved ones.) We don't have to wait for them to be fully qualified though for feedback. Just passing the screener is a great early indicator of the potential long term acceptance.
For example, a number of potential leads are screened out because they've not had children. A way to try to get fewer of these leads: Facebook lets you aim ads at parents of young children, so we test that. This still won't match our screening questions exactly, since parenthood obviously doesn't mean they've given birth, and not everyone who has given birth is a parent, but it's worth testing to see if it gives us a better pre-qualified audience with a higher rate of making it through the screener. The same goes for "Out of Area" and other questions. We try to find levers that move the needle, and keep testing new things and refining them until we get our best quality of leads.
So where are we losing qualified candidates? I'll take it in the order a lead experiences it: the campaigns (3a), our site's funnel (3b), the coordinator follow-up (3c). For each: the problem, then the evidence.
3a. The campaigns. Are we paying for the right people?
The problem: your biggest campaign is buying clicks, not candidates. The $80k ad produces the most leads and the worst quality; the smaller brand and referral campaigns quietly out-produce it on qualified candidates (qualified = attended a consult, our strongest early sign of an eventual surrogate). One caveat before the numbers: a lead takes about 6 weeks to become a surrogate, so recent months get judged on early signals, not finals.
How long conversion takes, which is why last month's spend can't be judged yet. A lead doesn't become a surrogate overnight. Counting from the day they first become a lead:
Acceptance lags about 6 weeks (and most within ~10–11 weeks), so a recent month's leads simply haven't had time to finish. You can't judge that spend on final numbers yet. That's why we need to rely on early signals (passed screening, started the application within 2 weeks) to forecast the outcome. The sooner you can react and adapt, the less money is wasted waiting two months to find out whether a change worked.
See it per campaign. Toggle between a clicky campaign and a quality one. Both funnels are drawn to the same scale (the biggest campaign's leads), so the clicky ad starts far wider but collapses to almost nothing, while a much smaller, quality campaign keeps more of its width, and ends up with more accepted surrogates:
So, a clicky ad might really draw more attention, but it wasn't from the people we wanted because they were mostly weeded out in the next step. Anytime we have a severe drop like this we need to evaluate the mix of people that came into that step of the funnel and how we can potentially prevent them from entering or weeding them out as soon as possible. In the case of the earn-up-to-80k campaign, it's intuitive to attribute some blame that the potential incentive is drawing in people that aren't likely to actually qualify.
Here we can see that earn-up-to-80k has the most leads (1,068), worst quality (~6% qualified).
That's the clicky $80k campaign you suspected. A smaller campaign (harbor-brand) beats it
on qualified candidates, and on accepted surrogates outright (52 vs 39).
One nuance worth noticing in that table: compensation isn't poison by itself.
how-much-do-surrogates-make is also a compensation angle and performs fine (19%
qualified), but it runs on search. Someone searching "how much do surrogates make" is
already considering this; someone interrupted mid-scroll by an $80k headline is mostly
just curious. Compensation works as an answer to a question she's already asking. It
fails as the hook that starts the conversation.
The extra Facebook money since May hasn't paid off. Leads from the $80k campaign jumped in May, but the share that qualify fell as volume rose. You bought more leads, not more qualified candidates:
What we do about it. The campaign levers are the ones from the media-buying overview, and we pull all three at once:
- Adjust the ads. The $80k ad is a compensation hook. It attracts clicks from people shopping a number, not considering surrogacy. Shift the May budget toward the messages already proven to produce qualified candidates (brand, referral) and rewrite the $80k ad so it speaks to the women we want instead of the widest possible audience.
- Adjust the targeting. Let the rejection reasons tell us who not to advertise to: leads disqualified for no prior birth or out-of-area shouldn't have entered in such large quantity. Target parents, target the service area, and test those settings one variable at a time, a few tests running at once so each gets enough volume to be judged.
- Measure on early signals. We don't wait 6 weeks to find out if a change worked. Passed-screening rate and application starts within 2 weeks tell us in days which test to feed and which to kill.
3b. The funnel before a coordinator. Where do we lose them on our site?
The problem: most leads die on our site before a human ever talks to them, and the reasons split into two different fixes. The eligibility reasons (BMI, age, out of area, no prior birth) are really campaign problems in disguise: fix them with targeting in 3a so fewer of those people enter. The process reasons (never started or never finished the application) are ours: the form and application experience. The form also gives us our best early read on quality: how soon they say they want to start.
Where leads get stuck. Most leads never get past pre-screen / the application step:
Why they drop out, and which lever each reason points to (targeting vs. process):
Inside the unfinished applications: the pregnancy-history wall. Of the applications that never get submitted, the biggest group goes quiet right at the pregnancy history questions:
Who's ready vs. just looking. The sooner a lead says they want to start, the more likely they qualify: the "right away" group qualifies at more than twice the rate of the just-researching group.
We'll use this answer twice: it tells the campaigns which audiences are worth buying (3a), and it hands the coordinators a follow-up date (3c).
What we do about it. These are form fixes, not ad fixes:
- Soften the pregnancy-history wall. It's the heaviest section and where most unfinished applications go quiet. Break it into smaller steps, say why we ask, and save progress so coming back is easy.
- Chase the stall, not just the no. Someone who stopped mid-application is warm, not gone. An automatic reminder within a day or two of going quiet costs nothing and reaches them while they still remember us.
- Watch completion per section. The chart above is the baseline; as we change the form we watch it move, the same early-signal habit as 3a.
3c. The coordinator step. Are we losing qualified candidates we already paid for?
The problem: qualified candidates we already paid for are sitting in a queue. Call a screened lead back inside 12 hours and 1 in 3 attends a consult; let it slip past that and the rate halves. It's not a skill problem, it's workload: one coordinator, Dana Ortiz, carries nearly two-thirds of all leads. Fixing that (rebalancing plus a hard reply-time rule) is worth roughly 65 to 80 more consults a year, a 13 to 16% lift, with zero extra ad spend.
The biggest lever isn't an ad at all. It's how fast someone calls the lead back. All charts below look only at leads who already passed screening (the screener runs in the form before any coordinator is involved, so it can't be caused by slow contact) and count how many go on to attend a consult:
Why some leads wait: one coordinator carries almost two-thirds of them.
How we know it's the wait and not the person. The same coordinator's own leads (same campaigns, same intent mix) split only by how long they waited:
What we do about it. These are process changes, not spend:
- Rebalance the queue. One coordinator holds almost two-thirds of the leads; spread them so nobody's queue forces a lead to wait a day. Everyone on this team converts fine when they reply fast.
- Set a callback rule: 4-hour target, 12-hour limit. The data can't tell hour 3 from hour 9 apart; the cliff is at 12. Aim at 4 so we never miss 12. This is where the 65 to 80 consults come from. Don't sweat call vs. text; in our data they convert the same.
- Follow up on the window they gave us. Leads who said "3–6 months" rarely come back on their own, and today nobody reaches out when their window arrives. Put the date on the calendar the day they sign up.
- Work the warm cold list. "Unresponsive" isn't always dead: 55% replied at least
once before going quiet (570 of our 1,037 unresponsive leads). That list is cheaper
to re-engage than any new lead, and the campaign meant to bring them back isn't
reaching them: despite the name, only 3 of
come-back-retarget's 78 leads are returning cold leads. The rest are brand-new people.
4. What I chose not to build, and why
Mostly I'm trying to keep everything that's not set-in-stone in the datalake, and we can ask questions we don't even know we want to ask yet. That helps us keep our data model clean with just semantics around leads, coordinators, etc., and we ask BI-style questions of the lake. I built it out just enough to be able to answer the questions at hand, but real data would drive the direction of the product in unforeseen ways. It's best to measure first before pre-aggregating stats for example.
In an ideal scenario, we'd also pull in data from Meta's Marketing API (its ads reporting/Insights endpoints) so that we can further refine and improve our data quality. E.g., I have an estimated cost per lead section that you can add to a campaign, but this is a very crude proxy for the near-real-time spend we'd get from Meta's API. Refining further in this direction would lead to features like creating and managing campaigns directly in the Simbe platform, reconciling spend with actual billing, etc.
Another main factor mostly left out of this build is dealing with the time aspect. A production system would need a first-class concept of filtering the data to a time-range, as well as being able to bucket at different granularities. This was mostly left out for two reasons: First, there isn't really enough data at this level to warrant splitting it into much smaller time slices. And second, I was trying to bring down the complexity to keep the scope from expanding too far. Further usage of the system would require at least some features around daily data.
Also, the scope of the ops is deliberately anemic. A real production system would need CI/CD, load balancing, scaling of worker resources, backups, disaster recovery, alerting/observability, security management, audit trail, dev/staging environments, safe deployment, data lifecycle (like being able to delete people/leads upon request).
Ingest operations would need to become a large part of this, and it's out of scope for this, but would require its own project.
5. What would need to change to support 50 agencies
This mostly comes down to usage. From a compute perspective, at this scale it's likely that nothing much would need to change for 50 of these agencies with this size of data set, just making sure that the instance size/cpu,memory,disk allocation are appropriate. The database would also likely be externalized as a managed service. But, all of the ops items from above would have to be implemented. The main aspect for consideration in scaling up is the data lake side. Today each agency's lake runs as an embedded DuckDB instance inside the web server, held in memory with its own Postgres connections; at 50 agencies that wants instance eviction, connection pooling, or a separate lake service. It may be best offloaded to a managed service, but a deeper cost and trade-off analysis would have to be made. There are a lot of considerations to be made here in terms of vendor management, licensing, cost, complexity, features, etc. We only just scratched the surface of providing a proof of concept BI tool in-app, but this would likely have the most pain points if we scaled up as-is. Otherwise in the wider app, it's just the lack of depth on all the features that would become a problem.
6. Roughly how many hours
- infra/scaffold/requirements - 2 hours
- domain model + ingest + isolation - 1 hour
- metric views - 1 hour
- lake + Explore - 1 hour
- onboarding (signup/wizard/upload) - 30 minutes
- economics & analysis - 1 hour
- write-up - 2 hours
