ENRICHLOOPSINTEGRATIONS CUSTOMER.IO
Firmographics in Customer.io.
No code required.
When someone signs up with a work email, EnrichLoops fills in company industry, size, and headquarters. You wire it with two Customer.io campaigns: one that calls a Loop, and one that receives the result and updates the profile.
- 01Someone signs up
Campaign 1 starts
- 02Call the Loop
Send domain + email
- 03EnrichLoops runs
Company data ready
- 04Webhook arrives
Campaign 2 starts
- 05Profile updates
Attributes mapped
Why you need
two campaigns.
A Loop accepts a domain and finishes later. Customer.io can send that request from a campaign, but the HTTP response is only a queued task. The company profile shows up afterward, on the Loop’s outbound webhook.
Campaign 1 handles the outbound call. Campaign 2 is a webhook-triggered campaign: you paste its URL into EnrichLoops, and Customer.io updates the person when enrichment completes.
What changes
on the profile.
Before enrichment you mostly have an email. After campaign 2 runs, you can segment and personalize on company fields.
- alex@rand.org
- created_at
- just now
- company_*
- —
- company_name
- RAND
- company_industry
- Non-profit Organizations
- company_employee_count
- 2822
- company_hq_city
- Santa Monica
- company_hq_country
- US
- company_type
- Nonprofit
Campaign 1.
On signup, call EnrichLoops.
- Create a Loop in EnrichLoops. You’ll point its webhook at campaign 2’s URL once that campaign exists.
- Create a Customer.io campaign that starts when someone signs up (person created, or your signup event).
- Exclude freemail. Filter out gmail.com, yahoo.com, outlook.com, and similar domains so you don’t spend credits on personal addresses.
- Add Send and receive data. Use
POSTto your Loop run URL. KeepContent-Type: application/json. AddAuthorization: Bearer YOUR_API_KEY. - Send domain and email in the JSON body. The domain is what EnrichLoops enriches. The email (or id) is how campaign 2 finds the right profile. Customer.io’s webhook campaigns expect an identifier in the payload.
- Set the action to send automatically, then send a test with a sample profile.
{
"domain": "{{ customer.email | split: '@' | last }}",
"email": "{{ customer.email }}"
}Personalize with Liquid. If a value might break JSON, Customer.io recommends filters like strip and escape.
Campaign 2.
Receive and update.
- Create a webhook-triggered campaign in Customer.io and copy the webhook URL it generates.
- Paste that URL into your EnrichLoops Loop as the webhook destination. It must be HTTPS and publicly reachable.
- Run a test enrichment so Customer.io records a sample payload (shape on the right). You’ll use that sample while mapping fields.
- Add Create or update profile. Set the identifier to the email (or id) you sent in campaign 1, for example
{{ trigger.task.input.email }}when that value is present ontask.input. - Map company attributes from
trigger.data.*with Liquid or JavaScript, then start the campaign.
{
"task": {
"id": "de817e0e-8ea2-4a21-b637-d258ae312ae1",
"object_type": "company",
"operation": "enrich",
"status": "succeeded",
"input": {
"domain": "rand.org"
},
"target": "rand.org",
"credits_charged": 1,
"loop_id": "35ba5b47-5b49-40b0-bed9-24edcd66b4a8",
"idempotency_key": null,
"created_at": "2026-08-11T23:28:35.803143+00:00",
"queued_at": "2026-08-11T23:28:35.803143+00:00",
"started_at": "2026-08-11T23:28:35.734+00:00",
"finished_at": "2026-08-11T23:28:35.734+00:00",
"expires_at": "2026-08-11T23:38:35.734+00:00"
},
"data": {
"name": "RAND",
"about": "RAND is a nonprofit, nonpartisan research organization that provides leaders with the information they need to make evidence-based decisions. The challenges facing humanity are complex. So is the policy landscape decisionmakers must navigate to create positive change. RAND's mission is straightforward. We help improve policy and decisionmaking through research and analysis. Our rigorous research is free from political or commercial pressures so that policy leaders can use the best possible data to make the best possible decisions. Why is this work so vital? Because when RAND can help improve policymaking, we can help make the world a better place for us all. http://www.rand.org/jobs.html",
"domain": "rand.org",
"slogan": "We arm leaders with the information they need to tackle the toughest policy problems. Nonprofit, nonpartisan.",
"created_at": "2026-08-05T01:48:32.729302+00:00",
"updated_at": "2026-08-11T04:11:02.193296+00:00",
"linkedin_id": "165654",
"headquarters": {
"city": "Santa Monica",
"country": "United States",
"postalCode": "90401",
"countryCode": "US",
"raw_address": "1776 Main Street, Santa Monica, CA 90401, US",
"stateProvince": "CA",
"isHeadquarters": true
},
"linkedin_url": "https://linkedin.com/company/rand-corporation",
"year_founded": 1946,
"employee_count": 2822,
"employee_range": "1,001-5,000 employees",
"last_enriched_at": "2026-08-05T01:48:32.641+00:00",
"primary_industry": "Non-profit Organizations",
"enrichment_status": "enriched",
"organization_type": "Nonprofit",
"linkedin_followers": 208953
}
}Attribute map.
Use these as starting points in Create or update profile. Leave data.about off the profile unless you display long company blurbs somewhere. It’s a poor fit for segments.
| EnrichLoops | Customer.io attribute | Liquid | Why it helps |
|---|---|---|---|
| data.name | company_name | {{ trigger.data.name }} | Show the company name in messages |
| data.domain | company_domain | {{ trigger.data.domain }} | Join key / skip re-enrichment |
| data.primary_industry | company_industry | {{ trigger.data.primary_industry }} | Industry-specific campaigns |
| data.organization_type | company_type | {{ trigger.data.organization_type }} | Filter nonprofit vs commercial |
| data.employee_count | company_employee_count | {{ trigger.data.employee_count }} | Numeric size scoring |
| data.employee_range | company_employee_range | {{ trigger.data.employee_range }} | Human-readable size buckets |
| data.headquarters.city | company_hq_city | {{ trigger.data.headquarters.city }} | Local / regional copy |
| data.headquarters.stateProvince | company_hq_region | {{ trigger.data.headquarters.stateProvince }} | State or province targeting |
| data.headquarters.countryCode | company_hq_country | {{ trigger.data.headquarters.countryCode }} | Country routing |
| data.linkedin_url | company_linkedin_url | {{ trigger.data.linkedin_url }} | Sales or CS follow-up |
| data.year_founded | company_year_founded | {{ trigger.data.year_founded }} | Company age signals |
| task.id | enrichloops_task_id | {{ trigger.task.id }} | Deduplicate retries / debug |
Before you
go live.
Five checks that catch most of the support tickets we see on this integration.
Don’t read company fields from campaign 1’s response
That response is a queued task. Firmographic data only arrives on the webhook that starts campaign 2.
Always send an identifier with the domain
Webhook-triggered campaigns start from data, not from a person already in the journey. Customer.io expects an email or id in the payload so Create or update profile can target the right person. Put that next to
domainin campaign 1.Filter freemail early
Keep consumer inboxes out of campaign 1. Enrichment on those domains is usually a wasted credit.
Enrich once, not on every event
Trigger on signup, or when
company_domainis empty. Page views and track calls are the wrong place to call the Loop.Stick to Customer.io’s webhook setup
Valid JSON, Liquid cleanup filters, editor test sends, and automatic send on the webhook action. If the UI labels have moved, start from their webhook action and webhook-triggered campaign docs.
More integrations.
See every guideMake a little data
go a long way.
Get your API key and put enrichment to work. Start with 100 free credits a month. No card. No sales call.
Get your free API key