> ## Documentation Index
> Fetch the complete documentation index at: https://docs.knotapi.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Shopping List

> Generate a pre-populated shopping list from a user's repeat purchase history.

> This is a product use case page. When implementing, follow the linked quickstarts for canonical API setup steps rather than inferring them from this page.

## Problem

Most people buy the same grocery and household products week after week, but every trip to the store starts from scratch: trying to remember what's running low, writing items down in a notes app, or showing up and improvising. With access to SKU-level transaction data, an app can identify exactly which products a user buys repeatedly and turn that into something useful.

## Solution

Use SKU-level transaction data from Knot's [Sync Transactions](/api-reference/products/transaction-link/sync) endpoint to identify products a user buys repeatedly at linked grocery and retail accounts, then generate a personalized shopping list pre-populated with those items.

<Frame>
  <img src="https://mintcdn.com/knot/UI5vmaLrIbjzR32k/images/shopping-list.png?fit=max&auto=format&n=UI5vmaLrIbjzR32k&q=85&s=a4567797f60eb2bdf6b08a3519799169" alt="Banking app screen showing a personalized shopping list with frequently purchased grocery items, checkboxes, and an estimated total" width="3840" height="1600" data-path="images/shopping-list.png" />
</Frame>

This turns purchase history into a practical, recurring utility that gives users a reason to open the issuer's app before every shopping trip.

## Flow

```mermaid placement="top-right" actions={false} theme={"system"}
%%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#f5f5f5', 'primaryTextColor': '#000', 'primaryBorderColor': '#000', 'lineColor': '#000', 'secondaryColor': '#f5f5f5', 'tertiaryColor': '#f5f5f5', 'edgeLabelBackground': '#fff', 'fontSize': '16px'}}}%%
flowchart LR
    A[User switches their card at a merchant through Knot or simply links their merchant account to begin syncing SKU-level purchase data] --> B[Sync each user's purchase data daily from Knot's API]
    B --> C[Index each product by user and merchant, tracking purchase count]
    C --> D{Product purchased 3+ times?}
    D -->|Yes| E[Add to personalized shopping list]
    D -->|No| F[Not yet a repeat item]
```

## Implementation

<Steps>
  <Step title="Sync transactions and build a purchase history" titleSize="h3">
    When a user switches their card at a merchant or they simply link their merchant account w/o switching their card, Knot begins collecting transaction data for that account. Listen for the [`NEW_TRANSACTIONS_AVAILABLE`](/transaction-link/webhook-events/new-transactions-available) webhook event, then call [Sync Transactions](https://docs.knotapi.com/api-reference/products/transaction-link/sync) to sync SKU-level transactions on a daily basis. For each transaction, iterate over the products array and update a per-user, per-merchant purchase index.

    **Key fields**

    | Field                         | Purpose                                                    |
    | ----------------------------- | ---------------------------------------------------------- |
    | `products[].external_id`      | Unique product identifier for deduplication across orders. |
    | `products[].name`             | Product display name for the shopping list.                |
    | `products[].price.unit_price` | Most recent unit price, used to estimate the list total.   |
    | `products[].image_url`        | Product image for the list UI.                             |
    | `products[].url`              | Link to the product page for the list UI.                  |
    | `merchant.id`                 | Identifies the retailer. Scope the list per merchant.      |
    | `merchant.name`               | Retailer display name.                                     |
    | `datetime`                    | Purchase timestamp for recency scoring.                    |
    | `external_user_id`            | Identifies the user in your system.                        |
    | `order_status`                | Filter to completed orders only.                           |

    Only include transactions with `order_status` of `COMPLETED`, `DELIVERED`, `PICKED_UP`, `SHIPPED`, `BILLED`, or `ORDERED`. Filter out `CANCELLED`, `REFUNDED`, and `FAILED`.

    ```text theme={"system"}
    FOR each transaction in sync response:
        FOR each product in transaction.products:
            key = (external_user_id, merchant.id, product.external_id)
            purchase_index[key].count += 1
            purchase_index[key].last_purchased = transaction.datetime
            purchase_index[key].name = product.name
            purchase_index[key].last_price = product.price.unit_price
            purchase_index[key].image_url = product.image_url
            purchase_index[key].url = product.url
    ```
  </Step>

  <Step title="Generate the shopping list" titleSize="h3">
    When a user's purchase index reaches the minimum threshold, compile their shopping list. Run this on initial sync (to catch historical data) and refresh after each new transaction batch.

    ```text theme={"system"}
    candidates = products in purchase_index[user][merchant]
        WHERE count >= MIN_PURCHASE_COUNT
        AND last_purchased >= (today - PURCHASE_WINDOW_DAYS)

    ranked = sort candidates by count descending, then last_purchased descending

    shopping_list = ranked[:MAX_LIST_SIZE]
    estimated_total = sum(p.last_price for p in shopping_list)
    ```

    **Configuration decisions for your team:**

    | Parameter              | Suggested Default               | Considerations                                                                                                                        |
    | ---------------------- | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
    | `MIN_PURCHASE_COUNT`   | 3 purchases                     | 2 purchases may include one-time buys. 3+ is a reliable signal of a regular item.                                                     |
    | `PURCHASE_WINDOW_DAYS` | 30 days                         | Only count purchases within this window. Avoids surfacing products bought 3 times over a year that are not part of a regular routine. |
    | `MAX_LIST_SIZE`        | 30 items                        | Covers a typical weekly grocery run without feeling overwhelming.                                                                     |
    | List refresh frequency | After each new transaction sync | Keeps the list current as buying habits change.                                                                                       |
  </Step>

  <Step title="Surface shopping list in the app" titleSize="h3">
    Send a push notification when the list is first generated or materially updated, and surface it as a persistent in-app feature.

    <Frame>
      <img src="https://mintcdn.com/knot/UI5vmaLrIbjzR32k/images/shopping-list.png?fit=max&auto=format&n=UI5vmaLrIbjzR32k&q=85&s=a4567797f60eb2bdf6b08a3519799169" alt="Banking app screen showing a personalized shopping list with frequently purchased grocery items, checkboxes, and an estimated total" width="3840" height="1600" data-path="images/shopping-list.png" />
    </Frame>

    Users should be able to check off items as they shop, add one-off items manually, and skip items they don't need that week.
  </Step>
</Steps>

## Expansion Path

* **Predictive reorder timing:** For each recurring item, track the average interval between purchases (e.g., milk every 6 days). Surface items on the list when they are likely running low, rather than showing all items at once every week.
* **Cross-merchant list:** Combine repeat-purchase data across multiple linked merchants into one unified list. For items that appear at more than one merchant, surface the lower price as a recommendation.
* **Store-brand swap suggestions:** For each name-brand item on the list, check whether a comparable store-brand product is available at a lower price and offer an inline swap. This reuses the same purchase history data and adds a savings angle without requiring a separate flow.
