# /users/import/validate - Validate CSV file for user import **POST /users/import/validate** Upload and validate a CSV file for bulk user import. Returns a row-by-row report with a verdict per row (`valid` / `error` / `warning` / `ambiguous`), separating blocking `errors[]` from non-blocking `warnings[]`. Validated data is stored in a temporary session (30 min TTL) keyed by `import_id`, which must be passed to `/users/import/confirm` to actually create or update the users. **CSV format** - Columns (in any order): `email`, `name`, `phone`, `company_name`, `roles`. - Required: `email`, `name`, `company_name`, `roles`. `phone` is optional. - `company_name` is the visible name of the user's organization (matched against distributors/resellers/customers in the caller's hierarchy). - `phone`, when present, must include the leading `+CC` country prefix (e.g. `+39 333 1234567`). Bare local numbers without `+CC` are rejected with `invalid_format` at validate time. Phone uniqueness against existing users is also checked (`already_used`). - `roles` is a semicolon-separated list of role names (e.g. `Admin;Support`); names are resolved against the in-memory role cache. - `company_name` is matched case-insensitively against the caller's hierarchy. If multiple organizations share that name the row is marked `ambiguous` and the response includes the candidates — the caller picks one in `resolutions` at confirm time. - The first data row is row `2` (the header row is row `1`). - Standard CSV (RFC 4180) — fields containing commas, quotes or newlines must be double-quoted (`"value, with comma"`). Spreadsheet tools auto-quote on save. **Row outcomes** | status | What it means | Confirm action | |--------|---------------|----------------| | `valid` | All checks passed | CREATE | | `error` | At least one blocking error in `errors[]` (`required`, `invalid_format`, `duplicate_in_csv`, `not_found`, `unknown`, `archived`, `already_used`, …) | always skipped | | `warning` | `email` already exists in DB (in `warnings[]`) | UPDATE if `override: true`, else skipped | | `ambiguous` | Organization name matches multiple orgs | CREATE with chosen org if `resolutions[row]` is set, else skipped | Errors and warnings can coexist on the same row; status precedence is `error` > `ambiguous` > `warning` > `valid`. **Limits** — max 10 MB, max 1000 rows. Larger files return `400`. ## 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: multipart/form-data (object) - **file** (string(binary)) CSV file to validate ## Responses ### 200 Validation report #### Body: application/json (object) - **code** (integer) - **message** (string) - **data** (object) Row-by-row validation report produced by `/import/validate`. The full set of CSV rows is always returned in `rows` (good and bad alike) so the frontend can render a preview and let the user decide whether to enable `override` for warnings or pick `resolutions` for ambiguous rows. The accompanying counters are pre-computed for UX summaries. ### 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)