> ## 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 Sub-Location Types

> Idempotently creates or updates sub-location types (e.g. Fridge, Auditorium) and their attribute definitions in one call, keyed by your own `externalId`. Safe to re-run.

Up to **100** types per call, **50** attributes per type, **100** options per select attribute.

**Type identity**, in priority order: `externalId` → `id` → `name` (case-insensitive). If nothing matches, the type is created. A matched type is renamed to `name`.

**Attribute identity** (within the type): `externalId` → `id` → `key` → `name` (case-insensitive). If nothing matches, the attribute is created and gets a `key` derived from its name (returned in the response). `key` is match-only: it must name an existing attribute of the type. An `externalId` already used by an attribute of another type is rejected, as are two attributes in one row that match the same existing attribute.

**Additive only.** Attributes and options you leave out are kept, never deleted. Options are matched by `id`, else by `label` (case-insensitive): a match is updated (label, color), anything else is appended.

**`type` is immutable.** Sending a different type for an existing attribute is rejected with `400`.

The response reports each type's `status`, so a match against a type archived in the web app is visible (sub-locations cannot be created under an archived type).

The call is all-or-nothing: if any row fails, nothing is written and the error message names the row (e.g. `types[1].attributes[0]`).

Requires the **Public API** workspace feature, `CAN_MANAGE_LOCATIONS`, and **Sub-Locations** enabled for the workspace (contact Xenia to enable it; until then every sub-location endpoint returns `404`).



## OpenAPI

````yaml POST /api/v1/mgt/enterprise/workspaces/{workspaceId}/sub-location-types/bulk-upsert
openapi: 3.1.0
info:
  title: Xenia Team API - Bulk Upsert Sub-Location Types
  description: >-
    Idempotently creates or updates sub-location types (e.g. Fridge, Auditorium)
    and their attribute definitions in one call, keyed by your own `externalId`.
    Safe to re-run.
  version: 1.0.0
servers:
  - url: https://api.xenia.team
security: []
paths:
  /api/v1/mgt/enterprise/workspaces/{workspaceId}/sub-location-types/bulk-upsert:
    post:
      tags:
        - Enterprise Sub-Locations
      summary: Bulk Upsert Sub-Location Types
      description: >-
        Idempotently creates or updates sub-location types (e.g. Fridge,
        Auditorium) and their attribute definitions in one call, keyed by your
        own `externalId`. Safe to re-run.


        Up to **100** types per call, **50** attributes per type, **100**
        options per select attribute.


        **Type identity**, in priority order: `externalId` → `id` → `name`
        (case-insensitive). If nothing matches, the type is created. A matched
        type is renamed to `name`.


        **Attribute identity** (within the type): `externalId` → `id` → `key` →
        `name` (case-insensitive). If nothing matches, the attribute is created
        and gets a `key` derived from its name (returned in the response). `key`
        is match-only: it must name an existing attribute of the type. An
        `externalId` already used by an attribute of another type is rejected,
        as are two attributes in one row that match the same existing attribute.


        **Additive only.** Attributes and options you leave out are kept, never
        deleted. Options are matched by `id`, else by `label`
        (case-insensitive): a match is updated (label, color), anything else is
        appended.


        **`type` is immutable.** Sending a different type for an existing
        attribute is rejected with `400`.


        The response reports each type's `status`, so a match against a type
        archived in the web app is visible (sub-locations cannot be created
        under an archived type).


        The call is all-or-nothing: if any row fails, nothing is written and the
        error message names the row (e.g. `types[1].attributes[0]`).


        Requires the **Public API** workspace feature, `CAN_MANAGE_LOCATIONS`,
        and **Sub-Locations** enabled for the workspace (contact Xenia to enable
        it; until then every sub-location endpoint returns `404`).
      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:
                types:
                  type: array
                  minItems: 1
                  maxItems: 100
                  items:
                    type: object
                    properties:
                      externalId:
                        type: string
                        maxLength: 255
                        description: >-
                          Your own stable key for this type. Strongly
                          recommended.
                      id:
                        type: string
                        format: uuid
                        description: Xenia type id, if you already know it.
                      name:
                        type: string
                        maxLength: 120
                      attributes:
                        type: array
                        maxItems: 50
                        items:
                          type: object
                          properties:
                            externalId:
                              type: string
                              maxLength: 255
                              description: Your own stable key for this attribute.
                            id:
                              type: string
                              format: uuid
                              description: Xenia attribute id.
                            key:
                              type: string
                              maxLength: 64
                              description: >-
                                Match-only: the key of an existing attribute of
                                this type (see List Sub-Location Types).
                            name:
                              type: string
                              maxLength: 120
                            type:
                              type: string
                              enum:
                                - single_select
                                - multi_select
                                - number
                                - boolean
                                - text
                                - date
                              description: Immutable once the attribute exists.
                            options:
                              type: array
                              minItems: 1
                              maxItems: 100
                              description: >-
                                Required when creating a
                                `single_select`/`multi_select` attribute,
                                forbidden for other types.
                              items:
                                type: object
                                properties:
                                  id:
                                    type: string
                                    format: uuid
                                    description: Xenia option id.
                                  label:
                                    type: string
                                    maxLength: 60
                                  color:
                                    type: string
                                    pattern: ^#[0-9A-Fa-f]{6}$
                                    description: Hex chip color, e.g. `#7C5CFC`.
                                required:
                                  - label
                                  - color
                            isRequired:
                              type: boolean
                              description: >-
                                Whether the web app requires this attribute.
                                Defaults to `false` on create; unchanged when
                                omitted.
                          required:
                            - name
                            - type
                    required:
                      - name
              required:
                - types
            example:
              types:
                - externalId: FRIDGE
                  name: Fridge
                  attributes:
                    - externalId: ZONE
                      name: Zone
                      type: single_select
                      options:
                        - label: Front
                          color: '#7C5CFC'
                        - label: Back
                          color: '#22B8CF'
                    - externalId: CAPACITY
                      name: Capacity
                      type: number
      responses:
        '200':
          description: >-
            One result per input type, in request order, with one entry per
            input attribute.
          content:
            application/json:
              example:
                data:
                  - externalId: FRIDGE
                    id: 5ea865b5-1503-4cc4-975d-101ecf67a5e4
                    name: Fridge
                    status: active
                    created: true
                    attributes:
                      - externalId: ZONE
                        id: 8b1d2c3e-4f50-4a61-8b72-93a4b5c6d7e8
                        key: zone
                        name: Zone
                        created: true
                      - externalId: CAPACITY
                        id: 9c2e3d4f-5061-4b72-9c83-a4b5c6d7e8f9
                        key: capacity
                        name: Capacity
                        created: true
                extra_meta:
                  message: Sub-location types upserted
        '400':
          description: >-
            Bad Request - an attribute's `type` would change; an `id` or `key`
            does not resolve; an attribute `externalId` belongs to another type;
            two rows or attributes match the same record.
        '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 - missing `CAN_MANAGE_LOCATIONS`, or the **Public API**
            feature is not enabled for the workspace.
        '404':
          description: Not Found - Sub-Locations is not enabled for this workspace.
        '409':
          description: >-
            Conflict - a rename would collide with another sub-location type's
            name.
        '422':
          description: >-
            Unprocessable Entity - body failed validation (missing
            `name`/`type`, `options` on a non-select type, color not `#RRGGBB`,
            limits exceeded).
        '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

````

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