> ## 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 Location Attributes

> Creates or updates up to **500** attribute definitions per call.

**Identity resolution**, in priority order: `externalId` (your key, recorded on first create so later runs resolve to the same attribute) → `id` (a Xenia UUID) → `name`. If nothing matches, the attribute is created.

**`type` is immutable.** It is required on every row; sending a type that conflicts with an existing attribute's type is rejected with `400`.

**Options** are required for `single_select` and `multi_select` and forbidden for the other four types. Options are replaced by exactly what you send: include an option's `id` to keep it (renaming it that way preserves stored values), omit `id` to add a new one, and leave one out to remove it. Prefer additive option changes — removing an option through this endpoint can leave per-location values pointing at an option that no longer exists, so re-send values for the affected locations afterwards.

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



## OpenAPI

````yaml POST /api/v1/mgt/enterprise/workspaces/{workspaceId}/location-attributes/bulk-upsert
openapi: 3.1.0
info:
  title: Xenia Team API - Bulk Upsert Location Attributes
  description: >-
    Idempotently creates or updates location-attribute definitions in one call,
    keyed by your own `externalId`. Safe to re-run: a row that matches an
    existing attribute updates it in place instead of creating a duplicate.
  version: 1.0.0
servers:
  - url: https://api.xenia.team
security: []
paths:
  /api/v1/mgt/enterprise/workspaces/{workspaceId}/location-attributes/bulk-upsert:
    post:
      tags:
        - Enterprise Locations
      summary: Bulk Upsert Location Attributes
      description: >-
        Creates or updates up to **500** attribute definitions per call.


        **Identity resolution**, in priority order: `externalId` (your key,
        recorded on first create so later runs resolve to the same attribute) →
        `id` (a Xenia UUID) → `name`. If nothing matches, the attribute is
        created.


        **`type` is immutable.** It is required on every row; sending a type
        that conflicts with an existing attribute's type is rejected with `400`.


        **Options** are required for `single_select` and `multi_select` and
        forbidden for the other four types. Options are replaced by exactly what
        you send: include an option's `id` to keep it (renaming it that way
        preserves stored values), omit `id` to add a new one, and leave one out
        to remove it. Prefer additive option changes — removing an option
        through this endpoint can leave per-location values pointing at an
        option that no longer exists, so re-send values for the affected
        locations afterwards.


        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:
                attributes:
                  type: array
                  minItems: 1
                  maxItems: 500
                  description: Attribute definitions to upsert.
                  items:
                    type: object
                    properties:
                      externalId:
                        type: string
                        maxLength: 255
                        description: >-
                          Your own stable key for this attribute. Strongly
                          recommended — it is what makes re-runs idempotent.
                      id:
                        type: string
                        format: uuid
                        description: Xenia attribute UUID, if you already know it.
                      name:
                        type: string
                        maxLength: 255
                        description: >-
                          Display name. Also the fallback match key when no
                          `externalId`/`id` is supplied.
                      description:
                        type: string
                        maxLength: 1000
                      type:
                        type: string
                        enum:
                          - single_select
                          - multi_select
                          - number
                          - boolean
                          - text
                          - date
                        description: >-
                          Attribute type. Required, and immutable once the
                          attribute exists.
                      options:
                        type: array
                        minItems: 1
                        description: >-
                          Required for `single_select`/`multi_select`, forbidden
                          otherwise.
                        items:
                          type: object
                          properties:
                            id:
                              type: string
                              format: uuid
                              description: >-
                                Omit to create a new option; supply to keep (or
                                rename) an existing one.
                            label:
                              type: string
                              maxLength: 255
                            color:
                              type: string
                              maxLength: 32
                              description: Hex chip color, e.g. `#7C5CFC`.
                          required:
                            - label
                            - color
                    required:
                      - name
                      - type
              required:
                - attributes
            example:
              attributes:
                - externalId: REGION
                  name: Region
                  description: Operational region
                  type: single_select
                  options:
                    - label: Southwest
                      color: '#7C5CFC'
                    - label: Midwest
                      color: '#22B8CF'
                - externalId: DRIVE_THRU_LANES
                  name: Drive Thru Lanes
                  type: number
      responses:
        '200':
          description: >-
            One result row per input row, in request order. `created`
            distinguishes an insert from an update.
          content:
            application/json:
              example:
                data:
                  - externalId: REGION
                    id: 1f6c9e0a-2b7d-4d5e-9a11-8c3f5b6d7e88
                    name: Region
                    created: true
                  - externalId: DRIVE_THRU_LANES
                    id: 2a7d0f1b-3c8e-4e6f-9b22-9d4a6c7e8f99
                    name: Drive Thru Lanes
                    created: false
                extra_meta:
                  message: Attributes upserted
        '400':
          description: >-
            Bad Request - a row conflicts with an existing attribute's immutable
            `type`, or references an attribute that cannot be resolved.
        '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.
        '409':
          description: >-
            Conflict - a concurrent write to the workspace's attributes won the
            race. Re-read and retry the call.
        '422':
          description: >-
            Unprocessable Entity - body failed validation (missing
            `name`/`type`, unknown field, `options` on a non-select type, more
            than 500 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

````