Supporting Multi-EIN Companies
Model an employer that operates multiple EINs as linked Check companies, and present a single unified experience in your product.
Some employers operate more than one legal entity, each with its own federal EIN — for example, a restaurant group with a separate LLC per location, or a staffing agency with regional subsidiaries. In Check, one company represents one EIN. There is no first-class parent or umbrella object that groups EINs together, so supporting these employers means creating one Check company per EIN and maintaining the relationship between them in your integration.
This is an advanced integration pattern. It works well, but it requires deliberate modeling on your side, and some duplication is inherent to the domain: each EIN is a distinct taxpayer with its own filings, so Check treats each one independently. This guide covers the recommended way to link related companies using metadata, and best practices for pulling together employee records across EINs so people appear once — not once per EIN — in your product.
What one company per EIN means in practice
Before building, set expectations with your customers about what is and isn't shared across EINs:
- Enrollment is per company. Each EIN completes its own company enrollment: legal details, federal and state tax parameters, bank account, and filing authorization.
- Payrolls are per company. A pay run that spans two EINs is two payrolls, created and approved separately.
- Taxes accrue and are filed per EIN. Wage bases (for example, the Social Security wage base and state unemployment wage bases) do not combine across EINs. A person employed by two EINs in the same year receives a W-2 from each.
- People are represented once per company. An individual who works for two EINs is two employee objects — one in each company — each with its own onboarding state, withholdings, and payment method.
None of this is configurable; it reflects how employment tax works at the entity level. The rest of this guide is about hiding this structural duplication from your users.
Link related companies with metadata
metadataEvery Check company supports a metadata field of string key-value pairs. Choose one stable identifier for the umbrella organization in your system — an organization or account ID that will never change — and stamp it on every Check company that belongs to that organization.
Set it when you create each company:
curl -X POST https://api.checkhq.com/companies \
-H "Authorization: Bearer {api_key}" \
-H "Content-Type: application/json" \
-d '{
"legal_name": "Capsule Restaurants of Ohio LLC",
"address": {
"line1": "1077 E Arques Ave",
"city": "Sunnyvale",
"state": "CA",
"postal_code": "94085",
"country": "US"
},
"metadata": {
"organization_id": "org_8f2a41c0",
"organization_name": "Capsule Restaurant Group"
}
}'For companies that already exist, add the keys with a PATCH to update a company — new keys are merged into existing metadata without overwriting other keys.
A few conventions that pay off later:
- Use your ID, not a display name, as the join key. Names get edited; IDs don't. Store a human-readable name in a second key if it's useful for debugging.
- Stamp every company in the organization, including the first one. Single-EIN organizations that later add an EIN are much easier to handle if the original company already carries the
organization_id. - Record structure only you know. If your product distinguishes a primary entity from secondaries, add a key like
"entity_role": "primary". Check does not interpret these keys; they exist for your integration.
Retrieving all companies in an organization
The List companies endpoint supports filtering by metadata, so you can fetch every EIN in an organization in one query:
curl "https://api.checkhq.com/companies?metadata%5Borganization_id%5D=org_8f2a41c0" \
-H "Authorization: Bearer {api_key}"(The brackets in metadata[organization_id] must be URL-encoded, as %5B and %5D.)
The same filter works in Console: append the metadata query parameter to the companies page URL to pre-filter the list to one organization:
https://console.checkhq.com/companies?metadata[organization_id]=org_8f2a41c0
This makes a handy deep link — construct it from your organization_id and put it wherever your team jumps from your internal tools into Console, so support and operations staff land on a view of all of an organization's related companies instead of searching for each EIN by name.
That said, don't rely on this query as your system of record. Treat your own database as the source of truth for which Check company IDs belong to each organization, and use the metadata filter as a convenience and a consistency check.
One person, many employee records
When the same person works for more than one EIN, create an employee object in each company, and stamp each record with a person-level identifier from your system:
curl -X POST https://api.checkhq.com/employees \
-H "Authorization: Bearer {api_key}" \
-H "Content-Type: application/json" \
-d '{
"company": "com_secondEinCompanyId",
"first_name": "Tony",
"last_name": "Stark",
"dob": "1970-05-29",
"ssn": "000-00-0000",
"residence": {
"line1": "630 Bedford Road",
"city": "Tarrytown",
"state": "NY",
"postal_code": "10591",
"country": "US"
},
"workplaces": ["wrk_i8nfu8234r8dkjhzlsdv"],
"metadata": {
"person_id": "usr_01HFXK3T"
}
}'The person_id key is the linchpin of deduplication. Everything else in this guide assumes each employee record carries it.
- Assign it at creation time, every time. Retrofitting person IDs onto existing records by matching names is error-prone; two different people can share a name, and the same person's name can be spelled differently across records.
- Reuse data you already collected. The Create an employee endpoint accepts
ssn,dob,residence, andstart_datedirectly, so when a person who already works for one EIN is added to a second, create the new record with the information you already have rather than sending the person through a second full onboarding flow. Some setup is unavoidably per-company — withholdings and payment method must still be completed for the new employee record (Employee Onboard links and components are scoped to a single employee object). - Propagate profile updates to every linked record. When a person updates their address, name, or date of birth in your product, PATCH every employee record that shares the
person_id. Nothing in Check synchronizes them for you, and records that drift apart cause incorrect tax calculations for the EIN with stale data.
Backfilling links on existing records
If you already have employee records that predate this pattern, backfill person_id using deterministic identifiers, in this order of preference:
- Your own records. If your system knows which of your users each Check employee ID belongs to, that mapping is authoritative — write it into
metadatadirectly. ssn_last_four+dobtogether as a matching heuristic, followed by human confirmation for any collisions. The employee object exposes only the last four digits of the SSN, so this narrows candidates but does not uniquely identify a person on its own.
Avoid matching on name or email alone. Names collide and emails change; a bad merge (two different people presented as one) is a worse failure than a missed merge.
Presenting a deduplicated roster
With companies linked by organization_id and employees linked by person_id, a unified employer-facing roster comes together client-side:
- Resolve the organization to its Check company IDs (from your database, or the metadata filter above).
- List employees for each company. This endpoint filters by
company— not bymetadata— so query per company and paginate through the results. - Group the combined results by
metadata.person_id. Each group is one person; each record in the group is that person's employment at one EIN.
Best practices for the grouped view:
- Render one row per person, with per-EIN detail beneath it. Payroll-level data — paystubs, W-2s, onboarding status, payment method — belongs to a specific employee record, so present it under the entity it belongs to rather than blending amounts across EINs.
- Aggregate with care. Summing compensation across a person's records for a dashboard is fine; presenting combined figures in anything tax-adjacent is misleading, because each EIN's filings stand alone.
- Expect partially-linked people. A person may be active in one EIN and terminated in another, or onboarded in one and mid-onboarding in the second. Drive per-EIN status from each record's own
onboard.statusandactivefields instead of assuming the group shares one state. - Employee-facing surfaces are per record. Components such as Employee Paystubs and Employee Tax Documents attach to a single employee object. For a person with records in multiple EINs, render one component instance per record — for example, behind an entity switcher — rather than expecting one component to span EINs.
Limitations to keep in mind
- There is no Check-level organization object; the
organization_idconvention lives entirely in your integration, and Check support and Console operate on individual companies. - List employees does not filter by
metadata, so person-level lookups across EINs are your integration's responsibility. - Cross-EIN reporting is assembled by you: run payroll reports per company and combine the outputs, using
metadataon report runs to tag which organization a run belongs to.
If you're planning a multi-EIN integration, we'd love to hear about it — reach out to your Check representative so we can share what has worked well for other partners.
Updated about 19 hours ago

