Create Shopping Lists

Learn how to construct and create new Shopping Lists via the API, both for registered customers or anonymous users.

After completing this page, you should be able to:

  • Determine appropriate scenarios for creating a new Shopping List, following best practices to avoid unnecessary list creation.
  • Construct a ShoppingListDraft including essential fields (name, user association via customerId or anonymousId, an initial item).
  • Distinguish between using the general and in-store API endpoints for creation and adjust the ShoppingListDraft accordingly (specifically regarding the inclusion or omission of the store field).

Create a new Shopping List only in response to a meaningful user action, rather than preemptively. Recommended scenarios:

  • When a user adds their first item to a new wishlist or shopping list.
  • When a user explicitly requests to create a new list (for example, setting up a new gift registry).

It's crucial to avoid unnecessary Shopping List creation to prevent database clutter and potential performance issues. Best practice dictates checking if a suitable list already exists for the user before creating a new one.

If no suitable list exists, you create one using a ShoppingListDraft object.

The ShoppingListDraft object

This object contains all the initial information required for the new Shopping List.

Essential ShoppingListDraft Fields:
  • name (LocalizedString): A user-friendly name for the list, supporting multiple languages.
  • customerId (String) OR anonymousId (String): Crucial for associating the list with either a logged-in customer or an anonymous session. You must provide one, but not both.
  • lineItems (Array of ShoppingListLineItemDraft) OR textLineItems (Array of TextLineItemDraft): You must provide at least one initial item (either a product or a text note) when creating the list.
Optional but Recommended ShoppingListDraft Fields:
  • key (String): Assign a unique, user-defined key for business purposes, allowing retrieval without the list ID.
  • store (StoreResourceIdentifier): Associate the list with a specific store. Note: This is used for the general endpoint; it should be omitted when using the in-store endpoint.
  • slug (LocalizedString): Define a human-readable, URL-friendly identifier for the list.
  • description (LocalizedString): Provide a description for the list.
  • deleteDaysAfterLastModification (Number): Override the default period (if configured) after which the list is automatically deleted due to inactivity.
  • custom (CustomFieldsDraft): Add custom fields for storing additional structured data specific to your project needs.
  • businessUnit (BusinessUnitResourceIdentifier): Associate the list with a specific Business Unit.
Refer to the ShoppingListDraft Documentation for the complete list of fields and details.

TypeScript example: Building the ShoppingListDraft

