Unique Identifiers
Unique identifiers determine how the ERP Toolkit finds existing entities in epilot. When processing an event, the system searches for entities matching the specified identifiers to decide whether to create or update.
Basic Usageโ
Specify one or more fields as unique identifiers:
{
"entity_schema": "contact",
"unique_ids": ["customer_number"],
"fields": [
{ "attribute": "customer_number", "field": "customerId" },
{ "attribute": "first_name", "field": "firstName" }
]
}
The system extracts customer_number from the mapped fields and searches for existing entities.
Lookup Behaviorโ
Single Identifierโ
"unique_ids": ["customer_number"]
Searches for entities where customer_number matches the incoming value.
Multiple Identifiers (AND Logic)โ
"unique_ids": ["customer_number", "branch_code"]
Searches for entities where both fields match. Useful for composite keys.
Lookup Resultsโ
| Result | Action |
|---|---|
| No match | Create new entity |
| Single match | Update existing entity |
| Multiple matches | Error - ambiguous identifier |
Special Identifier Typesโ
Direct Entity ID (_id)โ
If you know the epilot entity ID, bypass the search:
{
"entity_schema": "contact",
"unique_ids": ["_id"],
"fields": [
{ "attribute": "_id", "field": "epilotEntityId" },
{ "attribute": "first_name", "field": "firstName" }
]
}
This directly fetches the entity without Elasticsearch search.
Email Lookupโ
Email fields require special handling since they're stored as arrays:
{
"entity_schema": "contact",
"unique_ids": ["email"],
"fields": [
{ "attribute": "email", "field": "emailAddress", "_type": "email" }
]
}
The system automatically searches the repeatable email field structure.
Phone Lookupโ
Similar to email, phone fields are repeatable:
{
"entity_schema": "contact",
"unique_ids": ["phone"],
"fields": [
{ "attribute": "phone", "field": "phoneNumber", "_type": "phone" }
]
}
Multi-Field Composite Keysโ
For entities with composite business keys:
{
"entity_schema": "contract",
"unique_ids": ["contract_number", "contract_type"],
"fields": [
{ "attribute": "contract_number", "field": "contractId" },
{ "attribute": "contract_type", "field": "type" },
{ "attribute": "start_date", "field": "startDate" }
]
}
Both contract_number and contract_type must match for an entity to be found.
Fallback Strategiesโ
Primary and Secondary Identifiersโ
Process entities with different identifier strategies:
{
"entities": [
{
"entity_schema": "contact",
"unique_ids": ["customer_number"],
"enabled": "$exists(customerId)",
"fields": [
{ "attribute": "customer_number", "field": "customerId" }
]
},
{
"entity_schema": "contact",
"unique_ids": ["email"],
"enabled": "$not($exists(customerId)) and $exists(email)",
"fields": [
{ "attribute": "email", "field": "email", "_type": "email" }
]
}
]
}
This first tries to match by customer_number, falling back to email if not available.
Identifier Requirementsโ
Field Must Be Mappedโ
The identifier field must be included in the fields array:
// Correct
{
"unique_ids": ["customer_number"],
"fields": [
{ "attribute": "customer_number", "field": "customerId" } // Required
]
}
// Incorrect - will fail
{
"unique_ids": ["customer_number"],
"fields": [
{ "attribute": "first_name", "field": "firstName" }
// customer_number not mapped!
]
}
Non-Empty Valuesโ
Identifier fields must have non-empty values. Empty or null values cause lookup failures:
// Add validation
{
"attribute": "customer_number",
"field": "customerId",
"enabled": "$exists(customerId) and customerId != ''"
}
Best Practicesโ
Choose Stable Identifiersโ
Use business keys that don't change over time:
| Good | Avoid |
|---|---|
| Customer number | Email address |
| Contract ID | Phone number |
| Meter serial number | Name |
| External system ID | Address |
Avoid Ambiguityโ
Ensure your identifiers uniquely identify entities:
// Too broad - may match multiple entities
"unique_ids": ["last_name"]
// Better - unique business identifier
"unique_ids": ["customer_number"]
// Best - composite key for guaranteed uniqueness
"unique_ids": ["customer_number", "branch_code"]
Handle Legacy Dataโ
When migrating data, include both old and new identifiers:
{
"entity_schema": "contact",
"unique_ids": ["new_customer_id"],
"fields": [
{ "attribute": "new_customer_id", "field": "newId" },
{ "attribute": "legacy_id", "field": "oldId" }
]
}
Error Handlingโ
Multiple Matchesโ
When multiple entities match the identifier:
{
"status": "error",
"message": "Multiple entities found for unique identifier",
"error": {
"code": "AMBIGUOUS_IDENTIFIER",
"category": "validation"
}
}
Resolution: Use more specific identifiers or clean up duplicate data.
No Identifier Valueโ
When the identifier field is empty:
{
"status": "error",
"message": "Required unique identifier field is empty",
"error": {
"code": "MISSING_IDENTIFIER",
"category": "validation"
}
}
Resolution: Ensure source data includes the identifier value.
Next Stepsโ
- Relations - Link entities together
- Meter Readings - Handle meter reading data
- Examples - Complete integration examples