# Web SDK (/docs/reference/web-sdk)



The Web SDK is the JavaScript API a web page uses to call the Appbrew app it is running inside. For a walkthrough, start with [the Web SDK guide](/docs/build/web-sdk). This page is the surface.

```html
<script src="https://solutions.appbrew.tech/web-sdk/appbrew-sdk.js"></script>
```

The script defines `window.appbrewSDK`. It is untyped JavaScript with no build step and no npm package. It also leaves a helper named `getRequestId` on `window`, so avoid that name in your own code.

## Methods [#methods]

| Method                                                     | Returns                | Waits for the app |
| ---------------------------------------------------------- | ---------------------- | ----------------- |
| [`isApp()`](#isapp)                                        | `boolean`              | no, synchronous   |
| [`getUserDetails()`](#getuserdetails)                      | `Promise<UserDetails>` | yes, 5 s          |
| [`addToCart(handle, variantId, quantity)`](#addtocart)     | `Promise<CartResult>`  | yes, 5 s          |
| [`addMultipleItemsToCart(items)`](#addmultipleitemstocart) | `Promise<CartResult>`  | yes, 5 s          |
| [`gotoLink(link)`](#gotolink)                              | `Promise<undefined>`   | no                |
| [`goBackInApp()`](#gobackinapp)                            | `Promise<undefined>`   | no                |
| [`sendEvent(event, data)`](#sendevent)                     | `Promise<undefined>`   | no                |

`isApp()` is synchronous. The three methods that do not wait are declared `async`, so they return a promise, but it resolves immediately with `undefined` and carries no information about whether the app acted. There is nothing useful to `await`.

Every method except `isApp()` returns `undefined` when the bridge is absent, after logging to the console. Check `isApp()` first.

## isApp [#isapp]

```ts
isApp(): boolean
```

`true` when the app's bridge is present on the page. Equivalent to `typeof window.appbrew !== 'undefined'`.

## getUserDetails [#getuserdetails]

```ts
getUserDetails(): Promise<UserDetails | undefined>
```

Asks the app for the signed-in customer. Resolves within five seconds in every case, and never rejects.

The result is one of two shapes. On success the app sends every field below. On timeout the SDK synthesises `{ status: 'error', message: 'Could not fetch user details' }` and **nothing else** — `isLoggedIn`, `customerAccessToken`, `customer` and `tags` are absent, not null. Branch on `status` before reading anything else.

```ts
type UserDetails =
  | {
      status: 'success'
      isLoggedIn: boolean
      customerAccessToken: string | null
      customer: Customer | null
      tags: string[]
    }
  | { status: 'error'; message: string }
```

The success arm:

<TypeTable
  type="{
  status: {
    type: &#x22;'success'&#x22;,
    description: 'Always success when the app replied. The error arm never carries these fields.',
  },
  isLoggedIn: {
    type: 'boolean',
    description: 'False when nobody is signed in, and when the app could not confirm the session.',
  },
  customerAccessToken: {
    type: 'string | null',
    description: 'Shopify Storefront API customer access token. Null when signed out.',
  },
  customer: {
    type: 'Customer | null',
    description: 'The profile. Can be null even when isLoggedIn is true, if the app holds no cached profile and the fetch returned nothing.',
  },
  tags: {
    type: 'string[]',
    default: '[]',
    description: 'Shopify customer tags.',
  },
}"
/>

`Customer`:

<TypeTable
  type="{
  id: { type: 'string | null', description: 'Shopify global id, e.g. gid://shopify/Customer/123.' },
  email: { type: 'string | null', description: '' },
  firstName: { type: 'string | null', description: '' },
  lastName: { type: 'string | null', description: '' },
  phone: { type: 'string | null', description: '' },
  displayName: { type: 'string | null', description: '' },
  acceptsMarketing: { type: 'boolean', default: 'false', description: 'Never null.' },
  defaultAddress: { type: 'Address | null', description: 'Null when the customer has no saved address.' },
}"
/>

`Address` carries `id`, `address1`, `address2`, `city`, `province`, `country`, `countryCodeV2`, `zip`, `firstName`, `lastName`, and `phone`. Every one of them is `string | null`.

The app serves a cached profile and only fetches when the cache is empty, so an edit made elsewhere may not be reflected until the next fetch.

## addToCart [#addtocart]

```ts
addToCart(
  handle: string,
  variantId: string,
  quantity: number
): Promise<CartResult | undefined>
```

Adds one item to the app's cart. `variantId` is a Shopify variant global id. The handle is sent along but the app ignores it: items are added by variant.

```ts
type CartResult = { status: 'success' | 'error'; message: string }
```

`status` is `'error'` only when the app did not confirm within five seconds. Success is weaker than it looks: the app replies once it has finished handling the request, and an error on its side still resolves as `'success'`. A rejected or out-of-stock variant produces silence, and therefore a timeout. Re-read the cart rather than trusting either answer.

## addMultipleItemsToCart [#addmultipleitemstocart]

```ts
addMultipleItemsToCart(items: CartItem[]): Promise<CartResult | undefined>
```

Adds several items in one cart update. Same result shape and same timeout as `addToCart`.

```ts
type CartItem = {
  handle?: string        // ignored
  variantId: string
  quantity: number
  sellingPlanId?: string
  customAttributes?: Record<string, string> | Array<{ key: string; value: string }>
}
```

## gotoLink [#gotolink]

```ts
gotoLink(link: Link): Promise<undefined>
```

Hands navigation to the app. Returns before the app has done anything.

```ts
interface Link {
  kind: string
  value: string
  params?: Record<string, any>
  external?: boolean
}
```

### Link kinds [#link-kinds]

| `kind`       | `value`                           | Result                                                                                                                                                                                    |
| ------------ | --------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `product`    | product handle                    | Native product screen                                                                                                                                                                     |
| `collection` | collection handle, or a global id | Native collection screen                                                                                                                                                                  |
| `cart`       | `'cart'`                          | The cart screen                                                                                                                                                                           |
| `screen`     | screen name                       | That screen, with `params`                                                                                                                                                                |
| `page`       | page name                         | That page, with `params`                                                                                                                                                                  |
| `url`        | a URL                             | Opens the matching native screen when the URL resolves to one, otherwise the app's webview. Goes to the device browser when `external` is true, or when the value is not an `http(s)` URL |
| `share`      | the message                       | The device share sheet                                                                                                                                                                    |
| `back`       | any non-empty string              | Goes back one screen                                                                                                                                                                      |

Anything else is treated as a screen name.

A `value` is always required. `gotoLink` returns without doing anything when `kind` or `value` is empty, including for `back` and `cart`, where the value is otherwise unused.

Three `screen` values behave specially:

* `default-signin` resolves to whichever sign-in screen this app is configured to use, which is the right way to open sign-in from a page. A hard-coded `'signin'` is wrong for apps that use a different one.
* `search` is ignored unless `params.searchQuery` is long enough to search for.
* `notifications-inbox` opens the push provider's inbox rather than navigating to a screen.

`kind: 'product'` and `kind: 'screen'` with `value: 'products'` reach the same screen. The first is shorter; the second lets you pass extra `params`.

```js
window.appbrewSDK.gotoLink({ kind: 'product', value: 'blue-tee' })

window.appbrewSDK.gotoLink({
  kind: 'screen',
  value: 'products',
  params: { productHandle: 'blue-tee' },
})
```

## goBackInApp [#gobackinapp]

```ts
goBackInApp(): Promise<undefined>
```

Pops the current screen, returning the shopper to wherever they were before your page.

## sendEvent [#sendevent]

```ts
sendEvent(event: string, data?: Record<string, any>): Promise<undefined>
```

Forwards an event to every analytics tracker the app has configured. Use one of the platform's own event names to join the app's existing funnel, or your own name for something only your page emits.

```js
window.appbrewSDK.sendEvent('viewed_loyalty_page', { tier: 'gold' })
```

## Failure and timeouts [#failure-and-timeouts]

Nothing here rejects, and the app never sends an error. The three methods that wait resolve with `{ status: 'error' }` five seconds after a call that got no reply. A timeout means one of:

* The app is older than the action you called. Unknown actions are dropped silently.
* The page is not rendered by a web-view block, so nothing is listening.
* The underlying operation failed, and the app reports failure by staying quiet.

Distinguishing them from the page is not possible. See [version gates](/docs/build/webview-contract#version-gates).

## Live sample [#live-sample]

A page exercising every method is published at [solutions.appbrew.tech/web-sdk/appbrew-sdk-sample](https://solutions.appbrew.tech/web-sdk/appbrew-sdk-sample). Open it inside an app to see real payloads for that app's configuration.
