loading
Preparing LoginRadius developer resources
Mission: Help enterprises accelerate digital transformation with our fully-managed Customer IAM technology.
Skip to main content

Self Serve Migration

Self Serve Migration lets you migrate customer identity data into LoginRadius independently using the bulk upsert API. You control the timeline, data transformation, and batch orchestration. The platform handles deduplication, password hash preservation, and audit logging.

Key Capabilities

  • Batch upsert: Insert new users or update existing ones in a single API call
  • Password hash preservation: Migrate hashed passwords directly, so users keep their existing passwords
  • Delta migration: Idempotent re-runs that skip or overwrite duplicates based on your configuration
  • B2B support: Migrate users with org-role assignments in one request
  • Partial success: Failed records are reported individually; valid records are never blocked by invalid ones

Endpoint

POST /v2/bulk/upsert

Authentication

Required: API Key + API Secret passed via the X-LoginRadius-ApiKey and X-LoginRadius-ApiSecret request headers. The Authorization: Basic header is not accepted for this endpoint. Basic Auth maps to OAuth Client ID/Secret credentials, not the API Key/Secret. OAuth M2M tokens and request signatures are also not accepted for bulk migration.

X-LoginRadius-ApiKey: <apiKey>
X-LoginRadius-ApiSecret: <apiSecret>

The API Key must have the write:migrations scope enabled. Retrieve your API Key and Secret from the LoginRadius Admin Console under Security → API Credentials.

The API Key/Secret can also be passed as query parameters, but this is not recommended, as secrets in query strings are exposed in server access logs, proxies, and browser history. Always prefer the header form.

Required Request Headers

HeaderValue
X-LoginRadius-ApiKey<apiKey>
X-LoginRadius-ApiSecret<apiSecret>
Content-Typeapplication/json

Request Body

{
"PasswordEncryption": { ... },
"Profiles": [ ... ],
"DeltaMigrationModel": { ... }
}
FieldTypeRequiredDescription
PasswordEncryptionObjectNoHash algorithm config applied to all profiles in the batch. If omitted, the app-level password encryption config is used.
ProfilesArrayYesUser profiles to insert or upsert. Maximum 500 per request.
DeltaMigrationModelObjectNoControls upsert behavior. Defaults to insert-only if omitted.

PasswordEncryption Object

Defines how the submitted password hashes were generated. Applied uniformly to all profiles in the batch.

Supported Algorithm Types (Type)

ValueAlgorithm
pbkdf2PBKDF2
sha1_pbkdf2SHA1 + PBKDF2
sha512SHA-512
hmac_sha1HMAC-SHA1
hmac_sha256HMAC-SHA256
argon2iArgon2i
argon2idArgon2id

Refer to the Hashing Algorithm documentation for details.

For any hash algorithm not in this list (e.g. bcrypt, scrypt), contact support@loginradius.com before starting migration.


Profile Object

Each entry in the Profiles array represents one user. All fields are optional unless noted.

Note: The Password field (plaintext) is always discarded. Use PasswordHash to supply pre-hashed credentials.

Identity Fields

FieldTypeNotes
EmailArray of {Type, Value}Required when app uses email or email+phone login. Value is automatically lowercased.
UserNameStringRequired when app uses username-primary login mode.
PhoneIdStringRequired when app uses phone-only login.
PasswordHashStringPre-hashed password. Encoding must match PasswordHashEncodingType.
ExternalUserLoginIdStringExternal system identifier.

Duplicate email types are not supported. Each email type, such as Primary or Secondary, should appear only once per profile. If multiple email entries have the same Type, only one email is retained. Do not send multiple emails with the same type, such as two Secondary emails.

Uid, IsDeleted, IsLoginLocked, AcceptPrivacyPolicy, and ExternalIds are automatically stripped. These fields cannot be set via migration.

Personal Information

FieldType
FirstNameString
LastNameString
MiddleNameString
NickNameString
PrefixString
SuffixString
ProfileNameString
GenderString
BirthDateString
AboutString
TagLineString
CompanyString
IndustryString

