Skip to content
Ironflow is pre-1.0: APIs can change between releases. Pin a version. What is ready →

Managing Platform Users & Keys

Platform users and API keys are the two authentication methods for accessing platform management features. Users authenticate with email/password (interactive), while API keys authenticate automated systems.


Platform commands need a platform token or IRONFLOW_API_KEY. The curl examples on this page use the token as $PLATFORM_TOKEN.

The first platform admin comes from create-admin. It authenticates with IRONFLOW_API_KEY (an admin key of the default organization), prompts for a password, and refuses to run when a platform user already exists:

Terminal window
ironflow platform create-admin --email admin@example.com --name "Admin"

Then sign in. login prompts for the password and stores a platform JWT, valid for 24 hours, in ~/.config/ironflow/credentials.json:

Terminal window
ironflow platform login --email admin@example.com

For curl, send the same credentials to POST /api/v1/platform/auth/login. The response holds the token:

Terminal window
curl -X POST http://localhost:9123/api/v1/platform/auth/login \
-H "Content-Type: application/json" \
-d '{"email": "admin@example.com", "password": "secure-password"}'
{ "token": "eyJ...", "user": { "id": "puser_a1b2c3d4", "email": "admin@example.com", "name": "Admin", "roles": ["platform_admin"] } }

CLI:

Terminal window
ironflow platform users create --email ops@example.com --name "Ops User" --role-ids role_platform_admin

Password is entered interactively. The --role-ids flag is optional — users without roles have no permissions.

curl:

Terminal window
curl -X POST http://localhost:9123/api/v1/platform/users \
-H "Authorization: Bearer $PLATFORM_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"email": "ops@example.com",
"password": "secure-password",
"name": "Ops User",
"role_ids": ["role_platform_admin"]
}'

Response:

{
"id": "puser_d4e5f6g7",
"email": "ops@example.com",
"name": "Ops User",
"is_active": true,
"created_at": "2026-03-12T10:00:00Z",
"updated_at": "2026-03-12T10:00:00Z"
}
Terminal window
ironflow platform users list
ID EMAIL NAME ACTIVE
puser_a1b2c3d4 admin@example.com Admin true
puser_d4e5f6g7 ops@example.com Ops User true

Use --json for machine-readable output.

Terminal window
curl -X PUT http://localhost:9123/api/v1/platform/users/puser_d4e5f6g7 \
-H "Authorization: Bearer $PLATFORM_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "Operations Lead"}'

All fields are optional — only include fields you want to change. Available fields: name, email, password, is_active.

Deactivating a user prevents login and keeps the user record:

Terminal window
curl -X PUT http://localhost:9123/api/v1/platform/users/puser_d4e5f6g7 \
-H "Authorization: Bearer $PLATFORM_TOKEN" \
-H "Content-Type: application/json" \
-d '{"is_active": false}'

Deleting a user removes the user record permanently:

Terminal window
ironflow platform users delete puser_d4e5f6g7

Platform API keys use the ifplatform_ prefix, distinguishing them visually from tenant API keys (ifkey_ prefix). Under the hood, both are stored in the same api_keys table and use the same authentication path.

For full details on the unified key model, see API Keys.

Only a platform principal that holds platform:keys:manage (built in: platform_admin) or an admin key of the default organization (org_default) can create, rotate or delete platform keys. Listing needs platform:keys:read. An admin of any other organization gets 403.

/api/v1/apikeys is a tenant route, not a platform route. An admin key of the default organization reaches it with no extra header, and the CLI sends IRONFLOW_API_KEY, so set it to that key. A platform principal (a platform token or an ifplatform_ key) reaches it only by impersonation: send X-Ironflow-Org: org_default (for the CLI, --as-org org_default after ironflow platform login). Without the header the server answers 403 platform tokens require X-Ironflow-Org for tenant access.

A platform key created with no role has no permissions. Pass a platform role, such as role_platform_admin, as the examples do.

CLI:

Terminal window
ironflow apikey create my-ci-key --platform --role role_platform_admin
Created Platform Key: my-ci-key (id: ak_h8i9j0k1)
Key: ifplatform_a1b2c3d4e5f60123456789abcdef0123
Save this key — it will not be shown again.

curl:

Terminal window
curl -X POST http://localhost:9123/api/v1/apikeys \
-H "Authorization: Bearer $PLATFORM_TOKEN" \
-H "X-Ironflow-Org: org_default" \
-H "Content-Type: application/json" \
-d '{"name": "my-ci-key", "platform": true, "role_ids": ["role_platform_admin"]}'
Terminal window
ironflow apikey list --platform
ID NAME PREFIX ROLES CREATED LAST USED EXPIRES
ak_h8i9j0k1 my-ci-key ifplatform_a1b2c3d4 role_platform_admin 2026-03-12T10:00:00Z 2026-03-12T11:00:00Z -

The PREFIX column shows the first few characters for identification. The full key is never shown again.

Terminal window
ironflow apikey rotate ak_h8i9j0k1
Rotated API key: my-ci-key (id: ak_a1b2c3d4)
New key: ifplatform_fedcba9876543210abcdef0123456789
Save this key — it will not be shown again.

The old key is immediately invalidated. Update all systems using this key.

Terminal window
ironflow apikey delete ak_h8i9j0k1

Pass a platform API key in requests with the Authorization header:

Terminal window
curl http://localhost:9123/api/v1/platform/users \
-H "Authorization: Bearer ifplatform_a1b2c3d4e5f60123456789abcdef0123"

To impersonate a tenant with an API key, add the X-Ironflow-Org header:

Terminal window
curl -H "Content-Type: application/json" -d '{}' http://localhost:9123/ironflow.v1.IronflowService/ListFunctions \
-H "Authorization: Bearer ifplatform_a1b2c3d4e5f60123456789abcdef0123" \
-H "X-Ironflow-Org: org_x1y2z3w4"

See Impersonating Tenants for details on impersonation permissions.