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

# API client

> Reference for the Swish API client - query and mutate saved items, lists, orders, notifications, and smart lists.

The `swish.api` namespace provides a typed client for the Swish REST API. It handles authentication, caching, and request batching automatically. All methods return promises that resolve to an `ApiResponse` or `PaginatedApiResponse`.

```js theme={null}
const items = await swish.api.items.list();
const list = await swish.api.lists.findById("list_abc123");
```

## Response types

### `ApiResponse`

A discriminated union — a response contains either `data` or `error`, never both.

```ts theme={null}
type ApiResponse<T> = { data: T | null } | { error: ApiError };
```

Use `"error" in response` to narrow the type:

```ts theme={null}
const response = await swish.api.items.findById("item_abc123");

if ("error" in response) {
  console.error(response.error.message);
  return;
}

console.log(response.data);
```

### `PaginatedApiResponse`

```ts theme={null}
type PaginatedApiResponse<T> =
  | { data: T[]; pageInfo: PageInfo }
  | { error: ApiError };
```

The `PageInfo` object contains:

| Property | Type | Description |
| - | - | - |
| `next` | `string \| null` | Cursor for the next page of results. |
| `previous` | `string \| null` | Cursor for the previous page. |
| `totalCount` | `number` | Total number of items across all pages. |

> [!NOTE]
>
> When no customer profile exists (e.g. a guest visitor), list methods return
> empty arrays and find methods return `null` instead of throwing.

## Query parameters

List methods accept an optional `query` object for pagination and filtering. Every list method supports these shared parameters:

| Property | Type | Required | Description |
| - | - | - | - |
| `page` | `string` | No | Pagination cursor, taken from `pageInfo.next`. |
| `limit` | `number` | No | Number of results to return. |

Some methods accept additional filters, documented alongside each method below.

## Items

### `items.list(query?)`

List saved items.

