What it does

The service brings prospects, replies to outreach, and requests from the website into one CRM, linked to the people and companies they belong to, and turns every reply and every request into a task with a due date, escalating any request that runs late.

The situation

Legislative View, a legislative-intelligence company, finds and contacts prospects through a lead-enrichment tool and keeps its sales records in a separate CRM. The two have no built-in connection, so new prospects and their replies did not reach the CRM on their own.

Requests from the website’s main form also needed a path into the CRM.

What was built

A small Node.js service that keeps the CRM in step with the lead-enrichment tool. It reads each prospect row, turns it into records for the company and the person, and writes only what changed, so a second run changes nothing. A reply to outreach becomes a sales opportunity with a follow-up task due the next business day.

The service also listens for signed notices from the enrichment tool and starts a sync when someone replies. Requests from the website’s main form become brief records in the CRM, each with a delivery task due one business day out, and an hourly check escalates any brief that runs late. A daily check reads the outreach copy the enrichment tool writes for each prospect and raises one task for each distinct phrase that breaks the company’s language rules.

The CRM’s custom fields and record types are defined in code, including a record for the sourced brief, the product Legislative View sells, whose due date turns the one-business-day promise into a clock. A checker compares the live CRM with what the code expects, down to the filter on each saved view, so a change made by hand shows up as a failed check. A cleanup tool removes empty placeholder companies and the CRM’s sample data, runs as a dry run unless told otherwise, and never deletes a company with people attached.

How it works

  1. Read the prospect lists

    The service reads prospect rows from the enrichment tool, page by page. Lists that name the same field differently are read through one set of aliases, and a run never guesses which list to sync: when there is more than one, a person names it.

  2. Clean every value

    Placeholder values such as “null” or “n/a” are treated as blank, and emails and web addresses that are not valid are dropped before sending. A blank field can be filled in later, while a bad value can make the CRM reject the whole record.

  3. Sort companies into segments

    Each company’s customer segment comes from the enrichment tool’s read of the organization and the person’s job title. Only confident matches are written. The rest stay blank, with the reasoning saved on the record for a person to settle.

  4. Match before writing

    People are matched by email, then by LinkedIn profile, so someone who appears in two lists becomes one record. Companies are matched by web domain, and a company the CRM created on its own from an email address is completed in place rather than duplicated.

  5. Write only what changed

    Each record is compared with what the CRM already holds, and only changed fields are sent. Writes stay under the CRM’s rate limit, a request that either system turns away as busy is retried after the wait it asks for, and a record the CRM rejects is skipped while the rest of the run carries on. Records that fail are reported in a task in the CRM, at most one a day.

  6. Turn replies into follow-up

    Only a reply creates a sales opportunity; a sequence still running does not. Each opportunity gets a follow-up task due the next business day, linked to the person and the opportunity, and a re-run never adds a second task. When the enrichment tool sends word of a reply, the service checks the notice’s signature and syncs a minute later, so a burst of replies becomes one run.

  7. Log website requests

    A request from the website’s main form becomes a brief record linked to the person and their organization, with a delivery task due one business day later. Only the website’s own server can submit, and a request without a valid email or a question is turned away. A request made on a weekend starts the clock on Monday, and an hourly check escalates any brief that runs late.

How it is tested

An automated test suite runs in GitHub Actions each time code changes on the main branch and on every proposed change. It runs offline against a stand-in CRM and checks how rows are read, using the real column names from each list; that a second run creates and changes nothing; that one field changed upstream produces exactly one update; that each reply gets exactly one follow-up task, due on a weekday; how website requests are checked; and that notices with a bad or stale signature are rejected. The same run fails if a commit contains a live-looking key.

The sync, the CRM setup script, and the cleanup tool each have a dry run that writes nothing, and the sync has a canary mode that writes only the first few records before a full run. A read-only preview shows exactly what a sync would write, and a checker compares the live CRM with the setup the code expects and fails on any difference. The website request path was also tested end to end against the live CRM with test records, which were removed afterward.

Tools and who built it

Tools

JavaScript, Node.js, Twenty (CRM), Origami (lead enrichment), Railway, GitHub Actions

Who built it

Stratyx Ventures

Back to all work

Talk to our team about your project

Whatever you’re building or fixing, you’ll leave the first call with a straight answer on what it would take.

After the call, AI work starts with a Workflow Review for $2,500, and a site or an app with a written scope you approve.