Skip to main content
Ory Kratos provides powerful tools for importing identities from external systems and exporting identity data. This is useful for migrations, backups, and integrating with external user management systems.

Importing identities

You can import identities using the Admin API or CLI with support for credentials, traits, and metadata.

Import via CLI

The CLI supports importing single identities or batches from JSON files:
kratos import identities identity.json

Import via Admin API

For programmatic imports, use the Admin API:
1

Single identity import

Use the POST /admin/identities endpoint:
curl -X POST "https://kratos-admin/admin/identities" \
  -H "Content-Type: application/json" \
  -d @identity.json
2

Batch import

Use the PATCH /admin/identities endpoint for bulk operations:
curl -X PATCH "https://kratos-admin/admin/identities" \
  -H "Content-Type: application/json" \
  -d '{
    "identities": [
      {"create": {...}},
      {"create": {...}},
      {"create": {...}}
    ]
  }'

Import format

Basic identity

identity.json
{
  "schema_id": "default",
  "traits": {
    "email": "[email protected]",
    "name": {
      "first": "John",
      "last": "Doe"
    }
  },
  "state": "active"
}

Identity with credentials

identity-with-password.json
{
  "schema_id": "default",
  "traits": {
    "email": "[email protected]"
  },
  "credentials": {
    "password": {
      "config": {
        "hashed_password": "$2a$10$XQ7..."
      }
    }
  },
  "verifiable_addresses": [
    {
      "value": "[email protected]",
      "verified": true,
      "via": "email",
      "status": "completed"
    }
  ]
}

Identity with OIDC credentials

identity-with-oidc.json
{
  "schema_id": "default",
  "traits": {
    "email": "[email protected]"
  },
  "credentials": {
    "oidc": {
      "config": {
        "providers": [
          {
            "subject": "google-user-id-12345",
            "provider": "google"
          }
        ]
      }
    }
  }
}

Identity with metadata

identity-with-metadata.json
{
  "schema_id": "default",
  "traits": {
    "email": "[email protected]"
  },
  "metadata_public": {
    "theme": "dark",
    "language": "en"
  },
  "metadata_admin": {
    "internal_id": "legacy-12345",
    "migrated_from": "old-system",
    "migration_date": "2024-01-15"
  },
  "external_id": "external-system-id-12345"
}

Batch import

Batch request format

{
  "identities": [
    {
      "patch_id": "00000000-0000-0000-0000-000000000001",
      "create": {
        "schema_id": "default",
        "traits": {
          "email": "[email protected]"
        }
      }
    },
    {
      "patch_id": "00000000-0000-0000-0000-000000000002",
      "create": {
        "schema_id": "default",
        "traits": {
          "email": "[email protected]"
        }
      }
    }
  ]
}

Batch response format

{
  "identities": [
    {
      "action": "create",
      "identity": "9f425a8d-7efc-4768-8f23-7647a74fdf13",
      "patch_id": "00000000-0000-0000-0000-000000000001"
    },
    {
      "action": "error",
      "patch_id": "00000000-0000-0000-0000-000000000002",
      "error": {
        "code": 409,
        "status": "Conflict",
        "reason": "identity with this email already exists"
      }
    }
  ]
}

Batch limits

Import limits:
  • Up to 1,000 identities per request with hashed passwords
  • Up to 200 identities per request with plaintext passwords
Plaintext passwords must be hashed during import, which is CPU-intensive. Use pre-hashed passwords for large imports.

Password import

Supported hash formats

Kratos accepts passwords in PHC format:
$2a$10$XQ7...

Plaintext password import

{
  "credentials": {
    "password": {
      "config": {
        "password": "user-password-123"
      }
    }
  }
}
Plaintext passwords are hashed using bcrypt during import. For performance, pre-hash passwords before importing large batches.

Password migration hook

For just-in-time password migration:
{
  "credentials": {
    "password": {
      "config": {
        "use_password_migration_hook": true
      }
    }
  }
}
This triggers a webhook during the user’s first login to retrieve and migrate their password from the legacy system.

