# /users/import/confirm - Execute validated user import **POST /users/import/confirm** Confirm and execute a previously validated user import. Reuses the same pipelines as `POST /users` (CREATE) and `PUT /users/{id}` (UPDATE) for each row, so caller permissions are enforced per-row. **Inputs** - `import_id` — from the `/validate` response. Must still be valid (sessions expire after 30 min). - `override` — global flag. When `true`, every row with status `warning` (the user already exists, matched by email) is UPDATEd with the CSV values. When omitted/false, warning rows are skipped. - `resolutions` — required for any row with status `ambiguous` that you want to import; keyed by row number as a string, value is `{ organization_id }` picked from `candidates[]`. **Behaviour per row status** | status | `override` false | `override` true | |--------|------------------|------------------| | `valid` | CREATE | CREATE | | `error` | skipped (`reason: error`) | skipped (`reason: error`) | | `warning` | skipped (`reason: warning_not_overridden`) | UPDATE — `name`, `phone`, `company_name`, `roles` overwritten from CSV (`email` is the lookup key, never changed) | | `ambiguous` (no resolution) | skipped (`reason: ambiguous_unresolved`) | same | | `ambiguous` (resolution provided) | CREATE with chosen org | same | **RBAC for override** Updating an existing user requires the caller to have permission on the user's *current* organization (their hierarchy must include the target user's org). Organization changes ("moves") via override are allowed because the destination org has already been resolved against the caller's hierarchy at validate time. Per-row failures (status `failed`) are emitted when the caller cannot manage the existing user. **Atomicity** — non-atomic: failures during creation/update (Logto rate limits, RBAC denials) are reported per-row; rows that succeeded before the failure are kept. ## Servers - Backend API server (port 8080): https://api.your-domain.com/api (Backend API server (port 8080)) - Collect API server (port 8081): https://collect.your-domain.com/api (Collect API server (port 8081)) ## Authentication methods - Bearer auth ## Parameters ### Body: application/json (object) - **import_id** (string(uuid)) Import session ID from the validate response. Sessions expire after 30 min. - **override** (boolean) When `true`, all rows with status `warning` are UPDATEd using the CSV values (existing entity is looked up by email/name and overwritten field-by-field). When `false` or omitted, warning rows are skipped. - **resolutions** (object) Per-row organization choices for `ambiguous` rows, keyed by row number as a string (e.g. `"7"`). The chosen `organization_id` must be one of the `candidates[].logto_id` values returned by `/import/validate` for that row. Ambiguous rows without a resolution are skipped. ## Responses ### 200 Import results #### Body: application/json (object) - **code** (integer) - **message** (string) - **data** (object) Per-row outcome of `/import/confirm`. The operation is non-atomic: a partial failure does not roll back the rows that were already created or updated. The four counters always sum to `total_rows` of the validate response. ### 400 Bad request - validation error #### Body: application/json (object) - **code** (integer) HTTP error code - **message** (string) Error message - **data** (object) ### 401 Unauthorized - invalid or missing token #### Body: application/json (object) - **code** (integer) - **message** (string) - **data** (object | null) ### 403 Forbidden - insufficient permissions #### Body: application/json (object) - **code** (integer) - **message** (string) - **data** (object | null) [Powered by Bump.sh](https://bump.sh)