workos-migrate-clerk
WorkOS Migration: Clerk
Step 1: Fetch Documentation (BLOCKING)
STOP. Do not proceed until complete.
WebFetch: https://workos.com/docs/migrate/clerk
The migration guide is the source of truth. If this skill conflicts with the guide, follow the guide.
Step 2: Pre-Migration Assessment (Decision Tree)
User authentication methods?
|
+-- Passwords --> Step 3A: Export passwords via Clerk API
|
+-- Social (Google/Microsoft) --> Skip to Step 4 (auto-link by email)
|
+-- Mixed --> Complete both paths
Organization structure?
|
+-- B2B with orgs --> Step 5: Export organizations
|
+-- B2C (no orgs) --> Skip Step 5
MFA enabled?
|
+-- SMS-based --> Users MUST re-enroll (WorkOS does not support SMS)
|
+-- TOTP-based --> Users MUST re-enroll (cannot export secrets)
Critical limitation: Clerk TOTP secrets and SMS configurations cannot be exported. All MFA users must re-enroll after migration.
Step 3A: Export Passwords (If Password Auth Used)
Via Clerk Backend API
Use Clerk's user export API to generate CSV with password hashes.
Verify: CSV contains password_digest column with bcrypt hashes before proceeding.
Important: Clerk does NOT export plaintext passwords. The CSV contains bcrypt hashes only.
Field Mapping (Password Export)
Clerk CSV Column --> WorkOS User Creation Field
email_addresses --> email (primary)
first_name --> first_name
last_name --> last_name
password_digest --> password_hash (with password_hash_type='bcrypt')
Step 3B: Handle Multi-Email Users
Clerk exports multiple emails pipe-separated:
"email_addresses": "john@example.com|john.doe@example.com"
Problem: Export does NOT indicate which is primary.
Solution: If multiple emails detected, fetch User object via Clerk User API to determine primary email before WorkOS import.
Step 4: Import Users into WorkOS
Rate Limit Warning
User creation is rate-limited. Check https://workos.com/docs/reference/rate-limits for current limits.
Pattern: Batch imports with delay between batches to avoid 429 errors.
Import Method (Choose One)
Option A: Use WorkOS migration tool: https://github.com/workos/migrate-clerk-users
Option B: Write custom import using WorkOS User Creation API
User Creation Parameters
For each Clerk user, call Create User API:
Required fields:
email- from Clerkemail_addresses(primary only)first_name- from Clerkfirst_namelast_name- from Clerklast_name
Password import (if exported):
password_hash_type- MUST be'bcrypt'(Clerk's hash algorithm)password_hash- from Clerkpassword_digestfield (the bcrypt hash)
Example payload:
{
"email": "user@example.com",
"first_name": "John",
"last_name": "Doe",
"password_hash_type": "bcrypt",
"password_hash": "$2a$10$..."
}
Verification command:
# Check that imported user can authenticate
curl -X POST https://api.workos.com/user_management/authenticate \
-H "Authorization: Bearer $WORKOS_API_KEY" \
-d "email=user@example.com" \
-d "password=<test_password>"
Step 5: Migrate Social Auth Users
Supported Providers
WorkOS supports Google OAuth and Microsoft OAuth. Configure providers in WorkOS Dashboard before migration.
Auto-Linking Mechanism
No manual action required for social auth users. WorkOS auto-links by email:
- User signs in with Google/Microsoft via WorkOS
- WorkOS extracts email from OAuth provider
- WorkOS matches email to existing user created in Step 4
- User is linked automatically
Critical: Ensure users from Step 4 have correct email addresses. Mismatch = failed auto-link.
Step 6: Create Organizations (If B2B)
Export Clerk Organizations
Use Clerk Organization List API to paginate through organizations.
Pattern:
// Pseudocode - use Clerk Backend SDK
let hasMore = true;
let offset = 0;
while (hasMore) {
const { data, totalCount } = await clerkClient.organizations.getOrganizationList({
limit: 100,
offset
});
// Process data
hasMore = (offset + data.length) < totalCount;
offset += data.length;
}
Create WorkOS Organizations
For each Clerk organization, call Create Organization API.
Minimum required:
name- organization display name
Verification command:
# List created organizations
curl https://api.workos.com/organizations \
-H "Authorization: Bearer $WORKOS_API_KEY"
Step 7: Add Organization Memberships
Export Clerk Memberships
Use Clerk Membership List API for each organization.
Create WorkOS Memberships
For each membership, call Create Organization Membership API.
Required parameters:
user_id- WorkOS user ID (from Step 4 import mapping)organization_id- WorkOS organization ID (from Step 6 mapping)
Critical: Maintain mapping of Clerk user IDs → WorkOS user IDs and Clerk org IDs → WorkOS org IDs for this step.
Step 8: Handle MFA Re-Enrollment
SMS-Based MFA (Not Supported)
WorkOS does NOT support SMS-based MFA due to security concerns.
Users with SMS MFA MUST:
- Re-enroll using TOTP authenticator app, OR
- Use Magic Link authentication instead
Notify affected users before migration.
TOTP-Based MFA (Cannot Migrate)
Clerk does not export TOTP secrets. All TOTP users must re-enroll.
Re-enrollment flow:
- User signs in with password (migrated in Step 4)
- User navigates to MFA settings
- User scans new QR code to enroll TOTP
See WorkOS MFA guide for enrollment implementation.
Verification Checklist (ALL MUST PASS)
Run these checks before marking migration complete:
# 1. Verify users imported
curl https://api.workos.com/user_management/users \
-H "Authorization: Bearer $WORKOS_API_KEY" | jq '.data | length'
# Should match Clerk user count
# 2. Test password authentication for imported user
curl -X POST https://api.workos.com/user_management/authenticate \
-H "Authorization: Bearer $WORKOS_API_KEY" \
-d "email=test@example.com" \
-d "password=<known_password>"
# Should return 200 with user object
# 3. Verify organizations imported (if B2B)
curl https://api.workos.com/organizations \
-H "Authorization: Bearer $WORKOS_API_KEY" | jq '.data | length'
# Should match Clerk organization count
# 4. Verify memberships created (if B2B)
curl "https://api.workos.com/user_management/organization_memberships?user_id=<user_id>" \
-H "Authorization: Bearer $WORKOS_API_KEY"
# Should return memberships for test user
# 5. Test social auth auto-link
# Sign in via Google/Microsoft with email matching imported user
# Should link to existing WorkOS user, not create duplicate
Error Recovery
"User already exists" on import
Cause: Duplicate email addresses in import batch or re-running import.
Fix: Check for existing user before creation:
curl "https://api.workos.com/user_management/users?email=user@example.com" \
-H "Authorization: Bearer $WORKOS_API_KEY"
If exists, use Update User API instead of Create.
"Invalid password hash" on user creation
Cause:
password_hash_typenot set to'bcrypt'password_hashfield missing$2a$prefix (malformed bcrypt hash)
Fix: Verify Clerk export contains valid bcrypt hashes. They MUST start with $2a$ or $2b$.
Rate limit 429 on batch import
Cause: Exceeding WorkOS user creation rate limit.
Fix: Add delay between batches:
// Pseudocode
for (const batch of userBatches) {
await importBatch(batch);
await sleep(1000); // 1 second delay between batches
}
Check https://workos.com/docs/reference/rate-limits for current limits and adjust delay accordingly.
Social auth user creates duplicate instead of linking
Cause: Email mismatch between WorkOS user and OAuth provider email.
Fix:
- Check OAuth provider email:
curlthe OAuth token endpoint - Check WorkOS user email: Use Users API
- If mismatch, update WorkOS user email via Update User API
Organization membership creation fails with "User not found"
Cause: Attempting to create membership before user import completes, or using wrong user ID.
Fix:
- Verify user exists:
curlUsers API with email filter - Ensure you're using WorkOS user ID, not Clerk user ID
- Maintain ID mapping file:
clerk_user_id → workos_user_id
MFA users cannot sign in after migration
Expected behavior. All MFA users MUST re-enroll:
- Disable MFA requirement temporarily in WorkOS Dashboard
- Notify users to re-enroll via app settings
- Re-enable MFA requirement after re-enrollment window
Related Skills
- workos-authkit-nextjs - Integrate WorkOS AuthKit into Next.js after migration
- workos-authkit-react - Integrate WorkOS AuthKit into React after migration
- workos-mfa - Implement MFA re-enrollment flow
- workos-magic-link - Alternative to password auth for users who can't migrate
- workos-api-authkit - Core AuthKit API reference for custom integrations
- workos-api-organization - Organization management API details
- workos-sso - Configure SSO for migrated organizations