Platform API
Base URL: http://localhost:9123/api/v1/platform
All endpoints except login require platform authentication via Authorization: Bearer <token> header, where <token> is either a JWT from login or a platform API key (ifplatform_*). Platform API keys are created with ironflow apikey create --platform — see API Keys for details.
Bootstrap
Section titled “Bootstrap”POST /api/v1/platform/bootstrap
Section titled “POST /api/v1/platform/bootstrap”One-time bootstrap endpoint to initialize the platform. Creates the first admin user. Requires a tenant admin API key and refuses if any platform users already exist.
Request:
{ "email": "admin@example.com", "password": "secure-password", "name": "Admin User"}Response (201): Returns the created admin user.
| Status | Description |
|---|---|
| 201 | Admin user created |
| 409 | Platform already bootstrapped (users exist) |
Authentication
Section titled “Authentication”POST /api/v1/platform/auth/login
Section titled “POST /api/v1/platform/auth/login”Authenticate a platform user and receive a JWT token.
Request:
{ "email": "admin@example.com", "password": "your-password"}Response (200):
{ "token": "eyJhbGciOiJIUzI1NiIs...", "user": { "id": "puser_a1b2c3d4", "email": "admin@example.com", "name": "Platform Admin", "roles": ["platform_admin"] }}Also sets an ironflow_dashboard_token HttpOnly cookie (24h TTL).
| Status | Description |
|---|---|
| 200 | Login successful |
| 401 | Invalid credentials |
| 403 | Account is inactive |
GET /api/v1/platform/users
Section titled “GET /api/v1/platform/users”List all platform users.
Response (200):
[ { "id": "puser_a1b2c3d4", "email": "admin@example.com", "name": "Platform Admin", "is_active": true, "created_at": "2026-03-12T10:00:00Z", "updated_at": "2026-03-12T10:00:00Z" }]POST /api/v1/platform/users
Section titled “POST /api/v1/platform/users”Create a platform user.
Request:
{ "email": "ops@example.com", "password": "secure-password", "name": "Ops User", "role_ids": ["role_platform_admin"]}| Field | Type | Required | Description |
|---|---|---|---|
email | string | Yes | Unique email address |
password | string | Yes | User password |
name | string | Yes | Display name |
role_ids | string[] | No | Platform role IDs to assign |
Response (201): Returns the created user object.
| Status | Description |
|---|---|
| 201 | User created |
| 400 | Missing required fields |
| 409 | Email already exists |
GET /api/v1/platform/users/{id}
Section titled “GET /api/v1/platform/users/{id}”Get a single platform user by ID.
Response (200): Returns the user object.
| Status | Description |
|---|---|
| 200 | Success |
| 404 | User not found |
PUT /api/v1/platform/users/{id}
Section titled “PUT /api/v1/platform/users/{id}”Update a platform user. All fields are optional.
Request:
{ "name": "New Name", "email": "new@example.com", "password": "new-password", "is_active": false}Response (200): Returns the updated user object.
| Status | Description |
|---|---|
| 200 | User updated |
| 404 | User not found |
| 409 | Email already exists |
DELETE /api/v1/platform/users/{id}
Section titled “DELETE /api/v1/platform/users/{id}”Delete a platform user.
| Status | Description |
|---|---|
| 204 | User deleted |
| 404 | User not found |
API Keys
Section titled “API Keys”Platform API keys are now managed through the unified /api/v1/apikeys endpoint with platform=true. See API Keys for the full API key documentation.
GET /api/v1/apikeys?platform=true
Section titled “GET /api/v1/apikeys?platform=true”List all platform API keys. The full key value is never returned.
Response (200):
[ { "id": "ak_e5f6g7h8", "name": "my-ci-key", "prefix": "ifplatform_a1b2c3d4", "created_at": "2026-03-12T10:00:00Z" }]POST /api/v1/apikeys
Section titled “POST /api/v1/apikeys”Create a platform API key by setting "platform": true in the request body.
Request:
{ "name": "my-ci-key", "platform": true, "role_ids": ["role_platform_admin"]}| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Key name |
platform | bool | No | Set to true for platform keys |
role_ids | string[] | No | Role IDs to assign |
Response (201):
{ "id": "ak_e5f6g7h8", "name": "my-ci-key", "prefix": "ifplatform_a1b2c3d4", "key": "ifplatform_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6", "created_at": "2026-03-12T10:00:00Z"}The key field is only returned on creation and rotation.
DELETE /api/v1/apikeys/{id}
Section titled “DELETE /api/v1/apikeys/{id}”Delete (revoke) an API key.
| Status | Description |
|---|---|
| 204 | Key deleted |
| 404 | Key not found |
POST /api/v1/apikeys/{id}/rotate
Section titled “POST /api/v1/apikeys/{id}/rotate”Rotate a key — generates a new key value while keeping the same ID and role assignments. The old key is immediately invalidated.
Response (200):
{ "id": "ak_e5f6g7h8", "name": "my-ci-key", "prefix": "ifplatform_x9y8z7w6", "key": "ifplatform_x9y8z7w6v5u4t3s2r1q0p9o8n7m6l5k4", "created_at": "2026-03-12T10:00:00Z"}GET /api/v1/platform/roles
Section titled “GET /api/v1/platform/roles”List all platform roles (built-in and custom).
Response (200):
[ { "id": "role_platform_admin", "name": "platform_admin", "is_default": false, "created_at": "2026-03-12T10:00:00Z" }]GET /api/v1/platform/roles/{id}
Section titled “GET /api/v1/platform/roles/{id}”Get a single role.
POST /api/v1/platform/roles
Section titled “POST /api/v1/platform/roles”Create a custom platform role.
Request:
{ "name": "support-role", "policy_ids": ["pol_abc123"]}| Status | Description |
|---|---|
| 201 | Role created |
| 409 | Role name already exists |
PUT /api/v1/platform/roles/{id}
Section titled “PUT /api/v1/platform/roles/{id}”Update a custom role.
Request:
{ "name": "renamed-role"}| Status | Description |
|---|---|
| 200 | Role updated |
DELETE /api/v1/platform/roles/{id}
Section titled “DELETE /api/v1/platform/roles/{id}”Delete a custom role. Built-in roles cannot be deleted.
| Status | Description |
|---|---|
| 204 | Role deleted |
| 400 | Cannot delete built-in role |
Policies
Section titled “Policies”GET /api/v1/platform/policies
Section titled “GET /api/v1/platform/policies”List all platform policies.
GET /api/v1/platform/policies/{id}
Section titled “GET /api/v1/platform/policies/{id}”Get a single policy.
POST /api/v1/platform/policies
Section titled “POST /api/v1/platform/policies”Create a platform policy.
Request:
{ "name": "allow-tenant-read", "effect": "allow", "actions": "platform:tenants:read,platform:impersonate:read", "resources": "*", "condition": ""}| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Policy name |
effect | string | Yes | allow or deny |
actions | string | Yes | Comma-separated action list |
resources | string | Yes | Comma-separated resource patterns |
condition | string | No | CEL expression |
PUT /api/v1/platform/policies/{id}
Section titled “PUT /api/v1/platform/policies/{id}”Update a policy.
DELETE /api/v1/platform/policies/{id}
Section titled “DELETE /api/v1/platform/policies/{id}”Delete a policy.
Tenants
Section titled “Tenants”GET /api/v1/platform/tenants
Section titled “GET /api/v1/platform/tenants”List all tenant organizations (excludes the platform sentinel org).
Response (200):
[ { "id": "org_x1y2z3w4", "name": "Acme Corp", "created_at": "2026-03-12T10:00:00Z" }]GET /api/v1/platform/tenants/{id}
Section titled “GET /api/v1/platform/tenants/{id}”Get a single tenant.
POST /api/v1/platform/tenants
Section titled “POST /api/v1/platform/tenants”Provision a new tenant organization.
Request:
{ "name": "Acme Corp"}Response (201): Returns the created tenant object.
DELETE /api/v1/platform/tenants/{id}
Section titled “DELETE /api/v1/platform/tenants/{id}”Delete a tenant organization. Cannot delete the platform sentinel org (org_platform).
| Status | Description |
|---|---|
| 204 | Tenant deleted |
| 400 | Cannot delete platform org |
| 404 | Tenant not found |
POST /api/v1/platform/orgs
Section titled “POST /api/v1/platform/orgs”Platform-side alias for POST /api/v1/orgs. Creates an organization without bundling an environment or admin key (use POST /api/v1/platform/tenants for full tenant provisioning). Exists so platform admins can create orgs without an impersonation header — the tenant-scoped POST /api/v1/orgs is denied at the RBAC gate when called with platform credentials.
Request:
{ "name": "Acme Corp"}Response (201): Returns the created organization object.
GET /api/v1/platform/audit
Section titled “GET /api/v1/platform/audit”Query platform audit events.
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
event_type | string | Filter by event type (e.g., platform.user.created) |
from | string | Start timestamp (ISO 8601, inclusive) |
to | string | End timestamp (ISO 8601, inclusive) |
platform_key_id | string | Filter by acting platform API key |
impersonated_org_id | string | Filter by impersonated organization |
cursor | string | Pagination cursor from previous response |
limit | int | Max results per page (default: 100) |
Response (200):
{ "events": [ { "id": "01HXYZ...", "event_type": "platform.user.created", "scope": "platform", "platform_key_id": "ak_e5f6g7h8", "platform_user_id": "puser_a1b2c3d4", "impersonated_org_id": "", "payload": { "user_id": "puser_d4e5f6g7", "email": "ops@example.com" }, "created_at": "2026-03-12T10:00:00Z" } ], "total": 42}Event Types:
| Event Type | Description |
|---|---|
platform.user.created | Platform user created |
platform.user.updated | Platform user updated |
platform.user.deleted | Platform user deleted |
platform.key.created | Platform API key created |
platform.key.revoked | Platform API key deleted or rotated |
platform.role.changed | Platform role created, updated, or deleted |
platform.impersonated | Impersonated request initiated |
Impersonation Headers
Section titled “Impersonation Headers”Any non-platform endpoint can be called with platform credentials by adding the X-Ironflow-Org header:
curl http://localhost:9123/api/v1/functions \ -H "Authorization: Bearer $PLATFORM_TOKEN" \ -H "X-Ironflow-Org: org_x1y2z3w4"See Impersonating Tenants for details.
See Also
Section titled “See Also”- Platform CLI Reference — CLI equivalents for all endpoints
- Platform Roles Reference — role and action tables
- Managing Platform Users & Keys — usage guide