---
title: "Shopify"
slug: "shopify"
updated: 2025-04-03T10:04:22Z
published: 2025-04-03T10:04:22Z
canonical: "docs.newstore.com/shopify"
---

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

# Shopify

This document describes how the shopping app integrates with Shopify. It covers the integration on both a high and detailed level and covers the steps required to setup and configure the integration. See the “Detailed API documentation” section for specific details on which end points are used and how they are used.

## **Shopify integration overview**

The shopping app integrates with Shopify exclusively via the GraphQL Storefront API. Requests are made directly from Newstore’s middleware.

All data is pulled directly from Shopify and no data is stored within Newstore. The only exception being product data which is cached for up to 24 hours to limit the number of requests made to Shopify. Shopify is the source of truth in terms of data. eg customer accounts, baskets, pricing etc. For example if a user creates an account in the app then a request is made directly to Shopify to create the user account within the Shopify database. This means that user accounts on the web and app are shared.

The Shopify integration layer is part of the Newstore platform and the same integration is used by every Shopify customer that is on the Newstore shopping app platform.

### **Feature overview**

The following commerce features are supported within the Newstore - Shopify integration. Items marked with a * are not supported out of the box and would require customisation to the Shopify response.

- Category Tree - for navigation
- Product Lists
  - Sorting - Sorting options are configured within Newstore as these are not returned by Shopify
  - Filtering
- Product Details
  - Name
  - Images
  - Original Price
  - Sale Price
  - Description
  - Variation Attributes -eg size / colour
  - Share URL
  - Badges *
  - Size Guide *
  - Delivery Message *
- Baskets
  - Create Basket
  - Add Item to Basket
  - Update Item in Basket
  - Remove item from Basket
- Checkout - Shopify’s web checkout component is embedded into the app
- Accounts
  - Account Creation
  - Login
  - Add address to account - Only 1 address is supported

### Customization

The The integration between NewStore and Shopify cannot be customised in the sense that requests cannot be altered, nor can different endpoints be used. However additional attributes can be added to the Shopify product interface in the form of metadata allowing additional data to be added to the product. For example product labels

#### NewStore-specific customizations

You may well have other services that make use of the GraphQL Storefront API and as such you may wish to only apply some customisations to requests from Newstore. Requests from Newstore can be recognised using the User-Agent request header. The value will always be set to Highstreet Backend.

### Integration setup

In order to setup the Newstore - Shopify Integration Newstore will need:

