Mapping IDs Across Systems in n8n (Shopify, HubSpot, Xero, and More)
8kit Team•
Every automation builder hits this wall eventually: you need to connect records across two systems that don't share an ID. Your Shopify customer ID doesn't match your HubSpot contact ID. Your Xero invoice number has nothing to do with your internal order reference. Your CRM lead ID needs to map to your billing system's account ID.
This is the cross-system identity problem, and it's the silent killer of n8n automation reliability.
The Problem: Systems Don't Agree on Who's Who
When you build an n8n workflow that syncs data between two systems, you need to answer a fundamental question: how do I find the matching record on the other side?
The common approaches (and their failure modes)
1. Search by email/name on every execution
The most natural approach: when syncing a Shopify customer to HubSpot, search HubSpot for a contact with that email.
Shopify Trigger → Search HubSpot by email → Update or Create
Problems:
- API rate limits: Every execution fires a search query. At 500 orders/day, that's 500 extra API calls.
- Inconsistent matching: Emails change. Names have typos. A customer with
john@company.comin Shopify andjohn.doe@company.comin HubSpot won't match. - Duplicate creation: If the search fails (timeout, API error, email format mismatch), your workflow creates a duplicate record instead of updating the existing one.
2. Google Sheets lookup table
A popular n8n pattern: maintain a Google Sheet with columns for each system's ID.
| Shopify Customer ID | HubSpot Contact ID | Xero Contact ID |
|---|---|---|
| 12345 | 67890 | XC-001 |
| 12346 | 67891 | XC-002 |
Problems:
- Concurrency: Two executions reading and writing the same sheet simultaneously corrupt data (see: race conditions).
- Scale: Google Sheets slows dramatically past 10,000 rows. API quota limits hit fast with frequent reads.
- No transactions: If your workflow writes the Shopify ID but crashes before writing the HubSpot ID, you have a partial mapping. Next execution doesn't know what happened.
- Manual maintenance: Someone has to set this up, keep it clean, and debug when mappings go stale.
3. n8n Static Data
Store mappings in the workflow's static data:
const staticData = $getWorkflowStaticData('global');
const mapping = staticData.idMapping || {};
const hubspotId = mapping[shopifyCustomerId];
Problems:
- Per-workflow: If three workflows need the same Shopify-to-HubSpot mapping, you're maintaining three copies.
- No UI: You can't inspect or edit static data without running custom code.
- Size limits: Static data isn't designed for thousands of mappings.
- Lost on workflow reimport: If you export and reimport a workflow, static data resets.
The 8kit Approach: Data Mapping (Lookups)
8kit's Data Mapping pattern (Lookups) is purpose-built for cross-system ID resolution. Think of it as a persistent, shared lookup table with proper concurrency handling and a dashboard to inspect it.
How it works
- Create a mapping when you first link two records: "Shopify customer 12345 = HubSpot contact 67890"
- Look up a mapping in any workflow: "What's the HubSpot ID for Shopify customer 12345?"
- Update or remove mappings as records change
All mappings are stored server-side (your 8kit server), accessible from any workflow, with proper concurrent access handling.
Setting it up in n8n
When creating a record in the target system:
Shopify Trigger (new customer)
→ Create HubSpot Contact
→ 8kit Lookup: Set mapping
Namespace: "shopify-hubspot-customers"
Key: {{ $('Shopify Trigger').item.json.id }}
Value: {{ $('Create HubSpot Contact').item.json.id }}
When syncing an existing record:
Shopify Trigger (customer update)
→ 8kit Lookup: Get mapping
Namespace: "shopify-hubspot-customers"
Key: {{ $json.id }}
→ IF mapping exists:
→ Update HubSpot Contact (using mapped ID)
ELSE:
→ Create HubSpot Contact
→ 8kit Lookup: Set mapping
Why this is better
| Feature | Search by email | Google Sheets | Static Data | 8kit Lookups |
|---|---|---|---|---|
| API calls per sync | 1 extra search | 1 Sheet read | 0 (local) | 1 fast key lookup |
| Concurrent-safe | N/A | No | No | Yes |
| Shared across workflows | Yes (but expensive) | Manually | No | Yes |
| Scale | Depends on target API | ~10K rows | ~1K entries | 100K+ |
| Dashboard / inspection | No | Spreadsheet | No | Yes |
| Survives workflow reimport | Yes | Yes | No | Yes |
Real-world example: Multi-system customer sync
Here's a production pattern for keeping customer records in sync across Shopify, HubSpot, and Xero:
Workflow 1: New Shopify customer
Shopify Trigger (customer/create)
→ Create HubSpot Contact
→ 8kit Lookup: Set "shopify-hubspot" mapping
→ Create Xero Contact
→ 8kit Lookup: Set "shopify-xero" mapping
Workflow 2: Shopify customer update
Shopify Trigger (customer/update)
→ 8kit Lookup: Get "shopify-hubspot" mapping
→ Update HubSpot Contact (with mapped ID)
→ 8kit Lookup: Get "shopify-xero" mapping
→ Update Xero Contact (with mapped ID)
Workflow 3: HubSpot deal closed (bill the customer)
HubSpot Trigger (deal closed)
→ 8kit Lookup: Get "hubspot-xero" mapping (for the contact)
→ Create Xero Invoice (with mapped Xero contact ID)
Three workflows, three systems, all sharing the same mapping infrastructure. No Google Sheets. No redundant API searches. No stale data.
Namespace conventions
Keep your mappings organized with a consistent naming scheme:
shopify-hubspot-customers, Customer ID mapping between Shopify and HubSpotshopify-xero-contacts, Contact mapping between Shopify and Xerointernal-stripe-subscriptions, Internal user ID to Stripe subscription mapping
Use the 8kit dashboard to browse and inspect all your mappings by namespace.
Combining Lookups with Other 8kit Patterns
The real power comes from combining patterns:
Shopify Webhook
→ 8kit Uniqs: Deduplicate (skip if already processed)
→ 8kit Exclusivity: Lock "customer:{{ $json.id }}"
→ 8kit Lookup: Get HubSpot mapping
→ Update or Create in HubSpot
→ 8kit Lookup: Set/update mapping
→ 8kit Temporal: Update last-synced timestamp
→ 8kit Exclusivity: Release lock
This gives you:
- No duplicates (Uniqs)
- No race conditions (Exclusivity)
- Fast cross-system resolution (Lookups)
- Incremental sync awareness (Temporal)
All four patterns, working together. That's what bulletproof looks like.
Getting Started
- Install the 8kit community node in n8n
- Use the Lookup node with "Set" operation to create your first mapping
- Use the Lookup node with "Get" operation to resolve IDs in your sync workflows
- Check the 8kit dashboard to browse and manage your mapping namespaces
Stop maintaining spreadsheets. Stop burning API calls on search queries. Map it once, look it up forever.
8kit provides four enterprise automation patterns for n8n: deduplication, cross-system mapping, distributed locking, and change tracking. Learn more at 8kit.io