Skip to Content

User Import & Export API

The User Import & Export API enables bulk user operations for migration, onboarding, and backup scenarios. Administrators can import users from CSV or JSON files and export the full user directory to either format.

Import operations are processed asynchronously. After uploading a file, the import job progresses through status stages while rows are validated and users are created. Export operations are also asynchronous — once complete, the generated file is available for download for 24 hours.

Import Users

Upload Import File

POST/api/users/importRequires: admin:all

Upload a CSV or JSON file containing user records to import. The file is validated and an import job is created. Processing happens asynchronously — poll the job status to track progress. Maximum file size is 10MB.

Request body — multipart/form-data

FieldTypeRequiredDescription
filefileYesCSV or JSON file (max 10MB). File extension determines format

Success response

{ "ok": true, "data": { "id": "imp_abc123", "fileName": "users-batch-2025-02.csv", "format": "csv", "status": "PENDING", "totalRows": 150, "processedRows": 0, "successCount": 0, "errorCount": 0, "errors": [], "createdAt": "2025-02-18T10:00:00Z", "updatedAt": "2025-02-18T10:00:00Z" } }

Error codes

CodeHTTPDescription
VALIDATION_ERROR400File missing, unsupported format, exceeds 10MB limit, or file is empty
PARSE_ERROR400File could not be parsed (malformed CSV or invalid JSON structure)

List Import Jobs

GET/api/users/importRequires: admin:all

List all import jobs for the tenant. Jobs are ordered by creation date descending. Use this to monitor ongoing imports or review past import history.

Query parameters

ParameterTypeDescription
pageintegerPage number (default: 1)
limitintegerItems per page (default: 20, max: 100)

Success response

{ "ok": true, "data": { "data": [ { "id": "imp_abc123", "fileName": "users-batch-2025-02.csv", "format": "csv", "status": "COMPLETED", "totalRows": 150, "processedRows": 150, "successCount": 142, "errorCount": 8, "createdAt": "2025-02-18T10:00:00Z", "updatedAt": "2025-02-18T10:05:00Z" }, { "id": "imp_def456", "fileName": "migration-export.json", "format": "json", "status": "PARTIAL", "totalRows": 500, "processedRows": 500, "successCount": 487, "errorCount": 13, "createdAt": "2025-02-17T14:00:00Z", "updatedAt": "2025-02-17T14:12:00Z" }, { "id": "imp_ghi789", "fileName": "bad-file.csv", "format": "csv", "status": "FAILED", "totalRows": 25, "processedRows": 3, "successCount": 0, "errorCount": 3, "createdAt": "2025-02-16T09:00:00Z", "updatedAt": "2025-02-16T09:00:30Z" } ], "pagination": { "page": 1, "limit": 20, "total": 3, "totalPages": 1 } } }

Get Import Job Detail

GET/api/users/import/[id]Requires: admin:all

Retrieve detailed information about an import job, including per-row error details. Poll this endpoint while status is PENDING or PROCESSING to track progress.

Success response

{ "ok": true, "data": { "id": "imp_abc123", "fileName": "users-batch-2025-02.csv", "format": "csv", "status": "COMPLETED", "totalRows": 150, "processedRows": 150, "successCount": 142, "errorCount": 8, "errors": [ { "row": 12, "email": "[email protected]", "error": "Email already exists in this tenant" }, { "row": 34, "email": "invalid-email", "error": "Invalid email format" }, { "row": 56, "email": "[email protected]", "error": "Role 'super_admin' does not exist" }, { "row": 78, "email": "", "error": "Email is required" }, { "row": 91, "email": "[email protected]", "error": "Email already exists in this tenant" }, { "row": 102, "email": "[email protected]", "error": "Password does not meet minimum length requirement (8 characters)" }, { "row": 119, "email": "eve@test", "error": "Invalid email format" }, { "row": 133, "email": "[email protected]", "error": "Email already exists in this tenant" } ], "createdAt": "2025-02-18T10:00:00Z", "updatedAt": "2025-02-18T10:05:00Z" } }

