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

# Upsert stores

> Create or update up to 100 merchant locations by external id in one request.

Send an array of stores. Each item creates the store when its `id` is new, or updates the existing store when it already belongs to the merchant on the access token. Send only the fields to change. Each saved store matches [Get store](https://firespark.cloud/docs/integrations-api/stores/\[id]/get).

> [!NOTE]
>
> Requires an access token with the `stores:write` scope. See
> [Token](https://firespark.cloud/docs/integrations-api/oauth/token/post.md) to obtain a token.

`merchant_id` and `env` come from the token. `id` comes from each item. Coverage zones, labels, and `cms` are not written here.

When creating, `name`, `contact`, `location`, and `timezone` are required. Omitted `status` becomes `ACTIVE`. Omitted `availability` stays open with no schedule. Omitted `tags` stay empty. Omitted `channel_ids` and `fulfillment_ids` enable every channel and fulfillment the merchant has at that moment. Channels and fulfillments created later start disabled on existing stores.

## Request

```bash theme={null}
curl -X PUT "https://firespark.cloud/api/integrations/v1/stores" \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '[
    {
      "id": "pos-location-01",
      "name": "Main Street",
      "brand_id": null,
      "contact": {
        "email": "main@merchant.com",
        "phone": "+593991234567",
        "name": "Main Street"
      },
      "location": {
        "latitude": -2.1894,
        "longitude": -79.8891,
        "address_line_1": "Calle Principal 456",
        "city": "Guayaquil",
        "country": "Ecuador",
        "postal_code": "090101",
        "business_name": "Main Street Restaurant"
      },
      "timezone": "America/Guayaquil",
      "channel_ids": ["web", "app"],
      "fulfillment_ids": ["delivery", "pickup"],
      "status": "ACTIVE"
    },
    {
      "id": "pos-location-02",
      "status": "INACTIVE"
    }
  ]'
```

The body is an array of 1 to 100 items. Each item takes these fields:

| Field | Description |
| - | - |
| `id` | External store id. Required. Alphanumeric characters, `_`, and `-`, 1–64 characters. |
| `name` | Store name. Required when creating. |
| `brand_id` | External brand id. `null` clears it. |
| `contact` | Email and phone. Replaces the whole contact when sent. Required when creating. |
| `location` | Address and coordinates. Replaces the whole location when sent. Required when creating. |
| `timezone` | IANA timezone. Required when creating. |
| `instructions` | Localized pickup or arrival notes. `null` clears them. |
| `availability` | Schedule and open status. Replaces the whole object when sent. |
| `channel_ids` | External channel ids this store accepts orders from. Replaces the whole list when sent. `[]` disables every channel. |
| `fulfillment_ids` | External fulfillment ids this store offers. Replaces the whole list when sent. `[]` disables every fulfillment. |
| `tags` | Arbitrary key-value tags. Replaces the whole object when sent. |
| `cms_template_id` | CMS template UUID. `null` clears it. |
| `status` | `ACTIVE` or `INACTIVE`. |

Ignored on write: `labels`, `cms`, `coverage_zones`, `uid`, `merchant_id`, `env`.

## Response

Zod validates the entire body before saving. If any field is invalid, HTTP `400` is returned and no items are saved. When the body is valid, items are saved one after another. HTTP `200` only when every item is saved. If a later item hits a business error, HTTP is that item's status (`422` or `500`).

| Field | Description |
| - | - |
| `id` | The item `id`. `null` when it was missing or invalid. |
| `status` | `200` when saved. `422` for a duplicate `id`, or when creating without `name`, `contact`, `location`, and `timezone`. `500` for an unexpected failure. |
| `data` | The saved store, including labels, cms, and coverage zones. Only when `status` is `200`. |
| `error` | Error message. Only when `status` is not `200`. |
| `details` | Error details for item-level failures. |

```json Success theme={null}
{
  "data": [
    {
      "id": "pos-location-01",
      "status": 200,
      "data": {
        "id": "pos-location-01",
        "name": "Main Street",
        "timezone": "America/Guayaquil",
        "status": "ACTIVE"
      }
    },
    {
      "id": "pos-location-02",
      "status": 422,
      "error": "Name, contact, location, and timezone are required",
      "details": null
    }
  ]
}
```

```json Invalid fields theme={null}
{
  "error": "Invalid fields",
  "details": {
    "0.contact.phone": "Invalid phone number"
  }
}
```

## Error responses

| Status | Description |
| - | - |
| `400` | The body is not an array of 1 to 100 items, or any item has invalid fields. No items are saved. |
| `401` | Missing or invalid access token. |
| `403` | Token does not include the `stores:write` scope. |


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