AWS Security ChangesHomeSearch

AWS connect: Customer Profiles mapping docs rewritten with key/identity guidance

Service: connect · 2026-09-27 · Documentation medium

File: connect/latest/adminguide/examples-object-type-mappings.md

Summary

Rewrites object type mapping examples and adds guidance on fields, keys, standard identifiers, content types, and key-name sharing to avoid merging unrelated profiles.

Security assessment

The diff is a documentation overhaul for Customer Profiles object type mappings. It adds best-practice guidance on key naming and identity matching, including a warning that reusing a key name can collapse unrelated customers into one profile, which is a privacy/data-integrity concern. No specific vulnerability, CVE, or incident is referenced.

Evidence

+A key name is a domain-wide index: two objects with the same key name and the same value land on the same profile, even when they come from different object types.

Diff

diff --git a/connect/latest/adminguide/examples-object-type-mappings.md b/connect/latest/adminguide/examples-object-type-mappings.md
index d82eaba3a..35ca3b429 100644
--- a//connect/latest/adminguide/examples-object-type-mappings.md
+++ b//connect/latest/adminguide/examples-object-type-mappings.md
@@ -7 +7 @@
-An object type mapping that generates a profileAn object type mapping that doesn't populate the standard profile
+The shape of a mappingHow to build a mappingExample 1: The smallest valid mappingExample 2: Populate the standard profileExample 3: Populate a standard objectExample 4: Link two object types togetherBefore you submit
@@ -11 +11 @@ An object type mapping that generates a profileAn object type mapping that doesn
-## An object type mapping that generates a profile
+This topic builds a mapping from the ground up. Each example builds on the one before it.
@@ -13 +13,6 @@ An object type mapping that generates a profileAn object type mapping that doesn
-The following example shows data that populates the standard profile.
+Example | What it adds  
+---|---  
+1\. The smallest valid mapping | The two required keys: `UNIQUE` and `PROFILE`  
+2\. Populate the standard profile | `Target`, `ContentType`, search-only keys  
+3\. Populate a standard object | Standard object identifiers and targets (`ASSET`, `_asset.*`)  
+4\. Link two object types together | Shared key names, one key serving two roles  
@@ -15 +20,3 @@ The following example shows data that populates the standard profile.
-Following is the incoming object:
+## The shape of a mapping
+
+Every mapping is a set of `Fields` and a set of `Keys`. Fields pull values out of the incoming object. Keys combine fields into identifiers that Customer Profiles uses to match and search.
@@ -19,6 +26,6 @@ Following is the incoming object:
-      "account": 1234,
-      "email": "[email protected]",
-      "address": {
-         "address1": "Street",
-         "zip": "Zip",
-         "city": "City"
+        "Fields": {
+            "{fieldName}": {                     <-- your name for this value
+                "Source": "{source}",            <-- where to read it from the incoming object
+                "Target": "{target}",            <-- optional: where to write it on a standard object
+                "ContentType": "{contentType}"   <-- optional: how to normalize it (default STRING)
+            }
@@ -26,2 +33,7 @@ Following is the incoming object:
-      "firstName": "John",
-      "lastName": "Doe"
+        "Keys": {
+            "{keyName}": [                       <-- an array, so one key name can have several definitions
+                {
+                    "StandardIdentifiers": [...],  <-- optional: the key's role during ingestion
+                    "FieldNames": ["{fieldName}"]  <-- one or more fields defined above
+                }
+            ]
@@ -28,0 +41,23 @@ Following is the incoming object:
+    }
+
+###### Note
+
+**Fields and keys are separate namespaces**
+
+A field name is a local label used only inside this mapping. A key name is global to the domain and shared with every other object type that uses the same name. The examples below use different names for the two so the distinction stays visible.
+
+## How to build a mapping
+
+Work through these five questions in order. The answers become the mapping.
+
+  1. **What makes one incoming record distinct from the next?** That value becomes your `UNIQUE` key. Every object type needs exactly one. Re-ingesting a record with the same `UNIQUE` value replaces the previous one.
+
+  2. **What ties the record to a person?** A customer ID, email, phone, or account number. That becomes a `PROFILE` key. Every object type needs at least one.
+
+  3. **Which values do you want to appear on the customer's profile?** Give each one a field with a `Target` on the standard profile object, such as `_profile.FirstName`.
+
+  4. **Does the record also describe an asset, order, case, or similar record?** Those are other **standard objects** —predefined records with fixed schemas. If your record describes one, target it (`_asset.*`) and add its standard identifier (`ASSET`) to a key.
+
+  5. **Which other values do you want to be searchable?** Add a key for each, with no standard identifiers.
+
+
@@ -31 +66,31 @@ Following is the incoming object:
-The following code shows that incoming object mapping into a standard profile object and indexing `PersonalEmailAddress`, `fullName`, and `accountId`, which is a unique key.
+###### Note
+
+**Profile objects and standard objects behave differently**
+
+The record you ingest is a _profile object_. It's identified by its `UNIQUE` key and is replaced wholesale when the same `UNIQUE` value arrives again. A `Target` writes into a _standard object_ (the standard profile, an asset, an order). Standard objects are never replaced wholesale—they accumulate field values from many profile objects over time. For more information, see [Reference for standard objects in Customer Profiles](./standard-objects.html).
+
+###### Note
+
+**Every key is searchable**
+
+Matching is only half of what a key does. Each key is also indexed for the [SearchProfiles](https://docs.aws.amazon.com/customerprofiles/latest/APIReference/API_SearchProfiles.html) API, so you can find a profile after ingestion by passing a key name and a value. Because key names are global to the domain, searching one name finds profiles across every object type that defines it. Only stored keys stay searchable—see [Standard identifiers in Customer Profiles](./standard-identifiers.html) to control when keys are stored.
+
+###### Tip
+
+**Choose distinctive key values**
+
+Choose key values distinctive enough to identify a profile: a customer ID, an email address, an order number, a serial number. Low-cardinality values such as gender, state, country, status, or loyalty tier make poor keys—avoid them.
+
+## Example 1: The smallest valid mapping
+
+A mapping needs one `UNIQUE` key and one `PROFILE` key. Nothing else is required. A single key can carry both roles.
+
+Incoming object:
+    
+    
+    {
+      "customerId": "C-10045",
+      "signupDate": "2025-01-14"
+    }
+
+Mapping:
@@ -36,12 +101,3 @@ The following code shows that incoming object mapping into a standard profile ob
-            "accountId": {
-                "Source": "_source.account",
-                "Target": "_profile.AccountNumber",
-                "ContentType": "NUMBER"
-            },
-            "shippingAddress.address1": {
-                "Source": "_source.address.address1",
-                "Target": "_profile.ShippingAddress.Address1"
-            },
-            "shippingAddress.postalCode": {
-                "Source": "_source.address.zip",
-                "Target": "_profile.ShippingAddress.PostalCode"
+            "customerId": {
+                "Source": "_source.customerId"
+            }
@@ -49,3 +105,51 @@ The following code shows that incoming object mapping into a standard profile ob
-            "shippingAddress.city": {
-                "Source": "_source.address.city",
-                "Target": "_profile.ShippingAddress.City"
+        "Keys": {
+            "crmCustomerId": [
+                {
+                    "StandardIdentifiers": ["PROFILE", "UNIQUE"],
+                    "FieldNames": ["customerId"]
+                }
+            ]
+        }
+    }
+
+What happens at ingestion:
+
+  * Customer Profiles looks for a profile whose `crmCustomerId` key equals `C-10045`. If it finds exactly one, the record attaches to it. If it finds none and `AllowProfileCreation = true`, a new inferred profile is created.
+
+  * The whole incoming record is stored as a profile object, including `signupDate`—storage doesn't require a field definition. Only the values you want indexed or written to a standard object need fields.
+
+  * Nothing appears on the standard profile, because no field has a `Target`. Agents looking at the profile would see an empty record.
+
+
+
+
+Example 2 addresses that last point.
+
+## Example 2: Populate the standard profile
+
+Adding a `Target` to a field writes its value onto the standard profile, where agents and downstream applications can read it.
+
+Incoming object:
+    
+    
+    {
+      "customerId": "C-10045",
+      "email": "[email protected]",
+      "phone": "+15551234567",
+      "firstName": "John",
+      "lastName": "Doe",
+      "address": {
+        "street": "123 Main St",
+        "city": "Seattle",
+        "zip": "98101"
+      }
+    }
+
+Mapping:
+    
+    
+    {
+        "Fields": {
+            "customerId": {
+                "Source": "_source.customerId",
+                "Target": "_profile.AccountNumber"
@@ -53 +157 @@ The following code shows that incoming object mapping into a standard profile ob
-            "personalEmailAddress": {
+            "emailAddress": {
@@ -55 +159 @@ The following code shows that incoming object mapping into a standard profile ob
-                "Target": "_profile.PersonalEmailAddress",
+                "Target": "_profile.EmailAddress",
@@ -58,2 +162,4 @@ The following code shows that incoming object mapping into a standard profile ob
-            "fullName": {
-                "Source": "{{_source.firstName}} {{_source.lastName}}"
+            "phoneNumber": {
+                "Source": "_source.phone",
+                "Target": "_profile.PhoneNumber",
+                "ContentType": "PHONE_NUMBER"
@@ -63 +169,2 @@ The following code shows that incoming object mapping into a standard profile ob
-                "Target": "_profile.FirstName"
+                "Target": "_profile.FirstName",
+                "ContentType": "NAME"
@@ -67 +174,18 @@ The following code shows that incoming object mapping into a standard profile ob
-                "Target": "_profile.LastName"
+                "Target": "_profile.LastName",
+                "ContentType": "NAME"
+            },
+            "fullName": {
+                "Source": "{{#removeExtraSpace}}{{_source.firstName}} {{_source.lastName}}{{/removeExtraSpace}}",
+                "ContentType": "NAME"
+            },