Error codes

CodeHTTPDescription
NOT_FOUND404Import job does not exist

Import Status Progression

Import jobs move through these statuses:

StatusDescription
PENDINGFile uploaded and validated, waiting for processing to begin
PROCESSINGRows are being processed. processedRows increments as each row is handled
COMPLETEDAll rows processed successfully (errorCount is 0)
PARTIALAll rows processed but some had errors (errorCount > 0, successCount > 0)
FAILEDProcessing failed entirely (file corruption, system error, or all rows had errors)

For large imports (500+ rows), processing may take several minutes. Poll the job detail endpoint every 2-3 seconds to track progress. The processedRows field updates in real time.

Import File Formats

CSV Format

The first row must be a header row with column names. Column order does not matter. Columns are matched by name (case-insensitive).

email,firstName,lastName,roles,password [email protected],Jane,Doe,editor,SecurePass123! [email protected],Bob,Smith,"editor,viewer",AnotherPass456! [email protected],Alice,Johnson,admin, [email protected],Carol,Williams,,
  • Multiple roles are comma-separated within quotes: "editor,viewer"
  • Empty password field means the user must use magic link or password reset to set one
  • Empty roles field means the user is created with no roles assigned

JSON Format

The file must contain a JSON array of user objects at the top level.

[ { "email": "[email protected]", "firstName": "Jane", "lastName": "Doe", "roles": ["editor"], "password": "SecurePass123!" }, { "email": "[email protected]", "firstName": "Bob", "lastName": "Smith", "roles": ["editor", "viewer"], "password": "AnotherPass456!" }, { "email": "[email protected]", "firstName": "Alice", "lastName": "Johnson", "roles": ["admin"] }, { "email": "[email protected]", "firstName": "Carol", "lastName": "Williams" } ]

Field Reference

FieldTypeRequiredDescription
emailstringYesUser’s email address. Must be unique within the tenant
firstNamestringNoUser’s first name
lastNamestringNoUser’s last name
rolesstring/arrayNoRole name(s) to assign. Must match existing roles in the tenant
passwordstringNoInitial password. Must meet the tenant’s password policy requirements

Passwords are optional. When omitted, the user account is created without a password. The user must use a magic link or the password reset flow to set their password on first login. This is the recommended approach for bulk imports.

Per-Row Error Handling

Each row is processed independently. If a row fails validation, it is skipped and the error is recorded. Processing continues with the remaining rows. Common error reasons:

ErrorDescription
Email is requiredThe email field is missing or empty
Invalid email formatThe email address is not syntactically valid
Email already exists in this tenantA user with this email already exists
Role 'X' does not existThe specified role name was not found
Password does not meet minimum length requirementPassword is shorter than the tenant’s configured minimum
Password does not meet complexity requirementsPassword fails uppercase/lowercase/number/special character requirements

Export Users

Trigger Export

POST/api/users/exportRequires: admin:all

Trigger an export of all users in the tenant. The export runs asynchronously — poll the export job status or list endpoint to know when the file is ready for download.

Request body

{ "format": "csv" }
FieldTypeRequiredDescription
formatstringYesOutput format: csv or json

Success response

{ "ok": true, "data": { "id": "exp_abc123", "format": "csv", "status": "PENDING", "totalUsers": 0, "createdAt": "2025-02-18T11:00:00Z", "expiresAt": "2025-02-19T11:00:00Z" } }

Passwords are never included in exports. Password hashes are non-reversible and are excluded for security reasons. Exported users who are imported into another tenant will need to set new passwords.

List Export Jobs

GET/api/users/exportRequires: admin:all

List all export jobs for the tenant. Jobs are ordered by creation date descending.

Query parameters

ParameterTypeDescription
pageintegerPage number (default: 1)
limitintegerItems per page (default: 20, max: 100)

Success response

