Batch file format
Feature availability is part of a phased rollout. Your organization may not see this functionality yet. It will become available when your environment is included in the rollout.
Profile import uses the JSON Lines (JSONL) format for both input and output files. Each line in the file is a standalone JSON object. This topic describes the structure of input records, how identity resolution works, update behavior, and the format of output results.
Input file format
Input files must meet the following requirements:
- Encoding: UTF-8.
- Format: JSON Lines (JSONL). One JSON object per line, with no trailing commas.
- Size: Maximum 100 MB per file.
Each line in the input file represents a single record to import.
Record structure
Each record must contain the following required fields:
| Field | Type | Required | Description |
|---|---|---|---|
recordType | String | Yes | The type of record. Use profile for profile records. Case-insensitive. |
identifiers | Array | Yes | An array of identifier objects, each with a provider and id field. Identifier(s) must have a provider that matches a configured identity rule and a non-empty id. |
Each record must contain at least one of the following fields with data:
| Field | Type | Required | Description |
|---|---|---|---|
contact | Object | No | Contact information for the profile, such as firstName, lastName, and email. |
extensions | Object | No | Custom attributes for the profile. Use this field for any additional data your organization tracks. |
The record payload must contain at least one non-null field in contact or extensions. Records with no updatable fields fail with the NO_UPDATABLE_FIELDS error code.
Array values in the record payload are not supported. Any record that contains an array value in contact or extensions fails with the INVALID_RECORD error code.
The following tables describe the fields available in the contact and extensions objects:
profile.contact
| Field | Type | Description | Allowed / Format | Example |
|---|---|---|---|---|
address | object | A structured representation of a physical location, including street, city, state, and postal code. | See address | |
dateOfBirth | date | The date of birth formatted in ISO 8601 standard, representing the year, month, and day. | ISO 8601 (YYYY-MM-DD) | "1995-01-01" |
email | string | The contact's email address. | RFC 5321 | "[email protected]" |
firstName | string | The individual's first name. | Non-empty string (1–500 characters) | "John" |
gender | string | The gender of the individual. Free text is accepted, but one of the suggested values is recommended. | Suggested: male | female | unknown | other (1–100 characters) | "male" |
language | string | The language code represented in ISO 639-1 format, using two uppercase letters. | ISO 639-1 uppercase (2 letters) | "EN" |
lastName | string | The individual's last name. | Free text (1–500 characters) | "Smith" |
phoneNumbers | object | A structured representation of various phone numbers, including home, work, and mobile. | See phoneNumbers | { "home": "", "work": "", "mobile": "" } |
title | string | A title for the individual, which can be free text or one of the specified enumerated values. | Suggested: "Br", "Brigadier", "Capt", "Colonel", "Dame", "Dr", "Elder", "Fr", "General", "Hon", "Judge", "Lord", "Master", "Miss", "Mr", "Mrs", "Ms", "Mstr", "Prof", "Rabbi", "Rev", "Shaikha", "Sheikh", "Sir", "Sister", "Sr" (1–100 characters) | "Mr" |
profile.contact.address
| Field | Type | Description | Allowed / Format | Example |
|---|---|---|---|---|
city | string | The name of the city. | Free text (1–500 characters) | "Madrid" |
country | string | The country code. | ISO 3166-1 alpha-2 (required if address is present) | "ES" |
postCode | string | The postal code, formatted according to the country. | Country-appropriate (1–500 characters) | "AB 123" |
state | string | The name of the state or region. | Free text (1–500 characters) | "Colorado" |
street | string | The street address. | Free text (1–500 characters) | "12 Beaker Street" |
profile.contact.phoneNumbers
| Field | Type | Description | Allowed / Format | Example |
|---|---|---|---|---|
home | string | Home phone number. | E.164 or empty | "+34911222333" |
work | string | Work phone number. | E.164 or empty | "+34914445566" |
mobile | string | Mobile phone number. | E.164 or empty | "+353861234567" |
profile.extensions
General-purpose extensible attributes defined by your organization to meet specific business needs. You can include any number of custom key-value pairs directly inside the extensions object. The field names are defined by you, not by the API.
| Field | Type | Description | Allowed / Format | Example |
|---|---|---|---|---|
<your-field-name> | string, boolean, number, object | A custom field with a name and value defined by your organization. | string, boolean, number, object | "extensions": {"tier": "gold", "score": 42} |
Input example
The following example shows a JSONL file with 12 profile records:
In this example:
- Records 1–5 each use a single
emailidentifier and include bothcontactandextensions. - Record 6 has two identifiers from different providers (
useridandemail) and omits the optionalextensionsfield. - Records 7–12 are identical, each with a single
userididentifier and noextensionsfield.
Identity resolution
When processing each profile record, profile import resolves the record's identifiers against existing profiles to determine whether to create a new profile or update an existing one. The order of the records in the file is not preserved while processing the file.
How identity resolution works
- The system checks only identifiers whose
providermatches a configured identity rule for your environment. Identifiers with unrecognized providers are ignored during resolution. - Identifier values are case-insensitive. For example,
[email protected]and[email protected]resolve to the same profile. - The system looks up each qualifying identifier to find matching profiles.
Resolution outcomes
| Matching profiles found | Action | Description |
|---|---|---|
| 0 | Create | A new profile is created with a system-generated profile ID. |
| 1 | Update | The existing profile is updated with the data from the record. |
| More than 1 | Fail | The record fails with the INCONSISTENT_IDENTIFIERS error code because the identifiers resolve to multiple different profiles. |
Adding new identifiers to existing profiles
You must create the identity rule in your SitecoreAI environment before you upload the batch. Identifiers with unrecognized providers are ignored.
When a record contains identifiers from multiple providers, profile import adds any new identifiers to the matched profile. This lets you associate a new identifier type with profiles that were previously identified by a different provider.
For example, if your SitecoreAI environment uses email as the default identifier and you later create an identity rule for userid (CRM ID), you can upload a batch where each record contains both an email and a userid identifier. Profile import resolves the record against the existing profile using the email identifier, then adds the userid as an additional identifier on that profile.
Update behavior
When a record matches an existing profile, the system updates the profile using deep-merge semantics:
- Object fields are merged recursively. Incoming fields are added to or overwrite existing fields. Fields not present in the incoming record are preserved.
- Null values remove the corresponding field from the profile.
- Only
contactandextensionsare written to the profile. - If the incoming data is identical to the existing profile and there are no missing identifiers to add, no write occurs. The profile's
modifiedAttimestamp is not updated.
Example
If the existing profile contains:
And the incoming record contains:
The resulting profile is:
In this example, firstName and lastName are preserved because they were not included in the incoming record; email is added as a new field; extensions.where is overwritten with the new value.
Updating extension data
Data extensions hold your business-specific key-value data. On update, keys are merged one by one.
If the existing profile contains:
And the incoming record contains:
The resulting profile is:
In this example, loyaltyTier is preserved because it wasn't in the incoming record; where is overwritten with the new value; favoriteColor is added as a new field.
Adding identifiers
The identifiers array is what recognizes a guest across systems. Each entry has a provider and an id. Sending a new identifier adds it alongside the existing ones rather than replacing the array. If the existing identifier is sent with a new id, the system treats is as a new profile.
If the existing profile contains:
And the incoming record contains:
The resulting profile is:
The existing "email" identifier stays, and the new "crm" identifier is appended. From here, identity resolution (via the IDENTITY event) can match profiles with added identifiers, in the order defined in identity rules.
Note that you must create an identity rule with a new identifier before uploading a batch with the new identifiers.
Delete behavior
Deleting a single field
To delete a single field inside an object, set that key to null. Only the nulled key is removed; all other fields in the object are preserved.
If the existing profile contains:
And the incoming record contains:
The resulting profile is:
In this example, lastName is deleted because it was set to null. firstName and email are preserved because they were not nulled. The same pattern applies to a key inside extensions.
Deleting an entire object
To delete an entire object, set either contact or extensions to null. Do not set both to null. The identifier must resolve to an existing profile, and at least one of contact or extensions must remain in the profile after the update.
If the existing profile contains:
And the incoming record contains:
The resulting profile is:
In this example, the entire extensions object is removed because it was set to null. contact is preserved.
At least one object is required
A record that sets both contact and extensions to null is rejected. At least one of contact or extensions must contain data for the record to be processed.
The following record fails with the NO_UPDATABLE_FIELDS error code:
The following constraints apply to all delete operations:
- Every record must include a valid identifier. An identifier with a
nullor unrecognizedprovideris ignored during resolution, and the record fails if no valid identifier remains. - At least one of
contactorextensionsmust be present with content. Setting both tonullreturns theNO_UPDATABLE_FIELDSerror code.
Output file format
After a batch reaches a final state (COMPLETED, COMPLETED_WITH_ERRORS, or FAILED), you can view the results in the Profile import page or download the results file using the following API request:
Output record structure
Each output record contains the following fields:
| Field | Type | Description |
|---|---|---|
recordIndex | Integer | The zero-based index of the record in the input file. |
id | String (UUID) | The correlation ID from the input record, if one was provided. |
recordType | String | The record type from the input record, such as profile. Omitted if the record failed before the type could be parsed. |
outcome | String | The result of processing: CREATED, UPDATED, or FAILED. |
profileId | String (UUID) | The profile ID. Present for CREATED and UPDATED outcomes. |
errorCode | String | The error code. Present only for FAILED outcomes. See Record-level error codes. |
errorDescription | String | A human-readable description of the error. Present only for FAILED outcomes. |