Skip to main content
The Ory Kratos Admin API provides complete control over identity lifecycle management. This guide covers creating, reading, updating, and deleting identities programmatically.

Admin API endpoints

The Admin API is exposed on a separate port from the public API for security:
EndpointMethodDescription
/admin/identitiesGETList identities
/admin/identitiesPOSTCreate identity
/admin/identitiesPATCHBatch create identities
/admin/identities/{id}GETGet identity by ID
/admin/identities/{id}PUTUpdate identity
/admin/identities/{id}PATCHPatch identity
/admin/identities/{id}DELETEDelete identity
/admin/identities/{id}/credentials/{type}DELETEDelete credential
/admin/identities/by/external/{externalID}GETGet by external ID

Create identity

Create a new identity with traits and optional credentials.

Basic creation

curl -X POST "https://kratos-admin/admin/identities" \
  -H "Content-Type: application/json" \
  -d '{
    "schema_id": "default",
    "traits": {
      "email": "[email protected]",
      "name": {
        "first": "Jane",
        "last": "Smith"
      }
    }
  }'

Create with password

POST /admin/identities
{
  "schema_id": "default",
  "traits": {
    "email": "[email protected]"
  },
  "credentials": {
    "password": {
      "config": {
        "password": "secure-password-123"
      }
    }
  },
  "state": "active"
}

Create with external ID

Link to an external system using external_id:
POST /admin/identities
{
  "schema_id": "default",
  "traits": {
    "email": "[email protected]"
  },
  "external_id": "legacy-system-user-123",
  "metadata_admin": {
    "imported_from": "legacy_db",
    "import_date": "2024-01-15"
  }
}
The external_id must be unique across all identities. Use it to maintain references to users in external systems.

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:00.000Z",
  "traits": {
    "email": "[email protected]"
  },
  "verifiable_addresses": [],
  "recovery_addresses": [],
  "metadata_public": null,
  "created_at": "2024-01-15T09:30:00.000Z",
  "updated_at": "2024-01-15T09:30:00.000Z"
}

Get identity

Get by ID

curl "https://kratos-admin/admin/identities/{id}"

Get with credentials

Include credential configuration using the include_credential parameter:
curl "https://kratos-admin/admin/identities/{id}?include_credential=password&include_credential=oidc"
Response includes credentials:
{
  "id": "...",
  "credentials": {
    "password": {
      "type": "password",
      "identifiers": ["[email protected]"],
      "config": {}
    },
    "oidc": {
      "type": "oidc",
      "identifiers": ["google:123456"],
      "config": {
        "providers": [
          {
            "subject": "123456",
            "provider": "google",
            "initial_id_token": "eyJ...",
            "initial_access_token": "ya29...",
            "initial_refresh_token": "1//..."
          }
        ]
      }
    }
  }
}

Get by external ID

Lookup identity by external identifier:
curl "https://kratos-admin/admin/identities/by/external/{externalID}"

List identities

Basic listing

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

Filter by credentials identifier

Find identities by email or username:
# Exact match
curl "https://kratos-admin/admin/[email protected]"

# Similar match (experimental)
curl "https://kratos-admin/admin/identities?preview_credentials_identifier_similar=john"

Filter by IDs

Retrieve specific identities:
curl "https://kratos-admin/admin/identities?ids=id1&ids=id2&ids=id3"
The ids filter:
  • Maximum 500 IDs per request
  • Does not support pagination
  • Ignores duplicate or non-existent IDs
  • May return results in different order than requested

Filter by organization

curl "https://kratos-admin/admin/identities?organization_id={org-uuid}"

Pagination

Kratos supports both page-based and keyset pagination:
# First page
curl "https://kratos-admin/admin/identities?page_size=250"

# Get next page token from Link header
curl "https://kratos-admin/admin/identities?page_size=250&page_token=..."

Update identity

Full update (PUT)

Replace the entire identity:
curl -X PUT "https://kratos-admin/admin/identities/{id}" \
  -H "Content-Type: application/json" \
  -d '{
    "schema_id": "default",
    "traits": {
      "email": "[email protected]",
      "name": {
        "first": "John",
        "last": "Updated"
      }
    },
    "state": "active"
  }'

Partial update (PATCH)

