Create or update one or more people (merge)
The merge Ortto endpoint of the person entity is used to create or update one or more person records in Ortto’s customer data platform (CDP).
This page provides descriptions of this endpoint’s:
- the response payload.
HTTP method and request resource
POST https://api.ap3api.com/v1/person/mergeNOTE: Ortto customers who have their instance region set to Australia or Europe will need to use specific service endpoints relative to the region:
Australia: https://api.au.ap3api.com/ Europe: https://api.eu.ap3api.com/For example: https://api.eu.ap3api.com/v1/<entity/endpoint>
All other Ortto users will use the default service endpoint (https://api.ap3api.com/).
Path and query parameters
This endpoint takes no additional path and/or query parameters.
Headers
This endpoint requires a custom API key and content type (
application/jsonfor the request body) in the header of the request:
X-Api-Key: CUSTOM-PRIVATE-API-KEYContent-Type: application/jsonRequest body
The request body consists of a JSON object whose valid elements are listed in the table below.
The following JSON object is an example of field and object data that Ortto can recognize to create or update one or more person records in your Ortto account’s CDP.
Example create/update people request body in Ortto’s CDP
json
{ "people": [ { "fields": { "str::first": "Chris", "str::last": "Smith", "str::email": "chris.smith@example.com", "str:cm:job-title": "Technician" }, "location": { "source_ip": "119.18.0.218" } }, { "fields": { "str::first": "Alex", "str::email": "alex@example.com" }, "location": { "source_ip": "119.18.0.218" } } ], "async": true, "merge_by": ["str::email"], "merge_strategy": 2, "find_strategy": 0, "suppression_list_field_id": "str::email" }IMPORTANT: If you are sending a large number of synchronous (
"async": false) API updates using a merge key (e.g."merge_by": ["str::email"]) this can end up hitting a concurrency limit and your API requests may start to fail.To avoid this, and to speed up the processing of the API requests, we recommend using asynchronous (
"async": true) updates where possible, or if the person ID of the contact is known, merging byperson_idinstead, as it’s a guaranteed unique identifier of the contact and so no lookup request is needed to search through all contacts. See below for an example of merging by person ID.TIP: You can provide up to three fields in the
merge_byarray.Merge_by array
If you provide any fields in the
merge_byarray, only those fields will be used for merging.If you do not send any fields in the
merge_byarray, we will fall back to using the unique identifier list defined in your Custom API data source.Check against other field
You can replicate the Check against another field option from the account unique identifiers in your API payload by using the following syntax:
json
"merge_by_alt_fields": { "str::email": ["{your alt email field id}"] }Learn more about the check against another field option.
Merging person records using a person’s ID
If merging by a person’s ID, then
person_idmust be the only field in themerge_byarray.Example create/update people and merge by a person’s ID
json
{ "people": [ { "fields": { "str::first": "Jack", "str::last": "Skellington", "str::person_id": "00647687d2e43b25a0261f00" } }, { "fields": { "str::first": "Sally", "str::last": "O'Hara", "str::person_id": "00647687d2f43b25b0261f01" } } ], "async": false, "merge_by": ["str::person_id"], "merge_strategy": 2, "find_strategy": 1 }Valid request body elements
The following table lists all valid request body elements (arrays, objects, or fields), which are available to this endpoint.
Element
Type
Description
people
arrayof objectsThe
peoplearray consists of an array of objects, where each object contains data associated with a person being created or updated in your Ortto account’s CDP. Each of these objects can contain:
fieldsandlocationobjects.tagsandunset_tagsarrays.Between 1 to 100 people (each as individual objects of this
array) can be created and/or updated in a single request body call to this endpoint.→
fieldsObject containing person field members
The object containing the fields for a person being created or updated in your Ortto account’s CDP. This person is either created or updated in Ortto’s CDP based on these criteria:
If thisfieldsobject contains a person field member whose value matches themerge_bymember’s value submitted in this request, and this person field’s value does not match that of an existing person in Ortto’s CDP, then Ortto creates this person as a new record. Otherwise, if the person field member’s value does already match that of an existing person in Ortto’s CDP, then Ortto updates the fields of that person’s record in the Ortto CDP, based on themerge_strategyvalue of this request.→
locationObject containing location field data
The
locationobject either accepts a single IP address (as asource_ipfield member), or a full address (in either acustomoraddressobject). Thelocationobject provides more flexible options for specifying a person’s location and address details rather than having to specify this information viageo-type person fields in afieldsobject (above).→
tags
arrayofstringvaluesEach
stringvalue in thetagsarray represents a tag that is applied to this person in this request. Tags can be applied to a person, regardless of whether their record is being created or updated in your Ortto account’s CDP. If a specified tag already exists in the CDP, then that tag is re-used when applied to this person. Otherwise, a new tag is automatically created in the CDP and applied to this person.→
unset_tags
arrayofstringvaluesEach
stringvalue in theunset_tagsarray represents a tag that is removed from this person in this request. Be aware that if theunset_tagsarray:
is used in conjunction with thetagsarray in a single request, avoid specifying the same tag in both arrays, since the processing order of these arrays may differ from one request to the next, resulting in unpredictable tagging outcomes. contains tags which were not applied to this person, then specifying them in this array has no effect.Therefore, a construct like:
json
"people": [ { "fields": { "str::email": "chris.smith@example.com" }, "tags": ["Tag2", "Tag3"], "unset_tags": ["Tag1"] } ]would result in Tag2 and Tag3 being applied to this person. Tag1 would be removed if it had already been applied to this person. Otherwise, if Tag1 were not applied to this person, the tag’s explicit removal (as depicted in this code example), has no effect.
→
clear_fieldsObject containing person field members
The
clear_fieldsobject enables you to determine which person field values will be overwritten by the data provided in the payload atfields(when themerge_strategyis set to2). Learn more about clearing and setting a person's field values.
async
booleanWhen set to
true, theasyncelement enables you to queue the ingestion of merged person data. You will receive an immediate response confirming the queued ingestion.IMPORTANT: If you are sending a large number of synchronous (
"async": false) API updates using a merge key (e.g."merge_by": ["str::email"]) this can end up hitting a concurrency limit and your API requests may start to fail.To avoid this, and to speed up the processing of the API requests, we recommend using asynchronous (
"async": true) updates where possible, or if the person ID of the contact is known, merging byperson_idinstead, as it’s a guaranteed unique identifier of the contact and so no lookup request is needed to search through all contacts. See Request body for an example of merging by person ID.
merge_by
arrayof one or twostringvaluesThe
merge_byelement’s array allows up to three field ID values that specify the person fields used to determine whether the people’s records are either created or updated in your Ortto account’s CDP.When the value of the person field member (determined by the relevant
merge_bymember value), submitted in this request matches that of an existing person in Ortto’s CDP, then this person’s record is updated in the CDP and where appropriate, existing field values are merged according to the strategy below. Otherwise, Ortto creates a new person’s record in the CDP.These values respectively override the default person fields associated with the custom API key submitted in this request. These default field values are defined by the Merge strategy associations configured for this custom API key. If a
merge_byelement is not specified in the request, then these default person field values are utilized instead. The first of these values determines the mainmerge_byperson field utilized by Ortto in the request, whereas the second (optional) value determines the fallbackmerge_byperson field (which behaves as a backup should the first field - e.g. a custom field - not be available within the person’s record of Ortto’s CDP).NOTE: If merging by a person’s ID, then`person_id` must be the only field in the
merge_byarray, i.e."merge_by": ["str::person_id"]
merge_strategy
integer(default value is2Overwrite existing)When the
merge_bymember value (and its corresponding person field member value) submitted in this request determines that an existing person’s record in Ortto’s CDP will be updated, then thismerge_strategymember value determines how the person’s existing field values are merged. Learn more about this value in Merge strategy below. Ifmerge_strategyis not specified in the request, then the default value of2(Overwrite existing) is used.
find_strategy
integer(default value is0[any])The Find strategy determines how the
merge_byfields are used in finding existing people to merge with. Learn about the different options in Find strategy below. Iffind_strategy" is not specified in the request, then the default value of `0([any]) is used.
skip_non_existing
booleanThe
skip_non_existingflag enables you to perform updates to existing contacts and not add contacts that do not already exist in your CDP.For example, this is useful when you want to update the email address for a contact by their data source ID (in this example, a Chargebee customer ID):
"people": [ { "fields": { "str::email": "customer@email.com", "str:cb:c_id": "123456789" } } ], "async": false, "merge_by": ["str:cb:c_id"], "merge_strategy": 1, "find_strategy": 0, "skip_non_existing": true
skip_non_existingworks at the record level, not the field level. As such, where the person exists in the CDP, the merge strategy comes into play. Using the example above, when used in conjunction withmerge-strategy:
with a value of1("merge_strategy": 1; append), the API will only add email addresses to contact records if the email address does not already exist (it will not add new contacts). with a value of2("merge_strategy": 2; overwrite), the API will only overwrite exiting contact records to update existing email addresses.NOTE: When using. a merge key that is read-only (such as the Chargebee customer ID above:
"str:cb:c_id"), you must use"merge strategy": 2(withskip_non_existing), as the API cannot write updates to a new contact based on a read-only merge key.
suppression_list_field_id
stringThe
suppression_list_field_idenables you to skip creating new contacts who have an email address that already exists on your Email suppression list, so you don't create a contact you won't be able to send emails too.The value of this setting should be the field that contains the email address you want to compare against the suppression list, which in most cases will be the default email address, for example:
"suppression_list_field_id": "str::email"When a contact is skipped because their email address is suppressed, you will get this response:
json
{ "status": "suppressed", "error": "Email is suppressed" }NOTE: When updating a contact's multi-select field values, if the new values are intended to replace existing values, the field must first be cleared.
Learn more about clearing and setting a person's field values.
About empty values
When you use a filter to search for people, the Has any value filter option will find matches for activity attribute and field values that have a value of
0or""(empty string). However, Has any value won’t find attribute or field values that arenull.You can set values according to your needs by updating a person’s data using this API endpoint (
v1/person/merge). To:
Include an empty value in a search: set an existingnullto0or"". Exclude an empty value from search: set an existing""or0value tonull.For example, updating a person’s field value to exclude it from search can look like this:
json
"people": [ { "fields": { "str::first": "John", "str::last": "Apple", "str::email": "japple@email.com", "str:cm:job-title": null }Person fields
In Ortto, a person field:
contains the data for a specific piece of information (i.e. field) about each person in Ortto’s CDP, is referenced via the Ortto API using a specific ID format, could be a built in Ortto field or a custom field you have defined yourself (which also has its own ID format), and is defined as a member for each person (within their respectivefields : { … }object) submitted in the request to this endpoint.The following built-in person fields are accessible through Ortto’s API when creating or updating people in the CDP.
Field name
Example
Description
First name
"str::first": "Chris"A string whose value is this person’s first name.
Last name
"str::last": "Smith"A string whose value is this person’s last name.
Person ID
"str::person_id": "00647687d2f43b25b0261f00"A string value representing a unique identifier for the person’s CDP record.
Phone number
json
"phn::phone": { "c": "61", "n": "401234567" }or
json
"phn::phone": { "phone": "61401234567", "parse_with_country_code": true }A phone number field can be provided in one of two ways:
1 - an object of two members consisting of valid country code digits (
c) and a phone number (n), representing this person’s phone number. For the phone number, omit the initial trunk prefix/digit (e.g.0) that is typically dialed when calling the number locally.2 - An object of two members consisting of the phone number (
phone) and whether that number should be considered to start with a country code or not (parse_with_country_code). Use this object to provide the full phone number in one field, not split out between the number and the country code.
"str::email": "chris.smith@example.com"A string whose value is this person’s email address. This person field and its value is commonly used as the main
merge_byfield that determines whether a person’s record in Ortto’s CDP is either created or updated. This field is mandatory if the External ID field is not provided in the containingfieldsobject.City
json
"geo::city": { "name": "Melbourne" }A geographical data object consisting of a member
namewhose string value is this person’s current city.Country
json
"geo::country": { "name": "Australia" }A geographical data object consisting of a member
namewhose string value is this person’s current country.Birthday
json
"dtz::b": { "year": 1980, "month": 3, "day": 4, "timezone": "Australia/Sydney" }A date object consisting of members
year,monthanddaywhose respective integer values represent this person’s date of birth, along with atimezonestring representing the person’s current time zone.Region
json
"geo::region": { "name": "Victoria" }A geographical data object consisting of a member
namewhose string value is this person’s current region (e.g. state or province) within their country.Postal
"str::postal": "90210"A string whose value is this person’s current postal code.
External ID
"str::ei": "c533532fe5d16c7d4fa4c7f0"A string whose value is any ID used to uniquely identify this person. This value is mandatory if the email field is not provided in the containing
fieldsobject.GDPR
"bol::gdpr": trueA boolean value where
trueflags that GDPR is a requirement for this person, orfalseif not.Email subscription permission
"bol::p": false(default value istrue)A boolean value where
trueflags that the person has their Email permission set to true, or Subscribed through the Ortto user interface (UI), andfalseflags that this person’s permission is set to false (or Unsubscribed). If this person field is not specified in the request, then this value is assumed to betrueby default.Custom context message for the email unsubscribe action
"str::u-ctx": "Unsubscribed from email using a custom context message"(default value is"Unsubscribed via API")A string value that allows you to customize the default activity context message from Unsubscribed via API to something else, when setting the email subscription permission to
false. These messages appear in people’s Activities updates in the Ortto UI.Custom context message for the email subscribe action
"str::s-ctx": "Subscribed to email using a custom context message"(default value is"Subscribed via API")A string value that allows you to customize the default activity context message from Subscribed via API to something else, when setting the email subscription permission to
true. These messages appear in people’s Activities updates in the Ortto UI.SMS subscription permission
"bol::sp": true(default value isfalse)A boolean value where
falseflags that the person has their SMS permission set to false, or Unsubscribed through the Ortto UI, andtrueflags that this person’s permission is set to true (or Subscribed). If this person field is not specified in the request, then this value is assumed to befalseby default.Custom context message for the SMS subscribe action
"str::soi-ctx": "Subscribed to SMS using a custom context message"(default value is"Subscribed via API")A string value that allows you to customize the default activity context message from Subscribed via API to something else, when setting the SMS subscription permission to
true. These messages appear in people’s Activities updates in the Ortto UI.Custom context message for the SMS unsubscribe action
"str::soo-ctx": "Unsubscribed from SMS using a custom context message"(default value is"Unsubscribed via API")A string value that allows you to customize the default activity context message from Unsubscribed via API to something else, when setting the SMS subscription permission to
false. These messages appear in people’s Activities updates in the Ortto UI.Language
"str::language": "de"A string which determines the person’s preferred language. This can be used to present email campaigns in the person’s preferred language (where supported) using Ortto’s multi-language feature.
See a list of available language values at List of languages.
FCM iOS push notification token
"str::fcm_ios_token": "my-token"If a user has already given push permission to your mobile app before implementing Ortto's SDK, you can use these fields to submit the notification token to Ortto so it can be re-used for sending Ortto's push notifications without having to ask the customer for permission again.
APN iOS push notification token
"str::apn_ios_token": "my-token"Android push notification token
"str::android_token": "my-token"Person field ID format
Each person field is referenced by an ID.
Since Ortto integrates with many third-party products, references to person fields in Ortto’s CDP are both strongly-typed and namespace-specific. Therefore, each person field’s ID is based on the format:
type:namespace:field-nameFor:
Ortto’s own built-in person fields, thenamespacevalue is unnecessary and is omitted. Hence, these built-in fields are referenced by an ID based on the format:
type::field-name Up to 100 custom fields can be added to an Ortto account/instance.Thefield-namefor custom fields is typically based on their configured names converted to kebab-case.
type:cm:field-nameNOTE:
Up to 150 fields can be sent in a single request The total number of custom fields you can have in an Ortto account/instance depends on your plan. Thefield-namefor custom fields is typically based on their configured names converted to kebab-case.Person field type abbreviations
The following person field type abbreviations are used to form the first part (
type) of each person field’s ID for built-in fields:
Field type abbreviation
Type of value
bolBoolean
dtzDate (object)
geoGeographical data (object)
intInteger. For internal operations and calculations, the Ortto API treats decimal values as integers multiplied by 1,000. This is done to preserve the precision of values resulting from these calculations.
Note: Integers are processed as
int64
phnPhone number (object)
strString
Merge strategy
The merge strategy determines how a person’s existing field values are merged.
When the
merge_bymember value (and its corresponding person field member value) submitted in this request determines that an existing person’s record in Ortto’s CDP will be updated, then one of the followingmerge_strategyvalues in the request determines how the person’s existing field values are merged:
merge_strategy (integer)
Strategy
Description
1
Append only
Using this strategy, all fields with existing values in Ortto’s CDP are not changed. Ortto only adds new data (for fields without a value). For example, assuming you have a custom field
str:cm:place-of-birth, and the request contains the person field value:"str:cm:place-of-birth": "Oslo",
If this person’s existingstr:cm:place-of-birthvalue is"Sydney"in the CDP, then this existing value would not be changed after the request is submitted, and the value would remain"Sydney"in the CDP. If, however, this person’s existingstr:cm:place-of-birthvalue is empty in the CDP, then this empty field would be updated to "Oslo" in the CDP.2
Overwrite existing (default)
Using this strategy, any person fields specified in the request are updated in Ortto’s CDP, even when existing values are present, and hence are overwritten. A person’s field in the CDP can be cleared by specifying the corresponding person field’s value in the request as
null. For example, assuming you have a custom fieldstr:cm:place-of-birthand this person’s existingstr:cm:place-of-birthvalue in the CDP is"Sydney", then a request containing the person field value:
"str:cm:place-of-birth": "Oslo"would be changed to"Oslo"in the CDP after the request is submitted,"str:cm:place-of-birth": nullwould be cleared and made empty in the CDP after the request is submitted.Any person fields which are not specified in the request are not cleared (and retain their value) in the person’s CDP record.
3
Ignore
Using this strategy, no updates are applied to the existing person’s record in Ortto’s CDP, but a new person will be created if it doesn’t exist. If you do not wish to create a new person you need to provide the
skip_non_existingtotrue.TIP: Use this merge strategy to enforce only adding new people to the CDP, leaving existing people’s records untouched.
Find strategy
The find strategy determines how the
merge_byfields are used to detect an existing person match.The find strategy is only relevant if you have 2 or more
merge_byfields provided. When you have only 1 field, this setting makes no difference to the outcome. When 2 or moremerge_byfields are provided, thefind_strategyvalue determines how we utilise the fields in detecting an existing person match:
find_strategy (integer)
Strategy
Description
0
Any (default)
Using this strategy, all
merge_byfields are used in detecting an existing person to merge with. For example, assuming you havestr::emailandphn::phoneas your twomerge_byfields, and provide both fields in your request:
Starting with the firstmerge_byfield (in this case,str::email), try to find an existing person match using that field, and if a match is found, merge. If a match is not found using the first field, the second field is then used (in this case,phn::phone) to try and find a match, and if found, merge. If neither of the provided values find a match, then a new record would be created (depending on your merge_strategy)1
Next only if previous empty
Using this strategy, the first
merge_byfield is prioritized, and the second field is only used if the first field had no value for the existing contact. For example, assuming you havestr::emailandphn::phoneas your twomerge_byfields, and provide both fields in your request:
Starting with the firstmerge_byfield (in this case,str::email), try to find an existing person match using that field, and if a match is found, merge.If a match is not found using the first field, there are 2 scenarios that can happen:
There are no existing contacts who do not have a value for the first merge key (str::email). In this case the second field (phn::phone) is ignored and we do not check for a match. There are existing contacts who do not have a value for the first merge key (str::email). In this case, because the first value is empty, we check the second merge key (phn::phone) for a match, and if a contact with no email address does match on phone number, we merge.2
All
Using this strategy, all
merge_byfields are used in detecting an existing person to merge with. For example, assuming you havestr::emailandphn::phoneas your two merge_by fields, and provide both fields in your request:
Try and find an existing person who has the same email AND phone number, and match with them. If a match is not found on both email and phone number, then a new contact will be created.If only one of the
merge_byfields are provided for the contact in the request, then we will just match on that single field against the existing contacts.Key combinations to achieve different merge strategies
When you use Ortto's user interface (UI) to import contacts, such as when you connect a data source like Salesforce or Segment, or perform a CSV import, you will be presented with a number of options for the merge strategy and merge key strategies.
If you are creating or merging contacts via the
v1/person/mergeendpoint, the merge strategies presented in the UI are achieved according to themerge_strategyandskip_non_existingvalues you use.The equivalent combinations are:
Import and merge new data only: “merge_strategy”: 1, “skip_non_existing”: false Import and merge new data for existing records only: “merge_strategy”: 1, “skip_non_existing”: true Import and overwrite any data that exist (recommended): “merge_strategy”: 2, “skip_non_existing”: false Import and overwrite any data that exist for existing records “merge_strategy”: 2, “skip_non_existing”: true Import new records only: “merge_strategy”: 3, “skip_non_existing”: falseThe merge key strategy is determined by the identifiers you set at
merge_byand thefind_strategyvalue.The equivalent merge key strategies are:
Match only if previous merge key is empty: “find_strategy”: 1 Merge with any key match: “find_strategy”: 0

