Lead enrichment is a workflow problem
Most signup systems start simple and then gradually accumulate logic around them. First you save the lead. Then you enrich the company domain. Then you verify whether the address is personal, disposable, or worth following up. Then someone asks for a Slack alert for promising signups. Before long, that "small" backend feature is spread across API handlers, background jobs, retry scripts, and a few hard-to-trace conditionals.
That is exactly the kind of problem orchestration should own.
This workflow shows how to keep the entire signup qualification path in one Unmeshed process while using Supabase Postgres as the system of record. The flow validates the incoming lead, stores it immediately, enriches the email domain with Hunter, verifies the mailbox with Kickbox, classifies the lead, routes high-value signups to Slack, and writes the enrichment result back to the same user record.
What this workflow does
The process definition is named enrich_classify_and_route, and the shape is straightforward:
signup payload
-> validate_input
-> postgres_insert_user
-> http_hunter_domain_search
-> http_kickbox_verify
-> classify_lead
-> switch_lead_route
-> slack_message_high_value | exit_skip_slack_low_value
-> postgres_update_user_enrichment
The workflow follows a simple but important sequence:
- The lead is written to Supabase before any external enrichment call runs.
- Hunter and Kickbox add context without turning the initial signup into a brittle all-or-nothing transaction.
- Slack only receives leads that pass the qualification rule.
- Supabase is updated at the end so the source-of-truth record reflects the outcome of the workflow.
That sequencing is practical. If Hunter is slow or unavailable, you still have the signup stored safely. If Kickbox flags the email as free or disposable, the workflow records that information and exits the alerting path cleanly.
Step-by-step walkthrough
1. Validate and normalize the incoming lead
The first JavaScript step, validate_input, expects a payload with email and name. It trims the values, lowercases the email, confirms the format is valid, and extracts the domain. If the payload is malformed, the workflow fails immediately with a clear error instead of writing incomplete data downstream.
This is a small step, but it sets the tone for the whole process: every later step receives a predictable structure.
2. Insert the lead into Supabase
postgres_insert_user persists the lead with a simple SQL statement:
INSERT INTO users (email, full_name)
VALUES (:#email, :#full_name)
RETURNING email;
Unmeshed binds the validated values directly from validate_input, so the workflow writes a clean row before it asks any third-party service for more information. That design is useful in production because enrichment vendors can fail independently of your core signup path.
Supabase fits naturally here because, for this workflow, it is standard PostgreSQL. Unmeshed connects through the Postgres integration and treats the users table as the durable record of the lead.
3. Enrich the company context with Hunter
The http_hunter_domain_search step calls Hunter's combined find API using the saved email address:
https://api.hunter.io/v2/combined/find?email={{steps.postgres_insert_user.output.results[0].email}}&api_key={{secrets.hunter_io_api_key}}
This step is marked optional, which is a thoughtful choice. Company enrichment is valuable, but it should not prevent the workflow from completing. If Hunter returns company data, the workflow can use it. If not, the process can still continue with the best information it has.
4. Verify the mailbox with Kickbox
The http_kickbox_verify step checks the same email with Kickbox:
https://api.kickbox.com/v2/verify?email={{steps.postgres_insert_user.output.results[0].email}}&apikey={{secrets.kickbox_api_key}}
The important signal in this workflow is whether the address is free or disposable. That becomes the main input to qualification in the next step.
5. Classify the lead
classify_lead merges the outputs from validation, Hunter, and Kickbox into one normalized result object:
email
full_name
company
is_free_email
lead_type
The current implementation uses a simple rule: if Kickbox reports free === true, the lead is classified as low_value; otherwise it becomes high_value.
That is deliberately easy to understand and extend. Today, the workflow uses mailbox quality as the qualification gate. Tomorrow, the same step can incorporate company size, industry, geography, or CRM scoring without changing the outer structure of the process.
6. Route qualified leads to Slack
The switch_lead_route step checks classify_lead.output.result.lead_type.
high_value -> slack_message_high_value
low_value -> exit_skip_slack_low_value
That keeps notification logic out of application code. Sales or operations teams can trust that only leads meeting the workflow rule are pushed into Slack, and the message already includes the details that matter: name, email, and company.
7. Update the user record with enrichment results
The final Postgres step writes the classification back to Supabase:
UPDATE users
SET
company = :#company,
is_free_email = :#is_free_email,
lead_type = :#lead_type
WHERE email = :#email
RETURNING email;
This closes the loop neatly. The signup row starts with the basic identity fields, then the workflow appends the enrichment outcome once external checks are complete.
Why this design works well in practice
There are plenty of ways to assemble this with controllers, queue workers, and custom scripts. The problem is not whether that approach can work. It is how long it stays understandable.
With a single orchestration definition, teams get a few advantages immediately:
- The full lifecycle of a lead is visible in one place.
- Each step has explicit inputs, outputs, and execution history.
- Secrets for Hunter and Kickbox stay in workflow-managed configuration.
- Routing logic is easy to inspect and change without hunting through multiple services.
- Database writes and notifications become part of one traceable process instead of separate concerns.
For teams running growth, onboarding, or outbound workflows, that visibility matters just as much as automation.
A few implementation details worth noticing
This workflow has several good design choices that are easy to miss on a first read:
This is the kind of workflow that tends to grow with a product. Once the basic pipeline is working, most teams add CRM sync, deduplication, account assignment, welcome-email branching, or human review. Starting with an orchestration makes those additions much easier to reason about.
Natural next extensions
The current version is already useful, but it is also a strong foundation for a more opinionated lead-ops pipeline. Common next steps include:
- Add company-size or industry checks inside
classify_leadfor more granular scoring. - Write Hunter metadata such as domain confidence or organization details back to Supabase.
- Push high-value leads into a CRM in parallel with the Slack alert.
- Add retry policies or fallback behavior around third-party enrichment calls.
- Store workflow outcomes in a separate audit table for reporting and sales analytics.
Because the workflow is already definition-driven, those changes stay additive instead of forcing a broader rewrite.
Final thoughts
The strongest part of this example is not that it uses Supabase, Hunter, Kickbox, or Slack. It is that the business process is explicit. A lead arrives, gets validated, gets stored, gets enriched, gets classified, and gets routed. Every step has a clear responsibility, and the resulting state is written back to the database your team already uses.
That is what good orchestration looks like in practice: less hidden glue code, fewer scattered decisions, and a cleaner path from signup to follow-up.