Update specific fields using JSON Patch:
curl -X PATCH "https://kratos-admin/admin/identities/{id}" \
  -H "Content-Type: application/json" \
  -d '[
    {
      "op": "replace",
      "path": "/traits/email",
      "value": "[email protected]"
    }
  ]'

Protected fields

The following fields cannot be modified via PATCH:
  • id
  • stateChangedAt
  • credentials
When the state field changes, state_changed_at is automatically updated to the current timestamp.

Delete identity

Delete single identity

curl -X DELETE "https://kratos-admin/admin/identities/{id}"
Deletion is permanent and irreversible. All associated sessions, credentials, and addresses are also deleted.

Delete credential

Remove a specific credential type:
curl -X DELETE "https://kratos-admin/admin/identities/{id}/credentials/password"
Supported credential types:
  • password
  • oidc
  • saml
  • totp
  • lookup_secret
  • webauthn

Identity states

Identities can be in one of two states:
StateDescription
activeIdentity can authenticate and use the system
inactiveIdentity is disabled and cannot sign in

Change state

curl -X PATCH "https://kratos-admin/admin/identities/{id}" \
  -H "Content-Type: application/json" \
  -d '[{
    "op": "replace",
    "path": "/state",
    "value": "inactive"
  }]'

Metadata

Identities support two metadata fields:

Public metadata

Visible to the identity itself:
{
  "metadata_public": {
    "theme": "dark",
    "language": "en",
    "notifications_enabled": true
  }
}

Admin metadata

Only accessible through Admin API:
{
  "metadata_admin": {
    "internal_id": "emp-12345",
    "department": "Engineering",
    "hire_date": "2024-01-15",
    "notes": "VIP customer"
  }
}
Do not store sensitive information like credit scores or health data in metadata. Use a separate secure database for sensitive data.

Error handling

Common errors

Invalid request format or validation error:
{
  "error": {
    "code": 400,
    "status": "Bad Request",
    "reason": "traits.email: Does not match format 'email'"
  }
}
Identity does not exist:
{
  "error": {
    "code": 404,
    "status": "Not Found",
    "reason": "Unable to locate the resource"
  }
}
Duplicate identifier (e.g., email already exists):
{
  "error": {
    "code": 409,
    "status": "Conflict",
    "reason": "This identity conflicts with another identity that already exists."
  }
}

Code examples

Complete CRUD workflow

package main

import (
    "context"
    "fmt"
    kratos "github.com/ory/kratos/pkg/httpclient"
)

func main() {
    ctx := context.Background()
    client := kratos.NewAPIClient(kratos.NewConfiguration())

    // Create
    identity, _, err := client.IdentityAPI.CreateIdentity(ctx).
        CreateIdentityBody(kratos.CreateIdentityBody{
            SchemaId: "default",
            Traits: map[string]interface{}{
                "email": "[email protected]",
            },
        }).Execute()
    if err != nil {
        panic(err)
    }
    fmt.Printf("Created: %s\n", identity.Id)

    // Read
    identity, _, err = client.IdentityAPI.
        GetIdentity(ctx, identity.Id).Execute()
    if err != nil {
        panic(err)
    }
    fmt.Printf("Read: %v\n", identity.Traits)

    // Update
    identity, _, err = client.IdentityAPI.UpdateIdentity(ctx, identity.Id).
        UpdateIdentityBody(kratos.UpdateIdentityBody{
            SchemaId: "default",
            Traits: map[string]interface{}{
                "email": "[email protected]",
            },
            State: kratos.IDENTITYSTATE_ACTIVE,
        }).Execute()
    if err != nil {
        panic(err)
    }
    fmt.Printf("Updated: %v\n", identity.Traits)

    // Delete
    _, err = client.IdentityAPI.DeleteIdentity(ctx, identity.Id).Execute()
    if err != nil {
        panic(err)
    }
    fmt.Println("Deleted")
}

Rate limiting

Admin API endpoints have different rate limit buckets:
BucketEndpointsTypical Limit
kratos-admin-lowGET single identityHigh
kratos-admin-mediumList operationsMedium
kratos-admin-highCreate, Update, DeleteLower
Exact rate limits depend on your deployment configuration. Check response headers for limit information:
  • X-RateLimit-Limit
  • X-RateLimit-Remaining
  • X-RateLimit-Reset

Build docs developers (and LLMs) love