- The domain of the Shopify store for all environments.
- A storefront access token. Details of how to set that up can be found here: [https://shopify.dev/docs/custom-storefronts/building-with-the-storefront-api/getting-started](https://shopify.dev/docs/custom-storefronts/building-with-the-storefront-api/getting-started)
- An Admin API Access token. Details of how to set that up can be found here: [h](https://shopify.dev/docs/api/admin-rest#authentication)[ttps://shopify.dev/docs/api/admin-rest#authentication](https://shopify.dev/docs/api/admin-rest#authentication)
- A navigation menu will have to be create specifically for the app. This navigation menu should represent the category tree. Only links to collections are supported. The menu ID will need to be shared with Newstore

## Detailed API documentation

This sections goes into detailed specifics of how this integration works on an endpoint level.

### Categories / Navigation

Queries used:

**menu**

Integration details:

A navigation menu within Shopify needs to be created specifically for the app. This menu is then fetch by using the menu query. Each menu item must link to a collection. No other item type is supported within the shopping app navigation.

See example below for a representation of a basic category tree. Every level of category mentioned must have products assigned to them. It’s the expectation that the top level category would contain all products of the sub categories combined.

```plaintext
women
  - women-bottoms
    - women-bottoms-long
    - women-bottoms-short
  - women-tops
    - women-tops-short-sleeve
    - women-tops-long-sleeve    

men
  - men-bottoms
    - men-bottoms-long
    - men-bottoms-short
  - men-tops
    - men-tops-short-sleeve
    - men-tops-long-sleeve
```

The following attributes on the category object are used. All other attributes are ignored.

| Name | Type | Comment |
| --- | --- | --- |
| id | String | The id of the menu item, this is also the value that will be used to find the corresponding collection ID when performing a plv search request. |
| title | String | The name of the category. This should be localised. |

### Product lister view

Queries Used

- **lookupCollectionID**
- **products**

**Integration details**

When performing a request to load the plv of a specific category the app has access to the menu item Id this is sent to Shopify within the **lookupCollectionID** query to fetch the corresponding collection ID. This collection ID is then included in the products query in order to fetch all product IDs that are assigned to the specified collection. The filters that are assigned to this collection are also returned in this request. With these product IDs another query is then sent to Shopify to fetch the details of each product

**Sorting options**

A list of Sorting options are exposed by the Shopify API. Therefore these are configured within Newstore and can be altered. Only the following filters are supported: TITLE, PRICE, BEST_SELLING, CREATED, ID, MANUAL, COLLECTION_DEFAULT, RELEVANCE

The default sorting options that are shown are:

| ID | Title |
| --- | --- |
| best_selling.asc | Best Selling |
| created.desc | What's New |
| price.asc | Price: low to high |
| price.desc | Price: high to low |

**Filters**

The filters that are shown in the app are returned within the filters attribute of the response. These are converted 1:1 and displayed in the app. The label attribute of the filter should contain the localised display name of the filter.

Collection query:

```plaintext
query lookupCollectionID($menu_item_id: ID!) {
    node(id: $menu_item_id) {
        ... on MenuItem {
            type
            resourceId
        }
    }
}
```

Product search query:

```plaintext
query products(
    $collection_id: ID!
    $first: Int!
    $after: String
    $sort: ProductCollectionSortKeys!
    $reverse: Boolean
    $filters: [ProductFilter!]
) {
    collection(id: $collection_id) {
        products(
            first: $first
            after: $after
            sortKey: $sort
            reverse: $reverse
            filters: $filters
        ) {
            edges {
                cursor
                node {
                    id
                    
                }
            }
            filters {
                id
                label
                type
                values {
                    label
                    count
                }
            }
        }
    }
}
```

Product detail query:

```plaintext
query ($ids: [ID!]!, $country: CountryCode) @inContext (country: $country) {
    nodes(ids: $ids) {
        __typename

        ... on Product {
            availableForSale
            descriptionHtml
            description
            id
            title
            onlineStoreUrl
            publishedAt
            totalInventory
            vendor

            priceRange {
                maxVariantPrice {
                    amount
                }
                minVariantPrice {
                    amount
                }
            }

            compareAtPriceRange {
                maxVariantPrice {
                    amount
                }
                minVariantPrice {
                    amount
                }
            }
            images(first: 100) {
                edges {
                    node {
                        url
                    }
                }
            }
            metafields(first: 100) {
                edges {
                    node {
                        id
                        description
                        key
                        namespace
                        value
                    }
                }
            }
            options(first: 100) {
                id
                name
                values
            }

            variants(first: 250) {
                edges {
                    node {
                        priceV2 {
                            amount
                        }
                        compareAtPriceV2 {
                            amount
                        }
                    }
                }
            }
        }
    }
}
```

### Product search

When performing a product search request the products query is used just like with the plv request however the query is slightly different. Again this will return a list of product IDs and the details of these product IDs are then fetch from Shopify

#### Barcode scanner

When a product barcode is scanned within the app a product search request will also be performed. The products EAN will be sent as the value for the query variable. For this reason the product EAN must be available on the product search index

Example query:

```plaintext
query products(
    $first: Int!
    $after: String
    $query: String!
    $sort: ProductSortKeys!
    $reverse: Boolean
) {
    products(
        first: $first
        after: $after
        query: $query
        sortKey: $sort
        reverse: $reverse
    ) {
        edges {
            cursor
            node {
                id
            }
        }
        pageInfo {
            hasNextPage
        }
    }
}
```

Example query variables:

```plaintext
{
    "first": 250,
    "query": "available_for_sale:true AND {{search_term}}",
    "sort": "RELEVANCE",
    "reverse": false
}
```

---

### Product details

```plaintext
query ($ids: [ID!]!, $country: CountryCode) @inContext (country: $country) {
    nodes(ids: $ids) {
        __typename

        ... on Product {
            availableForSale
            descriptionHtml
            description
            id
            title
            onlineStoreUrl
            publishedAt
            totalInventory
            vendor

            priceRange {
                maxVariantPrice {
                    amount
                }
                minVariantPrice {
                    amount
                }
            }

            compareAtPriceRange {
                maxVariantPrice {
                    amount
                }
                minVariantPrice {
                    amount
                }
            }
            images(first: 100) {
                edges {
                    node {
                        url
                    }
                }
            }
            metafields(first: 100) {
                edges {
                    node {
                        id
                        description
                        key
                        namespace
                        value
                    }
                }
            }
            options(first: 100) {
                id
                name
                values
            }

            variants(first: 250) {
                edges {
                    node {
                        priceV2 {
                            amount
                        }
                        compareAtPriceV2 {
                            amount
                        }
                    }
                }
            }
        }
    }
}
```

```plaintext
{
    "ids": ["{{product_id}}"],
    "country": "{{country}}"
}
```

#### Name

The product name is fetched from the **title** attribute.

#### Description

The product description is fetched from the **descriptionHtml** attribute if this is missing or empty then the description attribute is used as a fallback.

```xml
<h1>Product Details</h1>
<p>These are the product details</p>
<h1>Composition</h1>
<ul>
  <li>80% Cotton</li>
  <li>20% Elastane</li>
</ul>
```

#### Images

The list of product images are fetched from the media attribute. The url of the image is then requested by the app. Videos can also be supported. The video must be in an mp4 format.

Newstore makes use of an Image resizing service and CDN. This service sits in between the app and the image source. This means that the highest resolution is needed as the app will add the necessary resizing parameters to the request url. The resizing service will then resize the image and serve the image via the CDN reducing the load on the original image source. Images should be publicly available and not require authentication.

> [!WARNING]
> Important:
> 
> If an image is updated within your system but the original url of the image is not changed then the new image will not be visible in the app. This is because it will remain cached by the image CDN. Therefore if an image is updated it is essential that the image url is also updated.

#### Pricing

The app supports the display of 2 prices for a single product. The original and effective price.

- To get the effective price the priceRange.minVariantPrice.amount is used
- To get the original price the compareAtPriceRange.maxVariantPrice.amount is used

#### Web / share URI

The web uri is used when sharing a product. It is essentially the url of the product detail page on the website. To get this the **onlineStoreUrl** attribute is used.

#### Pricing

The app supports the display of 2 prices for a single product. The original and effective price.

- To get the effective price the **priceRange.minVariantPrice.amount** is used
- To get the original price the **compareAtPriceRange.maxVariantPrice.amount** is used

#### Web/share URI

The web uri is used when sharing a product. It is essentially the url of the product detail page on the website. To get this the **onlineStoreUrl** attribute is used.

#### Size chart

The shopping app can display a link to a product's size guide on the product detail view. The app will then open the link within a webview and display the contents of the webpage. As such a webpage needs to be created that hosts the contents of the size guide.

The url will then be configured within a Newstore extension. The product ID will be added as a parameter to the url so that the correct size guide can be shown for the requested product.

Example: https://brand.com/sizeguide?product_id=123

Examples from other clients:

- [Size guide | Find the perfect fit | G-STAR®](https://www.g-star.com/en_nl/size-guide?gender=men&amp;guide=tops&amp;unit=cm)
- [Size Guide Women Clothing](https://www.allsaints.com/sizechart-women-clothing.html?highstreet=true)

#### Badges

Product badges or labels can be displayed in the app to indicate additional messaging. For example **Sale recycled 20% off**.

This information will need to be exposed as a custom attribute on the product. The value should be a list of badges that should be shown on the specific product.

- The text, text colour and background colour can be dynamic.
- A product can have multiple badges

```plaintext
"c_hsProductLabels": [
  {
    "id": "sale",
    "text": "Sale",
    "text_color": "#ffffff",
    "background_color": "#ffffff"
  }
]
```

| Name | Type |
| --- | --- |
| id | String |
| text | String |
| text_color | String |
| background_color | String |

This attribute will need to be added on the product variant level.

#### Stock / Inventory

Product Stock is fetched using the following query. Stock is fetched on both the product and variant level

If **availableForSale** and **quantityAvailable** is 0 then the variant will be displayed as out of stock in the app. If all variants of a product are out of stock then the buy button will be completely disabled on the PDV

Example Query

```plaintext
query($ids: [ID!]!) {
    nodes(ids: $ids) {
        __typename
        id

        ... on Product {
            availableForSale
            totalInventory
        }

        ... on ProductVariant {
            availableForSale
            quantityAvailable
        }
    }
}
```

### **Baskets**

**Creating a basket**

Mutations used:

**checkoutCreate**

Integration details:

An empty basket is created using the **checkoutCreate** mutation. This will return the checkout ID which is then saved within the app and used in all subsequent requests

Example Query

```plaintext
mutation checkoutCreate($input: CheckoutCreateInput!) {
    checkoutCreate(input: $input) {
        checkout {
            id
        }

        checkoutUserErrors {
            code
            field
            message
        }
    }
}
```

#### Retrieving a basket

Queries used:

**node**

Integration details:

Example query

```plaintext
query ($checkoutID: ID!, $country: CountryCode) @inContext(country: $country) {
    node(id: $checkoutID) {
        id
        ... on Checkout {
            ready
            subtotalPriceV2 {
                amount
                currencyCode
            }
            totalTaxV2 {
                amount
                currencyCode
            }
            totalPriceV2 {
                amount
                currencyCode
            }
            paymentDueV2 {
                amount
                currencyCode
            }
            lineItemsSubtotalPrice {
                amount
                currencyCode
            }
            email

            lineItems(first: 250) {
                edges {
                    node {
                        id
                        variant {
                            title
                            id
                            priceV2 {
                                amount
                            }
                            compareAtPriceV2 {
                                amount
                            }
                            product {
                                id
                                title
                            }
                        }
                        unitPrice {
                            amount
                            currencyCode
                        }
                        quantity
                        discountAllocations {
                            allocatedAmount {
                                amount
                                currencyCode
                            }
                        }
                    }
                }
            }
            shippingLine {
                handle
                title
                priceV2 {
                    amount
                    currencyCode
                }
            }
            availableShippingRates {
                ready
                shippingRates {
                handle
                priceV2 {
                    amount
                }
                title
                }
            }
        }
    }
}
```

Example variables

```plaintext
{
    "checkoutID": "{{checkout_id}}",
    "country": "{{country}}"
}
```

#### Adding an item to the basket

Mutations used:

**checkoutLineItemsAdd**

Integration details:

Example query

```plaintext
mutation checkoutLineItemsAdd(
    $lineItems: [CheckoutLineItemInput!]!
    $checkoutId: ID!
) {
    checkoutLineItemsAdd(lineItems: $lineItems, checkoutId: $checkoutId) {
        checkout {
            id
            lineItems(first: 50) {
                edges {
                    node {
                        id
                        quantity
                        customAttributes {
                            key
                            value
                        }
                        variant {
                            id
                        }
                    }
                }
            }
        }
        checkoutUserErrors {
            code
            field
            message
        }
    }
}
```

Example Variables

```json
{
    "lineItems": [{
        "quantity": 1,
        "variantId": "{{product_variant_id}}"
    }],
    "checkoutId": "{{checkout_id}}"
}
```

#### Updating an item in the basket

Mutations used:

**checkoutLineItemsUpdate**

Integration details:

Example query

```plaintext
mutation checkoutLineItemsUpdate(
    $checkoutId: ID!
    $lineItems: [CheckoutLineItemUpdateInput!]!
) {
    checkoutLineItemsUpdate(checkoutId: $checkoutId, lineItems: $lineItems) {
        checkout {
            id
            lineItems(first: 10) {
                edges {
                    node {
                        id
                        variant {
                            id
                        }
                    }
                }
            }
        }
        checkoutUserErrors {
            code
            field
            message
        }
    }
}
```

Example variables

```json
{
    "lineItems": [{
        "quantity": {{quantity}},
        "id": "{{checkout_line_item_id}}",
        "variantId": "{{product_variant_id}}"
    }],
    "checkoutId": "{{checkout_id}}"
}
```

#### Removing an item from the basket

Mutations used

**checkoutLineItemsRemove**

Integration details:

Example query

```plaintext
mutation checkoutLineItemsRemove($checkoutId: ID!, $lineItemIds: [ID!]!) {
    checkoutLineItemsRemove(checkoutId: $checkoutId, lineItemIds: $lineItemIds) {
        checkout {
            id
        }
        checkoutUserErrors {
            code
            field
            message
        }
    }
}
```

Example variables

```json
{
    "checkoutId": "{{checkout_id}}",
    "lineItemIds": ["{{checkout_line_item_id}}"]
}
```

### Checkout

The Shopify checkout component is used. This is opened within a secure webview in the app and would appear exactly like the checkout experience on the mobile version of the website. The addition of the shipping address, billing address, shipping methods, payment method, coupon code and payment is all handled by Shopify. Newstore is not involved in this process

integration details:

The url of the checkout is fetched from Shopify using the following query. This url is returned to the app and opened within a webview. The url of the success page is configured within the app. If the checkout requests this url then the app knows that the transaction is complete, closes the webview and displays the native order confirmation view.

Example query

```plaintext
query ($checkoutID: ID!) {
    node(id: $checkoutID) {
        id
        ... on Checkout {
            webUrl
        }
    }
}
```

### Accounts

Users have the option to create an account in the app or to login to the app using credentials of an account that was created on the website. All requests are made directly to Shopify meaning that an account created in the app is created directly within Shopify. No user account data is stored anywhere within Newstore.

#### Login

Query used:

**customerAccessTokenCreate**

Integration details:

A user can login using their email and password. In the case that a user has just created an account then the app will automatically make this request in order to ensure that the user is logged in automatically. The returned access token will be stored in the app and added to all requests to Shopify that require an access token to be present.

If the access token has expired a new one will be requested using the customerAccessTokenRenew mutation. If this fails because the access token has expired then the app will automatically attempt to log the user back in using their email and password as these are securely stored within the app’s keychain

Example Query

```plaintext
mutation ($input: CustomerAccessTokenCreateInput!) {
    customerAccessTokenCreate(input: $input) {
        customerUserErrors {
            code
            field
            message
        }
        customerAccessToken {
            accessToken
            expiresAt
        }
    }
}
```

Example variables

```json
{
    "input": {
        "email": "{{email}}",
        "password": "{{password}}"
    }
}
```

#### Creating an account

**customerCreate**

Example mutation

```plaintext
mutation ($input: CustomerCreateInput!) {
    customerCreate(input: $input) {
        customerUserErrors {
            field
            code
            message
        }
        customer {
            id
            email
            firstName
            lastName
            defaultAddress {
                address1
                address2
                city
                company
                countryCodeV2
                firstName
                id
                lastName
                phone
                province
                zip
            }
        }
    }
}
```

Example variables

```plaintext
{
    "input": {
        "email": "{{email}}",
        "firstName": "App",
        "lastName": "Tester",
        "password": "{{password}}"
    }
}
```

The following attributes are used from the response

| Attribute | Type | Description |
| --- | --- | --- |
| firstName | String | User’s first name |
| lastName | String | User’s last name |
| id | String | User’s ID |
| email | String | The user’s email address |

#### Fetching a customer

Query used:

**customer**

Example query

```plaintext
query customer($token: String!) {
    customer(customerAccessToken: $token) {
        id
        email
        firstName
        lastName
        defaultAddress {
            address1
            address2
            city
            company
            countryCodeV2
            firstName
            id
            lastName
            phone
            province
            zip
        }
    }
}
```

Example variables

```plaintext
{
    "token": "{{access_token}}"
}
```

#### Updating a customer

Query used:

**customerUpdate**

Integration details:

Only a customer’s First and Last name can be updated via the app.

Example query

```plaintext
mutation ($token: String!, $customer: CustomerUpdateInput!) {
    customerUpdate(customerAccessToken: $token, customer: $customer) {
        customerUserErrors {
            field
            code
            message
        }
        customer {
            id
            email
            firstName
            lastName
            defaultAddress {
                address1
                address2
                city
                company
                countryCodeV2
                firstName
                id
                lastName
                phone
                province
                zip
            }
        }
    }
}
```

Example variables

```plaintext
{
    "token": "{{access_token}}",
    "customer": {
        "firstName": "App",
        "lastName": "Tester"
    }
}
```

#### Adding an address

Query used:

**customerAddressCreate**

Integration details:

If a customer does not yet have an address saved on their account then a new address will be created.

Example query

```plaintext
mutation ($address: MailingAddressInput!, $token: String!) {
    customerAddressCreate(customerAccessToken: $token, address: $address) {
        customerUserErrors {
            code
            field
            message
        }
    }
}
```

Example variables

```plaintext
{
    "address": {
        "city": "Utrecht",
        "country": "NL",
        "firstName": "App",
        "address2": "Loft 3.14",
        "lastName": "Tester",
        "zip": "3511 ED",
        "address1": "Stationsplein 32"
    },
    "token": "{{access_token}}"
}
```

#### Updating an address

Query used:

**customerAddressCreate**

Integration details:

If a customer already has an address then any changes made in the app will cause the address to be updated

Example query

```plaintext
mutation ($id: ID!, $address: MailingAddressInput!, $token: String!) {
    customerAddressUpdate(
        customerAccessToken: $token
        id: $id
        address: $address
    ) {
        customerUserErrors {
            code
            field
            message
        }
    }
}
```

Example variables

```plaintext
{
    "id": "{{default_address_id}}",
    "address": {
        "city": "Berlin",
        "country": "DE",
        "firstName": "App",
        "address2": "",
        "lastName": "Tester",
        "zip": "10785",
        "address1": "Potsdamer Str 7",
        "phone": "12345678"
    },
    "token": "{{access_token}}"
}
```

#### Reset password

Query used:

**customerRecover**

Integration details:

This mutation will trigger a password reset email to be sent to the customer. The link in the email will link to the password reset flow of the website. There is no native password reset view in the app. Once the user has reset their password on the web they will need to open the app again to login.

Example query

```plaintext
mutation customerRecover($email: String!) {
    customerRecover(email: $email) {
        customerUserErrors {
            code
            field
            message
        }
    }
}
```

Example variables

```plaintext
{
    "email": "{{email}}"
}
```

### Order history

#### Order summary

Query used:

**customer**

Integration details:

To get a list of orders that a customer has previously made the customer.orders query is used. This is used to get the list of all orders a customer has made. To the fetch the details of each order the orderByID query is used. This request is made when the user opens individual order view.

Example query

```plaintext
query ($customerAccessToken: String!) {
    customer(customerAccessToken: $customerAccessToken) {
        orders(first: 250) {
            edges {
                node {
                    processedAt
                    currencyCode
                    id
                    name

                    currencyCode
                    financialStatus
                    totalPriceV2 {
                        amount
                    }
                    totalShippingPriceV2 {
                        amount
                    }
                    currentSubtotalPrice {
                        amount
                    }
                    totalTaxV2 {
                        amount
                    }
                    discountApplications(first: 50) {
                        ... on DiscountApplicationConnection {
                            edges {
                                node {
                                    value {
                                        ... on MoneyV2 {
                                            amount
                                        }
                                    }
                                }
                            }
                        }
                    }

                    lineItems(first: 250) {
                        edges {
                            node {
                                title
                                quantity
                                discountedTotalPrice {
                                    amount
                                    currencyCode
                                }
                                variant {
                                    selectedOptions {
                                        name
                                        value
                                    }
                                    id
                                    priceV2 {
                                        amount
                                    }
                                    image {
                                        url
                                        altText
                                    }
                                }
                            }
                        }
                    }

                    shippingAddress {
                        address1
                        address2
                        city
                        company
                        countryCodeV2
                        firstName
                        id
                        lastName
                        phone
                        province
                        zip
                    }
                    fulfillmentStatus
                    successfulFulfillments {
                        trackingCompany
                        trackingInfo {
                            url
                        }
                    }
                }
            }
        }
    }
}
```

Example variables

```plaintext
{
    "customerAccessToken": "{{access_token}}"
}
```

#### Order details

Query used:

**orderByID**

Integration details:

This request is made when the user opens individual order view

Example query

```plaintext
query orderByID($id: ID!) {
    node(id: $id) {
        id

        ... on Order {
            processedAt
            currencyCode
            id
            name

            currencyCode
            financialStatus
            totalPriceV2 {
                amount
            }
            totalShippingPriceV2 {
                amount
            }
            currentSubtotalPrice {
                amount
            }
            totalTaxV2 {
                amount
            }
            discountApplications(first: 50) {
                ... on DiscountApplicationConnection {
                    edges {
                        node {
                            value {
                                ... on MoneyV2 {
                                    amount
                                }
                            }
                        }
                    }
                }
            }

            lineItems(first: 250) {
                edges {
                    node {
                        title
                        quantity
                        discountedTotalPrice {
                            amount
                            currencyCode
                        }
                        variant {
                            selectedOptions {
                                name
                                value
                            }
                            id
                            priceV2 {
                                amount
                            }
                            image {
                                url
                                altText
                            }
                        }
                    }
                }
            }

            shippingAddress {
                address1
                address2
                city
                company
                countryCodeV2
                firstName
                id
                lastName
                phone
                province
                zip
            }
            fulfillmentStatus
            successfulFulfillments {
                trackingCompany
                trackingInfo {
                    url
                }
            }
        }
    }
}
```

Example variables

```plaintext
{
    "customerAccessToken": "{{access_token}}"
}
```

### Order tracking

NewStore tracks all user events made within the app including the purchase event. To accurately track the purchase event Newstore makes use of the orders/create webhook. This will be sent to a Newstore endpoint once the order has been successfully created and this will be used to track the order within analytics. During onboarding Newstore will setup this webhook using the Admin API.

> [!WARNING]
> If too many errors are returned by the specified endpoint, shopify will delete the webhook subscription and will stop sending requests.

Webhook - REST

Example request

```plaintext
POST https://{{admin_api_key}}:{{admin_api_password}}@{{store_url}}/admin/api/2022-04/webhooks.json

{
    "webhook": {
        "address": "https://MERCHANT-ID.api.highstreetapp.com/shopify/webhook",
        "topic": "orders/create",
        "format": "json"
    }
}
```
