Turn a vertical database into a 3-touch outbound engine
Earlier campaigns sourced accounting firms and real estate brokers with Clay. For the owners campaign we pulled a licensed real estate operator database into Tightrope's own Postgres and cut it down to one verified owner per firm. Each owner got LinkedIn, then email, then a call from the founder. Sequence data syncs back into the same database, so a scheduled job knows who finished without replying and puts a call task in the CRM.
Works ifA vertical database already lists your buyers with contacts, and a founder will make the closing calls.
The system
Clay
Find Companies
Accounting firms by industry and size.
Clay
Find People
Commercial real estate brokers.
Licensed database
Real estate operators
Firms, owners and contacts, via API.
1
Blank connect, then up to 3 chats
2
Value email, then a bump
3
Call
Task for the founder if no reply
The data model
thesis
- pkidbigint
- row_hashtext
- fkcompany_iduuid
- gp_nametext
- websitetext
- themestext
- units_ownedtext
Operator firms. row_hash = md5 of the full raw record, the dedup key.
- pkidbigint
- row_hashtext
- fkgp_idbigint
- fkcontact_iduuid
- emailtext
- titletext
- linkedin_urltext
Decision-makers. Linked to their firm by name match at load.
- pkidbigint
- row_hashtext
- fkcompany_iduuid
- gp_nametext
- signal_typetext
- article_urltext
- article_datetimestamptz
News mentions per firm (acquisition, asset sale). Mirrored, not used to trigger outreach.
core
- pkiduuid
- nametext
- domaintext
- linkedin_urltext
- created_attimestamptz
- updated_attimestamptz
Identity anchor. Unique on domain. Holds worked records only; the vendor database is joined by domain until a record enters a campaign.
- pkiduuid
- fkcompany_iduuid
- first_nametext
- last_nametext
- emailtext
- titletext
- linkedin_urltext
Unique on lower(email).
hubspot
- pkhs_object_idtext
- fkcontact_iduuid
- fkcompany_iduuid
- hs_company_idtext
- emailtext
- lead_statustext
- utm_campaigntext
CRM mirror, PK = HubSpot object id, full record in payload jsonb.
- pkhs_object_idtext
- fkcompany_iduuid
- deal_nametext
- deal_stagetext
- pipelinetext
- amountnumeric
- close_datetimestamptz
instantly
- pkidtext
- nametext
- statusint
- timestamp_createdtimestamptz
- payloadjsonb
- pkidtext
- fkcampaign_idtext
- fkcontact_iduuid
- fkcompany_iduuid
- emailtext
- statustext
- custom_variablesjsonb
Sequence status per lead; the call-task job reads completion and reply count from here.
heyreach
- pkidbigint
- nametext
- statustext
- total_usersint
- users_finishedint
- users_failedint
- pkidtext
- fkcampaign_idbigint
- correspondent_profile_urltext
- last_message_attimestamptz
- last_message_sendertext
- total_messagesint
- lead_repliedboolean
One row per LinkedIn inbox thread. lead_replied is the LinkedIn equivalent of an email reply. No FK into core yet.
ops
- pkcontact_emailtext
- hs_contact_idtext
- hs_company_idtext
- campaign_idtext
- hs_task_idtext
- statustext
- created_attimestamptz
Idempotency ledger: one call task per person, ever. Soft links to hubspot.contacts and instantly.campaigns by id, no FK constraint.
The signal
Owner-level fit from a vertical operator database, then sequence status: a lead who finishes the emails without replying queues a founder call.
Why it predicts a purchase
The buyer was the owner-operator: they spend their own capital, so savings land on them, while a third-party manager bills by the hour. Trust was the constraint, so every owner got the full sequence instead of a cold-email-only tier.
How it works
Mirror the database
We pulled the full operator database through its API into Postgres, in its own schema. It has no record IDs and different people share emails, so the dedup key is a hash of the whole raw row. The 60k listed contacts came down to about 41k real people.
Cut one owner per firm
Claude subagents classified every job title, because a regex let vice presidents in through the word 'president'. The cohort kept owner titles with an email, a rental theme and a unit band, one owner per firm, firms with a LinkedIn page ranked by portfolio size. Only emails that passed two verifiers went out: 685.
Run LinkedIn, then email
Contacts, companies and phone numbers went into HubSpot. HeyReach sends a blank connection request and up to three chats, then hands leads to Instantly for a value email and a bump. The handoff drops custom fields, so a scheduled job patches the asset type into each email lead and the first email waits a day for it.
Close on a call
Every three hours Trigger.dev mirrors Instantly and HeyReach into Postgres. A lead who finished the emails without replying gets a HubSpot call task for the founder, with the firm, asset type and prior touches in the body. A ledger table keeps it to one task per person.
Build notes
- The dashboard first read email only and showed the owners campaign as nearly dead, because the campaign ran mostly on LinkedIn. Mirroring HeyReach into the same database fixed the read.
- A tile labeled 'Meetings booked' was renamed 'Opportunities'. The number was a manual flag in the sequencer, not a confirmed meeting.
- The vendor database never gets bulk-loaded into the core tables. A record is promoted when it enters a campaign, so core stays the worked pipeline.
Questions
Why not run everything out of the CRM?
The CRM is where the founder works deals and makes calls. Its native integrations with the sequencers flatten campaign, step and variant, so each tool is mirrored into Postgres on its own and the dashboard reads SQL views there. The CRM gets the campaign contacts and the call tasks.
When does the founder pick up the phone?
After the LinkedIn touches and the email sequence. A call task is only created when a lead finished the emails without replying and did not unsubscribe, and the task lists the touches that already happened so the call opens warm.
More builds
All builds →Turn website visitors into qualified leads
You already identify companies visiting your site.
Build lists from data you already own
You have a CRM or database you have never fully worked.
Put every signal in one table
You run more than one signal source and want one view per account.
The next step