> ## 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 Hierarchies

> Creates or extends up to **100** hierarchies per call, each with 1–20 levels.

**Identity resolution**, in priority order: `externalId` → `id` → `name`. Unmatched rows are created; the first hierarchy in a workspace becomes its default.

**Level rules** (enforced on create): exactly **one** level must be marked `isSite: true` — the leaf level that real, task-bearing locations live on — and `order` values must be unique within the hierarchy. Orders are stored as you send them.

**On a match**, levels are appended by title: a level whose `title` already exists is left alone, anything new is appended **after** the hierarchy's current last level (the server assigns its `order`, so the `order` you send is ignored), and a level marked `isSite: true` is skipped — a hierarchy keeps the single site level it was created with. Levels are never renamed, reordered, or deleted through this endpoint, so an existing hierarchy's shape is never destructively rewritten.

**No re-parenting.** This endpoint shapes the hierarchy; it does not move locations within it. See *Bulk Upsert Locations* for what placement changes are and are not supported.

Rows are processed one at a time rather than as one atomic batch: if a later row fails, earlier rows stay created. Re-running the same payload is safe, because every row is matched before it is created.

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



## OpenAPI

````yaml POST /api/v1/mgt/enterprise/workspaces/{workspaceId}/hierarchy/bulk-upsert
openapi: 3.1.0
info:
  title: Xenia Team API - Bulk Upsert Location Hierarchies
  description: >-
    Creates location hierarchies and their levels (for example Company → Region
    → District → Store) in bulk, keyed by your own `external_id`. Existing
    hierarchies are matched, never rewritten: only levels you have not defined
    yet are appended.
  version: 1.0.0
servers:
  - url: https://api.xenia.team
security: []
paths:
  /api/v1/mgt/enterprise/workspaces/{workspaceId}/hierarchy/bulk-upsert:
    post:
      tags:
        - Enterprise Locations
      summary: Bulk Upsert Hierarchies
      description: >-
        Creates or extends up to **100** hierarchies per call, each with 1–20
        levels.


        **Identity resolution**, in priority order: `externalId` → `id` →
        `name`. Unmatched rows are created; the first hierarchy in a workspace
        becomes its default.


        **Level rules** (enforced on create): exactly **one** level must be
        marked `isSite: true` — the leaf level that real, task-bearing locations
        live on — and `order` values must be unique within the hierarchy. Orders
        are stored as you send them.


        **On a match**, levels are appended by title: a level whose `title`
        already exists is left alone, anything new is appended **after** the
        hierarchy's current last level (the server assigns its `order`, so the
        `order` you send is ignored), and a level marked `isSite: true` is
        skipped — a hierarchy keeps the single site level it was created with.
        Levels are never renamed, reordered, or deleted through this endpoint,
        so an existing hierarchy's shape is never destructively rewritten.


        **No re-parenting.** This endpoint shapes the hierarchy; it does not
        move locations within it. See *Bulk Upsert Locations* for what placement
        changes are and are not supported.


        Rows are processed one at a time rather than as one atomic batch: if a
        later row fails, earlier rows stay created. Re-running the same payload
        is safe, because every row is matched before it is 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:
                hierarchies:
                  type: array
                  minItems: 1
                  maxItems: 100
                  items:
                    type: object
                    properties:
                      externalId:
                        type: string
                        maxLength: 255
                        description: Your own stable key for this hierarchy.
                      id:
                        type: string
                        format: uuid
                        description: Xenia hierarchy UUID, if known.
                      name:
                        type: string
                        maxLength: 255
                      description:
                        type: string
                        maxLength: 1000
                      color:
                        type: string
                        pattern: ^#[0-9A-Fa-f]{6}$
                        description: Six-digit hex colour, e.g. `#7C5CFC`.
                      levels:
                        type: array
                        minItems: 1
                        maxItems: 20
                        description: 'Levels top-down. Exactly one must have `isSite: true`.'
                        items:
                          type: object
                          properties:
                            title:
                              type: string
                              maxLength: 255
                            order:
                              type: integer
                              minimum: 1
                              description: >-
                                1-based position, unique within the hierarchy.
                                Used on create; ignored when appending a level
                                to an existing hierarchy.
                            isSite:
                              type: boolean
                              description: >-
                                Marks the leaf/site level that real locations
                                belong to.
                          required:
                            - title
                            - order
                            - isSite
                    required:
                      - name
                      - levels
              required:
                - hierarchies
            example:
              hierarchies:
                - externalId: OPS
                  name: Operations
                  description: Company reporting structure
                  color: '#7C5CFC'
                  levels:
                    - title: Region
                      order: 1
                      isSite: false
                    - title: District
                      order: 2
                      isSite: false
                    - title: Store
                      order: 3
                      isSite: true
      responses:
        '200':
          description: >-
            One row per input row. `levelsCreated` counts only the levels this
            call added.
          content:
            application/json:
              example:
                data:
                  - externalId: OPS
                    id: 9c2a1b7e-4d3f-4a11-9c88-2f0e5a6b1c34
                    name: Operations
                    created: true
                    levelsCreated: 3
                extra_meta:
                  message: Hierarchies upserted
        '400':
          description: >-
            Bad Request - level shape rejected on create (not exactly one
            `isSite` level, duplicate `order` values).
        '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 (missing `levels`, bad
            `color` format, more than 100 hierarchies or 20 levels).
        '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

````