Skip to main content
POST
Provision Users
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.
Need role_id/location_ids values? See Get Roles and Get Locations.
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.
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.
Nothing is emailed unless you ask for it. notification defaults to none, so an integration already live on this endpoint keeps creating users silently. Pass notification: "auto" to invite each newly created user, routed by their email domain: an address on one of the workspace’s verified SSO domains gets an Active account with no password and a sign-in link, everyone else gets a Pending account and the standard invite email where they set their own password.Users who already existed are never emailed, whatever you pass — re-provisioning is a sync, not an onboarding event, and the alternative is re-mailing your whole directory every time the integration runs.
notification: "auto" requires the API key to have a default user set, because both emails are sent on a named person’s behalf (“X has invited you to Xenia”). Without one, the entire batch is rejected before anything is written, rather than failing partway with some users created and unnotified. notification: "none" has no such requirement.
location_ids are expanded to their leaf locations before membership is written, so passing a district or region grants the stores beneath it. This matters because membership is stored flat and several read paths match a location literally rather than walking the hierarchy — a user holding only a parent node would sign in to empty Submissions and Tasks pages. Passing leaf locations directly is unaffected.
If you want Xenia to read your identity provider’s directory rather than being told who to create, see Directory Sync — it mirrors the directory over SCIM and invites from it using rules that assign role and locations from directory attributes.

Authorizations

x-client-key
string
header
required

Client API key for authentication

x-client-secret
string
header
required

Client secret for authentication

Body

application/json
users
object[]
required
Minimum array length: 1
location_mode
enum<string>
default:add

add (default) only adds missing location memberships, never removes. replace makes the user's locations exactly match location_ids, removing any not listed.

Available options:
add,
replace
notification
enum<string>
default:none

Whether newly created users are emailed. none (default) creates them silently, which is what every integration already live on this endpoint does. auto notifies each NEWLY created user and routes on their email domain: an address on one of the workspace's verified SSO domains gets an Active account with no password plus a sign-in link, and every other address gets a Pending account plus the standard invite email where they set their own password. Users who already existed are never emailed — re-provisioning is a sync, not an onboarding event. Requires the API key to have a default user set, since notifications are sent on a named person's behalf; without one the whole batch is rejected before anything is written.

Available options:
none,
auto

Response

Batch processed. Per-row outcomes are in results/errors — HTTP 200 does not mean every row succeeded, check errors.

status
boolean
results
object[]
errors
object[]