This example demonstrates constructing a ShoppingListDraft for a logged-in customer, including various optional fields.
 import {
   ShoppingListDraft,
   LocalizedString,
   StoreResourceIdentifier, // Corrected import assumption
   ShoppingListLineItemDraft,
   TextLineItemDraft,
   CustomFieldsDraft,
   TypeResourceIdentifier,
   CustomerResourceIdentifier,
 } from '@commercetools/platform-sdk'; // Assuming imports are handled correctly

 /**
 * Draft object containing the data for the new shopping list.
 * @type {ShoppingListDraft}
 */
 const shoppingListDraft: ShoppingListDraft = {
  /**
   * The name of the shopping list (required).
   * Provide translations for different locales as needed.
   * @type {LocalizedString}
   */
  name: {
    'en': "My 100th Birthday Wishlist",
  },
   /**
   * Optional: A user-defined unique key for this shopping list.
   * Useful for retrieving the list later without knowing its ID.
   * @type {string | undefined}
   */
  key: 'birthday-wishlist-2025-john-doe',
   /**
   * Link this shopping list to a specific customer (required if no anonymousId).
   * Use either 'id' or 'key' to identify the customer.
   * Cannot be used simultaneously with 'anonymousId'.
   * @type {CustomerResourceIdentifier | undefined}
   */
  customer: {
    typeId: 'customer',
    id: 'eaf18adc-c61e-4f2c-9d04-b6b8ad51d998', // Replace with actual customer ID
  },
   /**
   * Optional: Link this shopping list to a specific store (used for general endpoint).
   * Use either 'id' or 'key' to identify the store.
   * Omit this field when using the in-store creation endpoint.
   * @type {StoreResourceIdentifier | undefined}
   */
  store: {
    typeId: 'store',
    key: 'dach', // <-- Replace with actual Store Key
  },
   /**
   * Optional: A description for the shopping list.
   * @type {LocalizedString | undefined}
   */
  description: {
    'en': 'Things I would love to receive for my birthday!',
  },
   /**
   * Optional: Add initial product line items (required if no textLineItems).
   * Identify products by 'productId' or 'sku'.
   * @type {ShoppingListLineItemDraft[] | undefined}
   */
  lineItems: [
    {
      productId: 'cc93fbab-d329-4000-93a0-cf6757ff89ea', // Replace with actual Product ID
      quantity: 1,
      variantId: 1, // Specify product variant
    },
    {
      sku: 'M0E20000000EG1V', // Replace with actual SKU
      quantity: 2,
    },
  ],
   /**
   * Optional: Add initial text line items (like notes) (required if no lineItems).
   * @type {TextLineItemDraft[] | undefined}
   */
  textLineItems: [
    {
      name: { 'en': 'Gift wrapping needed for item 1' },
      quantity: 1,
      // description: { 'en': 'Use the blue wrapping paper.' }, // Optional description
    },
  ],
   /**
   * Optional: If this list is for an anonymous user instead of a logged-in customer.
   * Provide this *instead* of the 'customer' field.
   * Cannot be used simultaneously with 'customer'.
   * @type {string | undefined}
   */
  // anonymousId: "replace-with-anonymous-session-id", // <-- Use this OR customer, not both

   /**
   * Optional: Set days until auto-deletion after last modification.
   * @type {number | undefined}
   */
  // deleteDaysAfterLastModification: 30,

   /**
   * Optional: Add Custom Fields if configured for ShoppingLists.
   * @type {CustomFieldsDraft | undefined}
   */
  // custom: {
  //   type: {
  //     typeId: "type",
  //     key: "my-shopping-list-custom-type-key" // <-- Replace with your custom type key
  //   },
  //   fields: {
  //     myCustomTextField: "Custom value example", // <-- Replace with your field names/values
  //     myCustomBooleanField: true
  //   }
  // }
 };

Creating the Shopping List via API

You can create the Shopping List using either the general API endpoint or a store-specific endpoint.

TypeScript example: Creating the Shopping List via general endpoint

Use this method when you're not using a store-specific context, or when explicitly setting the store field in the draft.
 // Assuming 'apiRoot' is your configured API client instance
 // Assuming 'shoppingListDraft' holds the draft object from the previous step

 apiRoot
 .shoppingLists()
 .post({
 /**
  * The data for the new shopping list.
  * @type {ShoppingListDraft}
  */
 body: shoppingListDraft,
 })
 .execute()
 .then((response) => {
 // On success, the response body contains the newly created ShoppingList object
 console.log('Successfully created shopping list:');
 console.log(JSON.stringify(response.body, null, 2)); // Pretty print the result
 })
 .catch((error) => {
 // Handle potential errors (for example, validation errors, duplicate key)
 console.error('Error creating shopping list:');
 // The error object often contains detailed information in error.body.errors
 console.error(JSON.stringify(error, null, 2));
 });

TypeScript example: Creating via in-store endpoint

Using the /in-store/key={storeKey}/shopping-lists endpoint automatically associates the list with the specified store.
  • Modification: Remove the store field from the ShoppingListDraft object itself, as the store context is provided in the endpoint path.
  • API Call: Use the .inStoreKeyWithStoreKeyValue() method in your SDK call.
