Importing Companies
The Companies import creates and updates B2B companies in your Shopify store — the company record, its main contact, and one or more company locations with their shipping and billing addresses — from a CSV/Excel file, a Google Sheet, or directly from another connected store. Rows are matched by External ID (with a Name fallback), so running the same file twice updates the same companies instead of creating duplicates.
For the wizard itself — uploading, mapping, dry runs, schedules — see Importing into EcomSolo.
Requires B2B (Shopify Plus)
Companies are a Shopify B2B feature, which is available on Shopify Plus. The target store must have B2B enabled — if it doesn't, Shopify rejects the create and the reason appears as a per-row error in the report. Nothing you import here affects your regular (non-B2B) customers.
One row per location
A company can have several locations, so the file is laid out one CSV row per company location (the same Matrixify-style layout you may know from other bulk tools):
- The company header and main contact fields go on the first row of each company.
- Each additional location goes on its own follow-up row, with only the
Location: …and address columns filled. - The company key (
Name/External ID) repeats on every row so the importer knows which rows belong together.
Rows for the same company must be contiguous (right after each other).
| Name | External ID | Main contact: email | Location: name | Location: city |
|---|---|---|---|---|
| Acme Corp | acme-001 | buyer@acme.com | HQ | Berlin |
| Acme Corp | acme-001 | Warehouse | Hamburg |
The two rows above create one company (Acme Corp) with two locations.
How companies are matched
Every company is matched to an existing company in the target store, in order:
- First by External ID — Shopify's unique company identifier.
- If no External ID is given, by Name as a fallback.
Then, depending on the operation picked in Step 1:
- Create — new companies only; a row matching an existing company fails.
- Update — existing companies (and their locations) only; a row matching nothing fails.
- Upsert (the default) — creates or updates as needed. Re-runs are safe.
- Delete — permission-gated and irreversible; see Deleting companies.
The dry run shows the create/update split before anything is written. If a company exists on Shopify but hasn't synced into EcomSolo yet, the import still recognizes the collision and updates it rather than failing.
Columns
Columns are grouped: the company header and main contact on the first row of each company, then one location (with its shipping and billing addresses) per row.
Company
| Column | Notes |
|---|---|
| Name | The company's name. Required. Also the identity fallback when no External ID is supplied. |
| External ID | Shopify's unique company identifier — the primary match key. Set it once and re-runs update in place. |
| Note | Internal note shown on the company in Shopify admin. |
| Customer Since | The date the company became a customer, as YYYY-MM-DD. |
Main contact
The company's primary contact, taken from the first row of each company.
| Column | Notes |
|---|---|
| Main contact: email | The contact's email — how the contact customer is matched/created. |
| Main contact: first name | |
| Main contact: last name | |
| Main contact: title | The contact's job title at the company. |
| Main contact: locale | Locale code such as en or de. |
| Main contact: location role | Ordering only or Location admin. Grants the contact that role at all of the company's locations — this is what marks the company's ordering as Approved in Shopify. Leave blank to manage roles manually. |
Location
One location per row. The first row's location is the company's main location; each follow-up row adds another.
| Column | Notes |
|---|---|
| Location: name | The location's name (e.g. HQ, Warehouse). |
| Location: external ID | A unique identifier for the location — used to match locations on a re-run. |
| Location: note | Internal note on the location. |
| Location: phone | Contact phone for the location. |
| Location: locale | Locale code such as en or de. |
| Location: tax registration ID | The location's tax/VAT registration number. |
| Location: tax exemptions | Semicolon-separated list of Shopify tax-exemption codes. |
| Location: checkout to draft | true/false — whether this location checks out to a draft order for review rather than paying immediately. |
| Location: allow shipping to any address | true/false — whether buyers at this location may ship to any address, not only the saved ones. |
| Location: billing same as shipping | true/false — when true, the billing address is copied from the shipping address and the billing columns can be left blank. |
Location shipping address & billing address
Each location has a shipping address and a billing address. Both use the
same set of columns, prefixed Location shipping address: … and
Location billing address: …. If Location: billing same as shipping is
true, fill only the shipping columns.
| Column | Notes |
|---|---|
| … first name | |
| … last name | |
| … recipient | The recipient / attention line. |
| … address 1 | Street address. |
| … address 2 | Suite, unit, etc. |
| … city | City / town. |
| … ZIP | ZIP / postal code. |
| … province code | State/province/region code — an ISO code (e.g. CA, NY). |
| … country code | Two-letter ISO country code (e.g. US, CA). |
| … phone | Contact phone for the address. |
Country and province use ISO codes, not full names — US not
United States, CA not California.
Importing from another store
On the wizard's Source step, pick From another store to copy companies from a connected store — no file needed. Company details, the main contact, and all locations with their shipping and billing addresses come across, matched by External ID (Name fallback). Combined with a schedule this becomes a recurring one-way company sync. The source store must have B2B synced, and the target store must have B2B enabled.
Contact roles don't transfer store-to-store (role assignments aren't part of the synced company data) — copied companies arrive with their main contact but ordering not yet approved. Use a file with the Main contact: location role column, or assign roles in the target's Shopify admin.
Known limitations
- Location: Checkout Payment Terms is not yet applied. A payment-terms column can be present in your file, but the import does not set payment terms on the location — that needs a payment-terms-template lookup that isn't wired up yet. Everything else on the location still imports; set payment terms in the Shopify admin for now.
- Catalogs and Store Credit are out of scope. Company catalog assignments and store-credit accounts are not imported (that's a later phase, currently deferred). Manage them in the Shopify admin.
Good to know
- Every Shopify company has at least one location. If a company's rows
don't include any
Location:columns, Shopify automatically creates a default location named after the company — that's Shopify behavior, not an import error. When your file does include locations, only those are created (no extra default). - Customer Since is set on creation only. Shopify doesn't allow changing it afterwards, so on updates the import applies your other columns and leaves the original Customer Since date in place.
- Deleted a company in Shopify and re-importing it? The import notices the company no longer exists and creates it fresh — you don't need to wait for the next sync.
- "Ordering: Not approved" in Shopify? A company shows as approved for
ordering once a contact holds a role at its locations. Set
Main contact: location role (
Ordering only/Location admin) to have the import grant it; without that column, assign roles in the Shopify admin.
Results and freshness
Companies you create or update take effect in Shopify immediately, and EcomSolo's Companies dashboard refreshes within seconds of the run.
Sample file
A ready-to-run sample lives at
features/importing/examples/companies-sample.csv (a two-location company, a
single-location company, and a header-only company that gets Shopify's default
location):
Name,External ID,Customer Since,Main Contact: Customer Email,Main Contact: First Name,Location: Name,Location: Shipping Address 1,Location: Shipping City,Location: Shipping Province Code,Location: Shipping Country Code
Acme Corp,acme-001,2024-01-15,buyer@acme.com,Jane,HQ,100 Market St,San Francisco,CA,US
Acme Corp,acme-001,,,,Warehouse,25 Dock Rd,Newark,NJ,US
Globex Ltd,globex-002,2023-09-01,ops@globex.com,Ben,Head Office,10 King St,Toronto,ON,CA
Dry run: will create 2 companies (Acme's second row is its second location).
Deleting companies
Pick the Delete operation in Step 1 (permission-gated) and upload a file with just the identity columns — matching works exactly like updates (External ID first, Name as the fallback):
Name,External ID
Acme Wholesale,ACME-001
Globex Ltd,GLOBEX-002
Initech,
Ghost Co That Never Existed,
A ready-to-run copy lives at
features/importing/examples/companies-delete-sample.csv. The dry run shows a
red "Will delete 3" (the ghost row is skipped — rows that match nothing
are no-ops, so re-running the same delete file is safe). Deleting a company
permanently removes it from Shopify along with its locations and contacts —
this cannot be undone. The contact's underlying customer record is NOT deleted.
Troubleshooting
| Error in the report | What it means / what to do |
|---|---|
row requires a Name | Every company's first row needs a Name (the identity fallback). |
B2B is not enabled on this store | Companies require Shopify B2B (Shopify Plus) on the target store — enable it and re-run. |
country code '…' is invalid | Use a two-letter ISO country code (US, CA, GB), not the full country name. |
province code '…' is invalid | Use the ISO province/state code (CA, NY, ON), not the full name. |
Customer Since is not a valid date | Use YYYY-MM-DD. |
FAQ
Will re-importing create duplicates? No — companies match by External ID (then Name), and the operation is an upsert. Re-runs update in place.
Does it delete companies? Only if you choose the Delete operation, which is permission-gated and irreversible — see Deleting companies. The default Upsert only creates and updates.
Why one row per location? A company can have many locations, so the file repeats the company key on each location row — the same layout other bulk B2B tools use — and the first row also carries the company header and main contact.
Can I set payment terms? Not yet — see Known limitations.