FullName is set automatically. We compose and save the profile's full name by joining Prefix, FirstName, MiddleName, LastName, and Suffix with spaces.

Contact & Location

FieldType
PhoneNumbersArray of {PhoneType, PhoneNumber}
AddressesArray of address objects
MainAddressString
HomeTownString
City, StateString
CountryObject: {Code, Name}
TimeZoneString
LocalLanguageString

Online Presence

FieldType
WebsiteString
ImageUrlString
ThumbnailImageUrlString
ProfileUrlString
ProfileImageUrlsMap of {String: String}
WebProfilesMap of {String: String}

State & Flags

FieldTypeNotes
EmailVerifiedBooleanWhether the email address has been verified.
PhoneIdVerifiedBooleanWhether the phone number has been verified.
IsEmailSubscribedBooleanMarketing email opt-in status.

Migration Date Fields

All dates must be parseable ISO 8601 strings. Dates in the past are accepted; future dates are rejected except for PasswordExpirationDate.

FieldTypeFuture Date Allowed
CreatedDateString (ISO 8601)No
ModifiedDateString (ISO 8601)No
LastLoginDateString (ISO 8601)No
LastPasswordChangeDateString (ISO 8601)No
PasswordExpirationDateString (ISO 8601)Yes

Login Statistics

FieldTypeNotes
NoOfLoginsIntegerHistorical login count. Must be ≥ 0.

Custom Data

FieldTypeNotes
CustomFieldsArray of {String: String}Arbitrary key-value pairs. Keys must be pre-registered in the tenant before migration.
SecurityQuestionAnswerMap of {String: String}Security Q&A pairs as a key-value object (question ID → answer).

To include SecurityQuestionAnswer in bulk migration, the corresponding security questions must already exist in the tenant configuration (added via the Admin Console). Retrieve the Security Question IDs (used as the map keys) from the LoginRadius Configuration API.

Social Providers

Use SocialProviders to link external social identities to the migrated profile.

"SocialProviders": [
{
"Name": "google",
"Id": "google-user-id-123"
}
]

The Name field must be one of the supported social providers: facebook, google, apple, amazon, or linkedin. Any other value is rejected during per-record validation (see the "Invalid social provider" error below).

SocialProviders are applied only when a profile is inserted (a new user). On an existing record matched during delta migration, submitted SocialProviders are ignored.

B2B Fields

Only valid when the tenant is configured for B2B. If OrgRoles is provided, a primary email is required.

"RoleIds": ["role-id-1", "role-id-2"],
"OrgRoles": [
{
"OrgId": "org-abc123",
"RoleIds": ["member", "admin"]
}
]

Users with no roles specified are automatically assigned the tenant's DefaultMemberRole.


DeltaMigrationModel Object

