Skip to main content

OutDo Data API (0.1.0)

Download OpenAPI specification:Download

The public REST API for the OutDo platform: authentication, the workspace model (workspaces, modules, rails, features, columns, views), row data, aggregates, and files.

Authentication

Sign in with POST /v1/auth/token to obtain a bearer token, then click Authorize and paste the access_token. Every request runs with the signed-in user's own permissions — the API enforces the platform's row-level security and never widens access.

Machines authenticate with an organization API key (outdo_sk_…) in the same Authorization: Bearer header. A key belongs to one organization and carries scopes: read for safe methods, write for row writes, and structure for changing the model itself (features, columns, views, automations). structure is additive — it is only granted together with write.

Conventions

  • Errors follow RFC 7807 (application/problem+json); 422 responses carry an actionable detail. Every error also carries a stable code (e.g. rate_limited, quota_exceeded, no_seats_available); branch on code, not on the English detail text.
  • Pagination is cursor-based: pass next_cursor from the previous page back as cursor.
  • Idempotency: send an Idempotency-Key header on any POST to make retries safe. POST /v1/auth/* and POST /v1/api-keys are excluded: their responses carry credential material, which is never cached; retrying key creation mints a new key.
  • Rate limits apply per route and per client (and per API key when one is used); 429 responses carry a Retry-After header.
  • Versioning: this surface is /v1; breaking changes ship under a new prefix.

Machine-readable structure: GET /v1/schema (the caller's visible model) and GET /v1/schema/definitions (what each entity's config accepts).

Documentation

The guides live at docs.out-do.app: the REST reference, automations (the definition envelope, the validator, and the draft/apply cycle), the generated catalogs of steps and triggers, and the MCP server.

health

Service liveness.

Liveness probe

Unauthenticated. Touches no downstream service — safe for load-balancer checks.

Responses

Response samples

Content type
application/json
{
  • "status": "ok"
}

auth

Sign-in and token refresh, delegated to the platform's identity provider.

Refresh an access token

Exchanges a refresh token for a new session; the previous refresh token is rotated.

Request Body schema: application/json
required
refresh_token
required
string (Refresh Token)

Responses

Request samples

Content type
application/json
{
  • "refresh_token": "string"
}

Response samples

Content type
application/json
{
  • "access_token": "string",
  • "expires_in": 0,
  • "refresh_token": "string"
}

Sign in with email and password

Exchanges OutDo credentials for a bearer session. Authentication failures return a generic 401 (no cause disclosed) and the endpoint is strictly rate-limited.

Request Body schema: application/json
required
email
required
string (Email)
password
required
string (Password)

Responses

Request samples

Content type
application/json
{
  • "email": "string",
  • "password": "string"
}

Response samples

Content type
application/json
{
  • "access_token": "string",
  • "expires_in": 0,
  • "refresh_token": "string"
}

organizations

Organizations the caller belongs to.

List organizations

Cursor-paginated. Returns the organizations the caller owns or belongs to.

Authorizations:
HTTPBearer
query Parameters
Cursor (string) or Cursor (null) (Cursor)

Opaque cursor taken from next_cursor of the previous page.

limit
integer (Limit) >= 1
Default: 25

Page size, 1 to 200. Defaults to 25.

header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_cursor": "string"
}

Get an organization

Returns 404 when the organization does not exist or is not visible to the caller.

Authorizations:
HTTPBearer
path Parameters
organization_id
required
string <uuid> (Organization Id)
header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)

Responses

Response samples

Content type
application/json
{
  • "config": { },
  • "id": "string",
  • "label": "string",
  • "name": "string"
}

Update an organization

Partial update of label and/or config. config is REPLACED whole, not merged: read it first and send the complete object. Requires an organization-level admin role; a write the data store refuses comes back as 403, never a silent 200. API keys need the write scope (not structure: this is not model structure).

Authorizations:
HTTPBearer
path Parameters
organization_id
required
string <uuid> (Organization Id)
header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)
Request Body schema: application/json
required
Config (object) or Config (null) (Config)
Label (string) or Label (null) (Label)

Responses

Request samples

Content type
application/json
{
  • "config": { },
  • "label": "string"
}

Response samples

Content type
application/json
{
  • "config": { },
  • "id": "string",
  • "label": "string",
  • "name": "string"
}

workspaces

Workspaces — the top-level containers of the model.

List workspaces

Cursor-paginated. Returns only what row-level security lets the caller see.

Authorizations:
HTTPBearer
query Parameters
Cursor (string) or Cursor (null) (Cursor)

Opaque cursor taken from next_cursor of the previous page.

limit
integer (Limit) >= 1
Default: 25

Page size, 1 to 200. Defaults to 25.

header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_cursor": "string"
}

Create a workspace

The caller must be a member of the target organization.

Authorizations:
HTTPBearer
header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)
Request Body schema: application/json
required
Config (object) or Config (null) (Config)
label
required
string (Label) [ 1 .. 120 ] characters
name
required
string (Name) [ 1 .. 120 ] characters
organization_id
required
string (Organization Id)
Timezone (string) or Timezone (null) (Timezone)
Visibility Policy (string) or Visibility Policy (null) (Visibility Policy)

Responses

Request samples

Content type
application/json
{
  • "config": { },
  • "label": "string",
  • "name": "string",
  • "organization_id": "string",
  • "timezone": "string",
  • "visibility_policy": "string"
}

Response samples

Content type
application/json
{
  • "config": { },
  • "default_module_id": "string",
  • "id": "string",
  • "label": "string",
  • "module_order_config": { },
  • "name": "string",
  • "organization_id": "string",
  • "timezone": "string",
  • "visibility_policy": "string"
}

Delete a workspace

Destructive: the database cascades to every module, rail, feature, row, and view inside. A non-empty workspace returns 409 unless force=true.

Authorizations:
HTTPBearer
path Parameters
workspace_id
required
string <uuid> (Workspace Id)
query Parameters
force
boolean (Force)
Default: false

Confirm deleting a NON-EMPTY workspace.

header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)

Responses

Get a workspace

Returns 404 when the workspace does not exist or is not visible to the caller.

Authorizations:
HTTPBearer
path Parameters
workspace_id
required
string <uuid> (Workspace Id)
header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)

Responses

Response samples

Content type
application/json
{
  • "config": { },
  • "default_module_id": "string",
  • "id": "string",
  • "label": "string",
  • "module_order_config": { },
  • "name": "string",
  • "organization_id": "string",
  • "timezone": "string",
  • "visibility_policy": "string"
}

Update a workspace

Partial update — only the fields present in the request body are changed.

Authorizations:
HTTPBearer
path Parameters
workspace_id
required
string <uuid> (Workspace Id)
header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)
Request Body schema: application/json
required
Config (object) or Config (null) (Config)
Default Module Id (string) or Default Module Id (null) (Default Module Id)
Label (string) or Label (null) (Label)
Module Order Config (object) or Module Order Config (null) (Module Order Config)
Name (string) or Name (null) (Name)
Timezone (string) or Timezone (null) (Timezone)
Visibility Policy (string) or Visibility Policy (null) (Visibility Policy)

Responses

Request samples

Content type
application/json
{
  • "config": { },
  • "default_module_id": "string",
  • "label": "string",
  • "module_order_config": { },
  • "name": "string",
  • "timezone": "string",
  • "visibility_policy": "string"
}

Response samples

Content type
application/json
{
  • "config": { },
  • "default_module_id": "string",
  • "id": "string",
  • "label": "string",
  • "module_order_config": { },
  • "name": "string",
  • "organization_id": "string",
  • "timezone": "string",
  • "visibility_policy": "string"
}

modules

Navigation modules inside a workspace.

List modules

Cursor-paginated. Optionally filtered to a single workspace.

Authorizations:
HTTPBearer
query Parameters
Workspace Id (string) or Workspace Id (null) (Workspace Id)

Only modules of this workspace.

Cursor (string) or Cursor (null) (Cursor)

Opaque cursor taken from next_cursor of the previous page.

limit
integer (Limit) >= 1
Default: 25

Page size, 1 to 200. Defaults to 25.

header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_cursor": "string"
}

Create a module

The organization is derived from the parent workspace — it is never accepted from the client.

Authorizations:
HTTPBearer
header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)
Request Body schema: application/json
required
Icon (string) or Icon (null) (Icon)
label
required
string (Label) [ 1 .. 120 ] characters
name
required
string (Name) [ 1 .. 120 ] characters
workspace_id
required
string (Workspace Id)

Responses

Request samples

Content type
application/json
{
  • "icon": "string",
  • "label": "string",
  • "name": "string",
  • "workspace_id": "string"
}

Response samples

Content type
application/json
{
  • "default_rail_id": "string",
  • "icon": "string",
  • "id": "string",
  • "label": "string",
  • "name": "string",
  • "organization_id": "string",
  • "rail_order_config": { },
  • "workspace_id": "string"
}

Delete a module

A module that still has rails returns 409 unless force=true (rails cascade).

Authorizations:
HTTPBearer
path Parameters
module_id
required
string <uuid> (Module Id)
query Parameters
force
boolean (Force)
Default: false

Confirm deleting a module that still has rails.

header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)

Responses

Get a module

Returns 404 when the module does not exist or is not visible to the caller.

Authorizations:
HTTPBearer
path Parameters
module_id
required
string <uuid> (Module Id)
header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)

Responses

Response samples

Content type
application/json
{
  • "default_rail_id": "string",
  • "icon": "string",
  • "id": "string",
  • "label": "string",
  • "name": "string",
  • "organization_id": "string",
  • "rail_order_config": { },
  • "workspace_id": "string"
}

Update a module

Partial update — only the fields present in the request body are changed.

Authorizations:
HTTPBearer
path Parameters
module_id
required
string <uuid> (Module Id)
header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)
Request Body schema: application/json
required
Default Rail Id (string) or Default Rail Id (null) (Default Rail Id)
Icon (string) or Icon (null) (Icon)
Label (string) or Label (null) (Label)
Name (string) or Name (null) (Name)
Rail Order Config (object) or Rail Order Config (null) (Rail Order Config)

Responses

Request samples

Content type
application/json
{
  • "default_rail_id": "string",
  • "icon": "string",
  • "label": "string",
  • "name": "string",
  • "rail_order_config": { }
}

Response samples

Content type
application/json
{
  • "default_rail_id": "string",
  • "icon": "string",
  • "id": "string",
  • "label": "string",
  • "name": "string",
  • "organization_id": "string",
  • "rail_order_config": { },
  • "workspace_id": "string"
}

rails

Navigation rails inside a module; a rail's config lists the views it shows.

List rails

Cursor-paginated. Optionally filtered to a single module.

Authorizations:
HTTPBearer
query Parameters
Module Id (string) or Module Id (null) (Module Id)

Only rails of this module.

Cursor (string) or Cursor (null) (Cursor)

Opaque cursor taken from next_cursor of the previous page.

limit
integer (Limit) >= 1
Default: 25

Page size, 1 to 200. Defaults to 25.

header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_cursor": "string"
}

Create a rail

The organization is derived from the parent module. config lists the views the rail shows; an object is accepted and normalized to the platform's single-item-list shape.

Authorizations:
HTTPBearer
header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)
Request Body schema: application/json
required
Array of Config (any) or Config (object) or Config (null) (Config)
label
required
string (Label) [ 1 .. 120 ] characters
module_id
required
string (Module Id)
path
string (Path)
Default: ""

Responses

Request samples

Content type
application/json
{
  • "config": [
    ],
  • "label": "string",
  • "module_id": "string",
  • "path": ""
}

Response samples

Content type
application/json
{
  • "config": [
    ],
  • "id": "string",
  • "label": "string",
  • "module_id": "string",
  • "organization_id": "string",
  • "path": "string"
}

Delete a rail

Removes the navigation entry; the views it pointed at are not touched.

Authorizations:
HTTPBearer
path Parameters
rail_id
required
string <uuid> (Rail Id)
header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)

Responses

Get a rail

Returns 404 when the rail does not exist or is not visible to the caller.

Authorizations:
HTTPBearer
path Parameters
rail_id
required
string <uuid> (Rail Id)
header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)

Responses

Response samples

Content type
application/json
{
  • "config": [
    ],
  • "id": "string",
  • "label": "string",
  • "module_id": "string",
  • "organization_id": "string",
  • "path": "string"
}

Update a rail

Partial update — only the fields present in the request body are changed.

Authorizations:
HTTPBearer
path Parameters
rail_id
required
string <uuid> (Rail Id)
header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)
Request Body schema: application/json
required
Array of Config (any) or Config (object) or Config (null) (Config)
Label (string) or Label (null) (Label)
Path (string) or Path (null) (Path)

Responses

Request samples

Content type
application/json
{
  • "config": [
    ],
  • "label": "string",
  • "path": "string"
}

Response samples

Content type
application/json
{
  • "config": [
    ],
  • "id": "string",
  • "label": "string",
  • "module_id": "string",
  • "organization_id": "string",
  • "path": "string"
}

features

Features (data tables) and their column definitions.

List features

Cursor-paginated. Optionally filtered to a single workspace.

Authorizations:
HTTPBearer
query Parameters
Workspace Id (string) or Workspace Id (null) (Workspace Id)

Only features of this workspace.

Cursor (string) or Cursor (null) (Cursor)

Opaque cursor taken from next_cursor of the previous page.

limit
integer (Limit) >= 1
Default: 25

Page size, 1 to 200. Defaults to 25.

header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_cursor": "string"
}

Create a feature

The organization is derived from the parent workspace — it is never accepted from the client.

Authorizations:
HTTPBearer
header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)
Request Body schema: application/json
required
Config (object) or Config (null) (Config)
Label (string) or Label (null) (Label)
name
required
string (Name) [ 1 .. 120 ] characters
workspace_id
required
string (Workspace Id)

Responses

Request samples

Content type
application/json
{
  • "config": { },
  • "label": "string",
  • "name": "string",
  • "workspace_id": "string"
}

Response samples

Content type
application/json
{
  • "config": { },
  • "id": "string",
  • "label": "string",
  • "name": "string",
  • "organization_id": "string",
  • "workspace_id": "string"
}

Delete a feature

Destructive: the database cascades to the feature's rows, columns, and views. A feature that still has rows returns 409 unless force=true.

Authorizations:
HTTPBearer
path Parameters
feature_id
required
string <uuid> (Feature Id)
query Parameters
force
boolean (Force)
Default: false

Confirm deleting a feature that still has rows.

header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)

Responses

Get a feature

Returns 404 when the feature does not exist or is not visible to the caller.

Authorizations:
HTTPBearer
path Parameters
feature_id
required
string <uuid> (Feature Id)
header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)

Responses

Response samples

Content type
application/json
{
  • "config": { },
  • "id": "string",
  • "label": "string",
  • "name": "string",
  • "organization_id": "string",
  • "workspace_id": "string"
}

Update a feature

Partial update — only the fields present in the request body are changed.

Authorizations:
HTTPBearer
path Parameters
feature_id
required
string <uuid> (Feature Id)
header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)
Request Body schema: application/json
required
Config (object) or Config (null) (Config)
Label (string) or Label (null) (Label)
Name (string) or Name (null) (Name)

Responses

Request samples

Content type
application/json
{
  • "config": { },
  • "label": "string",
  • "name": "string"
}

Response samples

Content type
application/json
{
  • "config": { },
  • "id": "string",
  • "label": "string",
  • "name": "string",
  • "organization_id": "string",
  • "workspace_id": "string"
}

List columns

All column definitions of the feature, in display order.

Authorizations:
HTTPBearer
path Parameters
feature_id
required
string <uuid> (Feature Id)
header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)

Responses

Response samples

Content type
application/json
[
  • {
    }
]

Create a column

type and default_component are validated against the platform registries — see GET /v1/schema/definitions for the allowed values.

Authorizations:
HTTPBearer
path Parameters
feature_id
required
string <uuid> (Feature Id)
header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)
Request Body schema: application/json
required
object (Config)
default_component
string (Default Component)
Default: "textfield"
externally_visible
boolean (Externally Visible)
Default: false
hidden
boolean (Hidden)
Default: false
Initial Value (string) or Initial Value (null) (Initial Value)
inline_editable
boolean (Inline Editable)
Default: false
label
required
string (Label) [ 1 .. 120 ] characters
name
required
string (Name) [ 1 .. 64 ] characters
Order (integer) or Order (null) (Order)
read_only
boolean (Read Only)
Default: false
required
boolean (Required)
Default: false
type
string (Type)
Default: "text"

Responses

Request samples

Content type
application/json
{
  • "config": { },
  • "default_component": "textfield",
  • "externally_visible": false,
  • "hidden": false,
  • "initial_value": "string",
  • "inline_editable": false,
  • "label": "string",
  • "name": "string",
  • "order": 0,
  • "read_only": false,
  • "required": false,
  • "type": "text"
}

Response samples

Content type
application/json
{
  • "config": { },
  • "default_component": "string",
  • "externally_visible": false,
  • "feature_id": "string",
  • "hidden": true,
  • "id": "string",
  • "initial_value": "string",
  • "inline_editable": false,
  • "label": "string",
  • "name": "string",
  • "order": 0,
  • "read_only": true,
  • "required": true,
  • "type": "string"
}

Delete a column

Removes the column DEFINITION. Row data keeps the orphaned key — it simply stops being displayed and validated.

Authorizations:
HTTPBearer
path Parameters
feature_id
required
string <uuid> (Feature Id)
column_id
required
string <uuid> (Column Id)
header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)

Responses

Update a column

Partial update — only the fields present in the request body are changed.

Authorizations:
HTTPBearer
path Parameters
feature_id
required
string <uuid> (Feature Id)
column_id
required
string <uuid> (Column Id)
header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)
Request Body schema: application/json
required
Config (object) or Config (null) (Config)
Default Component (string) or Default Component (null) (Default Component)
Externally Visible (boolean) or Externally Visible (null) (Externally Visible)
Hidden (boolean) or Hidden (null) (Hidden)
Initial Value (string) or Initial Value (null) (Initial Value)
Inline Editable (boolean) or Inline Editable (null) (Inline Editable)
Label (string) or Label (null) (Label)
Name (string) or Name (null) (Name)
Order (integer) or Order (null) (Order)
Read Only (boolean) or Read Only (null) (Read Only)
Required (boolean) or Required (null) (Required)
Type (string) or Type (null) (Type)

Responses

Request samples

Content type
application/json
{
  • "config": { },
  • "default_component": "string",
  • "externally_visible": true,
  • "hidden": true,
  • "initial_value": "string",
  • "inline_editable": true,
  • "label": "string",
  • "name": "string",
  • "order": 0,
  • "read_only": true,
  • "required": true,
  • "type": "string"
}

Response samples

Content type
application/json
{
  • "config": { },
  • "default_component": "string",
  • "externally_visible": false,
  • "feature_id": "string",
  • "hidden": true,
  • "id": "string",
  • "initial_value": "string",
  • "inline_editable": false,
  • "label": "string",
  • "name": "string",
  • "order": 0,
  • "read_only": true,
  • "required": true,
  • "type": "string"
}

rows

Row data: CRUD, bulk insert, filtering, sorting, and aggregates.

List rows

Cursor-paginated. Supports filtering (filter, match), sorting (sort), and response-side projection (fields) — see each parameter for the grammar.

Authorizations:
HTTPBearer
path Parameters
feature_id
required
string <uuid> (Feature Id)
query Parameters
Cursor (string) or Cursor (null) (Cursor)

Opaque cursor taken from next_cursor of the previous page.

limit
integer (Limit) >= 1
Default: 25

Page size, 1 to 200. Defaults to 25.

Array of Filter (strings) or Filter (null) (Filter)

Repeatable column:op:value, combined with AND (or OR via match=any). Text ops (data->>col as text): eq, neq, gt, gte, lt, lte, like, ilike (* wildcards), contains, ncontains, startswith, endswith (case-insensitive substring/prefix/suffix), in (status:in:open|closed), is (null|true|false). Numeric ops (data->col as JSONB, correct numeric order for stored JSON numbers): eqn, neqn, gtn, gten, ltn, lten. Any op can be negated with the not. prefix (status:not.eq:closed). Columns are the row data keys plus id/created_at/updated_at.

Match (string) or Match (null) (Match)

How the filters combine: all (AND, default) or any (OR).

Sort (string) or Sort (null) (Sort)

Sort key: column asc, -column desc. Changes the cursor scheme — do not mix cursors across sorts.

Fields (string) or Fields (null) (Fields)

Comma-separated projection of the row data keys, e.g. fields=title,amount.

header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_cursor": "string"
}

Create a row

data is validated against the feature's column definitions by default — see the validate parameter. Send an Idempotency-Key header to make retries safe.

Authorizations:
HTTPBearer
path Parameters
feature_id
required
string <uuid> (Feature Id)
query Parameters
Validate (string) or Validate (null) (Validate)

Write validation against the feature's columns: on (default — required columns present, typed columns storable, check digits of masked numbers, and a best-effort uniqueness probe for the columns that declare it), strict (also rejects unknown data keys), off. Uniqueness is ALWAYS enforced by the database, whatever the mode: a repeated value comes back as 409 naming the column. Currency columns are stored rounded to their declared decimal places (2 when unset).

header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)
Request Body schema: application/json
required
required
object (Data)
property name*
additional property
any

Responses

Request samples

Content type
application/json
{
  • "data": { }
}

Response samples

Content type
application/json
{
  • "created_at": "string",
  • "created_by": "string",
  • "data": { },
  • "feature_id": "string",
  • "id": "string",
  • "organization_id": "string",
  • "updated_at": "string",
  • "updated_by": "string"
}

Delete a row

Returns 204 on success; 404 when the row does not exist, is not visible, or the caller may not delete it.

Authorizations:
HTTPBearer
path Parameters
feature_id
required
string <uuid> (Feature Id)
row_id
required
string <uuid> (Row Id)
header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)

Responses

Get a row

Returns 404 when the row does not exist or is not visible to the caller.

Authorizations:
HTTPBearer
path Parameters
feature_id
required
string <uuid> (Feature Id)
row_id
required
string <uuid> (Row Id)
header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)

Responses

Response samples

Content type
application/json
{
  • "created_at": "string",
  • "created_by": "string",
  • "data": { },
  • "feature_id": "string",
  • "id": "string",
  • "organization_id": "string",
  • "updated_at": "string",
  • "updated_by": "string"
}

Update a row

Replaces the whole data object by default; with merge=true the payload is shallow-merged over the current data and a null value removes the key.

Authorizations:
HTTPBearer
path Parameters
feature_id
required
string <uuid> (Feature Id)
row_id
required
string <uuid> (Row Id)
query Parameters
Validate (string) or Validate (null) (Validate)

Write validation against the feature's columns: on (default — required columns present, typed columns storable, check digits of masked numbers, and a best-effort uniqueness probe for the columns that declare it), strict (also rejects unknown data keys), off. Uniqueness is ALWAYS enforced by the database, whatever the mode: a repeated value comes back as 409 naming the column. Currency columns are stored rounded to their declared decimal places (2 when unset).

merge
boolean (Merge)
Default: false

true → shallow-merge the payload over the current data (a null value removes the key); false (default) → replace the whole data object.

header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)
Request Body schema: application/json
required
required
object (Data)
property name*
additional property
any

Responses

Request samples

Content type
application/json
{
  • "data": { }
}

Response samples

Content type
application/json
{
  • "created_at": "string",
  • "created_by": "string",
  • "data": { },
  • "feature_id": "string",
  • "id": "string",
  • "organization_id": "string",
  • "updated_at": "string",
  • "updated_by": "string"
}

Aggregate rows (count, sum, avg)

count is exact and accepts the same filters as the list endpoint. sum/avg compute over a capped sample (10,000 rows — narrow with filters beyond that); non-numeric values are skipped and reported via rows_considered.

Authorizations:
HTTPBearer
path Parameters
feature_id
required
string <uuid> (Feature Id)
query Parameters
op
required
string (Op)
Enum: "count" "sum" "avg"
Column (string) or Column (null) (Column)

Row data key (required for sum/avg).

Array of Filter (strings) or Filter (null) (Filter)

Repeatable column:op:value, combined with AND (or OR via match=any). Text ops (data->>col as text): eq, neq, gt, gte, lt, lte, like, ilike (* wildcards), contains, ncontains, startswith, endswith (case-insensitive substring/prefix/suffix), in (status:in:open|closed), is (null|true|false). Numeric ops (data->col as JSONB, correct numeric order for stored JSON numbers): eqn, neqn, gtn, gten, ltn, lten. Any op can be negated with the not. prefix (status:not.eq:closed). Columns are the row data keys plus id/created_at/updated_at.

Match (string) or Match (null) (Match)

How the filters combine: all (AND, default) or any (OR).

header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)

Responses

Response samples

Content type
application/json
{
  • "column": "string",
  • "op": "string",
  • "rows_considered": 0,
  • "value": 0
}

Create rows in bulk

Up to 500 rows in one atomic insert — if any row is rejected, the whole batch rolls back. Validation errors name the offending row (rows[i]: …).

Authorizations:
HTTPBearer
path Parameters
feature_id
required
string <uuid> (Feature Id)
query Parameters
Validate (string) or Validate (null) (Validate)

Write validation against the feature's columns: on (default — required columns present, typed columns storable, check digits of masked numbers, and a best-effort uniqueness probe for the columns that declare it), strict (also rejects unknown data keys), off. Uniqueness is ALWAYS enforced by the database, whatever the mode: a repeated value comes back as 409 naming the column. Currency columns are stored rounded to their declared decimal places (2 when unset).

header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)
Request Body schema: application/json
required
required
Array of objects (Rows) [ 1 .. 500 ] items
Array ([ 1 .. 500 ] items)
required
object (Data)
property name*
additional property
any

Responses

Request samples

Content type
application/json
{
  • "rows": [
    ]
}

Response samples

Content type
application/json
{
  • "count": 0,
  • "items": [
    ]
}

views

Saved views over a feature (table, kanban, form, …).

List views

Cursor-paginated. Optionally filtered to a single feature.

Authorizations:
HTTPBearer
query Parameters
Feature Id (string) or Feature Id (null) (Feature Id)

Only views of this feature.

Cursor (string) or Cursor (null) (Cursor)

Opaque cursor taken from next_cursor of the previous page.

limit
integer (Limit) >= 1
Default: 25

Page size, 1 to 200. Defaults to 25.

header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_cursor": "string"
}

Create a view

type is validated against the view registry (see GET /v1/schema/definitions). config accepts an object or a list and is normalized to the platform's single-item-list shape. API keys need the structure scope.

Authorizations:
HTTPBearer
header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)
Request Body schema: application/json
required
Array of Config (any) or Config (object) or Config (null) (Config)
feature_id
required
string (Feature Id)
label
required
string (Label) [ 1 .. 120 ] characters
name
required
string (Name) [ 1 .. 120 ] characters
type
required
string (Type)

Responses

Request samples

Content type
application/json
{
  • "config": [
    ],
  • "feature_id": "string",
  • "label": "string",
  • "name": "string",
  • "type": "string"
}

Response samples

Content type
application/json
{
  • "config": [
    ],
  • "feature_id": "string",
  • "id": "string",
  • "label": "string",
  • "name": "string",
  • "organization_id": "string",
  • "type": "string"
}

Delete a view

Removes the saved view; the feature's data is not touched.

Authorizations:
HTTPBearer
path Parameters
view_id
required
string <uuid> (View Id)
header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)

Responses

Get a view

Returns 404 when the view does not exist or is not visible to the caller.

Authorizations:
HTTPBearer
path Parameters
view_id
required
string <uuid> (View Id)
header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)

Responses

Response samples

Content type
application/json
{
  • "config": [
    ],
  • "feature_id": "string",
  • "id": "string",
  • "label": "string",
  • "name": "string",
  • "organization_id": "string",
  • "type": "string"
}

Update a view

Partial update — only the fields present in the request body are changed.

Authorizations:
HTTPBearer
path Parameters
view_id
required
string <uuid> (View Id)
header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)
Request Body schema: application/json
required
Array of Config (any) or Config (object) or Config (null) (Config)
Label (string) or Label (null) (Label)
Name (string) or Name (null) (Name)

Responses

Request samples

Content type
application/json
{
  • "config": [
    ],
  • "label": "string",
  • "name": "string"
}

Response samples

Content type
application/json
{
  • "config": [
    ],
  • "feature_id": "string",
  • "id": "string",
  • "label": "string",
  • "name": "string",
  • "organization_id": "string",
  • "type": "string"
}

schema

The caller's whole visible model in one call, plus config definitions.

Get the workspace model

Workspaces → features → columns + views, nested. Structure only (no rows). truncated: true means a size cap was hit — narrow with workspace_id.

Authorizations:
HTTPBearer
query Parameters
Workspace Id (string) or Workspace Id (null) (Workspace Id)
header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)

Responses

Response samples

Content type
application/json
{
  • "counts": {
    },
  • "truncated": true,
  • "workspaces": [
    ]
}

Get configuration definitions

Static, versioned reference for programmatic builders (the AI MCP): allowed column types and components, view types and their config shapes, feature/workspace/rail/module config keys, row-data and file-metadata conventions. The write endpoints validate against these same allow-lists. source tells you where the reference came from: generated (emitted from the platform's field declarations) or legacy (the hand-maintained fallback, which may lag the code). format=legacy forces the older hand-maintained SHAPE (top-level column, view, components, view_config, ...): kept for one release so existing callers can migrate, and then removed. Build new clients on the default.

Authorizations:
HTTPBearer
query Parameters
format
string (Format)
Deprecated
Default: "generated"
Enum: "generated" "legacy"

DEPRECATED: legacy serves the previous shape for one release.

header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)

Responses

Response samples

Content type
application/json
{ }

files

File uploads and signed downloads for file columns.

Upload a file

Multipart upload. The object path is built server-side as feature_{feature_id}/column_{column_id}/{timestamp}_{fileName}; the response is the exact metadata object a file component stores in row data.

Authorizations:
HTTPBearer
header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)
Request Body schema: multipart/form-data
required
column_id
required
string <uuid> (Column Id)
feature_id
required
string <uuid> (Feature Id)
file
required
string <application/octet-stream> (File)

Responses

Response samples

Content type
application/json
{
  • "fileName": "string",
  • "fileSize": 0,
  • "mimeType": "string",
  • "path": "string",
  • "syncStatus": "synced"
}

Create a signed download URL

Returns a time-limited URL (1 minute to 7 days, default 1 hour) for a stored object. Visibility follows the same permissions as the file's feature.

Authorizations:
HTTPBearer
query Parameters
path
required
string (Path)

The stored object path (FileOut.path).

Expires In (integer) or Expires In (null) (Expires In)

Lifetime in seconds, 60 to 604800. Defaults to 3600.

header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)

Responses

Response samples

Content type
application/json
{
  • "expires_in": 0,
  • "url": "string"
}

workflows

Workflow definitions, triggering, and run status.

List workflows

Cursor-paginated. Optionally filtered to a single workspace.

Authorizations:
HTTPBearer
query Parameters
Workspace Id (string) or Workspace Id (null) (Workspace Id)

Only workflows of this workspace.

Cursor (string) or Cursor (null) (Cursor)

Opaque cursor taken from next_cursor of the previous page.

limit
integer (Limit) >= 1
Default: 25

Page size, 1 to 200. Defaults to 25.

header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_cursor": "string"
}

Get a workflow

Returns 404 when the workflow does not exist or is not visible to the caller.

Authorizations:
HTTPBearer
path Parameters
workflow_id
required
string <uuid> (Workflow Id)
header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)

Responses

Response samples

Content type
application/json
{
  • "description": "string",
  • "id": "string",
  • "is_active": true,
  • "name": "string",
  • "organization_id": "string",
  • "trigger_config": { },
  • "workspace_id": "string"
}

List runs of a workflow

Newest first. Cursor-paginated.

Authorizations:
HTTPBearer
path Parameters
workflow_id
required
string <uuid> (Workflow Id)
query Parameters
Cursor (string) or Cursor (null) (Cursor)

Opaque cursor taken from next_cursor of the previous page.

limit
integer (Limit) >= 1
Default: 25

Page size, 1 to 200. Defaults to 25.

header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_cursor": "string"
}

Get a workflow run

Returns 404 when the run does not exist or is not visible to the caller.

Authorizations:
HTTPBearer
path Parameters
workflow_id
required
string <uuid> (Workflow Id)
run_id
required
string <uuid> (Run Id)
header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)

Responses

Response samples

Content type
application/json
{
  • "completed_at": "string",
  • "error_message": "string",
  • "id": "string",
  • "input_data": { },
  • "is_dry_run": true,
  • "output_data": { },
  • "started_at": "string",
  • "status": "string",
  • "steps_executed": 0,
  • "total_steps": 0,
  • "trigger_type": "string",
  • "triggered_by": "string",
  • "workflow_id": "string",
  • "workspace_id": "string"
}

Trigger a workflow run

Queues one run of the workflow with the given input and returns it immediately (202 — execution is asynchronous). Poll the run's status until it reaches success, failed, or cancelled. Inactive workflows return 409. Send an Idempotency-Key header to make retries safe.

Authorizations:
HTTPBearer
path Parameters
workflow_id
required
string <uuid> (Workflow Id)
header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)
Request Body schema: application/json
required
dry_run
boolean (Dry Run)
Default: false
object (Input Data)

Responses

Request samples

Content type
application/json
{
  • "dry_run": false,
  • "input_data": { }
}

Response samples

Content type
application/json
{
  • "completed_at": "string",
  • "error_message": "string",
  • "id": "string",
  • "input_data": { },
  • "is_dry_run": true,
  • "output_data": { },
  • "started_at": "string",
  • "status": "string",
  • "steps_executed": 0,
  • "total_steps": 0,
  • "trigger_type": "string",
  • "triggered_by": "string",
  • "workflow_id": "string",
  • "workspace_id": "string"
}

messages

Messages posted to a scope (channel, feature, row, workspace, or organization).

Post a message

Post a message to a scope — a channel, feature, feature row, workspace, or organization. The organization is derived from the scope and the sender is the authenticated caller; neither is accepted from the body. Authorization is decided by the Message INSERT policy (organization membership): a caller who cannot see the scope gets 404.

Authorizations:
HTTPBearer
header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)
Request Body schema: application/json
required
content
required
string (Content) non-empty
Metadata (object) or Metadata (null) (Metadata)
Parent Id (string) or Parent Id (null) (Parent Id)
scope_id
required
string (Scope Id)
scope_type
required
string (Scope Type)
type
string (Type)
Default: "chat"

Responses

Request samples

Content type
application/json
{
  • "content": "string",
  • "metadata": { },
  • "parent_id": "string",
  • "scope_id": "string",
  • "scope_type": "string",
  • "type": "chat"
}

Response samples

Content type
application/json
{
  • "content": "string",
  • "created_at": "string",
  • "id": "string",
  • "metadata": { },
  • "organization_id": "string",
  • "parent_id": "string",
  • "scope_id": "string",
  • "scope_type": "string",
  • "sender_id": "string",
  • "type": "string",
  • "updated_at": "string"
}

api-keys

Scoped API keys (machine identities) — create, list, and revoke.

List API keys

Cursor-paginated. Returns key metadata (never the secret). Optionally filtered to one organization.

Authorizations:
HTTPBearer
query Parameters
Organization Id (string) or Organization Id (null) (Organization Id)

Only keys of this organization.

Cursor (string) or Cursor (null) (Cursor)

Opaque cursor taken from next_cursor of the previous page.

limit
integer (Limit) >= 1
Default: 25

Page size, 1 to 200. Defaults to 25.

header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_cursor": "string"
}

Create an API key

Mints a scoped API key backed by a machine user. The response carries the plaintext secret once — store it now, it cannot be retrieved again. The key acts as a member of the organization with the given role and scopes; requires permission to manage the organization's keys. Set expires_at (ISO-8601) or expires_in_days for a time-limited key; omit both for a non-expiring key.

Authorizations:
HTTPBearer
header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)
Request Body schema: application/json
required
Expires At (string) or Expires At (null) (Expires At)

Absolute expiry as ISO-8601 UTC. Mutually exclusive with expires_in_days.

Expires In Days (integer) or Expires In Days (null) (Expires In Days)

Relative expiry in days from creation. Mutually exclusive with expires_at.

name
required
string (Name) [ 1 .. 120 ] characters
organization_id
required
string (Organization Id)
role
string (Role)
Default: "user"
Enum: "admin" "dev" "user" "view"
scopes
Array of strings (Scopes) non-empty
Items Enum: "read" "write" "structure"
Workspace Id (string) or Workspace Id (null) (Workspace Id)

Responses

Request samples

Content type
application/json
{
  • "expires_at": "string",
  • "expires_in_days": 1,
  • "name": "string",
  • "organization_id": "string",
  • "role": "admin",
  • "scopes": [
    ],
  • "workspace_id": "string"
}

Response samples

Content type
application/json
{
  • "created_at": "string",
  • "created_by": "string",
  • "expires_at": "string",
  • "id": "string",
  • "key_prefix": "string",
  • "last_used_at": "string",
  • "name": "string",
  • "organization_id": "string",
  • "revoked_at": "string",
  • "role": "string",
  • "scopes": [
    ],
  • "secret": "string",
  • "workspace_id": "string"
}

Revoke an API key

Revokes the key and tears down its machine user's membership immediately — the key can no longer authenticate. Idempotent.

Authorizations:
HTTPBearer
path Parameters
key_id
required
string <uuid> (Key Id)
header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)

Responses

Response samples

Content type
application/json
{
  • "code": "error",
  • "detail": "string",
  • "instance": "string",
  • "status": 0,
  • "title": "string",
  • "type": "about:blank"
}

Get an API key

Returns key metadata (never the secret). 404 when not visible to the caller.

Authorizations:
HTTPBearer
path Parameters
key_id
required
string <uuid> (Key Id)
header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)

Responses

Response samples

Content type
application/json
{
  • "created_at": "string",
  • "created_by": "string",
  • "expires_at": "string",
  • "id": "string",
  • "key_prefix": "string",
  • "last_used_at": "string",
  • "name": "string",
  • "organization_id": "string",
  • "revoked_at": "string",
  • "role": "string",
  • "scopes": [
    ],
  • "workspace_id": "string"
}

workflows-v2

WorkflowV2 automations: list and read definitions, validate a graph, create a workflow as a draft, add a version, and apply a draft. Writes need the structure scope. Activation is never an API call — a person turns the automation on in the app. The envelope of a definition and the full vocabulary are documented at docs.out-do.app/reference/workflows-v2.

List workflow definitions

Cursor-paginated. Each item is ONE VERSION of a workflow; workflow_id groups the versions of a logical workflow. Pass active=true for the versions that are currently firing. active=false is not a filter: a superseded version is also inactive, so it would list live workflows as if they were stopped.

Authorizations:
HTTPBearer
query Parameters
Workspace Id (string) or Workspace Id (null) (Workspace Id)

Only workflows of this workspace.

Active (boolean) or Active (null) (Active)

Only the currently firing versions.

Cursor (string) or Cursor (null) (Cursor)

Opaque cursor taken from next_cursor of the previous page.

limit
integer (Limit) >= 1
Default: 25

Page size, 1 to 200. Defaults to 25.

header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_cursor": "string"
}

Create a workflow

Creates the automation as a DRAFT (a draft never fires). The definition is validated first: errors are a 422 and nothing is written. organization_id and workflow_id are not accepted: the first is derived from the workspace, the second is minted server-side. Pass is_draft: false to create AND apply it in one call; the result is an applied but INACTIVE version, which you then activate in the app. Send an Idempotency-Key header to make retries safe. API keys need the structure scope. The envelope is documented at docs.out-do.app/reference/workflows-v2.

Authorizations:
HTTPBearer
header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)
Request Body schema: application/json
required
Description (string) or Description (null) (Description)
object (Graph Config)

What happens next: { "steps": [ { "id"?: "stp_…", "key": <step key>, "config": {…} } ] }. There are no nodes, edges or coordinates: the list order IS the chain. A step that branches keeps its branches INSIDE its own config, as lists of steps with the same shape (then and else on ifElse, the routes on switch). id is optional (stp_ plus 12 alphanumerics) but must be unique, and it is what {{vars.steps.<id>.<output>}} refers to. See the step catalog.

is_draft
boolean (Is Draft)
Default: true

Create as a draft, which never fires, and publish it later with POST /v1/workflows-v2/{workflow_id}/apply. Pass false to create AND apply in one call; either way the result is INACTIVE — a person activates the automation in the app.

Label (string) or Label (null) (Label)
name
required
string (Name) [ 1 .. 120 ] characters
object (Trigger Config)

What fires the automation: { "type": <trigger key>, "config": {…} }. type is a trigger key from the catalog (record.event, cron, …) and config is that trigger's own configuration, which each trigger validates. See the trigger catalog.

workspace_id
required
string <uuid> (Workspace Id)

Responses

Request samples

Content type
application/json
{
  • "graph_config": {
    },
  • "name": "Note validated purchases",
  • "trigger_config": {
    },
  • "workspace_id": "a5a50556-ef07-449d-a556-077a2a310461"
}

Response samples

Content type
application/json
{
  • "archived_at": "string",
  • "created_at": "string",
  • "created_by": "string",
  • "deactivation_reason": "string",
  • "description": "string",
  • "graph_config": { },
  • "id": "string",
  • "is_active": false,
  • "is_draft": false,
  • "label": "",
  • "name": "",
  • "organization_id": "string",
  • "trigger_config": { },
  • "updated_at": "string",
  • "version": 1,
  • "workflow_id": "string",
  • "workspace_id": "string"
}

Validate a workflow definition

Runs the platform's graph validator over {trigger_config, graph_config} and returns {ok, issues}. Writes NOTHING: a definition with errors is a 200 with ok: false, not a rejection. ok means no ERRORS; warnings never block. Each issue carries a stable code to branch on, and target tells you where it is fixed (trigger or the path inside the graph). Pass workspace_id (or call with an API key, which carries its organization) so the validator can authorize. It writes nothing, so an API key only needs the read scope.

Authorizations:
HTTPBearer
header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)
Request Body schema: application/json
required
object (Graph Config)

What happens next: { "steps": [ { "id"?: "stp_…", "key": <step key>, "config": {…} } ] }. There are no nodes, edges or coordinates: the list order IS the chain. A step that branches keeps its branches INSIDE its own config, as lists of steps with the same shape (then and else on ifElse, the routes on switch). id is optional (stp_ plus 12 alphanumerics) but must be unique, and it is what {{vars.steps.<id>.<output>}} refers to. See the step catalog.

object (Trigger Config)

What fires the automation: { "type": <trigger key>, "config": {…} }. type is a trigger key from the catalog (record.event, cron, …) and config is that trigger's own configuration, which each trigger validates. See the trigger catalog.

Workflow Id (string) or Workflow Id (null) (Workflow Id)

The workflow this graph belongs to, so self-recursion is detected.

Workspace Id (string) or Workspace Id (null) (Workspace Id)

Responses

Request samples

Content type
application/json
{
  • "graph_config": {
    },
  • "trigger_config": {
    },
  • "workspace_id": "a5a50556-ef07-449d-a556-077a2a310461"
}

Response samples

Content type
application/json
{
  • "issues": [
    ],
  • "ok": true
}

Get a workflow definition

By default returns the version that represents the workflow: the active one, or the latest APPLIED one when the workflow is paused. Never the unapplied draft, unless the workflow has nothing else. Pass version for an exact snapshot.

Authorizations:
HTTPBearer
path Parameters
workflow_id
required
string <uuid> (Workflow Id)
query Parameters
Version (integer) or Version (null) (Version)

An exact version of this workflow.

header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)

Responses

Response samples

Content type
application/json
{
  • "archived_at": "string",
  • "created_at": "string",
  • "created_by": "string",
  • "deactivation_reason": "string",
  • "description": "string",
  • "graph_config": { },
  • "id": "string",
  • "is_active": false,
  • "is_draft": false,
  • "label": "",
  • "name": "",
  • "organization_id": "string",
  • "trigger_config": { },
  • "updated_at": "string",
  • "version": 1,
  • "workflow_id": "string",
  • "workspace_id": "string"
}

Apply a workflow's draft

Promotes the draft to a version, PRESERVING the activation state: a paused workflow stays paused and a brand-new one is born inactive. Validated first; the data store also re-checks, under a lock, that neither the draft nor its base moved (409 if they did, which means reload and reconcile, never retry). API keys need the structure scope.

Authorizations:
HTTPBearer
path Parameters
workflow_id
required
string <uuid> (Workflow Id)
header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)

Responses

Response samples

Content type
application/json
{
  • "archived_at": "string",
  • "created_at": "string",
  • "created_by": "string",
  • "deactivation_reason": "string",
  • "description": "string",
  • "graph_config": { },
  • "id": "string",
  • "is_active": false,
  • "is_draft": false,
  • "label": "",
  • "name": "",
  • "organization_id": "string",
  • "trigger_config": { },
  • "updated_at": "string",
  • "version": 1,
  • "workflow_id": "string",
  • "workspace_id": "string"
}

Add a version to a workflow

Atomically deactivates the current active version and inserts the next one as active. Validated first. Requires the workflow to HAVE an active version: a paused one, or one that only has a draft, is a 409 telling you to apply first. Fields you omit keep their previous value. API keys need the structure scope.

Authorizations:
HTTPBearer
path Parameters
workflow_id
required
string <uuid> (Workflow Id)
header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)
Request Body schema: application/json
required
Description (string) or Description (null) (Description)
Graph Config (object) or Graph Config (null) (Graph Config)

What happens next: { "steps": [ { "id"?: "stp_…", "key": <step key>, "config": {…} } ] }. There are no nodes, edges or coordinates: the list order IS the chain. A step that branches keeps its branches INSIDE its own config, as lists of steps with the same shape (then and else on ifElse, the routes on switch). id is optional (stp_ plus 12 alphanumerics) but must be unique, and it is what {{vars.steps.<id>.<output>}} refers to. See the step catalog. Omit to keep the current graph.

Label (string) or Label (null) (Label)
Name (string) or Name (null) (Name)
Trigger Config (object) or Trigger Config (null) (Trigger Config)

What fires the automation: { "type": <trigger key>, "config": {…} }. type is a trigger key from the catalog (record.event, cron, …) and config is that trigger's own configuration, which each trigger validates. See the trigger catalog. Omit to keep the current trigger.

Responses

Request samples

Content type
application/json
{
  • "description": "string",
  • "graph_config": { },
  • "label": "string",
  • "name": "string",
  • "trigger_config": { }
}

Response samples

Content type
application/json
{
  • "archived_at": "string",
  • "created_at": "string",
  • "created_by": "string",
  • "deactivation_reason": "string",
  • "description": "string",
  • "graph_config": { },
  • "id": "string",
  • "is_active": false,
  • "is_draft": false,
  • "label": "",
  • "name": "",
  • "organization_id": "string",
  • "trigger_config": { },
  • "updated_at": "string",
  • "version": 1,
  • "workflow_id": "string",
  • "workspace_id": "string"
}

templates

The template catalog (system, public, and the caller's own organization). Read-only here: publishing and promotion are human actions in the app.

List template apply jobs

Cursor-paginated, newest state included: every apply job the caller can see, optionally narrowed to one workspace and one status. This is how a workspace finds the job that brought its sample rows in, since the job row itself never syncs to the app. API keys need the structure scope.

Authorizations:
HTTPBearer
query Parameters
Workspace Id (string) or Workspace Id (null) (Workspace Id)

Only jobs applied to this workspace.

Status (string) or Status (null) (Status)

Only jobs in this state.

Cursor (string) or Cursor (null) (Cursor)

Opaque cursor taken from next_cursor of the previous page.

limit
integer (Limit) >= 1
Default: 25

Page size, 1 to 200. Defaults to 25.

header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_cursor": "string"
}

Get the state of a template apply job

Poll this after POST /v1/templates/{id}/apply. status is queued (not started), structure (creating tables, columns and views), rows (loading data in batches), done, failed or cancelled. report counts what was created, skipped and inserted, plus published — how many automations came out published instead of draft — and is only complete once the job is done; error says what stopped a failed one. Returns 404 when the job does not exist or is not visible to the caller.

Authorizations:
HTTPBearer
path Parameters
job_id
required
string <uuid> (Job Id)
header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)

Responses

Response samples

Content type
application/json
{
  • "created_at": "string",
  • "error": "string",
  • "id": "string",
  • "include_rows": true,
  • "locked_until": "string",
  • "report": { },
  • "status": "string",
  • "template_id": "string",
  • "updated_at": "string",
  • "workspace_id": "string"
}

Put the caller in the empty user columns of a template apply

Fills the user columns of the rows this job inserted with the caller, and only where they are empty: a row that already has someone keeps them. The structure is not touched, and neither are tables without a user column. Idempotent: a second run fills nothing and reports zero. A job that has not finished is a 409; one applied before the executor started recording what it inserted is a 422 naming ledger_without_row_ids. API keys need the structure scope.

Authorizations:
HTTPBearer
path Parameters
job_id
required
string <uuid> (Job Id)
header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)

Responses

Response samples

Content type
application/json
{
  • "job_id": "string",
  • "tables": [ ],
  • "total": 0
}

Remove the rows a template apply inserted

Deletes the rows this job inserted, and nothing else: the tables, columns, views and workflows the package created stay exactly as they are, because work of your own may already live in them. Rows added after the apply are untouched too — only the ones the job itself created are named in its ledger. Idempotent: running it a second time removes nothing and reports zero. A job that has not finished is a 409; one applied before the executor started recording what it inserted is a 422 naming ledger_without_row_ids. API keys need the structure scope.

Authorizations:
HTTPBearer
path Parameters
job_id
required
string <uuid> (Job Id)
header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)

Responses

Response samples

Content type
application/json
{
  • "job_id": "string",
  • "tables": [ ],
  • "total": 0
}

List templates

Cursor-paginated catalog: platform templates (system), shared ones (public), and the caller's own organization's. The package itself (manifest) is NOT included here, because it can be large; read one template to get it.

Authorizations:
HTTPBearer
query Parameters
Scope (string) or Scope (null) (Scope)

Only this scope: organization|workspace|module|feature.

Visibility (string) or Visibility (null) (Visibility)

Only this visibility: system|org|public.

Cursor (string) or Cursor (null) (Cursor)

Opaque cursor taken from next_cursor of the previous page.

limit
integer (Limit) >= 1
Default: 25

Page size, 1 to 200. Defaults to 25.

header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_cursor": "string"
}

Get a template

The catalog row plus the package itself (manifest). Returns 404 when the template does not exist or is not visible to the caller.

Authorizations:
HTTPBearer
path Parameters
template_id
required
string <uuid> (Template Id)
header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)

Responses

Response samples

Content type
application/json
{
  • "category": "string",
  • "description": "string",
  • "icon": "string",
  • "id": "string",
  • "label": "string",
  • "manifest": { },
  • "name": "string",
  • "organization_id": "string",
  • "published_at": "string",
  • "published_by": "string",
  • "scope": "string",
  • "semver": "string",
  • "source_workspace_id": "string",
  • "tags": [ ],
  • "visibility": "string"
}

Apply a template to a workspace

Starts a background job that applies the template's package to the target workspace: structure in one transaction, rows in batches. Returns the job id immediately — poll GET /v1/template-jobs/{id} for progress and the final report. Applying the SAME template to the SAME workspace twice creates nothing the second time and says so in the report. A second call while a job is still running is a 409 naming the job that is running. API keys need the structure scope.

Authorizations:
HTTPBearer
path Parameters
template_id
required
string <uuid> (Template Id)
header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)
Request Body schema: application/json
required
include_rows
boolean (Include Rows)
Default: true
Job Id (string) or Job Id (null) (Job Id)

Resume this job instead of starting a new one. Omit it and the server decides: it resumes the last unfinished job for this template and workspace, and starts a fresh one when the last apply finished. include_rows, resolutions and rows_modes are ignored when resuming — the job carries the plan it was created with.

object (Resolutions)

What to do with each dependency the package names, one entry per ref. {"action": "import"} brings the dependency in from the package's own blueprint; {"action": "map", "id": "<feature id>", "columns": {"<column ref>": "<column id>"}} points it at a table that already exists in the destination organization, with the columns matched one by one; {"action": "skip"} clears every reference to it and reports each cleared field with reason dependencia_saltada — nothing is left pointing at an id from the source organization. Mapped ids have to belong to the destination organization: one that does not is refused, naming the ref.

object (Rows Modes)

What to do with the rows of each table the package carries, one entry per feature ref. all inserts every row and is the default for a ref that is not listed; missingByKey inserts only the rows whose key is not in the destination yet, and needs the package to carry a VERIFIED key for that table (keyColumn with keyUnique: true) — asking for it without one is refused, naming the ref; none skips that table's rows entirely. include_rows: false is the global switch and always wins: with it, no mode is read at all. The report of the job counts the rows left out by missingByKey under existing.

workspace_id
required
string <uuid> (Workspace Id)

Responses

Request samples

Content type
application/json
{
  • "include_rows": true,
  • "job_id": "453bd7d7-5355-4d6d-a38e-d9e7eb218c3f",
  • "resolutions": { },
  • "rows_modes": {
    },
  • "workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9"
}

Response samples

Content type
application/json
{
  • "job_id": "string",
  • "status": "string"
}

Suggest how a template's dependencies map onto a workspace

For each dependency the package declares, the destination tables that look like it, ranked by confidence (0 to 1) from name/label similarity and the overlap of the columns the package actually uses, each with its column candidates. proposed is what a wizard would pre-select: map above 0.8, import when the package carries a blueprint, decide otherwise. Read-only: it changes nothing. Feed the answer straight into resolutions of apply_template. API keys need the structure scope.

Authorizations:
HTTPBearer
path Parameters
template_id
required
string <uuid> (Template Id)
header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)
Request Body schema: application/json
required
workspace_id
required
string <uuid> (Workspace Id)

Responses

Request samples

Content type
application/json
{
  • "workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9"
}

Response samples

Content type
application/json
{
  • "suggestions": [ ]
}

external

The client-portal gateway: what an external (client) account may read.

The features published to client accounts

The organization's features that carry an external_user column — adding that column IS publishing the feature — each with the columns marked externally visible.

A published feature with no visible columns is listed with an empty columns: it is a real state (rows marked, nothing chosen to show yet), not an error. The external_user column itself is never listed.

Authorizations:
HTTPBearer
query Parameters
organization_id
required
string (Organization Id) non-empty

The organization whose area is being read. Must be one of the organizations returned by GET /v1/external/me.

header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)

Responses

Response samples

Content type
application/json
{
  • "items": [
    ]
}

The caller's client accounts

Resolves the signed-in identity to its client accounts — one per organization that invited it — and is the call the portal makes first. An identity with no account, or with only revoked ones, is refused with 403 and a body that does not say which of the two it was.

First call activates. An account still invited becomes active here: signing in to the portal IS the acceptance of the invite. Until then it can read no data, and after a revocation it cannot again.

Authorizations:
HTTPBearer
header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)

Responses

Response samples

Content type
application/json
{
  • "accounts": [
    ],
  • "user_id": "string"
}

The caller's rows of one published feature

Cursor-paginated. Returns ONLY the rows whose external_user column contains the caller's client account, projected to the columns the organization marked externally visible — both imposed server-side, neither expressible by the caller.

A feature without an external_user column answers 404, and so does one whose column was removed a moment ago: the configuration is re-read on every request, so unpublishing takes effect immediately. An account with nothing marked gets an empty page, not an error.

Authorizations:
HTTPBearer
query Parameters
feature_id
required
string <uuid> (Feature Id)

A feature listed by GET /v1/external/features.

Cursor (string) or Cursor (null) (Cursor)

Opaque cursor taken from next_cursor of the previous page.

limit
integer (Limit) >= 1
Default: 25

Page size, 1 to 200. Defaults to 25.

Fields (string) or Fields (null) (Fields)

Comma-separated subset of the row data keys to return. It can only NARROW the organization's own choice of externally visible columns — a key that is not visible is not returned by naming it here.

organization_id
required
string (Organization Id) non-empty

The organization whose area is being read. Must be one of the organizations returned by GET /v1/external/me.

header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_cursor": "string"
}

external-accounts

Invitations and lifecycle of external (client-portal) accounts.

Invite an external account

Provisions a client account for the organization: reuses the identity when one already exists for the email, otherwise creates it and sends the invite. The account is never duplicated — inviting the same email again returns the same account, and what else happens follows its status: active is returned untouched (the person already has a login), invited sends the mail again, and revoked reactivates the account and mails. The account never becomes a member of the organization. Requires organization administrator.

Which mail goes out depends on the identity: a person who has no login yet gets the invite, a person who already has one gets an access link (a sign-in link — they have no password to set, and the identity provider refuses to invite them at all). Except for no_action, every outcome means a mail left.

outcome reports which of those happened (created, access_link_sent, reactivated, invite_resent, no_action) — the returned account does not show it, since all paths answer 200 with status='invited'. Do not infer the outcome from the account's previous status.

Authorizations:
HTTPBearer
header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)
Request Body schema: application/json
required
email
required
string (Email) [ 3 .. 320 ] characters
Name (string) or Name (null) (Name)
organization_id
required
string (Organization Id) non-empty

Responses

Request samples

Content type
application/json
{
  • "email": "string",
  • "name": "string",
  • "organization_id": "string"
}

Response samples

Content type
application/json
{
  • "created_at": "string",
  • "email": "string",
  • "id": "string",
  • "invited_at": "string",
  • "name": "string",
  • "organization_id": "string",
  • "outcome": "created",
  • "status": "string",
  • "updated_at": "string",
  • "user_id": "string"
}

Revoke an external account

Sets the account to revoked — access stops on the caller's next request — and closes the identity's way back in when this was its last external account and it holds no OutDo membership. Idempotent. Requires organization administrator.

Authorizations:
HTTPBearer
path Parameters
account_id
required
string (Account Id)
header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)

Responses

Response samples

Content type
application/json
{
  • "created_at": "string",
  • "email": "string",
  • "id": "string",
  • "invited_at": "string",
  • "name": "string",
  • "organization_id": "string",
  • "status": "string",
  • "updated_at": "string",
  • "user_id": "string"
}

ai

AI control plane: per-organization model configuration, credit balance, usage records, and provider secrets. Not an inference endpoint.

List the active AI model catalog (operator-curated)

Authorizations:
HTTPBearer
query Parameters
Organization Id (string) or Organization Id (null) (Organization Id)

Filter the catalog to the models this org's tier may use.

header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)

Responses

Response samples

Content type
application/json
[
  • {
    }
]

Get an organization's AI credit balance (member-gated)

Authorizations:
HTTPBearer
query Parameters
organization_id
required
string <uuid> (Organization Id)

Organization id.

header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)

Responses

Response samples

Content type
application/json
{
  • "allowance_remaining": 0,
  • "organization_id": "string",
  • "period_start": "string",
  • "tier_name": "string",
  • "topup_balance": 0
}

Set/replace the organization's BYOK key for a provider (owner/admin)

Authorizations:
HTTPBearer
path Parameters
organization_id
required
string <uuid> (Organization Id)
provider
required
string (Provider)
header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)
Request Body schema: application/json
required
secret
required
string (Secret) non-empty

Responses

Request samples

Content type
application/json
{
  • "secret": "string"
}

List which providers the organization has a BYOK key for (no values)

Authorizations:
HTTPBearer
path Parameters
organization_id
required
string <uuid> (Organization Id)
header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)

Responses

Response samples

Content type
application/json
[
  • {
    }
]

Get the global service-default mode->model_key map (editable by superadmin)

Authorizations:
HTTPBearer
header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)

Responses

Response samples

Content type
application/json
{
  • "defaults": {
    }
}

Get an organization's per-mode model selection

Authorizations:
HTTPBearer
query Parameters
organization_id
required
string <uuid> (Organization Id)

Organization id.

header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)

Responses

Response samples

Content type
application/json
{
  • "mode_models": {
    },
  • "organization_id": "string"
}

Set an organization's per-mode model selection (owner/admin)

Authorizations:
HTTPBearer
header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)
Request Body schema: application/json
required
required
object (Mode Models)
organization_id
required
string <uuid> (Organization Id)

Responses

Request samples

Content type
application/json
{
  • "mode_models": {
    },
  • "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6"
}

Get an organization's AI usage summary (member-gated)

Authorizations:
HTTPBearer
query Parameters
organization_id
required
string <uuid> (Organization Id)

Organization id.

Since (string) or Since (null) (Since)

ISO-8601 timestamp; only events after this point are included.

header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)

Responses

Response samples

Content type
application/json
{
  • "organization_id": "string",
  • "rows": [
    ]
}

admin

Operational endpoints for platform administrators, outside the tenant surface.

Service stats (superadmin)

Version and uptime of the outdo_api_data service. Superadmin session only.

Authorizations:
HTTPBearer
header Parameters
X-Outdo-Service-Key (string) or X-Outdo-Service-Key (null) (X-Outdo-Service-Key)

Responses

Response samples

Content type
application/json
{
  • "service": "outdo_api_data",
  • "started_at": "string",
  • "uptime_s": 0,
  • "version": "string"
}