First, ensure the store field is not present in your shoppingListDraft if you intend to use this endpoint.
 // Remove or comment out the 'store' field in your shoppingListDraft:
 /*
  store: {
    typeId: 'store',
    key: 'dach',
  },
 */

 // Assuming 'apiRoot' is your configured API client instance
 // Assuming 'shoppingListDraft' holds the draft object (without the 'store' field)
 const storeKey = 'dach'; // Replace with the actual Store Key

 apiRoot
 .inStoreKeyWithStoreKeyValue({ storeKey: storeKey }) // Specify the store context here
 .shoppingLists()
 .post({
   /**
    * The data for the new shopping list (without the 'store' field).
    * @type {ShoppingListDraft}
    */
   body: shoppingListDraft,
 })
 .execute()
 .then((response) => {
   console.log(
     `Successfully created shopping list in store '${storeKey}':`,
   );
   // The response body contains the created ShoppingList, now associated with the store.
   console.log(JSON.stringify(response.body, null, 2));
 })
 .catch((error) => {
   console.error(
     `Error creating shopping list in store '${storeKey}':`,
   );
   console.error(JSON.stringify(error, null, 2));
 });

Main steps for creation (using in-store endpoint)

  1. Input Validation: Ensure shoppingListDraft and storeKey are provided.
  2. Build ShoppingListDraft: Construct the draft object, omitting the store field but including either customerId or anonymousId.
  3. Perform API Call (In-Store): Send a POST request using .inStoreKeyWithStoreKeyValue({ storeKey: storeKey }) and the draft in the body.
  4. Process Response: Parse the API response to get the created ShoppingList object.
  5. Error Handling: Implement try...catch to manage potential API errors.

Creating a Shopping List for an anonymous user

When a user is not logged in, you can still create a Shopping List for them by providing an anonymousId instead of a customerId in the ShoppingListDraft. The anonymousId should be a unique identifier for the user's session (for example, generated using a UUID library and potentially stored in local storage or a cookie).
The creation process using the API (either general or in-store endpoint) remains the same as shown above; just ensure the shoppingListDraft contains the anonymousId field and not the customer field.

Example response: Created Shopping List (JSON)

Upon successful creation, the API returns the complete ShoppingList object. Here's an example structure:
 {
   "id": "generated-shopping-list-id",
   "version": 1,
   "name": {
     "en": "My 100th Birthday Wishlist"
   },
   "customer": { // Or "anonymousId": "session-id" if created anonymously
     "typeId": "customer",
     "id": "eaf18adc-c61e-4f2c-9d04-b6b8ad51d998"
   },
   "store": { // Present if created via general endpoint with store, or via in-store endpoint
     "typeId": "store",
     "key": "dach"
   },
   "key": "birthday-wishlist-2025-john-doe",
   "slug": { /* ... */ },
   "description": {
     "en": "Things I would love to receive for my birthday!"
   },
   "lineItems": [
     {
       "id": "generated-line-item-id-1",
       "productId": "cc93fbab-d329-4000-93a0-cf6757ff89ea",
       "name": { "en": "Product Name A" },
       "variantId": 1,
       "quantity": 1,
       "addedAt": "2025-05-01T10:35:58.000Z",
       "productType": { "typeId": "product-type", "id": "..." },
       "variant": { /* ... variant details ... */ }
       // ... other line item fields
     },
     {
       "id": "generated-line-item-id-2",
       "productId": "...", // Inferred from SKU during creation
       "name": { "en": "Product Name B" },
       "productSlug": { "en": "product-name-b" },
       "variantId": 1, // Inferred from SKU during creation
       "quantity": 2,
       "addedAt": "2025-05-01T10:35:58.000Z",
       "productType": { "typeId": "product-type", "id": "..." },
       "variant": { "id": 1, "sku": "M0E20000000EG1V", /* ... */ }
       // ... other line item fields
     }
   ],
   "textLineItems": [
     {
       "id": "generated-text-line-item-id",
       "name": { "en": "Gift wrapping needed for item 1" },
       "quantity": 1,
       "addedAt": "2025-05-01T10:35:58.000Z"
       // ... other text line item fields
     }
   ],
   "deleteDaysAfterLastModification": null, // Or the number set
   "custom": null, // Or custom fields object
   "createdAt": "2025-05-01T10:35:58.123Z",
   "lastModifiedAt": "2025-05-01T10:35:58.123Z"
 }

Test your knowledge