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
/api/users/importRequires: admin:allUpload 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
| Field | Type | Required | Description |
|---|---|---|---|
file | file | Yes | CSV 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
| Code | HTTP | Description |
|---|---|---|
VALIDATION_ERROR | 400 | File missing, unsupported format, exceeds 10MB limit, or file is empty |
PARSE_ERROR | 400 | File could not be parsed (malformed CSV or invalid JSON structure) |
List Import Jobs
/api/users/importRequires: admin:allList 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
| Parameter | Type | Description |
|---|---|---|
page | integer | Page number (default: 1) |
limit | integer | Items 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
/api/users/import/[id]Requires: admin:allRetrieve 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
| Code | HTTP | Description |
|---|---|---|
NOT_FOUND | 404 | Import job does not exist |
Import Status Progression
Import jobs move through these statuses:
| Status | Description |
|---|---|
PENDING | File uploaded and validated, waiting for processing to begin |
PROCESSING | Rows are being processed. processedRows increments as each row is handled |
COMPLETED | All rows processed successfully (errorCount is 0) |
PARTIAL | All rows processed but some had errors (errorCount > 0, successCount > 0) |
FAILED | Processing 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
| Field | Type | Required | Description |
|---|---|---|---|
email | string | Yes | User’s email address. Must be unique within the tenant |
firstName | string | No | User’s first name |
lastName | string | No | User’s last name |
roles | string/array | No | Role name(s) to assign. Must match existing roles in the tenant |
password | string | No | Initial 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:
| Error | Description |
|---|---|
Email is required | The email field is missing or empty |
Invalid email format | The email address is not syntactically valid |
Email already exists in this tenant | A user with this email already exists |
Role 'X' does not exist | The specified role name was not found |
Password does not meet minimum length requirement | Password is shorter than the tenant’s configured minimum |
Password does not meet complexity requirements | Password fails uppercase/lowercase/number/special character requirements |
Export Users
Trigger Export
/api/users/exportRequires: admin:allTrigger 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"
}| Field | Type | Required | Description |
|---|---|---|---|
format | string | Yes | Output 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
/api/users/exportRequires: admin:allList all export jobs for the tenant. Jobs are ordered by creation date descending.
Query parameters
| Parameter | Type | Description |
|---|---|---|
page | integer | Page number (default: 1) |
limit | integer | Items 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
/api/users/export/[id]/downloadRequires: admin:allDownload 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
| Code | HTTP | Description |
|---|---|---|
NOT_FOUND | 404 | Export job does not exist |
NOT_READY | 400 | Export is still processing (status is PENDING) |
EXPIRED | 410 | Export file has expired and been deleted (past 24-hour window) |
Export Status Progression
| Status | Description |
|---|---|
PENDING | Export job created, file generation in progress |
COMPLETED | File generated and ready for download |
FAILED | File generation failed (system error) |
Exported Fields
The export file contains the following fields for each user:
| Field | CSV Column | JSON Key | Description |
|---|---|---|---|
email | email | User’s email address | |
| First Name | firstName | firstName | User’s first name |
| Last Name | lastName | lastName | User’s last name |
| Roles | roles | roles | Comma-separated role names (CSV) or string array (JSON) |
| Email Verified | emailVerified | emailVerified | Whether the email has been verified |
| Enabled | isEnabled | isEnabled | Whether the account is active |
| Created At | createdAt | createdAt | ISO 8601 account creation timestamp |
| Last Login At | lastLoginAt | lastLoginAt | ISO 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:00ZJSON 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:
- Export users from the source tenant via
POST /api/users/exportwith formatjson. - Download the export file via
GET /api/users/export/[id]/download. - Optionally edit the file to adjust roles or remove users.
- Import the file into the target tenant via
POST /api/users/import. - Monitor the import job via
GET /api/users/import/[id]until status isCOMPLETEDorPARTIAL. - Review any row-level errors in the
errorsarray. - Notify imported users to set their passwords via magic link or password reset (since passwords are not exported).
Permissions Reference
| Permission | Description |
|---|---|
manage:users | Upload 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.
Related
- Import & Export Guide — Step-by-step migration walkthrough
- User Import & Export — Run imports and exports from the Console
- Users API — Individual user management endpoints
- SCIM 2.0 Provisioning API — Automated provisioning alternative