Core Concepts
Understand workspaces, environments, entities, roles, and the data model that underpins every FlowOS studio.
Workspaces
A workspace is the top-level isolation boundary in FlowOS. All resources — workflows, apps, tables, incidents, users — belong to a workspace. Workspaces have their own slug, branding, domain, and billing plan.
Most SaaS customers have one workspace. Enterprises with separate business units or strict data isolation requirements create multiple workspaces. The workspace slug is used in all API calls via the X-Workspace header.
# Discover your workspace
GET /api/v1/workspaces/me
# List all workspaces your token has access to
GET /api/v1/workspacesEnvironments
Each workspace has up to three environments: development, staging, and production. Environments isolate data and workflow state — a workflow activated in staging doesn't run in production. Environment is specified via the X-Environment header (default: production).
Resource definitions (workflow configs, table schemas, connector configs) are promoted between environments via the FlowOS UI or the /api/v1/deployments endpoint. Data records are never promoted — only config.
X-Environment: production explicitly when your token was created in a non-production context. Omitting the header defaults to production, but being explicit prevents accidents.Entities and IDs
Every FlowOS resource is an entity with a typed, lexicographically sortable ID in the formatprefix_ULID. Prefixes indicate the resource type:
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| wf_ | Workflow | optional | Workflow definition | |
| run_ | WorkflowRun | optional | A single workflow execution | |
| app_ | App | optional | App Studio application | |
| tbl_ | Table | optional | DB Studio table | |
| rec_ | Record | optional | A row in a DB table | |
| con_ | Connector | optional | Integration connector | |
| inc_ | Incident | optional | ITSM incident | |
| chg_ | Change | optional | Change request | |
| prb_ | Problem | optional | Problem record | |
| usr_ | User | optional | Platform user | |
| rol_ | Role | optional | RBAC role definition | |
| ws_ | Workspace | optional | Workspace | |
| wh_ | Webhook | optional | Outbound webhook subscription | |
| art_ | Artifact | optional | SDK artifact |
Roles and Permissions
FlowOS uses role-based access control (RBAC). Roles are assigned at workspace level. Every API token inherits the permissions of the user who created it, within the scopes explicitly granted.
Built-in roles
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| viewer | Role | optional | — | Read-only access to all resources in the workspace |
| operator | Role | optional | — | Can view and update records; cannot create/delete resources or change configuration |
| developer | Role | optional | — | Full access to all studios; cannot manage users or billing |
| admin | Role | optional | — | Full access including user management, settings, and API key management |
| superadmin | Role | optional | — | Cross-workspace access; only available to instance owners |
Token scopes
When creating an API token programmatically, you can restrict it to specific scopes — even if your account has a higher role. This follows the principle of least privilege.
POST /api/v1/auth/tokens
{
"name": "CI/CD deploy token",
"expiresIn": "90d",
"scopes": [
"workflows:read",
"workflows:trigger",
"deployments:create"
]
}Available scopes
- •
workflows:read,workflows:write,workflows:trigger - •
apps:read,apps:write,apps:deploy - •
tables:read,tables:write,records:read,records:write - •
integrations:read,integrations:write - •
incidents:read,incidents:write,incidents:resolve - •
changes:read,changes:write,changes:approve - •
users:read,users:write - •
audit:read - •
webhooks:read,webhooks:write - •
sdk:read,sdk:write,sdk:publish
Audit Log
Every mutating action in FlowOS — whether through the UI or API — writes an immutable audit event. Audit events include the actor identity, resource type, resource ID, action, before/after state, and timestamp. They are tamper-proof and retained for the duration of your plan.
# Query audit log
GET /api/v1/audit?entityType=incident&action=created&from=2026-01-01&limit=50
# Single audit event
GET /api/v1/audit/:eventId{
"id": "aud_01HZ9MNPQRSTUV",
"action": "created",
"entityType": "incident",
"entityId": "inc_01HZ4KPQRSTUV",
"entityNumber": "INC-1042",
"actorId": "usr_01HZ4KPQ",
"actorName": "Alice Smith",
"actorIp": "203.0.113.1",
"workspaceId": "ws_01HZ4KPQRSTUV",
"environment": "production",
"before": null,
"after": { "title": "API latency spike", "severity": "P1", "status": "open" },
"createdAt": "2026-06-01T09:32:11.342Z"
}Common Response Envelope
All API responses use a consistent JSON envelope:
// Single resource
{
"data": { ...resource },
"meta": { "requestId": "req_01HZ..." }
}
// List resource
{
"data": [ ...resources ],
"meta": {
"total": 142,
"page": 1,
"pageSize": 20,
"nextCursor": "cursor_abc123",
"hasMore": true,
"requestId": "req_01HZ..."
}
}
// Error
{
"error": {
"code": "VALIDATION_ERROR",
"message": "title is required",
"statusCode": 422,
"fields": { "title": "required" },
"requestId": "req_01HZ..."
}
}Timestamps and Timezones
All timestamps in the API are in ISO 8601 UTC format: 2026-06-01T09:32:11.342Z. Store and process them as UTC in your application. The FlowOS UI converts to local timezone for display. Filter parameters accept both full ISO 8601 and YYYY-MM-DD date strings.
Idempotency
POST and PATCH requests accept an Idempotency-Key header. Repeat requests with the same key within 24 hours return the original response without re-executing. Use this to safely retry failed requests.
curl -X POST https://acme.flowos.io/api/v1/incidents \
-H "Authorization: Bearer $FLOWOS_TOKEN" \
-H "Idempotency-Key: client-side-uuid-here" \
-H "Content-Type: application/json" \
-d '{ "title": "API latency spike", "severity": "P1" }'Workspace Provisioning Sequence
When a new tenant or workspace is created, the platform runs an automatic provisioning sequence that seeds all the baseline data required for the workspace to be operational. Understanding this sequence matters when debugging a newly created workspace or writing platform tests.
1. Tenant creation
When a tenant is first created (POST /api/v1/tenants), the platform immediately:
- •Creates the tenant record in
root_tenants. - •Calls
seedCoreSystemDictionary(tenantId)— seeds the foundation field definitions (domains, choices, user_preferences, system_properties, feature_flags) intoroot_<tenantId>_<workspaceId>_sys_dictionaries. - •Calls
seedIntegrationDictionary(tenantId)— seeds integration-domain table definitions. - •Calls
seedUIConfigDictionary(tenantId)— seeds UI config table definitions. - •Creates the default workspace (
ws_default) if none is specified.
2. Workspace creation
When a workspace is provisioned inside an existing tenant, the platform:
- •Creates the workspace record scoped to the tenant.
- •Calls
seedSystemDictionaryForPackages(tenantId, packages)— seeds field definitions for all enabled package domains (foundation, itsm_core, cmdb, etc.) into the workspace'ssys_dictionariescollection. - •Seeds default
roles(admin, agent, end_user, readonly) intoroot_<tenantId>_<workspaceId>_roles. - •Seeds default
system_propertiesandfeature_flags. - •Creates the root domain node (
global) inroot_<tenantId>_<workspaceId>_domains. - •The
sys_counterscollection is NOT pre-populated — counter records are created lazily on the first ticket of each type.
3. Collection creation
No MongoDB collections are created during provisioning. Collections are created lazily on the first write to each table. The sys_db_objects registry is updated whenever a collection is first accessed, so DB Studio's schema view reflects the live state.
root_<tenantId>_<workspaceId>_incidents collection at all. Queries against empty or non-existent collections return an empty result, not an error.4. Model factory pattern
All Mongoose models are created at query time using one of two factory functions insrc/modules/feature-registry/model-factory.ts:
// Workspace-scoped — collection: root_<tenantId>_<workspaceId>_<tableName>
// Use for all standard tables and custom tables
getWorkspaceModel<T>(tenantId, workspaceId, tableName, schema)
// Tenant-scoped (legacy/core) — collection: root_<tenantId>_<tableName>
// Use only for tables that must be shared across all workspaces of a tenant
// (e.g. core users, tenants themselves)
getCoreModel<T>(tenantId, tableName, schema, { workspaceId? })Both functions cache the Mongoose model by collection name. Calling them repeatedly with the same arguments returns the cached model with no overhead. TheenforceTenantWorkspaceEnvelope helper (called by both factories in strict mode) automatically adds tenantId and workspaceId fields to the schema if they are not already present, and adds a compound index on them.
getWorkspaceModel for new tables. getCoreModel exists only for legacy compatibility with a handful of early tables. New modules and plugins must never create bare Mongoose models with hardcoded collection names — thecollection-guard.ts write guard will throw at runtime.