{ "ok": true, "data": { "data": [ { "id": "exp_abc123", "format": "csv", "status": "COMPLETED", "totalUsers": 342, "createdAt": "2025-02-18T11:00:00Z", "expiresAt": "2025-02-19T11:00:00Z" }, { "id": "exp_def456", "format": "json", "status": "COMPLETED", "totalUsers": 342, "createdAt": "2025-02-15T09:00:00Z", "expiresAt": "2025-02-16T09:00:00Z" } ], "pagination": { "page": 1, "limit": 20, "total": 2, "totalPages": 1 } } }

Download Export File

GET/api/users/export/[id]/downloadRequires: admin:all

Download the generated export file. Returns a binary response with appropriate Content-Type and Content-Disposition headers. The file is available for 24 hours after the export completes.

Response headers

Content-Type: text/csv (or application/json) Content-Disposition: attachment; filename="users-export-2025-02-18.csv"

The response body is the raw file content (CSV or JSON), not wrapped in the standard { ok, data } envelope.

Error codes

CodeHTTPDescription
NOT_FOUND404Export job does not exist
NOT_READY400Export is still processing (status is PENDING)
EXPIRED410Export file has expired and been deleted (past 24-hour window)

Export Status Progression

StatusDescription
PENDINGExport job created, file generation in progress
COMPLETEDFile generated and ready for download
FAILEDFile generation failed (system error)

Exported Fields

The export file contains the following fields for each user:

FieldCSV ColumnJSON KeyDescription
EmailemailemailUser’s email address
First NamefirstNamefirstNameUser’s first name
Last NamelastNamelastNameUser’s last name
RolesrolesrolesComma-separated role names (CSV) or string array (JSON)
Email VerifiedemailVerifiedemailVerifiedWhether the email has been verified
EnabledisEnabledisEnabledWhether the account is active
Created AtcreatedAtcreatedAtISO 8601 account creation timestamp
Last Login AtlastLoginAtlastLoginAtISO 8601 timestamp of most recent login, or empty/null

CSV export example

email,firstName,lastName,roles,emailVerified,isEnabled,createdAt,lastLoginAt [email protected],Jane,Doe,"admin,editor",true,true,2024-11-01T08:00:00Z,2025-02-18T09:30:00Z [email protected],Bob,Smith,viewer,true,true,2024-12-15T10:00:00Z,2025-02-17T14:20:00Z [email protected],Alice,Johnson,editor,false,true,2025-02-10T12:00:00Z, [email protected],Carol,Williams,,true,false,2025-01-20T09:00:00Z,2025-01-25T11:00:00Z

JSON export example

[ { "email": "[email protected]", "firstName": "Jane", "lastName": "Doe", "roles": ["admin", "editor"], "emailVerified": true, "isEnabled": true, "createdAt": "2024-11-01T08:00:00Z", "lastLoginAt": "2025-02-18T09:30:00Z" }, { "email": "[email protected]", "firstName": "Bob", "lastName": "Smith", "roles": ["viewer"], "emailVerified": true, "isEnabled": true, "createdAt": "2024-12-15T10:00:00Z", "lastLoginAt": "2025-02-17T14:20:00Z" } ]

Export files expire after 24 hours and are permanently deleted. Download the file promptly after generation completes. You can always trigger a new export if needed.

Migration Workflow

A typical tenant-to-tenant migration follows this pattern:

  1. Export users from the source tenant via POST /api/users/export with format json.
  2. Download the export file via GET /api/users/export/[id]/download.
  3. Optionally edit the file to adjust roles or remove users.
  4. Import the file into the target tenant via POST /api/users/import.
  5. Monitor the import job via GET /api/users/import/[id] until status is COMPLETED or PARTIAL.
  6. Review any row-level errors in the errors array.
  7. Notify imported users to set their passwords via magic link or password reset (since passwords are not exported).

Permissions Reference

PermissionDescription
manage:usersUpload import files, trigger exports, download export files, and view job history

Import and export operations are recorded in the audit log with user_import.created and user_export.created event types, including the file name, format, row counts, and the administrator who initiated the operation.