Controls behavior when a submitted profile matches an existing record (matched on the tenant's configured unique fields: email, phone, username, or a combination).

Email Matching in Delta Mode

When the tenant matches on email and the incoming profile carries multiple Email entries, the email used to look up an existing (duplicate) record in the database is chosen from the request body by type, not array position:

  • The incoming entry with Type: "Primary" is used to find the matching record.
  • If the incoming profile has no Primary entry, the first entry in its Email array is used as a fallback.

Because the lookup keys on the Primary email from the request rather than array order, a delta re-run produces the same result regardless of how the source export orders the Email array.

FieldTypeDefaultDescription
DeltaMigrationBooleanfalsefalse = insert-only; duplicates fail. true = upsert; duplicates are handled per OverWriteDuplicate.
OverWriteDuplicateBooleanfalsefalse = skip existing records. true = update existing records with submitted fields.

Behavior Matrix

DeltaMigrationOverWriteDuplicateExisting recordNew record
falseFails (reported in Failed[])Inserted
truefalseSkipped (no update)Inserted
truetrueUpdatedInserted

Insert-Only Fields (Never Overwritten in Delta Mode)

When OverWriteDuplicate=true, the following fields on existing records are protected and never overwritten:

  • Uid, _id, AppId, Provider, RegistrationProvider
  • IsActive, IsDeleted, IsLoginLocked, DisableLogin, IsProtected
  • CreatedDate, ModifiedDate, LastPasswordChangeDate, PasswordExpirationDate, LastLoginDate
  • NoOfLogins, FailedLoginAttempt, FailedResetAttempt, TokenSignSecret
  • SignupLog, PasswordHistory, B2BInfo, PrivacyPolicyHistory, PasskeyData, BreachedPassword

In delta mode, user roles (OrgRoles, RoleIds) and social identities (SocialProviders) are not updated on existing records, they are only set during initial insert.


Response

HTTP 200 is returned for both full success and partial failures. Inspect Failed[] to identify rejected records.

{
"Profile": {
"RecordInserted": 498,
"RecordUpdated": 0,
"Failed": [
{
"record": 3,
"message": "E11000 duplicate key error: email already exists"
}
]
}
}
FieldTypeDescription
Profile.RecordInsertedIntegerCount of successfully inserted records.
Profile.RecordUpdatedIntegerCount of successfully updated records (delta mode only).
Profile.FailedArrayPer-record errors. record is 1-indexed. message contains the error detail.

Error Codes

CodeHTTP StatusTrigger
7914400Empty or malformed JSON body
7992400Invalid PasswordEncryption configuration (Type, SaltAttachType, encoding values, numeric ranges)
8198400One or more profiles failed field validation. Includes Errors[] array with per-record detail.
8199400Request contains more than 500 profiles
8247400Migration is not enabled for your tenant's login configuration (phone, username, or compound login). Contact support to enable it.
8197500Unrecoverable database error during bulk write
401/403401/403Missing credentials, invalid API key/secret, missing write:migrations scope, or IP not whitelisted

Validation Error Response (8198)

{
"ErrorCode": 8198,
"Message": "One or more records have parameters that are not formatted correctly.",
"Description": "Some records in the request contain invalid or incorrectly formatted parameters. Please review the 'Errors' array for details on each affected record.",
"Errors": [
{
"FieldName": "Record 1, Field Email",
"ErrorMessage": "Email is required"
},
{
"FieldName": "Record 2, Field CreatedDate",
"ErrorMessage": "date cannot be in the future"
}
]
}

Migration Not Enabled (8247)

Returned when self-serve migration has not been provisioned for your tenant's login configuration.

{
"ErrorCode": 8247,
"Message": "Your account isn't ready for migration yet.",
"Description": "Migration isn't enabled for your login configuration (such as phone or username login). Please contact support and ask them to enable migration for your account."
}

Self-serve migration is enabled out of the box for email-based login. If your tenant uses phone, username, or a compound login mode (e.g. email + phone), migration must be enabled for your account before you begin.

If your tenant does not use standard email-based login, contact LoginRadius support before starting migration. Support will enable self-serve migration for your login configuration.

Per-Profile Validation Error Messages

Error MessageCause
"Email is required"App uses email login and no email provided
"Phone is required"App uses phone-only login and no PhoneId provided
"Username is required"App uses username-primary login and no UserName provided
"invalid date format"Date field cannot be parsed
"date cannot be in the future"CreatedDate, ModifiedDate, LastLoginDate, or LastPasswordChangeDate is a future timestamp
"value cannot be negative"NoOfLogins is negative
"email format is invalid"Server-side email validation is enabled and email fails the check
"Organization ID not found"OrgRoles[].OrgId does not exist in the tenant
"Role ID not found"A role ID in OrgRoles[].RoleIds or RoleIds[] does not exist
"Invalid social provider"SocialProviders[].Name is not one of the supported providers (facebook, google, apple, amazon, linkedin)

Examples

Insert with PBKDF2 Password Hash

curl -X POST "https://<your-lr-domain>/v2/bulk/upsert" \
-H "X-LoginRadius-ApiKey: <apiKey>" \
-H "X-LoginRadius-ApiSecret: <apiSecret>" \
-H "Content-Type: application/json" \
-d '{
"PasswordEncryption": {
"Type": "pbkdf2",
"SaltAttachType": "Attach",
"IsPerPasswordSalt": false,
"PasswordHasherVersion": "V3",
"Salt": "c2FsdFZhbHVlMTIz",
"NumberOfIteration": 10000,
"SubKeyLength": 32,
"SaltKeyLength": 16,
"PasswordHashEncodingType": "base64",
"PasswordSaltEncodingType": "base64"
},
"Profiles": [
{
"Email": [{ "Type": "Primary", "Value": "john.doe@example.com" }],
"FirstName": "John",
"LastName": "Doe",
"PasswordHash": "<base64-encoded-hash>",
"PhoneId": "12025551234",
"BirthDate": "1990-01-15",
"Gender": "M",
"TimeZone": "America/New_York",
"LocalLanguage": "en",
"EmailVerified": true,
"CreatedDate": "2021-03-10T08:00:00Z",
"LastLoginDate": "2024-11-01T12:00:00Z",
"NoOfLogins": 42,
"Addresses": [
{
"Type": "Home",
"Address1": "123 Main St",
"City": "New York",
"State": "NY",
"PostalCode": "10001",
"Country": "US"
}
],
"CustomFields": {
"subscription_tier": "premium",
"registration_source": "web"
}
}
]
}'

