> For the complete documentation index, see [llms.txt](https://the-wishlist-documentation.gitbook.io/the-wishlist-documentation-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://the-wishlist-documentation.gitbook.io/the-wishlist-documentation-docs/developer-reference/proxy-app-install-guide.md).

# Shopify Proxy App Install Guide

The Wishlist's Shopify Proxy App handles authentication between your Shopify store and our services. It enables support for Notify Me, Coming Soon and Recommendations widgets.   It supports token generation for both logged-in customers and anonymous visitors, so recommendation widgets work for all storefront visitors.

> **Note:** You can optionally ask us to do this for you.  &#x20;

***

### Step 1: Create the Proxy App in the Shopify Partners Dashboard

1. Go to the [Shopify Partners Dashboard](https://partners.shopify.com)
2. Create a new app ("TWC App Proxy")
3. Configure the **TWC** **App Proxy** with the following settings:

| Setting        | Value                                                    |
| -------------- | -------------------------------------------------------- |
| Subpath prefix | `apps`                                                   |
| Subpath        | `twc-sdk`                                                |
| Proxy URL      | `https://wh-fe.au-aws.thewishlist.io/api/v1/store-front` |

***

### Step 2: Install the App on the Store

1. In the Dev Dashboard, select **Custom Distribution**
2. Generate the installation link
3. Open the link and install the app on the target Shopify store
4. Be sure to capture the **Secret Key**

***

### Step 3: Configure the Proxy App Secret Key

The secret key is required to validate requests from Shopify's App Proxy. You'll need to contact The Wishlist team to configure this step.

You will need to provide us with your **Proxy App Secret Key**

***

### Step 4: Verify the Proxy Configuration

Test that the proxy is working before adding any widgets.

#### Anonymous visitor test

1. Open the store in an incognito/private browser window (not logged in)
2. Open developer tools (`F12`) → **Console** tab
3. Run:

```js
fetch('/apps/twc-sdk/auth/token')
  .then(r => r.json())
  .then(console.log)
```

#### Logged-in customer test

1. Log in as a customer on the store
2. Repeat the same `fetch` command above

#### Expected response (both tests)

```json
{
  "success": true,
  "data": {
    "access_token": "eyJ...",
    "shop": "abc123"
  }
}
```

#### If a test fails, check:

* The Proxy App is installed on the store
* The secret key is configured in The Wishlist backend (ask us to validate)

***

### Step 5: Integrate with the TWC SDK&#x20;

#### Proxy Endpoint

```
https://{store-name}.myshopify.com/apps/twc-sdk/auth/token
```

#### Integration Flow

The integration uses a Shopify App Proxy endpoint to obtain a TWC access token for the retailer tenant.

1. The TWC SDK sends a request to the proxy endpoint.
2. The Proxy App validates the request.
3. The Proxy App returns an access token.
4. The TWC SDK uses the access token to call TWC APIs.

***

#### Summary Flow

```
Guest or logged in customer opens the storefront widget
      ↓
The widget or SDK requests a token through the store's app proxy
      ↓
TWC validates the Shopify proxy signature and resolves the configured retailer tenant
      ↓
The proxy returns the tenant-scoped TWC access token
      ↓
The widget or SDK uses the token to submit the Customer Interest request
```

***

#### Sequence

```
1. TWC SDK requests an access token from:
   https://{store-name}.myshopify.com/apps/twc-sdk/auth/token
2. Proxy App validates the Shopify App Proxy request.
3. Proxy App authenticates with TWC.
4. Proxy App returns a tenant-scoped access token.
5. TWC SDK uses the token to call TWC APIs.
```

***

### Proxy Response Contract: `GET /auth/token`

#### Overview

This API is a Shopify App Proxy endpoint that:

1. Validates the Shopify App Proxy signature.
2. Authenticates with TWC / The Wishlist via a Keycloak `client_credentials` grant.
3. Returns a tenant-scoped access token.

The browser requests another access token through the proxy when needed; it does not use an OAuth refresh-token grant. The proxy backend manages its tenant-token cache and renewal.

***

#### Response Envelope

All responses follow this envelope:

```json
{
  "code": <int>,
  "data": <object|null>,
  "message": "<string>",
  "detail_error": <object|null>,
  "success": <boolean>
}
```

***

#### Success Response

**HTTP Status:** `200 OK`

```json
{
  "code": 200,
  "data": {
    "shop": "shop-hash-value",
    "access_token": "eyJhbGciOiJSUzI1NiIsInR5c..."
  },
  "message": "successful",
  "detail_error": null,
  "success": true
}
```

#### `data` Fields

| Field          | Type     | Description                                               |
| -------------- | -------- | --------------------------------------------------------- |
| `shop`         | `string` | Normalized shop hash.                                     |
| `access_token` | `string` | JWT access token from TWC Keycloak, scoped to the tenant. |

> \[!TIP]\
> The `access_token` is obtained via the Keycloak `client_credentials` OAuth2 grant. It is cached per tenant realm and auto-refreshed before expiry by a background goroutine.

***

### Error Responses

#### 1. Missing or Invalid Query Parameters

**HTTP Status:** `422 Unprocessable Entity`

#### Trigger

One or more required query parameters is missing or fails binding validation.

Required parameters:

* `shop`
* `timestamp`
* `signature`

#### Example Response

```json
{
  "code": 422,
  "data": null,
  "message": "Invalid request body!",
  "detail_error": {
    "detail error": "Key: 'GetTWCAccessTokenParams.Shop' Error:Field validation..."
  },
  "success": false
}
```

***

#### 2. General Failure

**HTTP Status:** `403 Forbidden`

**Trigger**

Any error returned from the gRPC service layer.

This includes multiple sub-cases such as:

* Invalid shop hash.
* Invalid Shopify proxy signature.
* Missing tenant configuration.
* Keycloak authentication or configuration failure.

#### Example Response

```json
{
  "code": 403,
  "data": null,
  "message": "GET TWC Access token fail!",
  "detail_error": {
    "detail error": "<gRPC error message>"
  },
  "success": false
}
```

***

#### gRPC Error Sub-Cases

<table><thead><tr><th width="215.90625">gRPC Code</th><th width="233.88671875">Error Message</th><th>Trigger</th></tr></thead><tbody><tr><td><code>INTERNAL</code></td><td><code>Failed to verify proxy signature!</code></td><td>HMAC signature mismatch, usually caused by an invalid <code>signature</code> parameter.</td></tr><tr><td><code>INVALID_ARGUMENT</code></td><td><code>Cannot get tenant config</code></td><td>Tenant configuration was not found for the shop.</td></tr><tr><td><code>INTERNAL</code></td><td><code>Failed to create tenant client: ...</code></td><td>Authentication or tenant configuration error.</td></tr></tbody></table>

> \[!WARNING]\
> All gRPC-layer errors are flattened to HTTP `403` by the controller. The original gRPC error code is lost. Only the error message string is preserved in `detail_error`.

***

### Support

* **Email:** <support@thewishlist.io>
* **Documentation:** Contact your TWC account manager
