# Commerce API

> Reference for the 22 public v1 operations of the Commerce API, generated from the OpenAPI spec.

Source: https://docs.nexafone.so/v1/api/commerce/

Generated from `api/openapi.public.yaml`, the same spec the API serves. Try these operations in the [API console](/v1/api/console/); errors are [Problem Details](/v1/errors/).

## List the store's product categories in order

`GET /api/v1/workspaces/{workspaceId}/commerce/categories`

| | |
| --- | --- |
| Operation id | `listProductCategories` |
| Authentication | A secret key (`sk_live_…` or `sk_test_…`), an SDK session token (`sdk_…`), or a secret partner key for one of the partner’s stores. |
| Permission (key scope) | `commerce:manage` |
| Plan feature | `commerce.catalog` |

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `workspaceId` | path | string | yes | The store's workspace id (UUID) |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| 200 | OK | `application/json`: [CategoriesBody](/v1/api/schemas/#categoriesbody) |
| default | Error | `application/problem+json`: [Problem](/v1/api/schemas/#problem) |

### Example

```bash
curl -X GET "https://api.nexafone.so/api/v1/workspaces/{workspaceId}/commerce/categories" \
  -H "Authorization: Bearer $NEXAFON_SECRET_KEY"
```

## Create or rename categories and set their order

`PUT /api/v1/workspaces/{workspaceId}/commerce/categories`

| | |
| --- | --- |
| Operation id | `upsertProductCategories` |
| Authentication | A secret key (`sk_live_…` or `sk_test_…`), an SDK session token (`sdk_…`), or a secret partner key for one of the partner’s stores. |
| Permission (key scope) | `commerce:manage` |
| Plan feature | `commerce.catalog` |

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `workspaceId` | path | string | yes | The store's workspace id (UUID) |

### Request body

`application/json`: [CategoriesInputBody](/v1/api/schemas/#categoriesinputbody)

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `categories` | array of [CategoryInputBody](/v1/api/schemas/#categoryinputbody) or null | yes | In display order; categories left out keep their names and follow these (min 1 items, max 500 items) |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| 200 | OK | `application/json`: [CategoriesBody](/v1/api/schemas/#categoriesbody) |
| 422 | Unprocessable Entity | `application/problem+json`: [Problem](/v1/api/schemas/#problem) |
| 500 | Internal Server Error | `application/problem+json`: [Problem](/v1/api/schemas/#problem) |

### Example

```bash
curl -X PUT "https://api.nexafone.so/api/v1/workspaces/{workspaceId}/commerce/categories" \
  -H "Authorization: Bearer $NEXAFON_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{"categories":[{"externalId":null,"name":null}]}'
```

## Get the store's delivery areas, fees and customer updates

`GET /api/v1/workspaces/{workspaceId}/commerce/delivery`

| | |
| --- | --- |
| Operation id | `getDeliverySettings` |
| Authentication | A secret key (`sk_live_…` or `sk_test_…`), an SDK session token (`sdk_…`), or a secret partner key for one of the partner’s stores. |
| Permission (key scope) | `commerce:manage` |
| Plan feature | `commerce.orders` |

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `workspaceId` | path | string | yes | The store's workspace id (UUID) |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| 200 | OK | `application/json`: [DeliveryBody](/v1/api/schemas/#deliverybody) |
| default | Error | `application/problem+json`: [Problem](/v1/api/schemas/#problem) |

### Example

```bash
curl -X GET "https://api.nexafone.so/api/v1/workspaces/{workspaceId}/commerce/delivery" \
  -H "Authorization: Bearer $NEXAFON_SECRET_KEY"
```

## Replace the store's delivery areas, fees and customer updates

`PUT /api/v1/workspaces/{workspaceId}/commerce/delivery`

Orders already placed keep the area name and fee they were priced with. Delivery needs at least one active area.

| | |
| --- | --- |
| Operation id | `saveDeliverySettings` |
| Authentication | A secret key (`sk_live_…` or `sk_test_…`), an SDK session token (`sdk_…`), or a secret partner key for one of the partner’s stores. |
| Permission (key scope) | `commerce:manage` |
| Plan feature | `commerce.orders` |

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `workspaceId` | path | string | yes | The store's workspace id (UUID) |

### Request body

`application/json`: [DeliverySettingsBody](/v1/api/schemas/#deliverysettingsbody)

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `areas` | array of [DeliveryAreaBody](/v1/api/schemas/#deliveryareabody) | yes | In the order checkout lists them; an area left out is removed (max 100 items) |
| `deliveryEnabled` | boolean | yes |  |
| `freeDeliveryAboveCents` | integer (int64) |  | Delivery is free from this order amount (after discounts) in cents; never free when absent (min 0, max 100000000000) |
| `notifyStatuses` | array of string | yes | Order statuses the customer gets a WhatsApp message for: awaiting_payment, paid, preparing, out_for_delivery, delivered, cancelled, refunded (max 7 items) |
| `pickupAddress` | string | yes | Where pickup orders are collected; shown to the customer (max length 500) |
| `pickupEnabled` | boolean | yes |  |
| `requestLocation` | boolean | yes | Ask the customer for a location pin after checkout when they chose delivery |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| 200 | OK | `application/json`: [DeliveryBody](/v1/api/schemas/#deliverybody) |
| 422 | Unprocessable Entity | `application/problem+json`: [Problem](/v1/api/schemas/#problem) |
| 500 | Internal Server Error | `application/problem+json`: [Problem](/v1/api/schemas/#problem) |

### Example

```bash
curl -X PUT "https://api.nexafone.so/api/v1/workspaces/{workspaceId}/commerce/delivery" \
  -H "Authorization: Bearer $NEXAFON_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{"areas":[{"active":null,"feeCents":null,"name":null}],"deliveryEnabled":true,"notifyStatuses":["string"],"pickupAddress":"string","pickupEnabled":true,"requestLocation":true}'
```

## List the store's number lists

`GET /api/v1/workspaces/{workspaceId}/commerce/number-lists`

| | |
| --- | --- |
| Operation id | `listNumberLists` |
| Authentication | A secret key (`sk_live_…` or `sk_test_…`), an SDK session token (`sdk_…`), or a secret partner key for one of the partner’s stores. |
| Permission (key scope) | `commerce:manage` |
| Plan feature | `commerce.orders` |

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `workspaceId` | path | string | yes | The store's workspace id (UUID) |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| 200 | OK | `application/json`: [NumberListsBody](/v1/api/schemas/#numberlistsbody) |
| default | Error | `application/problem+json`: [Problem](/v1/api/schemas/#problem) |

### Example

```bash
curl -X GET "https://api.nexafone.so/api/v1/workspaces/{workspaceId}/commerce/number-lists" \
  -H "Authorization: Bearer $NEXAFON_SECRET_KEY"
```

## Create a number list from uploaded numbers or a past-orders rule

`POST /api/v1/workspaces/{workspaceId}/commerce/number-lists`

| | |
| --- | --- |
| Operation id | `createNumberList` |
| Authentication | A secret key (`sk_live_…` or `sk_test_…`), an SDK session token (`sdk_…`), or a secret partner key for one of the partner’s stores. |
| Permission (key scope) | `commerce:manage` |
| Plan feature | `commerce.orders` |

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `workspaceId` | path | string | yes | The store's workspace id (UUID) |

### Request body

`application/json`: [NumberListFields](/v1/api/schemas/#numberlistfields)

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `kind` | `"uploaded"` \| `"past_orders"` | yes | uploaded: the numbers given; past_orders: every customer with at least minOrders paid orders |
| `minOrders` | integer (int32) |  | Required for past_orders (min 1, max 1000) |
| `name` | string | yes | (min length 1, max length 80) |
| `phones` | array of string or null |  | International numbers with their country code, for uploaded (max 10000 items) |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| 201 | Created | `application/json`: [NumberListBody](/v1/api/schemas/#numberlistbody) |
| 409 | Conflict | `application/problem+json`: [Problem](/v1/api/schemas/#problem) |
| 422 | Unprocessable Entity | `application/problem+json`: [Problem](/v1/api/schemas/#problem) |
| 500 | Internal Server Error | `application/problem+json`: [Problem](/v1/api/schemas/#problem) |

### Example

```bash
curl -X POST "https://api.nexafone.so/api/v1/workspaces/{workspaceId}/commerce/number-lists" \
  -H "Authorization: Bearer $NEXAFON_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{"kind":"uploaded","name":"string"}'
```

## Get a number list with its numbers

`GET /api/v1/workspaces/{workspaceId}/commerce/number-lists/{listId}`

| | |
| --- | --- |
| Operation id | `getNumberList` |
| Authentication | A secret key (`sk_live_…` or `sk_test_…`), an SDK session token (`sdk_…`), or a secret partner key for one of the partner’s stores. |
| Permission (key scope) | `commerce:manage` |
| Plan feature | `commerce.orders` |

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `workspaceId` | path | string | yes | The store's workspace id (UUID) |
| `listId` | path | string | yes | Number list id (UUID) |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| 200 | OK | `application/json`: [NumberListBody](/v1/api/schemas/#numberlistbody) |
| 404 | Not Found | `application/problem+json`: [Problem](/v1/api/schemas/#problem) |
| 422 | Unprocessable Entity | `application/problem+json`: [Problem](/v1/api/schemas/#problem) |
| 500 | Internal Server Error | `application/problem+json`: [Problem](/v1/api/schemas/#problem) |

### Example

```bash
curl -X GET "https://api.nexafone.so/api/v1/workspaces/{workspaceId}/commerce/number-lists/{listId}" \
  -H "Authorization: Bearer $NEXAFON_SECRET_KEY"
```

## Replace a number list

`PUT /api/v1/workspaces/{workspaceId}/commerce/number-lists/{listId}`

| | |
| --- | --- |
| Operation id | `updateNumberList` |
| Authentication | A secret key (`sk_live_…` or `sk_test_…`), an SDK session token (`sdk_…`), or a secret partner key for one of the partner’s stores. |
| Permission (key scope) | `commerce:manage` |
| Plan feature | `commerce.orders` |

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `workspaceId` | path | string | yes | The store's workspace id (UUID) |
| `listId` | path | string | yes | Number list id (UUID) |

### Request body

`application/json`: [NumberListFields](/v1/api/schemas/#numberlistfields)

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `kind` | `"uploaded"` \| `"past_orders"` | yes | uploaded: the numbers given; past_orders: every customer with at least minOrders paid orders |
| `minOrders` | integer (int32) |  | Required for past_orders (min 1, max 1000) |
| `name` | string | yes | (min length 1, max length 80) |
| `phones` | array of string or null |  | International numbers with their country code, for uploaded (max 10000 items) |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| 200 | OK | `application/json`: [NumberListBody](/v1/api/schemas/#numberlistbody) |
| 404 | Not Found | `application/problem+json`: [Problem](/v1/api/schemas/#problem) |
| 409 | Conflict | `application/problem+json`: [Problem](/v1/api/schemas/#problem) |
| 422 | Unprocessable Entity | `application/problem+json`: [Problem](/v1/api/schemas/#problem) |
| 500 | Internal Server Error | `application/problem+json`: [Problem](/v1/api/schemas/#problem) |

### Example

```bash
curl -X PUT "https://api.nexafone.so/api/v1/workspaces/{workspaceId}/commerce/number-lists/{listId}" \
  -H "Authorization: Bearer $NEXAFON_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{"kind":"uploaded","name":"string"}'
```

## Delete a number list

`DELETE /api/v1/workspaces/{workspaceId}/commerce/number-lists/{listId}`

Answers 409 promotion_in_use while a promotion uses it.

| | |
| --- | --- |
| Operation id | `deleteNumberList` |
| Authentication | A secret key (`sk_live_…` or `sk_test_…`), an SDK session token (`sdk_…`), or a secret partner key for one of the partner’s stores. |
| Permission (key scope) | `commerce:manage` |
| Plan feature | `commerce.orders` |

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `workspaceId` | path | string | yes | The store's workspace id (UUID) |
| `listId` | path | string | yes | Number list id (UUID) |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| 204 | No Content |  |
| 404 | Not Found | `application/problem+json`: [Problem](/v1/api/schemas/#problem) |
| 409 | Conflict | `application/problem+json`: [Problem](/v1/api/schemas/#problem) |
| 422 | Unprocessable Entity | `application/problem+json`: [Problem](/v1/api/schemas/#problem) |
| 500 | Internal Server Error | `application/problem+json`: [Problem](/v1/api/schemas/#problem) |

### Example

```bash
curl -X DELETE "https://api.nexafone.so/api/v1/workspaces/{workspaceId}/commerce/number-lists/{listId}" \
  -H "Authorization: Bearer $NEXAFON_SECRET_KEY"
```

## List the store's orders

`GET /api/v1/workspaces/{workspaceId}/commerce/orders`

Newest first. Members who do not see every team see the orders of their teams.

| | |
| --- | --- |
| Operation id | `listOrders` |
| Authentication | A secret key (`sk_live_…` or `sk_test_…`), an SDK session token (`sdk_…`), or a secret partner key for one of the partner’s stores. |
| Permission (key scope) | `orders:view` |
| Plan feature | `commerce.orders` |

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `workspaceId` | path | string | yes | The store's workspace id (UUID) |
| `status` | query | array of `"new"` \| `"awaiting_payment"` \| `"paid"` \| `"preparing"` \| `"out_for_delivery"` \| `"delivered"` \| `"cancelled"` \| `"refunded"` or null |  | Only orders in these statuses, comma-separated |
| `fulfilment` | query | `"delivery"` \| `"pickup"` |  |  |
| `phone` | query | string |  | Only the orders of this customer number |
| `number` | query | integer (int64) |  | The order with this number |
| `from` | query | string (date-time) |  | Created at or after |
| `to` | query | string (date-time) |  | Created before |
| `cursor` | query | string |  |  |
| `limit` | query | integer (int64) |  |  |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| 200 | OK | `application/json`: [StoreOrdersBody](/v1/api/schemas/#storeordersbody) |
| 422 | Unprocessable Entity | `application/problem+json`: [Problem](/v1/api/schemas/#problem) |
| 500 | Internal Server Error | `application/problem+json`: [Problem](/v1/api/schemas/#problem) |

### Example

```bash
curl -X GET "https://api.nexafone.so/api/v1/workspaces/{workspaceId}/commerce/orders" \
  -H "Authorization: Bearer $NEXAFON_SECRET_KEY"
```

## Get an order with its items and history

`GET /api/v1/workspaces/{workspaceId}/commerce/orders/{orderId}`

| | |
| --- | --- |
| Operation id | `getOrder` |
| Authentication | A secret key (`sk_live_…` or `sk_test_…`), an SDK session token (`sdk_…`), or a secret partner key for one of the partner’s stores. |
| Permission (key scope) | `orders:view` |
| Plan feature | `commerce.orders` |

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `workspaceId` | path | string | yes | The store's workspace id (UUID) |
| `orderId` | path | string | yes | Order id (UUID) |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| 200 | OK | `application/json`: [StoreOrderBody](/v1/api/schemas/#storeorderbody) |
| 404 | Not Found | `application/problem+json`: [Problem](/v1/api/schemas/#problem) |
| 422 | Unprocessable Entity | `application/problem+json`: [Problem](/v1/api/schemas/#problem) |
| 500 | Internal Server Error | `application/problem+json`: [Problem](/v1/api/schemas/#problem) |

### Example

```bash
curl -X GET "https://api.nexafone.so/api/v1/workspaces/{workspaceId}/commerce/orders/{orderId}" \
  -H "Authorization: Bearer $NEXAFON_SECRET_KEY"
```

## Cancel an order

`POST /api/v1/workspaces/{workspaceId}/commerce/orders/{orderId}/cancel`

Stock is not reserved for orders: a partner rejects an order it cannot fill by cancelling it.

| | |
| --- | --- |
| Operation id | `cancelOrder` |
| Authentication | A secret key (`sk_live_…` or `sk_test_…`), an SDK session token (`sdk_…`), or a secret partner key for one of the partner’s stores. |
| Permission (key scope) | `orders:manage` |
| Plan feature | `commerce.orders` |

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `workspaceId` | path | string | yes | The store's workspace id (UUID) |
| `orderId` | path | string | yes | Order id (UUID) |

### Request body

`application/json`: [OrderCancelBody](/v1/api/schemas/#ordercancelbody)

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `reason` | string |  | Why, such as out of stock (max length 500) |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| 200 | OK | `application/json`: [StoreOrderBody](/v1/api/schemas/#storeorderbody) |
| 404 | Not Found | `application/problem+json`: [Problem](/v1/api/schemas/#problem) |
| 409 | Conflict | `application/problem+json`: [Problem](/v1/api/schemas/#problem) |
| 422 | Unprocessable Entity | `application/problem+json`: [Problem](/v1/api/schemas/#problem) |
| 500 | Internal Server Error | `application/problem+json`: [Problem](/v1/api/schemas/#problem) |

### Example

```bash
curl -X POST "https://api.nexafone.so/api/v1/workspaces/{workspaceId}/commerce/orders/{orderId}/cancel" \
  -H "Authorization: Bearer $NEXAFON_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'
```

## Move an order to preparing, out for delivery, delivered or cancelled

`POST /api/v1/workspaces/{workspaceId}/commerce/orders/{orderId}/status`

Statuses only move forward; 409 order_status_not_allowed otherwise. The customer gets a WhatsApp message for the statuses the store chose, and the store's endpoints get order.status_changed.

| | |
| --- | --- |
| Operation id | `setOrderStatus` |
| Authentication | A secret key (`sk_live_…` or `sk_test_…`), an SDK session token (`sdk_…`), or a secret partner key for one of the partner’s stores. |
| Permission (key scope) | `orders:manage` |
| Plan feature | `commerce.orders` |

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `workspaceId` | path | string | yes | The store's workspace id (UUID) |
| `orderId` | path | string | yes | Order id (UUID) |

### Request body

`application/json`: [OrderStatusBody](/v1/api/schemas/#orderstatusbody)

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `note` | string |  | Kept in the order's history; for cancelled, the reason (max length 500) |
| `status` | `"preparing"` \| `"out_for_delivery"` \| `"delivered"` \| `"cancelled"` | yes | Payment and checkout set the other statuses |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| 200 | OK | `application/json`: [StoreOrderBody](/v1/api/schemas/#storeorderbody) |
| 404 | Not Found | `application/problem+json`: [Problem](/v1/api/schemas/#problem) |
| 409 | Conflict | `application/problem+json`: [Problem](/v1/api/schemas/#problem) |
| 422 | Unprocessable Entity | `application/problem+json`: [Problem](/v1/api/schemas/#problem) |
| 500 | Internal Server Error | `application/problem+json`: [Problem](/v1/api/schemas/#problem) |

### Example

```bash
curl -X POST "https://api.nexafone.so/api/v1/workspaces/{workspaceId}/commerce/orders/{orderId}/status" \
  -H "Authorization: Bearer $NEXAFON_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{"status":"preparing"}'
```

## List the store's products with their WhatsApp catalog status

`GET /api/v1/workspaces/{workspaceId}/commerce/products`

| | |
| --- | --- |
| Operation id | `listProducts` |
| Authentication | A secret key (`sk_live_…` or `sk_test_…`), an SDK session token (`sdk_…`), or a secret partner key for one of the partner’s stores. |
| Permission (key scope) | `commerce:manage` |
| Plan feature | `commerce.catalog` |

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `workspaceId` | path | string | yes | The store's workspace id (UUID) |
| `category` | query | string |  | Only the products of this category's externalId |
| `status` | query | `"queued"` \| `"in_review"` \| `"approved"` \| `"rejected"` \| `"failed"` \| `"inactive"` |  | Only products whose catalog status is this |
| `cursor` | query | string |  | The next value of the previous page |
| `limit` | query | integer (int64) |  |  |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| 200 | OK | `application/json`: [ProductsBody](/v1/api/schemas/#productsbody) |
| default | Error | `application/problem+json`: [Problem](/v1/api/schemas/#problem) |

### Example

```bash
curl -X GET "https://api.nexafone.so/api/v1/workspaces/{workspaceId}/commerce/products" \
  -H "Authorization: Bearer $NEXAFON_SECRET_KEY"
```

## Create or update a product by its external id

`PUT /api/v1/workspaces/{workspaceId}/commerce/products/{externalId}`

Answers 201 when the product is new and 200 otherwise. Sending the same product again changes nothing. Changes reach the store's WhatsApp catalog within a minute. A new product counts against commerce.products.max (403 plan_limit_reached).

| | |
| --- | --- |
| Operation id | `upsertProduct` |
| Authentication | A secret key (`sk_live_…` or `sk_test_…`), an SDK session token (`sdk_…`), or a secret partner key for one of the partner’s stores. |
| Permission (key scope) | `commerce:manage` |
| Plan feature | `commerce.catalog` |

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `workspaceId` | path | string | yes | The store's workspace id (UUID) |
| `externalId` | path | string | yes | The partner's own id for the product |

### Request body

`application/json`: [ProductFields](/v1/api/schemas/#productfields)

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `active` | boolean |  | false keeps the product but takes it out of the WhatsApp catalog; true when absent |
| `availability` | `"in_stock"` \| `"out_of_stock"` |  | (default `"in_stock"`) |
| `categoryExternalId` | string |  | A category upserted before; none when absent (max length 100, pattern `^[A-Za-z0-9._:-]*$`) |
| `currency` | `"USD"` |  | (default `"USD"`) |
| `description` | string |  | (max length 5000) |
| `imageUrls` | array of string or null | yes | HTTPS URLs on the public internet; Meta fetches them, Nexafon does not host them. The first is the main image (min 1 items, max 10 items) |
| `name` | string | yes | (min length 1, max length 200) |
| `priceCents` | integer (int64) | yes | In US cents (min 0, max 100000000000) |
| `saleEndsAt` | string (date-time) |  | Open-ended when absent |
| `salePriceCents` | integer (int64) |  | Below priceCents; shown while the sale window is open (min 0, max 100000000000) |
| `saleStartsAt` | string (date-time) |  | Open from now when absent |
| `stock` | integer (int32) |  | Absent when the partner does not count stock; 0 shows the product out of stock (min 0) |
| `variants` | array of [VariantBody](/v1/api/schemas/#variantbody) or null |  | Sizes or colours, each its own catalog item grouped under the product; the product itself is the one item when absent (max 50 items) |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| 200 | OK | `application/json`: [UpsertResultBody](/v1/api/schemas/#upsertresultbody) |
| 409 | Conflict | `application/problem+json`: [Problem](/v1/api/schemas/#problem) |
| 422 | Unprocessable Entity | `application/problem+json`: [Problem](/v1/api/schemas/#problem) |
| 500 | Internal Server Error | `application/problem+json`: [Problem](/v1/api/schemas/#problem) |

### Example

```bash
curl -X PUT "https://api.nexafone.so/api/v1/workspaces/{workspaceId}/commerce/products/{externalId}" \
  -H "Authorization: Bearer $NEXAFON_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{"imageUrls":["string"],"name":"string","priceCents":0}'
```

## Delete a product

`DELETE /api/v1/workspaces/{workspaceId}/commerce/products/{externalId}`

Its items leave the store's WhatsApp catalog within a minute.

| | |
| --- | --- |
| Operation id | `deleteProduct` |
| Authentication | A secret key (`sk_live_…` or `sk_test_…`), an SDK session token (`sdk_…`), or a secret partner key for one of the partner’s stores. |
| Permission (key scope) | `commerce:manage` |
| Plan feature | `commerce.catalog` |

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `workspaceId` | path | string | yes | The store's workspace id (UUID) |
| `externalId` | path | string | yes | The partner's own id for the product |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| 204 | No Content |  |
| 404 | Not Found | `application/problem+json`: [Problem](/v1/api/schemas/#problem) |
| 422 | Unprocessable Entity | `application/problem+json`: [Problem](/v1/api/schemas/#problem) |
| 500 | Internal Server Error | `application/problem+json`: [Problem](/v1/api/schemas/#problem) |

### Example

```bash
curl -X DELETE "https://api.nexafone.so/api/v1/workspaces/{workspaceId}/commerce/products/{externalId}" \
  -H "Authorization: Bearer $NEXAFON_SECRET_KEY"
```

## Create or update up to 500 products

`POST /api/v1/workspaces/{workspaceId}/commerce/products/batch`

All or nothing: one product that is not valid fails the request and stores none. Each result says whether the product was created, updated or unchanged.

| | |
| --- | --- |
| Operation id | `upsertProducts` |
| Authentication | A secret key (`sk_live_…` or `sk_test_…`), an SDK session token (`sdk_…`), or a secret partner key for one of the partner’s stores. |
| Permission (key scope) | `commerce:manage` |
| Plan feature | `commerce.catalog` |

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `workspaceId` | path | string | yes | The store's workspace id (UUID) |

### Request body

`application/json`: [BatchBody](/v1/api/schemas/#batchbody)

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `products` | array of [ProductUpsertBody](/v1/api/schemas/#productupsertbody) or null | yes | (min 1 items, max 500 items) |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| 200 | OK | `application/json`: [BatchResultBody](/v1/api/schemas/#batchresultbody) |
| 409 | Conflict | `application/problem+json`: [Problem](/v1/api/schemas/#problem) |
| 422 | Unprocessable Entity | `application/problem+json`: [Problem](/v1/api/schemas/#problem) |
| 500 | Internal Server Error | `application/problem+json`: [Problem](/v1/api/schemas/#problem) |

### Example

```bash
curl -X POST "https://api.nexafone.so/api/v1/workspaces/{workspaceId}/commerce/products/batch" \
  -H "Authorization: Bearer $NEXAFON_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{"products":[{"externalId":null,"imageUrls":null,"name":null,"priceCents":null}]}'
```

## List the store's promotions

`GET /api/v1/workspaces/{workspaceId}/commerce/promotions`

| | |
| --- | --- |
| Operation id | `listPromotions` |
| Authentication | A secret key (`sk_live_…` or `sk_test_…`), an SDK session token (`sdk_…`), or a secret partner key for one of the partner’s stores. |
| Permission (key scope) | `commerce:manage` |
| Plan feature | `commerce.orders` |

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `workspaceId` | path | string | yes | The store's workspace id (UUID) |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| 200 | OK | `application/json`: [PromotionsBody](/v1/api/schemas/#promotionsbody) |
| default | Error | `application/problem+json`: [Problem](/v1/api/schemas/#problem) |

### Example

```bash
curl -X GET "https://api.nexafone.so/api/v1/workspaces/{workspaceId}/commerce/promotions" \
  -H "Authorization: Bearer $NEXAFON_SECRET_KEY"
```

## Create a flash sale, a number-list discount or a coupon

`POST /api/v1/workspaces/{workspaceId}/commerce/promotions`

Only the best discount applies to an order unless promotions are combinable. A flash sale reaches the store's WhatsApp catalog as the items' sale price within a minute.

| | |
| --- | --- |
| Operation id | `createPromotion` |
| Authentication | A secret key (`sk_live_…` or `sk_test_…`), an SDK session token (`sdk_…`), or a secret partner key for one of the partner’s stores. |
| Permission (key scope) | `commerce:manage` |
| Plan feature | `commerce.orders` |

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `workspaceId` | path | string | yes | The store's workspace id (UUID) |

### Request body

`application/json`: [PromotionFields](/v1/api/schemas/#promotionfields)

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `active` | boolean |  | true when absent |
| `amountOffCents` | integer (int64) |  | In US cents: per unit for a flash sale, off the products in scope otherwise (min 1, max 100000000000) |
| `categoryExternalIds` | array of string or null |  | The categories' external ids when scope is categories (max 100 items) |
| `code` | string |  | The coupon code; compared without case (max length 32) |
| `combinable` | boolean |  | Adds to the other combinable promotions; otherwise only the best discount applies |
| `endsAt` | string (date-time) |  | Open-ended when absent |
| `kind` | `"flash_sale"` \| `"number_list"` \| `"coupon"` | yes | flash_sale: lowers the price of the products in scope inside its window and shows in the WhatsApp catalog; number_list: a discount for the numbers of a list; coupon: applies when the customer enters its code at checkout |
| `maxPerCustomer` | integer (int32) |  | A coupon's or number-list discount's uses per customer number (min 1) |
| `maxRedemptions` | integer (int32) |  | A coupon's or number-list discount's uses across all customers (min 1) |
| `minSubtotalCents` | integer (int64) |  | Applies only from this cart subtotal in cents (min 0, max 100000000000) |
| `name` | string | yes | (min length 1, max length 120) |
| `numberListId` | string (uuid) |  | Only the numbers of this list get it; required for number_list |
| `percentOff` | integer (int32) |  | Exactly one of percentOff and amountOffCents (min 1, max 100) |
| `productExternalIds` | array of string or null |  | The products' external ids when scope is products (max 500 items) |
| `scope` | `"all"` \| `"products"` \| `"categories"` |  | (default `"all"`) |
| `startsAt` | string (date-time) |  | Open from now when absent |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| 201 | Created | `application/json`: [PromotionBody](/v1/api/schemas/#promotionbody) |
| 409 | Conflict | `application/problem+json`: [Problem](/v1/api/schemas/#problem) |
| 422 | Unprocessable Entity | `application/problem+json`: [Problem](/v1/api/schemas/#problem) |
| 500 | Internal Server Error | `application/problem+json`: [Problem](/v1/api/schemas/#problem) |

### Example

```bash
curl -X POST "https://api.nexafone.so/api/v1/workspaces/{workspaceId}/commerce/promotions" \
  -H "Authorization: Bearer $NEXAFON_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{"kind":"flash_sale","name":"string"}'
```

## Get a promotion

`GET /api/v1/workspaces/{workspaceId}/commerce/promotions/{promotionId}`

| | |
| --- | --- |
| Operation id | `getPromotion` |
| Authentication | A secret key (`sk_live_…` or `sk_test_…`), an SDK session token (`sdk_…`), or a secret partner key for one of the partner’s stores. |
| Permission (key scope) | `commerce:manage` |
| Plan feature | `commerce.orders` |

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `workspaceId` | path | string | yes | The store's workspace id (UUID) |
| `promotionId` | path | string | yes | Promotion id (UUID) |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| 200 | OK | `application/json`: [PromotionBody](/v1/api/schemas/#promotionbody) |
| 404 | Not Found | `application/problem+json`: [Problem](/v1/api/schemas/#problem) |
| 422 | Unprocessable Entity | `application/problem+json`: [Problem](/v1/api/schemas/#problem) |
| 500 | Internal Server Error | `application/problem+json`: [Problem](/v1/api/schemas/#problem) |

### Example

```bash
curl -X GET "https://api.nexafone.so/api/v1/workspaces/{workspaceId}/commerce/promotions/{promotionId}" \
  -H "Authorization: Bearer $NEXAFON_SECRET_KEY"
```

## Replace a promotion

`PUT /api/v1/workspaces/{workspaceId}/commerce/promotions/{promotionId}`

| | |
| --- | --- |
| Operation id | `updatePromotion` |
| Authentication | A secret key (`sk_live_…` or `sk_test_…`), an SDK session token (`sdk_…`), or a secret partner key for one of the partner’s stores. |
| Permission (key scope) | `commerce:manage` |
| Plan feature | `commerce.orders` |

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `workspaceId` | path | string | yes | The store's workspace id (UUID) |
| `promotionId` | path | string | yes | Promotion id (UUID) |

### Request body

`application/json`: [PromotionFields](/v1/api/schemas/#promotionfields)

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `active` | boolean |  | true when absent |
| `amountOffCents` | integer (int64) |  | In US cents: per unit for a flash sale, off the products in scope otherwise (min 1, max 100000000000) |
| `categoryExternalIds` | array of string or null |  | The categories' external ids when scope is categories (max 100 items) |
| `code` | string |  | The coupon code; compared without case (max length 32) |
| `combinable` | boolean |  | Adds to the other combinable promotions; otherwise only the best discount applies |
| `endsAt` | string (date-time) |  | Open-ended when absent |
| `kind` | `"flash_sale"` \| `"number_list"` \| `"coupon"` | yes | flash_sale: lowers the price of the products in scope inside its window and shows in the WhatsApp catalog; number_list: a discount for the numbers of a list; coupon: applies when the customer enters its code at checkout |
| `maxPerCustomer` | integer (int32) |  | A coupon's or number-list discount's uses per customer number (min 1) |
| `maxRedemptions` | integer (int32) |  | A coupon's or number-list discount's uses across all customers (min 1) |
| `minSubtotalCents` | integer (int64) |  | Applies only from this cart subtotal in cents (min 0, max 100000000000) |
| `name` | string | yes | (min length 1, max length 120) |
| `numberListId` | string (uuid) |  | Only the numbers of this list get it; required for number_list |
| `percentOff` | integer (int32) |  | Exactly one of percentOff and amountOffCents (min 1, max 100) |
| `productExternalIds` | array of string or null |  | The products' external ids when scope is products (max 500 items) |
| `scope` | `"all"` \| `"products"` \| `"categories"` |  | (default `"all"`) |
| `startsAt` | string (date-time) |  | Open from now when absent |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| 200 | OK | `application/json`: [PromotionBody](/v1/api/schemas/#promotionbody) |
| 404 | Not Found | `application/problem+json`: [Problem](/v1/api/schemas/#problem) |
| 409 | Conflict | `application/problem+json`: [Problem](/v1/api/schemas/#problem) |
| 422 | Unprocessable Entity | `application/problem+json`: [Problem](/v1/api/schemas/#problem) |
| 500 | Internal Server Error | `application/problem+json`: [Problem](/v1/api/schemas/#problem) |

### Example

```bash
curl -X PUT "https://api.nexafone.so/api/v1/workspaces/{workspaceId}/commerce/promotions/{promotionId}" \
  -H "Authorization: Bearer $NEXAFON_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{"kind":"flash_sale","name":"string"}'
```

## Delete a promotion

`DELETE /api/v1/workspaces/{workspaceId}/commerce/promotions/{promotionId}`

Orders that used it keep their discount.

| | |
| --- | --- |
| Operation id | `deletePromotion` |
| Authentication | A secret key (`sk_live_…` or `sk_test_…`), an SDK session token (`sdk_…`), or a secret partner key for one of the partner’s stores. |
| Permission (key scope) | `commerce:manage` |
| Plan feature | `commerce.orders` |

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `workspaceId` | path | string | yes | The store's workspace id (UUID) |
| `promotionId` | path | string | yes | Promotion id (UUID) |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| 204 | No Content |  |
| 404 | Not Found | `application/problem+json`: [Problem](/v1/api/schemas/#problem) |
| 422 | Unprocessable Entity | `application/problem+json`: [Problem](/v1/api/schemas/#problem) |
| 500 | Internal Server Error | `application/problem+json`: [Problem](/v1/api/schemas/#problem) |

### Example

```bash
curl -X DELETE "https://api.nexafone.so/api/v1/workspaces/{workspaceId}/commerce/promotions/{promotionId}" \
  -H "Authorization: Bearer $NEXAFON_SECRET_KEY"
```