Verifiable and recovery addresses

Import verified addresses

{
  "traits": {
    "email": "[email protected]"
  },
  "verifiable_addresses": [
    {
      "value": "[email protected]",
      "verified": true,
      "via": "email",
      "status": "completed"
    }
  ],
  "recovery_addresses": [
    {
      "value": "[email protected]",
      "via": "email"
    }
  ]
}
Addresses must match fields in the identity schema. If a trait changes during an update, addresses not matching traits will be removed.

Exporting identities

Export via Admin API

curl "https://kratos-admin/admin/identities?page_size=100"

Export response format

[
  {
    "id": "9f425a8d-7efc-4768-8f23-7647a74fdf13",
    "schema_id": "default",
    "schema_url": "https://example.com/schemas/default",
    "state": "active",
    "state_changed_at": "2024-01-15T09:30:00Z",
    "traits": {
      "email": "[email protected]",
      "name": {
        "first": "John",
        "last": "Doe"
      }
    },
    "verifiable_addresses": [
      {
        "id": "...",
        "value": "[email protected]",
        "verified": true,
        "via": "email",
        "status": "completed",
        "created_at": "2024-01-15T09:30:00Z"
      }
    ],
    "recovery_addresses": [...],
    "metadata_public": {...},
    "created_at": "2024-01-15T09:30:00Z",
    "updated_at": "2024-01-15T09:30:00Z"
  }
]

Pagination

Use keyset pagination for large exports:
# First page
curl "https://kratos-admin/admin/identities?page_size=250"

# Next page (use page_token from Link header)
curl "https://kratos-admin/admin/identities?page_size=250&page_token=..."

Import validation

The import process performs validation:
1

Schema validation

Identity traits are validated against the specified schema.
2

Credential validation

Password hashes must be in valid PHC format. OIDC providers must have subject and provider fields.
3

Identifier uniqueness

Credential identifiers must be unique across all identities.
4

Address consistency

Verifiable and recovery addresses must match trait fields defined in the schema.

Handling import errors

When batch importing, check the response for errors:
identity/handler.go
type BatchIdentityPatchResponse struct {
    Action     BatchPatchAction        `json:"action"`
    IdentityID *uuid.UUID              `json:"identity,omitempty"`
    PatchID    *uuid.UUID              `json:"patch_id,omitempty"`
    Error      *herodot.DefaultError   `json:"error,omitempty"`
}

Best practices

Import considerations:
  • Pre-hash passwords for batches larger than 200 identities
  • Use patch_id to correlate responses with requests
  • Import verifiable addresses as verified: true to skip verification flows
  • Set external_id to link identities to your legacy system
  • Use metadata_admin for internal tracking data
  • Validate import files before batch operations

Performance optimization

  • Import in batches of 200-1000 identities
  • Use pre-hashed passwords to avoid CPU bottlenecks
  • Parallelize imports across multiple requests
  • Monitor database performance during large imports
  • Use transactions where possible

Example: Complete migration

#!/bin/bash
# Export from old system and import to Kratos

# 1. Export from legacy system
legacy-export-tool --output users.json

# 2. Transform to Kratos format
jq '[.[] | {
  create: {
    schema_id: "default",
    traits: {email: .email, name: .full_name},
    credentials: {
      password: {config: {hashed_password: .password_hash}}
    },
    verifiable_addresses: [{
      value: .email,
      verified: .email_verified,
      via: "email",
      status: "completed"
    }],
    external_id: .id,
    metadata_admin: {legacy_id: .id, migrated_at: now}
  }
}] | {identities: .}' users.json > kratos-import.json

# 3. Import to Kratos in batches
split -l 500 kratos-import.json batch-

for batch in batch-*; do
  curl -X PATCH "https://kratos-admin/admin/identities" \
    -H "Content-Type: application/json" \
    -d @"$batch"
done

Build docs developers (and LLMs) love