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

# Provision Users

> Batch-creates or syncs users. New users are created with the given role and locations. Existing users (matched by email, case-insensitive) have their role corrected if it differs and locations updated per `location_mode`; re-provisioning does not change an existing membership's status (a `Pending`/`Deactivated` user stays that way). Validation runs for the whole batch before any row is written; a bad `role_id` or `location_id` fails only that row, reported in `errors`. An email that appears more than once in the same batch fails every row that shares it, not just the extras.

<Note>
  This endpoint has no `hasPermission`/`hasFeature` gate of its own — any valid client key can provision users into its own workspace. Pre-provisioning a user here with a role and locations before their first SSO login means they land in that role and those locations instead of the workspace's bare default SSO role. See the [Single Sign-On guide](/guides/workflows/sso#provisioning-users-for-sso).
</Note>

<Note>
  Need `role_id`/`location_ids` values? See [Get Roles](/api-reference/endpoints/roles/get) and [Get Locations](/api-reference/endpoints/locations/get-locations).
</Note>

<Warning>
  This endpoint returns a custom envelope — `{ status, results, errors }` — not the `{ data, extra_meta }` shape used by other user-management endpoints. A `200` response does not mean every row succeeded: always check `errors`. A duplicated email within the same batch fails every row that shares it, not just the extra ones.
</Warning>

<Note>
  Re-provisioning an existing user corrects their role and locations, but does not change their membership status — a `Pending` or `Deactivated` user stays that way. It also doesn't bypass your workspace's user-seat limit: if provisioning a new user would exceed it, that row fails in `errors`.
</Note>


## OpenAPI

````yaml POST /api/v1/mgt/users/provision
openapi: 3.1.0
info:
  title: Xenia Team API - Provision Users
  description: >-
    Creates new users or syncs existing ones (matched by email) for a workspace
    in a single batch call. This is the API-key path for programmatic user
    creation — it works whether or not the workspace uses SSO, and creates users
    through the same internal path SSO's own just-in-time provisioning uses on a
    user's first login (the endpoint itself is not called by SSO login).
  version: 1.0.0
servers:
  - url: https://api.xenia.team
security: []
paths:
  /api/v1/mgt/users/provision:
    post:
      tags:
        - Users
      summary: Provision Users
      description: >-
        Batch-creates or syncs users. New users are created with the given role
        and locations. Existing users (matched by email, case-insensitive) have
        their role corrected if it differs and locations updated per
        `location_mode`; re-provisioning does not change an existing
        membership's status (a `Pending`/`Deactivated` user stays that way).
        Validation runs for the whole batch before any row is written; a bad
        `role_id` or `location_id` fails only that row, reported in `errors`. An
        email that appears more than once in the same batch fails every row that
        shares it, not just the extras.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - users
              properties:
                users:
                  type: array
                  minItems: 1
                  items:
                    type: object
                    required:
                      - email
                      - role_id
                      - location_ids
                    properties:
                      email:
                        type: string
                        format: email
                        description: >-
                          Lower-cased and used to match an existing user or
                          create a new one
                      role_id:
                        type: string
                        format: uuid
                        description: Must belong to the key's workspace
                      location_ids:
                        type: array
                        minItems: 1
                        items:
                          type: string
                          format: uuid
                        description: >-
                          Must all belong to the key's workspace; duplicates
                          within one entry are rejected
                      first_name:
                        type: string
                        maxLength: 100
                      last_name:
                        type: string
                        maxLength: 100
                location_mode:
                  type: string
                  enum:
                    - add
                    - replace
                  default: add
                  description: >-
                    `add` (default) only adds missing location memberships,
                    never removes. `replace` makes the user's locations exactly
                    match `location_ids`, removing any not listed.
            example:
              users:
                - email: jane.smith@example.com
                  role_id: 4ccf0b65-fb99-40da-92d6-3f98730df8b2
                  location_ids:
                    - a13ddb59-3e5c-4bdd-8673-fc913b93b9db
                  first_name: Jane
                  last_name: Smith
              location_mode: add
      responses:
        '200':
          description: >-
            Batch processed. Per-row outcomes are in `results`/`errors` — HTTP
            200 does not mean every row succeeded, check `errors`.
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: boolean
                  results:
                    type: array
                    items:
                      type: object
                      properties:
                        email:
                          type: string
                        status:
                          type: string
                          description: Always the literal string "ok" for a successful row
                        user_id:
                          type: string
                          format: uuid
                  errors:
                    type: array
                    items:
                      type: object
                      properties:
                        email:
                          type: string
                        error:
                          type: string
              example:
                status: true
                results:
                  - email: jane.smith@example.com
                    status: ok
                    user_id: 9c1e2f3a-1111-4b2c-8d3e-4f5a6b7c8d9e
                errors:
                  - email: dupe@example.com
                    error: Duplicate email in batch
        '401':
          description: >-
            Unauthorized - missing/invalid client credentials (`API key required
            (x-client-key / x-client-secret).` or `Invalid client credentials`)
        '422':
          description: >-
            Unprocessable - request body fails schema validation (e.g. missing
            `role_id`, empty `location_ids`)
        '500':
          description: Internal error processing provisioning request.
      security:
        - clientKey: []
          clientSecret: []
components:
  securitySchemes:
    clientKey:
      type: apiKey
      in: header
      name: x-client-key
      description: Client API key for authentication
    clientSecret:
      type: apiKey
      in: header
      name: x-client-secret
      description: Client secret for authentication

````