| Parameter | Type | Required | Description |
| - | - | - | - |
| `query` | `object` | No | [Query parameters](#query-parameters) and filters. |

In addition to the shared `page` and `limit`, the `query` object accepts:

| Property | Type | Required | Description |
| - | - | - | - |
| `query` | `string` | No | Item search query. |
| `listId` | `string` | No | Filter items to a specific list. |
| `sort` | `'recently_saved' \| 'custom'` | No | Sort order of the items. |

```js theme={null}
const items = await swish.api.items.list({
  limit: 10,
  query: "product:8234567890123",
});
```

Returns [`PaginatedApiResponse`](#paginatedapiresponse)\<[`Item`](#item)>.

Saved items carry only Shopify IDs (`productId`/`variantId`), not product details like title, price, or images. To render them — for a custom wishlist page, say — enrich each item with Shopify product data using [`swish.storefront.loadResolvedProduct`](https://swish.app/docs/references/sdk/storefront#loadresolvedproductargs). You can pass an item straight through; it reads `productId`/`variantId` and ignores the rest.

Fire the calls concurrently with `Promise.all`, not in a sequential `for` loop. Products are cached per product, and concurrent cache-miss fetches are coalesced into batched Storefront requests — so a parallel map doesn't pay one request per item, while a sequential loop would. Keep each `item` alongside its resolved product so you still have `item.id` (to unsave), `item.quantity`, and other fields when rendering:

```js theme={null}
const { data: items } = await swish.api.items.list({ limit: 24 });

const entries = await Promise.all(
  items.map(async (item) => ({
    item,
    ...(await swish.storefront.loadResolvedProduct(item)),
  })),
);

for (const { item, product, variant } of entries) {
  console.log(item, product, variant);
}
```

### `items.findById(itemId)`

Find a saved item by ID.

| Parameter | Type | Required | Description |
| - | - | - | - |
| `itemId` | `string` | Yes | The saved item ID. |

Returns [`ApiResponse`](#apiresponse)\<[`ItemDetail`](#itemdetail)>.

### `items.count()`

Get the total number of saved items.

```js theme={null}
const response = await swish.api.items.count();
console.log(response.data.count);
```

Returns [`ApiResponse`](#apiresponse)\<`{ count: number }`>.

### `items.create(item)`

Save a product as a new item.

| Parameter | Type | Required | Description |
| - | - | - | - |
| `item` | `CreateItem` | Yes | The product to save. |

The `CreateItem` object accepts:

| Property | Type | Required | Description |
| - | - | - | - |
| `productId` | `number` | Yes | The Shopify product ID. |
| `variantId` | `number` | No | The Shopify variant ID (required when `source` is `"cart"`). |
| `quantity` | `number` | No | The quantity of the item. |
| `tags` | `string[]` | No | Tags for the item (lowercase letters, numbers, hyphens, or underscores only). |
| `source` | `"cart"` | No | Source of the save. Use `"cart"` for items saved from cart. |

```js theme={null}
await swish.api.items.create({ productId: 8234567890123 });
```

Returns [`ApiResponse`](#apiresponse)\<[`Item`](#item)>.

### `items.updateById(itemId, body)`

Update a saved item.

| Parameter | Type | Required | Description |
| - | - | - | - |
| `itemId` | `string` | Yes | The saved item ID. |
| `body` | `UpdateItem` | Yes | Fields to update. |

The `UpdateItem` object accepts `productId`, `variantId`, and `quantity`, all optional.

```js theme={null}
await swish.api.items.updateById("item_abc123", { quantity: 2 });
```

Returns [`ApiResponse`](#apiresponse)\<[`Item`](#item)>.

### `items.deleteById(itemId)`

Delete a single saved item.

| Parameter | Type | Required | Description |
| - | - | - | - |
| `itemId` | `string` | Yes | The saved item ID. |

Returns [`ApiResponse`](#apiresponse)\<[`Item`](#item)> containing the deleted item.

### `items.delete(itemIds)`

Delete multiple saved items in one request.

| Parameter | Type | Required | Description |
| - | - | - | - |
| `itemIds` | `string[]` | Yes | IDs of the items to delete. |

```js theme={null}
await swish.api.items.delete(["item_abc123", "item_def456"]);
```

Returns [`ApiResponse`](#apiresponse)\<[`Item`](#item)\[]> containing the deleted items.

### `items.setListsById(itemId, listIds)`

Set which lists an item belongs to, replacing any previous list assignments.

| Parameter | Type | Required | Description |
| - | - | - | - |
| `itemId` | `string` | Yes | The saved item ID. |
| `listIds` | `string[]` | Yes | The IDs of the lists the item belongs to. |

```js theme={null}
await swish.api.items.setListsById("item_abc123", ["list_abc123"]);
```

Returns [`ApiResponse`](#apiresponse)\<[`ItemDetail`](#itemdetail)>.

## Lists

### `lists.list(query?)`

List all lists.

| Parameter | Type | Required | Description |
| - | - | - | - |
| `query` | `object` | No | [Query parameters](#query-parameters) and filters. |

In addition to the shared `page` and `limit`, the `query` object accepts:

| Property | Type | Required | Description |
| - | - | - | - |
| `sort` | `'recently_updated' \| 'custom'` | No | Sort order of the lists. |

```js theme={null}
const lists = await swish.api.lists.list();
```

Returns [`PaginatedApiResponse`](#paginatedapiresponse)\<[`ListDetail`](#listdetail)>.

### `lists.findById(listId)`

Find a list by ID.

| Parameter | Type | Required | Description |
| - | - | - | - |
| `listId` | `string` | Yes | The list ID. |

Returns [`ApiResponse`](#apiresponse)\<[`ListDetail`](#listdetail)>.

### `lists.create(list)`

Create a new list, optionally with items.

| Parameter | Type | Required | Description |
| - | - | - | - |
| `list` | `CreateList` | Yes | The list to create. |

The `CreateList` object accepts:

| Property | Type | Required | Description |
| - | - | - | - |
| `name` | `string` | Yes | The name of the list. |
| `items` | `CreateItem[]` | No | Items to add to the list when creating it. |

```js theme={null}
await swish.api.lists.create({ name: "Birthday" });
```

Returns [`ApiResponse`](#apiresponse)\<[`List`](#list)>.

### `lists.updateById(listId, body)`

Update a list's name or access level.

| Parameter | Type | Required | Description |
| - | - | - | - |
| `listId` | `string` | Yes | The list ID. |
| `body` | `UpdateList` | Yes | Fields to update. |

The `UpdateList` object accepts:

| Property | Type | Required | Description |
| - | - | - | - |
| `name` | `string` | No | The name of the list. |
| `access` | `"private" \| "public"` | No | The access level of the list. |

```js theme={null}
await swish.api.lists.updateById("list_abc123", { access: "public" });
```

Returns [`ApiResponse`](#apiresponse)\<[`List`](#list)>.

### `lists.deleteById(listId)`

Delete a list.

| Parameter | Type | Required | Description |
| - | - | - | - |
| `listId` | `string` | Yes | The list ID. |

Returns [`ApiResponse`](#apiresponse)\<[`List`](#list)> containing the deleted list.

### `lists.addItemsToList(listId, itemIds)`

Add existing items to a list by their IDs.

| Parameter | Type | Required | Description |
| - | - | - | - |
| `listId` | `string` | Yes | The list ID. |
| `itemIds` | `string[]` | Yes | IDs of the items to add. |

```js theme={null}
await swish.api.lists.addItemsToList("list_abc123", ["item_abc123"]);
```

Returns [`ApiResponse`](#apiresponse)\<[`ListDetail`](#listdetail)>.

### `lists.removeItemFromList(listId, itemId)`

Remove an item from a list without deleting the item itself.

| Parameter | Type | Required | Description |
| - | - | - | - |
| `listId` | `string` | Yes | The list ID. |
| `itemId` | `string` | Yes | The saved item ID. |

Returns [`ApiResponse`](#apiresponse)\<`void`>.

### `lists.orderItems(listId, itemIds)`

Set the order of items within a list.

| Parameter | Type | Required | Description |
| - | - | - | - |
| `listId` | `string` | Yes | The list ID. |
| `itemIds` | `string[]` | Yes | The item IDs in their desired order. |

```js theme={null}
await swish.api.lists.orderItems("list_abc123", ["item_def456", "item_abc123"]);
```

Returns [`ApiResponse`](#apiresponse)\<[`List`](#list)>.

## Orders

### `orders.list(query?)`

List orders.

| Parameter | Type | Required | Description |
| - | - | - | - |
| `query` | `object` | No | [Query parameters](#query-parameters) (`page`, `limit`). |

Returns [`PaginatedApiResponse`](#paginatedapiresponse)\<[`Order`](#order)>.

### `orders.findById(orderId)`

Find an order by ID.

| Parameter | Type | Required | Description |
| - | - | - | - |
| `orderId` | `number` | Yes | The order ID. |

Returns [`ApiResponse`](#apiresponse)\<[`OrderDetail`](#orderdetail)>.

## Notifications

> [!NOTE]
>
> Notification methods require a signed-in customer. They return empty results
> for guest visitors.

### `notifications.list(query?)`

List notifications.

| Parameter | Type | Required | Description |
| - | - | - | - |
| `query` | `object` | No | [Query parameters](#query-parameters) and filters. |

In addition to the shared `page` and `limit`, the `query` object accepts:

| Property | Type | Required | Description |
| - | - | - | - |
| `read` | `boolean` | No | Filter by read status. Use `read: false` for unread notifications. |
| `type` | `string` | No | Filter by [notification type](#notification). |

Returns [`PaginatedApiResponse`](#paginatedapiresponse)\<[`Notification`](#notification)>.

### `notifications.getUnreadCount()`

Get the number of unread notifications.

```js theme={null}
const response = await swish.api.notifications.getUnreadCount();
console.log(response.data.count);
```

Returns [`ApiResponse`](#apiresponse)\<`{ count: number }`>.

### `notifications.markAsRead(notificationIds)`

Mark specific notifications as read.

| Parameter | Type | Required | Description |
| - | - | - | - |
| `notificationIds` | `string[]` | Yes | IDs of notifications to mark as read. |

### `notifications.markAllAsRead()`

Mark all notifications as read.

### `notifications.deleteById(id)`

Delete a notification.

| Parameter | Type | Required | Description |
| - | - | - | - |
| `id` | `string` | Yes | The notification ID. |

## Smart lists

Smart lists are system-managed lists like "Recently viewed".

### `smartLists.list()`

List all smart lists.

```js theme={null}
const response = await swish.api.smartLists.list();
```

Returns [`ApiResponse`](#apiresponse)\<[`ListDetail`](#listdetail)\[]>.

### `smartLists.findById(smartListId)`

Find a smart list by ID.

| Parameter | Type | Required | Description |
| - | - | - | - |
| `smartListId` | `string` | Yes | The smart list ID (e.g. `"recently-viewed"`). |

> [!NOTE]
>
> Smart lists (`"recently-viewed"` and `"saved-from-cart"`) do not require a
> customer profile.

Returns [`ApiResponse`](#apiresponse)\<[`ListDetail`](#listdetail)>.

### `smartLists.findItems(smartListId, query?)`

List items in a smart list.

| Parameter | Type | Required | Description |
| - | - | - | - |
| `smartListId` | `string` | Yes | The smart list ID. |
| `query` | `object` | No | [Query parameters](#query-parameters) (`page`, `limit`). |

Returns [`PaginatedApiResponse`](#paginatedapiresponse)\<[`Item`](#item)>.

### `smartLists.removeItem(smartListId, itemId)`

Remove an item from a smart list.

| Parameter | Type | Required | Description |
| - | - | - | - |
| `smartListId` | `"recently-viewed" \| "saved-from-cart"` | Yes | The smart list to remove the item from. |
| `itemId` | `string` | Yes | The ID of the item to remove. |

```js theme={null}
await swish.api.smartLists.removeItem("saved-from-cart", itemId);
```

Returns [`ApiResponse`](#apiresponse)\<[`Item`](#item)> containing the removed item.

## Profiles

> [!NOTE]
>
> Profile methods require a signed-in customer. `getProfile` returns `null` for
> guest visitors.

### `profiles.getProfile()`

Get the current customer's profile.

```js theme={null}
const response = await swish.api.profiles.getProfile();

if (response.data) {
  console.log(response.data.displayName);
  console.log(response.data.email);
}
```

Returns [`ApiResponse`](#apiresponse)\<[`Profile`](#profile)>.

### `profiles.updateProfile(body)`

Update the current customer's profile.

| Parameter | Type | Required | Description |
| - | - | - | - |
| `body` | `UpdateProfile` | Yes | Fields to update on the profile. |

The `UpdateProfile` object accepts:

| Property | Type | Required | Description |
| - | - | - | - |
| `firstName` | `string` | No | First name. |
| `lastName` | `string` | No | Last name. |
| `email` | `string` | No | Email address. |
| `phone` | `string` | No | Phone number. |
| `about` | `string` | No | About text. |
| `birthdate` | `string \| null` | No | Date of birth (ISO 8601 date). Send `null` to clear. |
| `notifications` | [`NotificationSettings`](#notificationsettings) | No | Notification preferences. |
| `marketingConsent` | [`MarketingConsent`](#marketingconsent) | No | Marketing consent preferences. |

```js theme={null}
await swish.api.profiles.updateProfile({
  firstName: "Jane",
  lastName: "Doe",
  notifications: {
    priceDrop: true,
    backInStock: true,
    lowInStock: false,
  },
});
```

## Subscriptions

Back-in-stock subscriptions: a shopper asks to hear when a sold-out variant returns.

> [!NOTE]
>
> `create` works for guest visitors — the SDK issues a session token before the
> request, so a shopper who has never signed in can still subscribe. The reads
> need a profile, which the first write mints, and return empty until then.

Subscriptions are **variant-level**: both `productId` and `variantId` are required, and there is no product-wide subscription.

### `subscriptions.list(query?)`

List the current shopper's subscriptions.

| Parameter | Type | Required | Description |
| - | - | - | - |
| `query` | `object` | No | [Query parameters](#query-parameters) and filters. |

In addition to the shared `page` and `limit`, the `query` object accepts:

| Property | Type | Required | Description |
| - | - | - | - |
| `status` | `string` | No | Filter by status — `active` or `cancelled`. |
| `type` | `string` | No | Filter by subscription type, e.g. `back_in_stock`. |
| `productId` | `string` | No | Filter by Shopify product ID. |

Returns [`PaginatedApiResponse`](#paginatedapiresponse)\<[`Subscription`](#subscription)>.

### `subscriptions.create(body)`

Subscribe a contact to a variant.

| Parameter | Type | Required | Description |
| - | - | - | - |
| `body` | [`CreateSubscription`](#createsubscription) | Yes | The subscription to create. |

```js theme={null}
const response = await swish.api.subscriptions.create({
  contact: "shopper@example.com",
  productId: "8375112",
  variantId: "44921038",
  type: "back_in_stock",
  channel: "email",
});

if ("error" in response) {
  console.warn(response.error.message);
}
```

Returns [`ApiResponse`](#apiresponse)\<[`Subscription`](#subscription)>.

### `subscriptions.findById(id)`

Get one subscription.

| Parameter | Type | Required | Description |
| - | - | - | - |
| `id` | `string` | Yes | The subscription ID. |

Returns [`ApiResponse`](#apiresponse)\<[`Subscription`](#subscription)>.

### `subscriptions.updateById(id, body)`

Update a subscription — most usefully to cancel it by setting `status`.

| Parameter | Type | Required | Description |
| - | - | - | - |
| `id` | `string` | Yes | The subscription ID. |
| `body` | `UpdateSubscription` | Yes | Fields to update on the subscription. |

### `subscriptions.deleteById(id)`

Delete a subscription.

| Parameter | Type | Required | Description |
| - | - | - | - |
| `id` | `string` | Yes | The subscription ID. |

## Data types

The shapes returned by `swish.api`. For the exhaustive field-level schema of every response — including the deeply nested objects noted below — see the [REST API reference](https://swish.app/docs/references/api/overview.md).

### `Item`

| Property | Type | Description |
| - | - | - |
| `id` | `string` | The item ID. |
| `productId` | `number` | The Shopify product ID. |
| `variantId` | `number \| null` | The Shopify variant ID. |
| `quantity` | `number` | The quantity of the item. |
| `createdAt` | `string` | ISO 8601 timestamp when the item was created. |
| `updatedAt` | `string` | ISO 8601 timestamp when the item was updated. |
| `metadata` | `object \| null` | Optional item metadata. |

### `ItemDetail`

All [`Item`](#item) fields, plus:

| Property | Type | Description |
| - | - | - |
| `listIds` | `string[]` | IDs of the lists the item belongs to. |

### `List`

| Property | Type | Description |
| - | - | - |
| `id` | `string` | The list ID. |
| `name` | `string` | The name of the list. |
| `createdAt` | `string` | ISO 8601 timestamp when the list was created. |
| `updatedAt` | `string` | ISO 8601 timestamp when the list was updated. |
| `access` | `'private' \| 'public'` | The access level of the list. |
| `type` | `'user' \| 'smart' \| 'shared'` | The type of the list. |

### `ListDetail`

All [`List`](#list) fields, plus:

| Property | Type | Description |
| - | - | - |
| `itemCount` | `number` | The number of items in the list. |
| `previewImages` | [`PreviewImage`](#previewimage)`[]` | Preview images for the first few items. |
| `itemIds` | `string[]` | IDs of the items in the list. |

### `PreviewImage`

| Property | Type | Description |
| - | - | - |
| `id` | `string` | Unique identifier. |
| `altText` | `string` | Alt text for the image. |
| `url` | `string` | URL of the image. |
| `thumbhash` | `string` | Thumbhash placeholder hash. |

### `Order`

| Property | Type | Description |
| - | - | - |
| `id` | `string` | The order ID. |
| `name` | `string` | The order number. |
| `processedAt` | `string` | ISO 8601 timestamp when processed. |
| `displayStatus` | `'FULFILLED' \| 'IN_PROGRESS' \| 'PARTIALLY_FULFILLED' \| 'UNFULFILLED'` | Human-readable display status. |
| `totalQuantity` | `number` | Total quantity of items in the order. |
| `totalPrice` | [`Money`](#money) | Total price of the order. |
| `previewImages` | [`PreviewImage`](#previewimage)`[]` | Preview images from the order line items. |

### `OrderDetail`

All [`Order`](#order) fields (except `previewImages`), plus:

| Property | Type | Description |
| - | - | - |
| `fulfillments` | `Fulfillment[]` | Fulfillments for the order. |
| `unfulfilledLineItems` | `OrderLineItem[]` | Line items not yet fulfilled. |

The `Fulfillment` and `OrderLineItem` shapes are large; see the [REST API reference](https://swish.app/docs/references/api/overview.md) for their full fields.

### `Money`

| Property | Type | Description |
| - | - | - |
| `currencyCode` | `string` | The currency code. |
| `amount` | `number` | The amount. |

### `Notification`

| Property | Type | Description |
| - | - | - |
| `id` | `string` | The notification ID. |
| `type` | `string` | Notification type, e.g. `'saved_product_price_drop'`, `'saved_product_back_in_stock'`, `'saved_product_low_in_stock'` (and `subscribed_*` variants). Use it to look up a localized title and message on the client. |
| `product` | `NotificationProduct` | The product associated with the notification. |
| `variant` | `NotificationVariant \| null` | The variant associated with the notification. |
| `context` | `NotificationContext \| null` | Localization context (market, country, language). |
| `metadata` | `object \| null` | Notification metadata. |
| `read` | `boolean` | Whether the notification has been read. |
| `createdAt` | `string` | ISO 8601 timestamp when the notification was created. |
| `updatedAt` | `string` | ISO 8601 timestamp when the notification was updated. |

The `NotificationProduct`, `NotificationVariant`, and `NotificationContext` shapes are large; see the [REST API reference](https://swish.app/docs/references/api/overview.md) for their full fields.

### `Profile`

| Property | Type | Description |
| - | - | - |
| `id` | `string` | The customer GID. |
| `displayName` | `string` | Display name (defaults to Shopify name). |
| `firstName` | `string \| null` | First name. |
| `lastName` | `string \| null` | Last name. |
| `email` | `string \| null` | Email address. |
| `phone` | `string \| null` | Phone number. |
| `about` | `string` | About text. |
| `birthdate` | `string \| null` | Date of birth (ISO 8601 date). |
| `defaultAddress` | [`Address`](#address)` \| null` | Default address. |
| `addresses` | [`Address`](#address)`[]` | All customer addresses (up to 10). |
| `notifications` | [`NotificationSettings`](#notificationsettings) | Notification preferences. |
| `marketingConsent` | [`MarketingConsent`](#marketingconsent) | Marketing consent preferences. |

### `Address`

| Property | Type | Description |
| - | - | - |
| `id` | `string` | Address GID. |
| `address1` | `string \| null` | Address line 1. |
| `address2` | `string \| null` | Address line 2. |
| `city` | `string \| null` | City. |
| `province` | `string \| null` | Province/state. |
| `country` | `string \| null` | Country. |
| `zip` | `string \| null` | Zip/postal code. |
| `phone` | `string \| null` | Phone number. |
| `formattedArea` | `string \| null` | Formatted area (city, province, country). |

### `NotificationSettings`

| Property | Type | Description |
| - | - | - |
| `priceDrop` | `boolean` | Price-drop notifications enabled. |
| `backInStock` | `boolean` | Back-in-stock notifications enabled. |
| `lowInStock` | `boolean` | Low-in-stock notifications enabled. |

### `MarketingConsent`

| Property | Type | Description |
| - | - | - |
| `email` | `boolean` | Email marketing consent. |
| `phone` | `boolean` | Phone marketing consent. |

### `Subscription`

| Property | Type | Description |
| - | - | - |
| `id` | `string` | The subscription ID. |
| `contact` | `string` | The email or phone number to notify. |
| `productId` | `string` | The Shopify product ID. |
| `variantId` | `string` | The Shopify variant ID. |
| `type` | `string` | `back_in_stock`. |
| `channel` | `string` | `email`, `sms` or `whatsapp`. |
| `status` | `string` | `active` or `cancelled`. |
| `createdAt` | `string` | When the subscription was created (ISO 8601). |

### `CreateSubscription`

| Property | Type | Required | Description |
| - | - | - | - |
| `contact` | `string` | Yes | The email or phone number to notify. |
| `productId` | `string` | Yes | The Shopify product ID. |
| `variantId` | `string` | Yes | The Shopify variant ID. Subscriptions are variant-level. |
| `type` | `string` | Yes | `back_in_stock`. |
| `channel` | `string` | Yes | `email`, `sms` or `whatsapp`. |
| `marketContextType` | `string` | No | `country`. |
| `marketContextValue` | `string` | No | The market's country code, e.g. `IE`. |

## Authentication

### `setAuthToken(token)`

Manually set the auth token. Useful for headless environments where you manage authentication yourself.

| Parameter | Type | Required | Description |
| - | - | - | - |
| `token` | `string` | Yes | The auth token. |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.