npm package: @polar-analytics/pixel-sdk
Description: A JavaScript/TypeScript SDK for reporting customer touchpoints to Polar Analytics
Author: Polar Analytics
Latest version: 0.16.0

# Pixel SDK

A lightweight JavaScript utility for reporting user touchpoints, similar to the
Polar Analytics Pixel.

## Contents

- [Installation](#installation)
- [Example usage](#example-usage)
- [Headless Shopify Integration](#headless-shopify-integration)
- [API Reference](#api-reference)
- [For SDK developers](#for-sdk-developers)

## Installation

```sh
npm install @polar-analytics/pixel-sdk
```

## Example usage

```typescript
import {
  generatePayloadBase,
  sendPixelEvent,
  eventFactory,
} from "@polar-analytics/pixel-sdk";

const payloadBase = await generatePayloadBase({
  // Optional, include if available
  // customer: {
  //   id: <variable-containing-customer-id>,
  //   email: <variable-containing-customer-email>,
  // },
});

// An anonymous ID to identify the device + browser
// Please refer to https://community.shopify.com/c/shopify-apps/web-pixel-clientid/m-p/2664271/highlight/true#M80886
// As a fallback you can also use payloadBase.data.user_id.
const shopifyClientId =

// <the url of the shopify store> i.e. something.myshopify.com
const shopifyShopURL =

// Use the corresponding eventFactory for the desired event type.
const data = eventFactory.cart_viewed({
    shopifyEventId: crypto.randomUUID(),
    shopifyClientId,
    shopifyShopURL,
})

/* If you want to send a custom event not supported by the event factory,
   you can use this helper instead.

   Please only use this method if the event is not supported natively by
   the eventFactory. Using the custom event handler to send a non-custom
   supported event might result in sending incomplete data.

const data = eventFactory.custom({
  shopifyEventName: "<the-name-of-the-custom-event>",
  shopifyEventId: crypto.randomUUID(),
  shopifyClientId,
  shopifyShopURL,
})
*/

sendPixelEvent(
  pixelEndpoint /* obtained from polar analytics */,
  payloadBase,
  { data },
);
```

### (Optional) With callbacks and retries

`sendPixelEvent` retries failed requests with exponential backoff
(default: 3 retries) and returns a `SendPixelEventResult`.

```typescript
const result = await sendPixelEvent(
  pixelEndpoint,
  payloadBase,
  { data },
  {
    maxRetries: 5,
    onSuccess: (response) => {
      console.log("Event delivered", response.status);
    },
    onError: (error, attempts) => {
      console.error(`Failed after ${attempts} attempts`, error);
    },
    onRetry: (attempt, error, nextDelayMs) => {
      console.warn(`Retry ${attempt} in ${nextDelayMs}ms`);
    },
  },
);

if (!result.success) {
  // result.error and result.attempts are available
}
```

### (Optional) With `sendBeacon` (page unload)

Use `useBeacon: true` for page unload / `visibilitychange` scenarios.
When the beacon fires successfully the function returns immediately
with `{ success: true, usedBeacon: true }` — `onSuccess`/`onError`
callbacks are **not** invoked. If the beacon fails, the SDK falls
back to `fetch` with retries.

```typescript
sendPixelEvent(pixelEndpoint, payloadBase, { data }, { useBeacon: true });
```

## (Recommended) Cart Attribute Injection

Cart attributes persist tracking data (marketing cookies, UTM params, click IDs)
through Shopify checkout, enabling accurate attribution.

The examples below use [`@shopify/storefront-api-client`](https://www.npmjs.com/package/@shopify/storefront-api-client).

```typescript
import {
  generateCartAttribute,
  POLAR_ATTR_KEY,
} from "@polar-analytics/pixel-sdk";
import { createStorefrontApiClient } from "@shopify/storefront-api-client";

const client = createStorefrontApiClient({
  storeDomain: "https://your-store.myshopify.com",
  apiVersion: "2025-04",
  publicAccessToken: "your-public-access-token",
});
```

### Creating a cart with `polar_attr`

```typescript
const result = await generateCartAttribute();

const { data } = await client.request(
  `mutation cartCreate($input: CartInput!) {
    cartCreate(input: $input) {
      cart { id attributes { key value } }
    }
  }`,
  {
    variables: {
      input: {
        lines: [{ merchandiseId: variantId, quantity: 1 }],
        attributes: [{ key: POLAR_ATTR_KEY, value: result.encoded }],
      },
    },
  },
);
```

### Updating cart attributes (with merge)

When updating an existing cart, pass the current `polar_attr` value so
first-touch attribution data is preserved:

```typescript
const existingPolarAttr = cart.attributes.find(
  (a) => a.key === POLAR_ATTR_KEY,
)?.value;

const result = await generateCartAttribute({
  existingPolarAttr,
});

await client.request(
  `mutation cartAttributesUpdate($cartId: ID!, $attributes: [AttributeInput!]!) {
    cartAttributesUpdate(cartId: $cartId, attributes: $attributes) {
      cart { id attributes { key value } }
    }
  }`,
  {
    variables: {
      cartId: cart.id,
      attributes: [{ key: POLAR_ATTR_KEY, value: result.encoded }],
    },
  },
);
```

### Best Practices

- **Call `generateCartAttribute()` on every cart mutation** (create, add line,
  update). This ensures the latest session info is always attached.
- **Always pass `existingPolarAttr` on cart updates.** This preserves
  first-touch attribution (e.g. the original `gclid`).

## API Reference

### Supported Events

- [eventFactory.page_viewed](#eventfactorypage_viewed)
- [eventFactory.product_viewed](#eventfactoryproduct_viewed)
- [eventFactory.collection_viewed](#eventfactorycollection_viewed)
- [eventFactory.search_submitted](#eventfactorysearch_submitted)
- [eventFactory.cart_viewed](#eventfactorycart_viewed)
- [eventFactory.product_added_to_cart](#eventfactoryproduct_added_to_cart)
- [eventFactory.product_removed_from_cart](#eventfactoryproduct_removed_from_cart)
- [eventFactory.checkout_started](#eventfactorycheckout_started)
- [eventFactory.checkout_address_info_submitted](#eventfactorycheckout_address_info_submitted)
- [eventFactory.checkout_contact_info_submitted](#eventfactorycheckout_contact_info_submitted)
- [eventFactory.checkout_shipping_info_submitted](#eventfactorycheckout_shipping_info_submitted)
- [eventFactory.payment_info_submitted](#eventfactorypayment_info_submitted)
- [eventFactory.checkout_completed](#eventfactorycheckout_completed)
- [eventFactory.custom](#eventfactorycustom)

#### `eventFactory.page_viewed`

> The `page_viewed` event logs an instance where a customer visited a page. This event is available on the online store, checkout, and Order status pages.
>
> [Source: Shopify Documentation](https://shopify.dev/docs/api/web-pixels-api/standard-events/page_viewed)

<details>
<summary style="cursor: pointer;">Payload Specification</summary>
<pre lang="typescript">
type PageViewedEventPayload = {
  shopifyEventId: string;
  shopifyClientId: string;
  shopifyShopURL: string;
  shopifyPageType?: string;
  shopifyEventData?: {};
};
</pre>
</details>

#### `eventFactory.product_viewed`

> The `product_viewed` event logs an instance where a customer visited a product details page. This event is available on the product page.
>
> [Source: Shopify Documentation](https://shopify.dev/docs/api/web-pixels-api/standard-events/product_viewed)

<details>
<summary style="cursor: pointer;">Payload Specification</summary>
<pre lang="typescript">
type ProductViewedEventPayload = {
  shopifyEventId: string;
  shopifyClientId: string;
  shopifyShopURL: string;
  shopifyPageDetail: string;
  shopifyPageProductId: string | null;
  shopifyPageType: string;
  shopifyEventData: { // More digestible as the data property in the Shopify docs linked above.
    productVariant: {
      id: string | null;
      image: {
        src: string | null;
      } | null;
      price: {
        amount: number;
        currencyCode: string;
      };
      product: {
        id: string | null;
        title: string;
        type: string | null;
        untranslatedTitle: string | null;
        url: string | null;
        vendor: string;
      };
      sku: string | null;
      title: string | null;
      untranslatedTitle: string | null;
    };
  };
};
</pre>
</details>

#### `eventFactory.collection_viewed`

> The `collection_viewed` event logs an instance where a customer visited a product collection index page. This event is available on the online store page.
>
> [Source: Shopify Documentation](https://shopify.dev/docs/api/web-pixels-api/standard-events/collection_viewed)

<details>
<summary style="cursor: pointer;">Payload Specification</summary>
<pre lang="typescript">
type CollectionViewedEventPayload = {
  shopifyEventId: string;
  shopifyClientId: string;
  shopifyShopURL: string;
  shopifyPageDetail: string;
  shopifyPageType: string;
  shopifyPageCollectionId?: string;
  shopifyEventData?: { // More digestible as the data property in the Shopify docs linked above.
    collection: {
      id: string;
      productVariants: {
        id: string | null;
        image: {
          src: string | null;
        } | null;
        price: {
          amount: number;
          currencyCode: string;
        };
        product: {
          id: string | null;
          title: string;
          type: string | null;
          untranslatedTitle: string | null;
          url: string | null;
          vendor: string;
        };
        sku: string | null;
        title: string | null;
        untranslatedTitle: string | null;
      }[];
      title: string;
    };
  };
};
</pre>
</details>

#### `eventFactory.search_submitted`

> The `search_submitted` event logs an instance where a customer performed a search on the storefront. The products returned from the search query are in this event object (the first product variant for each product is listed in the array). This event is available on the online store page.
>
> [Source: Shopify Documentation](https://shopify.dev/docs/api/web-pixels-api/standard-events/search_submitted)

<details>
<summary style="cursor: pointer;">Payload Specification</summary>
<pre lang="typescript">
type SearchSubmittedEventPayload = {
  shopifyEventId: string;
  shopifyClientId: string;
  shopifyShopURL: string;
  shopifyEventData?: { // More digestible as the data property in the Shopify docs linked above.
    searchResult: {
      productVariants: {
        id: string | null;
        image: {
          src: string | null;
        } | null;
        price: {
          amount: number;
          currencyCode: string;
        };
        product: {
          id: string | null;
          title: string;
          type: string | null;
          untranslatedTitle: string | null;
          url: string | null;
          vendor: string;
        };
        sku: string | null;
        title: string | null;
        untranslatedTitle: string | null;
      }[];
      query: string;
    };
  };
};
</pre>
</details>

#### `eventFactory.cart_viewed`

> The `cart_viewed` event logs an instance where a customer visited the cart page.
>
> [Source: Shopify Documentation](https://shopify.dev/docs/api/web-pixels-api/standard-events/cart_viewed)

<details>
<summary style="cursor: pointer;">Payload Specification</summary>
<pre lang="typescript">
type CartViewedEventPayload = {
  shopifyEventId: string;
  shopifyClientId: string;
  shopifyShopURL: string;
  shopifyEventData?: { // More digestible as the data property in the Shopify docs linked above.
    cart: {
      attributes: {
        key: string;
        value: string;
      }[];
      cost: {
        totalAmount: {
          amount: number;
          currencyCode: string;
        };
      };
      id: string | null;
      lines: {
        cost: {
          totalAmount: {
            amount: number;
            currencyCode: string;
          };
        };
        merchandise: {
          id: string | null;
          image: {
            src: string | null;
          } | null;
          price: {
            amount: number;
            currencyCode: string;
          };
          product: {
            id: string | null;
            title: string;
            type: string | null;
            untranslatedTitle: string | null;
            url: string | null;
            vendor: string;
          };
          sku: string | null;
          title: string | null;
          untranslatedTitle: string | null;
        };
        quantity: number;
      }[];
      totalQuantity: number;
    } | null;
  };
};
</pre>
</details>

#### `eventFactory.product_added_to_cart`

> The `product_added_to_cart` event logs an instance where a customer adds a product to their cart. This event is available on the online store page.
>
> [Source: Shopify Documentation](https://shopify.dev/docs/api/web-pixels-api/standard-events/product_added_to_cart)

<details>
<summary style="cursor: pointer;">Payload Specification</summary>
<pre lang="typescript">
type ProductAddedToCartEventPayload = {
  shopifyEventId: string;
  shopifyClientId: string;
  shopifyShopURL: string;
  shopifyEventData?: { // More digestible as the data property in the Shopify docs linked above.
    cartLine: {
      cost: {
        totalAmount: {
          amount: number;
          currencyCode: string;
        };
      };
      merchandise: {
        id: string | null;
        image: {
          src: string | null;
        } | null;
        price: {
          amount: number;
          currencyCode: string;
        };
        product: {
          id: string | null;
          title: string;
          type: string | null;
          untranslatedTitle: string | null;
          url: string | null;
          vendor: string;
        };
        sku: string | null;
        title: string | null;
        untranslatedTitle: string | null;
      };
      quantity: number;
    } | null;
  };
};
</pre>
</details>

#### `eventFactory.product_removed_from_cart`

> The `product_removed_from_cart` event logs an instance where a customer removes a product from their cart. This event is available on the online store page.
>
> [Source: Shopify Documentation](https://shopify.dev/docs/api/web-pixels-api/standard-events/product_removed_from_cart)

<details>
<summary style="cursor: pointer;">Payload Specification</summary>
<pre lang="typescript">
type ProductRemovedFromCartEventPayload = {
  shopifyEventId: string;
  shopifyClientId: string;
  shopifyShopURL: string;
  shopifyEventData?: { // More digestible as the data property in the Shopify docs linked above.
    cartLine: {
      cost: {
        totalAmount: {
          amount: number;
          currencyCode: string;
        };
      };
      merchandise: {
        id: string | null;
        image: {
          src: string | null;
        } | null;
        price: {
          amount: number;
          currencyCode: string;
        };
        product: {
          id: string | null;
          title: string;
          type: string | null;
          untranslatedTitle: string | null;
          url: string | null;
          vendor: string;
        };
        sku: string | null;
        title: string | null;
        untranslatedTitle: string | null;
      };
      quantity: number;
    } | null;
  };
};
</pre>
</details>

#### `eventFactory.checkout_started`

> The `checkout_started` event logs an instance of a customer starting the checkout process. This event is available on the checkout page. For Checkout Extensibility, this event is triggered every time a customer enters checkout. For non-checkout extensible shops, this event is only triggered the first time a customer enters checkout.
>
> [Source: Shopify Documentation](https://shopify.dev/docs/api/web-pixels-api/standard-events/checkout_started)

<details>
<summary style="cursor: pointer;">Payload Specification</summary>
<pre lang="typescript">
type CheckoutStartedEventPayload = {
  shopifyEventId: string;
  shopifyClientId: string;
  shopifyShopURL: string;
  shopifyEventData?: { // More digestible as the data property in the Shopify docs linked above.
    checkout: {
      attributes: {
        key: string;
        value: string;
      }[];
      billingAddress: {
        address1: string | null;
        address2: string | null;
        city: string | null;
        country: string | null;
        countryCode: string | null;
        firstName: string | null;
        lastName: string | null;
        phone: string | null;
        province: string | null;
        provinceCode: string | null;
        zip: string | null;
      } | null;
      buyerAcceptsEmailMarketing: boolean;
      buyerAcceptsSmsMarketing: boolean;
      currencyCode: string | null;
      totalTax: {
        amount: number;
        currencyCode: string;
      };
      totalPrice: {
        amount: number;
        currencyCode: string;
      } | null;
      transactions: {
        amount: {
          amount: number;
          currencyCode: string;
        };
        gateway: string;
        paymentMethod: {
          name: string;
          type: string;
        };
      }[];
      delivery: {
        selectedDeliveryOptions: {
          cost: {
            amount: number;
            currencyCode: string;
          } | null;
          costAfterDiscounts: {
            amount: number;
            currencyCode: string;
          } | null;
          description: string | null;
          handle: string;
          title: string | null;
          type: string;
        }[];
      } | null;
      discountApplications: {
        allocationMethod: string;
        targetSelection: string;
        targetType: string;
        title: string;
        type: string;
        value:
          | {
              amount: number;
              currencyCode: string;
            }
          | {
              percentage: number;
            };
      }[];
      discountsAmount: {
        amount: number;
        currencyCode: string;
      } | null;
      email: string | null;
      token: string | null;
      lineItems: {
        finalLinePrice: {
          amount: number;
          currencyCode: string;
        };
        id: string | null;
        properties: {
          key: string;
          value: string;
        }[];
        quantity: number;
        sellingPlanAllocation: {
          sellingPlan: {
            id: string;
            name: string;
          };
        } | null;
        title: string | null;
        discountAllocations: {
          amount: {
            amount: number;
            currencyCode: string;
          };
          variant: {
            id: string | null;
            image: {
              src: string | null;
            } | null;
            price: {
              amount: number;
              currencyCode: string;
            };
            product: {
              id: string | null;
              title: string;
              type: string | null;
              untranslatedTitle: string | null;
              url: string | null;
              vendor: string;
            };
            sku: string | null;
            title: string | null;
            untranslatedTitle: string | null;
          } | null;
          discountApplication: {
            allocationMethod: string;
            targetSelection: string;
            targetType: string;
            title: string;
            type: string;
            value:
              | {
                  amount: number;
                  currencyCode: string;
                }
              | {
                  percentage: number;
                };
          };
        }[];
      }[];
      localization: {
        country: {
          isoCode: string | null;
        };
        language: {
          isoCode: string;
        };
        market: {
          handle: string | null;
          id: string | null;
        };
      };
      order: {
        customer: {
          id: string | null;
          isFirstOrder: boolean | null;
        } | null;
        id: string | null;
      } | null;
      phone: string | null;
      shippingAddress: {
        address1: string | null;
        address2: string | null;
        city: string | null;
        country: string | null;
        countryCode: string | null;
        firstName: string | null;
        lastName: string | null;
        phone: string | null;
        province: string | null;
        provinceCode: string | null;
        zip: string | null;
      } | null;
      shippingLine: {
        price: {
          amount: number;
          currencyCode: string;
        };
      } | null;
      smsMarketingPhone: string | null;
      subtotalPrice: {
        amount: number;
        currencyCode: string;
      } | null;
    };
  };
};
</pre>
</details>

#### `eventFactory.checkout_address_info_submitted`

> The `checkout_address_info_submitted` event logs an instance of a customer submitting their mailing address. This event is only available in checkouts where Checkout Extensibility for customizations is enabled.
>
> [Source: Shopify Documentation](https://shopify.dev/docs/api/web-pixels-api/standard-events/checkout_address_info_submitted)

<details>
<summary style="cursor: pointer;">Payload Specification</summary>
<pre lang="typescript">
type CheckoutAdressInfoSubmittedEventPayload = {
  shopifyEventId: string;
  shopifyClientId: string;
  shopifyShopURL: string;
  shopifyEventData?: { // More digestible as the data property in the Shopify docs linked above.
    checkout: {
      attributes: {
        key: string;
        value: string;
      }[];
      billingAddress: {
        address1: string | null;
        address2: string | null;
        city: string | null;
        country: string | null;
        countryCode: string | null;
        firstName: string | null;
        lastName: string | null;
        phone: string | null;
        province: string | null;
        provinceCode: string | null;
        zip: string | null;
      } | null;
      buyerAcceptsEmailMarketing: boolean;
      buyerAcceptsSmsMarketing: boolean;
      currencyCode: string | null;
      totalTax: {
        amount: number;
        currencyCode: string;
      };
      totalPrice: {
        amount: number;
        currencyCode: string;
      } | null;
      transactions: {
        amount: {
          amount: number;
          currencyCode: string;
        };
        gateway: string;
        paymentMethod: {
          name: string;
          type: string;
        };
      }[];
      delivery: {
        selectedDeliveryOptions: {
          cost: {
            amount: number;
            currencyCode: string;
          } | null;
          costAfterDiscounts: {
            amount: number;
            currencyCode: string;
          } | null;
          description: string | null;
          handle: string;
          title: string | null;
          type: string;
        }[];
      } | null;
      discountApplications: {
        allocationMethod: string;
        targetSelection: string;
        targetType: string;
        title: string;
        type: string;
        value:
          | {
              amount: number;
              currencyCode: string;
            }
          | {
              percentage: number;
            };
      }[];
      discountsAmount: {
        amount: number;
        currencyCode: string;
      } | null;
      email: string | null;
      token: string | null;
      lineItems: {
        finalLinePrice: {
          amount: number;
          currencyCode: string;
        };
        id: string | null;
        properties: {
          key: string;
          value: string;
        }[];
        quantity: number;
        sellingPlanAllocation: {
          sellingPlan: {
            id: string;
            name: string;
          };
        } | null;
        title: string | null;
        discountAllocations: {
          amount: {
            amount: number;
            currencyCode: string;
          };
          variant: {
            id: string | null;
            image: {
              src: string | null;
            } | null;
            price: {
              amount: number;
              currencyCode: string;
            };
            product: {
              id: string | null;
              title: string;
              type: string | null;
              untranslatedTitle: string | null;
              url: string | null;
              vendor: string;
            };
            sku: string | null;
            title: string | null;
            untranslatedTitle: string | null;
          } | null;
          discountApplication: {
            allocationMethod: string;
            targetSelection: string;
            targetType: string;
            title: string;
            type: string;
            value:
              | {
                  amount: number;
                  currencyCode: string;
                }
              | {
                  percentage: number;
                };
          };
        }[];
      }[];
      localization: {
        country: {
          isoCode: string | null;
        };
        language: {
          isoCode: string;
        };
        market: {
          handle: string | null;
          id: string | null;
        };
      };
      order: {
        customer: {
          id: string | null;
          isFirstOrder: boolean | null;
        } | null;
        id: string | null;
      } | null;
      phone: string | null;
      shippingAddress: {
        address1: string | null;
        address2: string | null;
        city: string | null;
        country: string | null;
        countryCode: string | null;
        firstName: string | null;
        lastName: string | null;
        phone: string | null;
        province: string | null;
        provinceCode: string | null;
        zip: string | null;
      } | null;
      shippingLine: {
        price: {
          amount: number;
          currencyCode: string;
        };
      } | null;
      smsMarketingPhone: string | null;
      subtotalPrice: {
        amount: number;
        currencyCode: string;
      } | null;
    };
  };
};
</pre>
</details>

#### `eventFactory.checkout_contact_info_submitted`

> The `checkout_contact_info_submitted` event logs an instance where a customer submits a checkout form. This event is only available in checkouts where Checkout Extensibility for customizations is enabled.
>
> [Source: Shopify Documentation](https://shopify.dev/docs/api/web-pixels-api/standard-events/checkout_contact_info_submitted)

<details>
<summary style="cursor: pointer;">Payload Specification</summary>
<pre lang="typescript">
type CheckoutAddressInfoSubmittedEventPayload = {
  shopifyEventId: string;
  shopifyClientId: string;
  shopifyShopURL: string;
  shopifyEventData?: { // More digestible as the data property in the Shopify docs linked above.
    checkout: {
      attributes: {
        key: string;
        value: string;
      }[];
      billingAddress: {
        address1: string | null;
        address2: string | null;
        city: string | null;
        country: string | null;
        countryCode: string | null;
        firstName: string | null;
        lastName: string | null;
        phone: string | null;
        province: string | null;
        provinceCode: string | null;
        zip: string | null;
      } | null;
      buyerAcceptsEmailMarketing: boolean;
      buyerAcceptsSmsMarketing: boolean;
      currencyCode: string | null;
      totalTax: {
        amount: number;
        currencyCode: string;
      };
      totalPrice: {
        amount: number;
        currencyCode: string;
      } | null;
      transactions: {
        amount: {
          amount: number;
          currencyCode: string;
        };
        gateway: string;
        paymentMethod: {
          name: string;
          type: string;
        };
      }[];
      delivery: {
        selectedDeliveryOptions: {
          cost: {
            amount: number;
            currencyCode: string;
          } | null;
          costAfterDiscounts: {
            amount: number;
            currencyCode: string;
          } | null;
          description: string | null;
          handle: string;
          title: string | null;
          type: string;
        }[];
      } | null;
      discountApplications: {
        allocationMethod: string;
        targetSelection: string;
        targetType: string;
        title: string;
        type: string;
        value:
          | {
              amount: number;
              currencyCode: string;
            }
          | {
              percentage: number;
            };
      }[];
      discountsAmount: {
        amount: number;
        currencyCode: string;
      } | null;
      email: string | null;
      token: string | null;
      lineItems: {
        finalLinePrice: {
          amount: number;
          currencyCode: string;
        };
        id: string | null;
        properties: {
          key: string;
          value: string;
        }[];
        quantity: number;
        sellingPlanAllocation: {
          sellingPlan: {
            id: string;
            name: string;
          };
        } | null;
        title: string | null;
        discountAllocations: {
          amount: {
            amount: number;
            currencyCode: string;
          };
          variant: {
            id: string | null;
            image: {
              src: string | null;
            } | null;
            price: {
              amount: number;
              currencyCode: string;
            };
            product: {
              id: string | null;
              title: string;
              type: string | null;
              untranslatedTitle: string | null;
              url: string | null;
              vendor: string;
            };
            sku: string | null;
            title: string | null;
            untranslatedTitle: string | null;
          } | null;
          discountApplication: {
            allocationMethod: string;
            targetSelection: string;
            targetType: string;
            title: string;
            type: string;
            value:
              | {
                  amount: number;
                  currencyCode: string;
                }
              | {
                  percentage: number;
                };
          };
        }[];
      }[];
      localization: {
        country: {
          isoCode: string | null;
        };
        language: {
          isoCode: string;
        };
        market: {
          handle: string | null;
          id: string | null;
        };
      };
      order: {
        customer: {
          id: string | null;
          isFirstOrder: boolean | null;
        } | null;
        id: string | null;
      } | null;
      phone: string | null;
      shippingAddress: {
        address1: string | null;
        address2: string | null;
        city: string | null;
        country: string | null;
        countryCode: string | null;
        firstName: string | null;
        lastName: string | null;
        phone: string | null;
        province: string | null;
        provinceCode: string | null;
        zip: string | null;
      } | null;
      shippingLine: {
        price: {
          amount: number;
          currencyCode: string;
        };
      } | null;
      smsMarketingPhone: string | null;
      subtotalPrice: {
        amount: number;
        currencyCode: string;
      } | null;
    };
  };
};
</pre>
</details>

#### `eventFactory.checkout_shipping_info_submitted`

> The `checkout_shipping_info_submitted` event logs an instance where the customer chooses a shipping rate. This event is only available in checkouts where Checkout Extensibility for customizations is enabled.
>
> [Source: Shopify Documentation](https://shopify.dev/docs/api/web-pixels-api/standard-events/checkout_shipping_info_submitted)

<details>
<summary style="cursor: pointer;">Payload Specification</summary>
<pre lang="typescript">
type CheckoutShippingInfoSubmittedEventPayload = {
  shopifyEventId: string;
  shopifyClientId: string;
  shopifyShopURL: string;
  shopifyEventData?: { // More digestible as the data property in the Shopify docs linked above.
    checkout: {
      attributes: {
        key: string;
        value: string;
      }[];
      billingAddress: {
        address1: string | null;
        address2: string | null;
        city: string | null;
        country: string | null;
        countryCode: string | null;
        firstName: string | null;
        lastName: string | null;
        phone: string | null;
        province: string | null;
        provinceCode: string | null;
        zip: string | null;
      } | null;
      buyerAcceptsEmailMarketing: boolean;
      buyerAcceptsSmsMarketing: boolean;
      currencyCode: string | null;
      totalTax: {
        amount: number;
        currencyCode: string;
      };
      totalPrice: {
        amount: number;
        currencyCode: string;
      } | null;
      transactions: {
        amount: {
          amount: number;
          currencyCode: string;
        };
        gateway: string;
        paymentMethod: {
          name: string;
          type: string;
        };
      }[];
      delivery: {
        selectedDeliveryOptions: {
          cost: {
            amount: number;
            currencyCode: string;
          } | null;
          costAfterDiscounts: {
            amount: number;
            currencyCode: string;
          } | null;
          description: string | null;
          handle: string;
          title: string | null;
          type: string;
        }[];
      } | null;
      discountApplications: {
        allocationMethod: string;
        targetSelection: string;
        targetType: string;
        title: string;
        type: string;
        value:
          | {
              amount: number;
              currencyCode: string;
            }
          | {
              percentage: number;
            };
      }[];
      discountsAmount: {
        amount: number;
        currencyCode: string;
      } | null;
      email: string | null;
      token: string | null;
      lineItems: {
        finalLinePrice: {
          amount: number;
          currencyCode: string;
        };
        id: string | null;
        properties: {
          key: string;
          value: string;
        }[];
        quantity: number;
        sellingPlanAllocation: {
          sellingPlan: {
            id: string;
            name: string;
          };
        } | null;
        title: string | null;
        discountAllocations: {
          amount: {
            amount: number;
            currencyCode: string;
          };
          variant: {
            id: string | null;
            image: {
              src: string | null;
            } | null;
            price: {
              amount: number;
              currencyCode: string;
            };
            product: {
              id: string | null;
              title: string;
              type: string | null;
              untranslatedTitle: string | null;
              url: string | null;
              vendor: string;
            };
            sku: string | null;
            title: string | null;
            untranslatedTitle: string | null;
          } | null;
          discountApplication: {
            allocationMethod: string;
            targetSelection: string;
            targetType: string;
            title: string;
            type: string;
            value:
              | {
                  amount: number;
                  currencyCode: string;
                }
              | {
                  percentage: number;
                };
          };
        }[];
      }[];
      localization: {
        country: {
          isoCode: string | null;
        };
        language: {
          isoCode: string;
        };
        market: {
          handle: string | null;
          id: string | null;
        };
      };
      order: {
        customer: {
          id: string | null;
          isFirstOrder: boolean | null;
        } | null;
        id: string | null;
      } | null;
      phone: string | null;
      shippingAddress: {
        address1: string | null;
        address2: string | null;
        city: string | null;
        country: string | null;
        countryCode: string | null;
        firstName: string | null;
        lastName: string | null;
        phone: string | null;
        province: string | null;
        provinceCode: string | null;
        zip: string | null;
      } | null;
      shippingLine: {
        price: {
          amount: number;
          currencyCode: string;
        };
      } | null;
      smsMarketingPhone: string | null;
      subtotalPrice: {
        amount: number;
        currencyCode: string;
      } | null;
    };
  };
};
</pre>
</details>

#### `eventFactory.payment_info_submitted`

> The `payment_info_submitted` event logs an instance of a customer submitting their payment information. This event is available on the checkout page.
>
> [Source: Shopify Documentation](https://shopify.dev/docs/api/web-pixels-api/standard-events/payment_info_submitted)

<details>
<summary style="cursor: pointer;">Payload Specification</summary>
<pre lang="typescript">
type PaymentInfoSubmittedEventPayload = {
  shopifyEventId: string;
  shopifyClientId: string;
  shopifyShopURL: string;
  shopifyEventData?: { // More digestible as the data property in the Shopify docs linked above.
    checkout: {
      attributes: {
        key: string;
        value: string;
      }[];
      billingAddress: {
        address1: string | null;
        address2: string | null;
        city: string | null;
        country: string | null;
        countryCode: string | null;
        firstName: string | null;
        lastName: string | null;
        phone: string | null;
        province: string | null;
        provinceCode: string | null;
        zip: string | null;
      } | null;
      buyerAcceptsEmailMarketing: boolean;
      buyerAcceptsSmsMarketing: boolean;
      currencyCode: string | null;
      totalTax: {
        amount: number;
        currencyCode: string;
      };
      totalPrice: {
        amount: number;
        currencyCode: string;
      } | null;
      transactions: {
        amount: {
          amount: number;
          currencyCode: string;
        };
        gateway: string;
        paymentMethod: {
          name: string;
          type: string;
        };
      }[];
      delivery: {
        selectedDeliveryOptions: {
          cost: {
            amount: number;
            currencyCode: string;
          } | null;
          costAfterDiscounts: {
            amount: number;
            currencyCode: string;
          } | null;
          description: string | null;
          handle: string;
          title: string | null;
          type: string;
        }[];
      } | null;
      discountApplications: {
        allocationMethod: string;
        targetSelection: string;
        targetType: string;
        title: string;
        type: string;
        value:
          | {
              amount: number;
              currencyCode: string;
            }
          | {
              percentage: number;
            };
      }[];
      discountsAmount: {
        amount: number;
        currencyCode: string;
      } | null;
      email: string | null;
      token: string | null;
      lineItems: {
        finalLinePrice: {
          amount: number;
          currencyCode: string;
        };
        id: string | null;
        properties: {
          key: string;
          value: string;
        }[];
        quantity: number;
        sellingPlanAllocation: {
          sellingPlan: {
            id: string;
            name: string;
          };
        } | null;
        title: string | null;
        discountAllocations: {
          amount: {
            amount: number;
            currencyCode: string;
          };
          variant: {
            id: string | null;
            image: {
              src: string | null;
            } | null;
            price: {
              amount: number;
              currencyCode: string;
            };
            product: {
              id: string | null;
              title: string;
              type: string | null;
              untranslatedTitle: string | null;
              url: string | null;
              vendor: string;
            };
            sku: string | null;
            title: string | null;
            untranslatedTitle: string | null;
          } | null;
          discountApplication: {
            allocationMethod: string;
            targetSelection: string;
            targetType: string;
            title: string;
            type: string;
            value:
              | {
                  amount: number;
                  currencyCode: string;
                }
              | {
                  percentage: number;
                };
          };
        }[];
      }[];
      localization: {
        country: {
          isoCode: string | null;
        };
        language: {
          isoCode: string;
        };
        market: {
          handle: string | null;
          id: string | null;
        };
      };
      order: {
        customer: {
          id: string | null;
          isFirstOrder: boolean | null;
        } | null;
        id: string | null;
      } | null;
      phone: string | null;
      shippingAddress: {
        address1: string | null;
        address2: string | null;
        city: string | null;
        country: string | null;
        countryCode: string | null;
        firstName: string | null;
        lastName: string | null;
        phone: string | null;
        province: string | null;
        provinceCode: string | null;
        zip: string | null;
      } | null;
      shippingLine: {
        price: {
          amount: number;
          currencyCode: string;
        };
      } | null;
      smsMarketingPhone: string | null;
      subtotalPrice: {
        amount: number;
        currencyCode: string;
      } | null;
    };
  };
};
</pre>
</details>

#### `eventFactory.checkout_completed`

> The `checkout_completed` event logs when a visitor completes a purchase. It's triggered once for each checkout, typically on the Thank you page. However, for upsells and post purchases, the checkout_completed event is triggered on the first upsell offer page instead. The event isn't triggered again on the Thank you page. If the page where the event is supposed to be triggered fails to load, then the checkout_completed event isn't triggered at all.
>
> [Source: Shopify Documentation](https://shopify.dev/docs/api/web-pixels-api/standard-events/checkout_completed)

<details>
<summary style="cursor: pointer;">Payload Specification</summary>
<pre lang="typescript">
type CheckoutCompletedEventPayload = {
  shopifyEventId: string;
  shopifyClientId: string;
  shopifyShopURL: string;
  shopifyEventData?: { // More digestible as the data property in the Shopify docs linked above.
    checkout: {
      attributes: {
        key: string;
        value: string;
      }[];
      billingAddress: {
        address1: string | null;
        address2: string | null;
        city: string | null;
        country: string | null;
        countryCode: string | null;
        firstName: string | null;
        lastName: string | null;
        phone: string | null;
        province: string | null;
        provinceCode: string | null;
        zip: string | null;
      } | null;
      buyerAcceptsEmailMarketing: boolean;
      buyerAcceptsSmsMarketing: boolean;
      currencyCode: string | null;
      totalTax: {
        amount: number;
        currencyCode: string;
      };
      totalPrice: {
        amount: number;
        currencyCode: string;
      } | null;
      transactions: {
        amount: {
          amount: number;
          currencyCode: string;
        };
        gateway: string;
        paymentMethod: {
          name: string;
          type: string;
        };
      }[];
      delivery: {
        selectedDeliveryOptions: {
          cost: {
            amount: number;
            currencyCode: string;
          } | null;
          costAfterDiscounts: {
            amount: number;
            currencyCode: string;
          } | null;
          description: string | null;
          handle: string;
          title: string | null;
          type: string;
        }[];
      } | null;
      discountApplications: {
        allocationMethod: string;
        targetSelection: string;
        targetType: string;
        title: string;
        type: string;
        value:
          | {
              amount: number;
              currencyCode: string;
            }
          | {
              percentage: number;
            };
      }[];
      discountsAmount: {
        amount: number;
        currencyCode: string;
      } | null;
      email: string | null;
      token: string | null;
      lineItems: {
        finalLinePrice: {
          amount: number;
          currencyCode: string;
        };
        id: string | null;
        properties: {
          key: string;
          value: string;
        }[];
        quantity: number;
        sellingPlanAllocation: {
          sellingPlan: {
            id: string;
            name: string;
          };
        } | null;
        title: string | null;
        discountAllocations: {
          amount: {
            amount: number;
            currencyCode: string;
          };
          variant: {
            id: string | null;
            image: {
              src: string | null;
            } | null;
            price: {
              amount: number;
              currencyCode: string;
            };
            product: {
              id: string | null;
              title: string;
              type: string | null;
              untranslatedTitle: string | null;
              url: string | null;
              vendor: string;
            };
            sku: string | null;
            title: string | null;
            untranslatedTitle: string | null;
          } | null;
          discountApplication: {
            allocationMethod: string;
            targetSelection: string;
            targetType: string;
            title: string;
            type: string;
            value:
              | {
                  amount: number;
                  currencyCode: string;
                }
              | {
                  percentage: number;
                };
          };
        }[];
      }[];
      localization: {
        country: {
          isoCode: string | null;
        };
        language: {
          isoCode: string;
        };
        market: {
          handle: string | null;
          id: string | null;
        };
      };
      order: {
        customer: {
          id: string | null;
          isFirstOrder: boolean | null;
        } | null;
        id: string | null;
      } | null;
      phone: string | null;
      shippingAddress: {
        address1: string | null;
        address2: string | null;
        city: string | null;
        country: string | null;
        countryCode: string | null;
        firstName: string | null;
        lastName: string | null;
        phone: string | null;
        province: string | null;
        provinceCode: string | null;
        zip: string | null;
      } | null;
      shippingLine: {
        price: {
          amount: number;
          currencyCode: string;
        };
      } | null;
      smsMarketingPhone: string | null;
      subtotalPrice: {
        amount: number;
        currencyCode: string;
      } | null;
    };
  };
};
</pre>
</details>

#### `eventFactory.custom`

> Use if you want to track something not covered by the standard events listed above.

<details>
<summary style="cursor: pointer;">Payload Specification</summary>
<pre lang="typescript">
type CustomEventPayload = {
    shopifyEventName: string; // Important! This will be the key of your event.
    shopifyEventId: string;
    shopifyClientId: string;
    shopifyShopURL: string;
    shopifyEventData?: { // You can include any data you feel is relevant.
        [x: string]: unknown;
    } | undefined;
}
</pre>
</details>

## For SDK developers

### How to deploy a new version

1. Make the changes you want to make, try to be backwards compatible when
   possible
2. Create a CHANGELOG.md entry using `npx changeset`
3. Deploy to NPM using `yarn deploy` (You need access to the NPM 2FA)
4. Commit and merge the changes.
5. Update the Polar Shopify Extension to use the latest version of the SDK
6. (If relevant) inform third-parties using the SDK that there is value in
   updating.