Delta Upsert (Update Existing Users)

curl -X POST "https://<your-lr-domain>/v2/bulk/upsert" \
-H "X-LoginRadius-ApiKey: <apiKey>" \
-H "X-LoginRadius-ApiSecret: <apiSecret>" \
-H "Content-Type: application/json" \
-d '{
"Profiles": [
{
"Email": [{ "Type": "Primary", "Value": "existing.user@example.com" }],
"FirstName": "UpdatedName",
"PasswordHash": "<new-base64-encoded-hash>"
}
],
"DeltaMigrationModel": {
"DeltaMigration": true,
"OverWriteDuplicate": true
}
}'

Argon2id Password Hash

curl -X POST "https://<your-lr-domain>/v2/bulk/upsert" \
-H "X-LoginRadius-ApiKey: <apiKey>" \
-H "X-LoginRadius-ApiSecret: <apiSecret>" \
-H "Content-Type: application/json" \
-d '{
"PasswordEncryption": {
"Type": "argon2id",
"SaltAttachType": "Attach",
"IsPerPasswordSalt": true,
"NumberOfIteration": 3,
"SubKeyLength": 32,
"SaltKeyLength": 16,
"PasswordHashThread": 4,
"PasswordHashMemory": 4096,
"PasswordHashEncodingType": "base64"
},
"Profiles": [
{
"Email": [{ "Type": "Primary", "Value": "user@example.com" }],
"PasswordHash": "<argon2id-encoded-hash-with-embedded-salt>"
}
]
}'

Troubleshooting

Authentication Errors

  • 401 / Invalid Credentials: Confirm your API Key and Secret are from the correct tenant environment (production vs sandbox).
  • IP Not Whitelisted: If your tenant enforces IP allowlisting, add your migration server's IP.

Data Validation Failures

  • Missing required field: Check which login mode the tenant is configured for (email, phone, username, or combo) and ensure every profile satisfies that requirement.
  • Invalid date format: Use ISO 8601 format (2023-01-15T10:30:00Z or 2023-01-15). Timestamps must not be in the future (except PasswordExpirationDate).
  • Custom field not found: Pre-register all custom field keys in the Admin Console before migration.

Batch Size & Performance

  • Too many records (8199): Split into batches of ≤ 500 profiles.
  • Slow throughput: Run multiple batches concurrently. Each request processes independently with ordered: false.
  • Partial failures (Failed[] is non-empty): Each entry includes a 1-indexed record number and an error message. Correct those records and re-submit only the failed ones using DeltaMigration: true, OverWriteDuplicate: false to avoid re-inserting already-successful records.

Migration Resources