# Migrate anonymous resources to authenticated Customer accounts When a user interacts with your storefront without being logged in, commercetools tracks their activities such as adding items to a Cart by using an `anonymousId`. This ID is typically stored in either a client-side cookie or local storage. Although Carts are the most common resource created anonymously, other resources such as Custom Objects and Shopping Lists can also be linked to an `anonymousId`. If the user signs up or logs in, then you must migrate these anonymous resources to their authenticated Customer account (`customerId`). Failing to do so results in a poor user experience because the user's Cart or other data appears to be lost upon login. ## Automatic Cart migration For Carts, commercetools provides a built-in mechanism to automate migration during sign-in or sign-up, which simplifies development for this common use case. ### During sign-in and sign-up To trigger automatic migration, provide the `anonymousCartId` in the request body of a `POST /{projectKey}/login` or `POST /{projectKey}/customers` request. The `anonymousCartSignInMode` field (for login) and the presence of `anonymousCartId` (for sign-up) control how the anonymous Cart is handled. ### anonymousCartSignInMode for login This field in the `POST /{projectKey}/login` request dictates how to integrate the anonymous Cart with the Customer's account. - **`MergeWithExistingCustomerCart` (default)**: This mode preserves all items from both the anonymous and any existing Customer Carts. - **Scenario**: Anonymous user has Cart A; Customer has an existing active Cart B. - **Result**: Line Items from Cart A are merged into Cart B. Cart B remains the active Cart for the logged-in Customer. Cart A is typically deleted or marked as Merged. - **Conflict resolution**: by default, if a Product Variant with identical Custom Fields exists in both Carts, then the higher quantity is retained. Otherwise, the item is added as a new Line Item. - **Scenario**: Anonymous user has Cart A; Customer has no active Cart. - **Result**: Cart A is assigned to the Customer and becomes their active Cart. - **`UseAsNewActiveCustomerCart`**: This mode replaces an existing Customer Cart with an anonymous Cart. - **Scenario**: Anonymous user has Cart A; Customer has an existing active Cart B. - **Result**: Cart A becomes the Customer's active Cart. Cart B remains in an `active` state, but is hidden in a single Cart scenario. To avoid confusion and prevent Cart B from reappearing after Cart A becomes an Order, you should consider deleting Cart B. - **Use case**: this is useful if the anonymous Cart is considered more current or if you want to override an older, stale customer Cart. - **Scenario**: Anonymous user has Cart A; Customer has no active Cart. - **Result**: Cart A is assigned to the Customer, which is the same outcome as `MergeWithExistingCustomerCart` in this case. ### Sign-up When `anonymousCartId` is provided in a `POST /{projectKey}/customers` request, the Cart is automatically assigned to the new Customer. No `anonymousCartSignInMode` is needed because there's no existing Customer Cart to merge with. ### Example: Merge Carts on login The following example shows how to log in a Customer and merge their anonymous Cart with an existing Customer Cart: ```ts import { apiRoot } from '../../client'; // Assuming apiRoot is initialized and exported here import { CustomerSignInResult } from '@commercetools/platform-sdk'; // Import the type for the response body // Function to handle login with anonymous cart merge async function customerLoginWithCartMergeInStore( storeKey: string, email: string, password: string, anonymousCartId: string ): Promise { try { const loginResponse = await apiRoot .inStoreKeyWithStoreKeyValue({ storeKey: storeKey }) .login() .post({ body: { email: email, password: password, anonymousCartId: anonymousCartId, anonymousCartSignInMode: 'MergeWithExistingCustomerCart', // Explicitly setting, though it's default }, }) .execute(); console.log('Login successful with cart merge for store "${storeKey}"!'); console.log('Customer details:', loginResponse.body.customer.email); console.log('Merged cart details:', loginResponse.body.cart); return loginResponse.body; } catch (error: any) { // Type 'any' for caught error for now, as stricter typing for HTTP errors can be complex console.error( 'Login failed with cart merge for store "${storeKey}":', error ); // You might add more specific error handling here based on commercetools error codes // For example, if (error.statusCode === 400 && error.body && error.body.errors.some(e => e.code === 'InvalidCredentials')) { ... } throw error; } } // Example login for David with anonymous cart, in a store (async () => { const davidEmail = 'david.shopper@example.com'; // Assuming David's email const davidPassword = 'DavidSecurePass!'; // Assuming David's password const davidAnonymousCartId = 'anonymous-cart-id-789'; // This ID would come from the client-side const storeKey = 'electronics-high-tech-store'; console.log( `\n--- Attempting store login for David with anonymous cart (${davidAnonymousCartId}) ---` ); try { const loginResult = await customerLoginWithCartMergeInStore( storeKey, davidEmail, davidPassword, davidAnonymousCartId ); console.log( `David's carts successfully merged upon store login. New active cart ID: ${loginResult.cart?.id}` ); } catch (err) { console.error('Failed to log in David and merge cart in the store.'); } })(); ``` ### Key takeaways - **Automatic for Carts**: commercetools automates Cart migration during login and sign-up. - **Trigger with `anonymousCartId`**: pass the `anonymousCartId` in the login or sign-up request to initiate migration. - **Control with `anonymousCartSignInMode`**: use `MergeWithExistingCustomerCart` (default) to combine Carts or `UseAsNewActiveCustomerCart` to replace the Customer's Cart with an anonymous Cart. - **Sign-up is simpler**: during sign-up, an anonymous Cart is directly assigned to the new Customer without needing a merge mode. ## Manual resource migration Although Carts can be migrated automatically, other resources associated with an `anonymousId` must be migrated manually. This is a critical responsibility of your application's backend or backend for frontend (BFF). ### Resources that require manual migration - **Custom Objects and Custom Fields**: any custom data linked to an `anonymousId`, such as user preferences or browsing history, requires manual migration. - **Shopping Lists**: Shopping Lists associated with an `anonymousId` aren't automatically transferred. - **Specific Line Items**: under certain conditions, such as a missing `shippingAddress` on the Customer's Cart, Line Items from the anonymous Cart might not be merged automatically. For more information, see the [Cart merge during sign-in](/api/customers-overview.md#cart-merge-during-sign-in-and-sign-up) documentation. ### Manual migration process Your application must implement the following logic to transfer ownership of these resources: 1. **Identify anonymous resources**: before authentication, query for any resources associated with the `anonymousId` from the client-side session. For example, you can query Shopping Lists or Custom Objects that store the `anonymousId`. 2. **Authenticate user**: after a successful sign-in or sign-up, obtain the `customerId` details. 3. **Update resources**: use the appropriate update actions to re-associate the anonymous resources with the `customerId`. - **Shopping Lists**: use the `setCustomer` update action. - **Custom Objects**: update a field in the object to reference the `customerId` or recreate the object with a new key or container that includes the `customerId`. 4. **Clean up**: after migration, remove the `anonymousId` from the client-side session and consider deleting the original anonymous resources to maintain data hygiene. ### Example - Assign a Shopping List to a Customer ```ts import { apiRoot } from '../../client'; // Assuming apiRoot is initialized and exported here import { ShoppingList, ShoppingListUpdate } from '@commercetools/platform-sdk'; // Import necessary types // Function to assign a Shopping List to a customer in a store async function assignShoppingListToCustomerInStore( storeKey: string, shoppingListId: string, shoppingListVersion: number, customerId: string ): Promise { try { const shoppingListUpdate: ShoppingListUpdate = { version: shoppingListVersion, actions: [ { action: 'setCustomer', customer: { typeId: 'customer', id: customerId, }, }, ], }; const updatedShoppingListResponse = await apiRoot .inStoreKeyWithStoreKeyValue({ storeKey: storeKey }) .shoppingLists() .withId({ ID: shoppingListId }) .post({ body: shoppingListUpdate }) .execute(); const updatedShoppingList: ShoppingList = updatedShoppingListResponse.body; console.log( `Shopping List ${shoppingListId} successfully assigned to customer ${customerId} within store "${storeKey}".` ); return updatedShoppingList; } catch (error: any) { console.error( `Error assigning Shopping List ${shoppingListId} to customer ${customerId} in store "${storeKey}":`, error ); throw error; } } // Example usage after David logs in (async () => { const ELECTRONICS_HIGH_TECH_STORE_KEY = 'electronics-high-tech-store'; const davidCustomerId = 'david-customer-id'; // Obtained from successful login // Assume you have an anonymous Shopping List ID and its current version, // which was associated with 'electronics-high-tech-store' const anonymousShoppingListId = 'anon-shoppingList-id-123'; // Replace with a real ID const anonymousShoppingListVersion = 1; // Replace with a real version console.log( `\n--- Assigning anonymous Shopping List to customer ${davidCustomerId} for store "${ELECTRONICS_HIGH_TECH_STORE_KEY}" ---` ); try { await assignShoppingListToCustomerInStore( ELECTRONICS_HIGH_TECH_STORE_KEY, anonymousShoppingListId, anonymousShoppingListVersion, davidCustomerId ); console.log( `Anonymous Shopping List successfully migrated to David's account within ${ELECTRONICS_HIGH_TECH_STORE_KEY}.` ); } catch (err) { console.error('Failed to migrate anonymous Shopping List in-store.'); } })(); ``` ### Key takeaways - **Manual migration is required**: resources such as Shopping Lists and Custom Objects aren't migrated automatically. - **Application responsibility**: your application's backend or BFF must implement the logic for manual migration. - **Process**: the process involves identifying anonymous resources, authenticating the user, updating the resources with the `customerId`, and cleaning up old anonymous data. - **Flexibility**: manual migration enables you to implement custom business logic for merging or transferring data beyond what the automatic process provides. You now have a solid understanding of how to manage the transition from anonymous browsing to authenticated Customer sessions, both automatically for carts and manually for other vital resources. This ensures a smooth and continuous shopping journey for your Customers. ## Entire process workflow Here's a flowchart diagram that illustrates the complete process of migrating anonymous resources to authenticated Customer accounts: ```mermaid graph TD A[Anonymous User Browse] --> B{"User Logs In or Signs Up?"}; B -- "Yes" --> C{"Are there Anonymous Resources?"}; C -- "Yes, Anonymous Cart exists" --> D[Automated Cart Migration]; D --> E{"Login or sign-up?"}; E -- "Login (POST /login or /in-store/key/login)" --> F{"anonymousCartSignInMode?"}; F -- "MergeWithExistingCustomerCart (Default)" --> G[Anonymous Cart Merged into Customer's Active Cart]; F -- "UseAsNewActiveCustomerCart" --> H[Anonymous Cart Replaces Customer's Active Cart]; E -- "Sign-up (POST /customers)" --> I[Anonymous Cart Assigned to New Customer]; C -- "Yes, Other Anonymous Resources
(e.g. Custom Objects)" --> J[Manual Resource Migration]; J --> K["Identify Anonymous Resources
(e.g. query by anonymousId)"]; K --> L["User Logs In / Signs Up
(Obtain customerId)"]; L --> M["Update Resources with customerId
(e.g. setCustomer action)"]; L --> N["Optionally, Delete Original Anonymous Resources"]; G --> O[Seamless Customer Experience]; H --> O; I --> O; N --> O; M --> O; C -- "No Anonymous Resources" --> O; ``` ## Related pages - [Area overview page with navigation](/learning-implement-carts-and-shopping-lists.md) - [Previous page: Password reset flow](/learning-implement-carts-and-shopping-lists/manage-signups-and-signins/password-reset-flow.md) - [Next page: Cart merge strategies](/learning-implement-carts-and-shopping-lists/manage-signups-and-signins/cart-merge-strategies.md) - [Search documentation and API specs](/search.md)