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

# Bulk Upsert Locations

> Creates or renames up to **2,000** locations per call.

**Identity resolution:** `externalId` (recorded on first create, so re-runs resolve to the same location) → `id`. At least one is required. Unmatched rows create a new location.

**Placement on create** is optional: `hierarchyId`, `levelId`, and a parent given as either `parentExternalId` or `parentId` (not both). New locations inherit the workspace timezone and get the next location number.

**Placement on an existing location is rejected.** Supplying a `hierarchyId`, `levelId`, or parent that differs from the location's current placement returns `400` — *"Location "…" cannot be moved or re-parented via the enterprise API (V1 supports create + rename only)"*. Omit the placement fields to rename in place. Moving a location between parents (for example a store from one district to another) is planned for a later version; until then, do it in the Xenia UI, or model the changing dimension as a **location attribute** and update it through *Bulk Upsert Attribute Values*.

The whole batch runs in **one transaction**: if any row fails, no locations from that call are created.

Requires the **Public API** and **Location Attributes** workspace features plus `CAN_MANAGE_LOCATIONS`.



## OpenAPI

````yaml POST /api/v1/mgt/enterprise/workspaces/{workspaceId}/locations/bulk-upsert
openapi: 3.1.0
info:
  title: Xenia Team API - Bulk Upsert Locations
  description: >-
    Creates and renames locations in bulk, keyed by your own `external_id`.
    Placement (hierarchy, parent, level) is applied when a location is created;
    moving an existing location to a different parent is not supported in this
    version and is rejected rather than silently applied.
  version: 1.0.0
servers:
  - url: https://api.xenia.team
security: []
paths:
  /api/v1/mgt/enterprise/workspaces/{workspaceId}/locations/bulk-upsert:
    post:
      tags:
        - Enterprise Locations
      summary: Bulk Upsert Locations
      description: >-
        Creates or renames up to **2,000** locations per call.


        **Identity resolution:** `externalId` (recorded on first create, so
        re-runs resolve to the same location) → `id`. At least one is required.
        Unmatched rows create a new location.


        **Placement on create** is optional: `hierarchyId`, `levelId`, and a
        parent given as either `parentExternalId` or `parentId` (not both). New
        locations inherit the workspace timezone and get the next location
        number.


        **Placement on an existing location is rejected.** Supplying a
        `hierarchyId`, `levelId`, or parent that differs from the location's
        current placement returns `400` — *"Location "…" cannot be moved or
        re-parented via the enterprise API (V1 supports create + rename only)"*.
        Omit the placement fields to rename in place. Moving a location between
        parents (for example a store from one district to another) is planned
        for a later version; until then, do it in the Xenia UI, or model the
        changing dimension as a **location attribute** and update it through
        *Bulk Upsert Attribute Values*.


        The whole batch runs in **one transaction**: if any row fails, no
        locations from that call are created.


        Requires the **Public API** and **Location Attributes** workspace
        features plus `CAN_MANAGE_LOCATIONS`.
      parameters:
        - name: workspaceId
          in: path
          required: true
          description: >-
            UUID of the workspace. Must be the workspace your API key is bound
            to.
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                locations:
                  type: array
                  minItems: 1
                  maxItems: 2000
                  items:
                    type: object
                    properties:
                      externalId:
                        type: string
                        maxLength: 255
                        description: >-
                          Your own stable key for this location — the
                          recommended handle for idempotent re-runs.
                      id:
                        type: string
                        format: uuid
                        description: Xenia location UUID, if known.
                      name:
                        type: string
                        maxLength: 255
                        description: >-
                          Location name. Renaming an existing location also
                          refreshes its slug.
                      description:
                        type: string
                        maxLength: 1000
                      hierarchyId:
                        type: string
                        format: uuid
                        description: >-
                          Hierarchy to place a NEW location in. Resolve ids from
                          *Bulk Upsert Hierarchies* or the locations read.
                      parentExternalId:
                        type: string
                        maxLength: 255
                        description: >-
                          Parent location addressed by your own key. Mutually
                          exclusive with `parentId`.
                      parentId:
                        type: string
                        format: uuid
                        description: >-
                          Parent location addressed by Xenia UUID. Mutually
                          exclusive with `parentExternalId`.
                      levelId:
                        type: string
                        format: uuid
                        description: Hierarchy level for a NEW location.
                    required:
                      - name
              required:
                - locations
            example:
              locations:
                - externalId: DISTRICT-SW-3
                  name: Southwest District 3
                  hierarchyId: 9c2a1b7e-4d3f-4a11-9c88-2f0e5a6b1c34
                  levelId: 5b6c7d8e-9f01-4a2b-8c3d-4e5f6a7b8c9d
                - externalId: STORE-1042
                  name: Store 1042 - Rosedale
                  hierarchyId: 9c2a1b7e-4d3f-4a11-9c88-2f0e5a6b1c34
                  levelId: 6c7d8e9f-0a12-4b3c-8d4e-5f6a7b8c9d0e
                  parentExternalId: DISTRICT-SW-3
      responses:
        '200':
          description: >-
            One row per input row, with the resolved Xenia id and whether it was
            newly created.
          content:
            application/json:
              example:
                data:
                  - externalId: DISTRICT-SW-3
                    id: 2f169333-21f1-4197-8cac-cd0860a83515
                    name: Southwest District 3
                    created: true
                  - externalId: STORE-1042
                    id: 8099a742-c9bb-40a7-976e-1963a07e77b6
                    name: Store 1042 - Rosedale
                    created: true
                extra_meta:
                  message: Locations upserted
        '400':
          description: >-
            Bad Request - a row asks to move or re-parent an existing location,
            or a `parentExternalId` / `parentId` could not be resolved in this
            workspace. Nothing in the batch is created.
        '401':
          description: >-
            Unauthorized - invalid client credentials, or the `workspaceId` in
            the path is not the workspace your key is bound to (`Client does not
            belong to this workspace`).
        '403':
          description: >-
            Forbidden - route not allow-listed for API keys, missing
            `CAN_MANAGE_LOCATIONS`, or the **Public API** / **Location
            Attributes** feature is not enabled for the workspace.
        '422':
          description: >-
            Unprocessable Entity - body failed validation (neither `externalId`
            nor `id`, both `parentExternalId` and `parentId`, missing `name`,
            more than 2,000 rows).
        '500':
          description: Internal Server Error.
      security:
        - clientKey: []
          clientSecret: []
components:
  securitySchemes:
    clientKey:
      type: apiKey
      in: header
      name: x-client-key
    clientSecret:
      type: apiKey
      in: header
      name: x-client-secret

````