Unique Identifiers
Unique identifiers determine how the ERP Toolkit finds existing entities in epilot. When an event is processed, the system searches for entities matching the specified identifiers to decide whether to create a new entity or update an existing one.
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 the value of 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 in epilot.
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