API reference

Flowral API v1.0.0

The Flowral public API: flows, the tasks in them, and the derived state that makes a dependency planner useful — what is ready, what is blocked, and when a project actually lands.

REST API https://api.flowral.app/v1
MCP server https://mcp.flowral.app
Import into Postman Download the collection and drag it into Postman — or use Import → Link with the spec URL, which stays current on its own. Set the apiKey collection variable once and every request is signed.
Download collection

Authentication. Every request carries a credential created in the app under API & AI. Either send an API key as a bearer token, or exchange a client id and secret for a short-lived token at POST /v1/oauth/token. A credential belongs to a workspace and carries its own role: it can never do more than that role allows, and it cannot move between workspaces.

Errors are { "error": { "code", "message" } }. Switch on code; the message is for humans and may change.

Lists are { "total", "items" } and take size and from.

Rate limit 120 requests/minute per credential, burst 240. Exceeding it returns 429 with Retry-After.

Stability. This is v1. Fields get added; nothing that exists is removed or repurposed without a new major version. The platform API the web app uses is a different, unversioned surface — it is not this, and it will change without notice.

OpenAPI 3.1.0 · 39 paths · generated from the service, not written by hand

auth

Getting a token.

POST /v1/oauth/token auth.token

Exchange client credentials for an access token

Standard OAuth 2.0 client-credentials grant (application/x-www-form-urlencoded). Credentials may also be sent as HTTP Basic. The token is a bearer token for this API and nothing else — it is not a planner session and cannot be used against the app.

Body application/x-www-form-urlencoded

grant_type required string Must be client_credentials.
client_id required string
client_secret required string
scope string Space-separated. Defaults to everything the credential was granted; naming scopes here can only narrow it.

Responses

200 SUCCESS Token
400 UNSUPPORTED_GRANT_TYPE · INVALID_SCOPE Error
401 INVALID_CLIENT Error

Example

curl -X POST 'https://api.flowral.app/v1/oauth/token' \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -d 'grant_type=client_credentials&client_id=fl_id_…&client_secret=fl_cs_…'
{
  "access_token": "fl_at_9c2f…",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "flows:read flows:write work:write"
}
POST /v1/oauth/revoke auth.revoke

Revoke an access token

RFC 7009. Kills one issued token immediately. To kill the credential itself, delete it in the app — that also invalidates every token it ever issued.

Body application/x-www-form-urlencoded

token required string

Responses

200 SUCCESS — also returned for a token that was already invalid, per the RFC.
401 INVALID_CLIENT Error

workspace

Who the credential is.

GET /v1/me workspace.context

Who this credential is

The workspace, the credential's role and scopes, and the plan. Cheap, and the right health check for an integration.

Responses

200 SUCCESS Context
401 UNAUTHORIZED · CREDENTIAL_REVOKED Error
403 FORBIDDEN · PLAN_REQUIRED · INSUFFICIENT_SCOPE Error
429 RATE_LIMITED Error

Example

curl 'https://api.flowral.app/v1/me' \
  -H 'Authorization: Bearer fl_sk_live_…'
{
  "workspaceId": "org_5c1",
  "workspaceName": "Acme Studio",
  "credentialId": "key_8fd2",
  "credentialName": "Acme CI",
  "role": "member",
  "scopes": [
    "flows:read",
    "flows:write",
    "work:write"
  ],
  "plan": "pro"
}

flows

Projects, and everything that operates on a whole one.

GET /v1/flows flows.list

List flows

Flows in the workspace, newest first. Archived flows are excluded unless archived=true.

Scopes: flows:read

Query

archived boolean Return archived flows instead of live ones.default false
q string Match on flow name.
size integer Page size, 1–100.default 25
from integer Offset.default 0

Responses

200 SUCCESS { total, items: FlowSummary[] }
401 UNAUTHORIZED Error
403 PLAN_REQUIRED · INSUFFICIENT_SCOPE Error
429 RATE_LIMITED Error

Example

curl 'https://api.flowral.app/v1/flows?size=2' \
  -H 'Authorization: Bearer fl_sk_live_…'
{
  "total": 2,
  "items": [
    {
      "id": "flw_2b8e10",
      "name": "Acme: Website relaunch",
      "archived": false,
      "taskCount": 14,
      "doneCount": 6,
      "readyCount": 2,
      "dueDate": "2026-09-14T00:00:00.000Z"
    },
    {
      "id": "flw_77a301",
      "name": "Northwind: Brand refresh",
      "archived": false,
      "taskCount": 9,
      "doneCount": 9,
      "readyCount": 0,
      "dueDate": null
    }
  ]
}
POST /v1/flows flows.create

Create a flow

Creates a flow and, optionally, its whole task graph in one call. A task's after references other tasks by index in this request, so the dependency chain arrives with the flow rather than in follow-up calls. Cycles are refused outright.

Scopes: flows:write · Role: member or higher

Body application/json

name required string
description string
dueDate string ISO 8601. Used by the schedule and report endpoints to answer "is this on track".
tasks TaskInput[] Tasks to create, in order. Each: { name, description?, estimateHours?, assigneeId?, after?: [index] }, where after holds indexes into this same array.

Responses

201 CREATED Flow
400 VALIDATION_FAILED · CIRCULAR_DEPENDENCY Error
401 UNAUTHORIZED Error
403 FORBIDDEN · PLAN_REQUIRED · SUBSCRIPTION_PAST_DUE Error
429 RATE_LIMITED Error

Example

curl -X POST 'https://api.flowral.app/v1/flows' \
  -H 'Authorization: Bearer fl_sk_live_…' \
  -H 'Content-Type: application/json' \
  -d '{"name":"Acme: Website relaunch","dueDate":"2026-09-14","tasks":[{"name":"Brief"},{"name":"Design","after":[0]},{"name":"Client approval","after":[1]}]}'
{
  "id": "flw_2b8e10",
  "name": "Acme: Website relaunch",
  "archived": false,
  "dueDate": "2026-09-14T00:00:00.000Z",
  "tasks": [
    {
      "id": "tsk_1",
      "flowId": "flw_2b8e10",
      "name": "Brief",
      "status": "ready",
      "dependsOn": [],
      "blockedBy": []
    },
    {
      "id": "tsk_2",
      "flowId": "flw_2b8e10",
      "name": "Design",
      "status": "blocked",
      "dependsOn": [
        "tsk_1"
      ],
      "blockedBy": [
        "tsk_1"
      ]
    },
    {
      "id": "tsk_3",
      "flowId": "flw_2b8e10",
      "name": "Client approval",
      "status": "blocked",
      "dependsOn": [
        "tsk_2"
      ],
      "blockedBy": [
        "tsk_2"
      ]
    }
  ]
}
GET /v1/flows/{flowId} flows.get

Get a flow

The flow with its full task graph and derived state — status, what is blocking what, and which tasks are on the critical path.

Scopes: flows:read

Path

flowId required string Flow id.

Responses

200 SUCCESS Flow
401 UNAUTHORIZED Error
403 FORBIDDEN · PLAN_REQUIRED · INSUFFICIENT_SCOPE Error
404 NOT_FOUND Error
429 RATE_LIMITED Error
PATCH /v1/flows/{flowId} flows.update

Update a flow

Changes the flow itself. Tasks are edited through the task endpoints.

Scopes: flows:write · Role: member or higher

Path

flowId required string

Body application/json

name string
description string
dueDate string ISO 8601, or null to clear it.

Responses

200 SUCCESS FlowSummary
401 UNAUTHORIZED Error
403 FORBIDDEN · SUBSCRIPTION_PAST_DUE Error
404 NOT_FOUND Error
429 RATE_LIMITED Error
DELETE /v1/flows/{flowId} flows.archive

Archive a flow

Archives rather than destroys: the flow leaves the list and can be restored. Nothing in Flowral deletes client history on a single call.

Scopes: flows:write · Role: member or higher

Path

flowId required string

Responses

204 ARCHIVED
401 UNAUTHORIZED Error
403 FORBIDDEN · SUBSCRIPTION_PAST_DUE Error
404 NOT_FOUND Error
429 RATE_LIMITED Error
POST /v1/flows/{flowId}/restore flows.restore

Restore an archived flow

Scopes: flows:write · Role: member or higher

Path

flowId required string

Responses

200 SUCCESS FlowSummary
401 UNAUTHORIZED Error
403 FORBIDDEN · PLAN_REQUIRED · INSUFFICIENT_SCOPE Error
404 NOT_FOUND · NOT_ARCHIVED Error
429 RATE_LIMITED Error
GET /v1/flows/{flowId}/schedule flows.schedule

Get the schedule

Runs the critical-path pass: earliest and latest start and finish per task, slack, and the projected finish for the flow. Computed from estimates and dependencies — no dates are stored, so this is always current.

Scopes: flows:read

Path

flowId required string

Query

startDate string ISO 8601. Defaults to today.

Responses

200 SUCCESS Schedule
401 UNAUTHORIZED Error
403 FORBIDDEN · PLAN_REQUIRED · INSUFFICIENT_SCOPE Error
404 NOT_FOUND Error
429 RATE_LIMITED Error
GET /v1/flows/{flowId}/report flows.report

Get the delivery report

Completion, effort remaining, workload per member, the tasks blocking the most work, and what is unestimated or orphaned. The same figures the Reporting view shows.

Scopes: flows:read

Path

flowId required string

Responses

200 SUCCESS Report
401 UNAUTHORIZED Error
403 FORBIDDEN · PLAN_REQUIRED · INSUFFICIENT_SCOPE Error
404 NOT_FOUND Error
429 RATE_LIMITED Error
POST /v1/flows/{flowId}/exports flows.export

Export a flow

Renders the flow through the same pipeline the app uses. pdf is the delivery document (diagram plus execution plan); png is the dependency diagram on its own. Responds with the bytes, Content-Disposition set.

Scopes: flows:read

Path

flowId required string

Body application/json

format required string pdf or png.

Responses

200 SUCCESS — application/pdf or image/png.
400 VALIDATION_FAILED Error
401 UNAUTHORIZED Error
403 FORBIDDEN · PLAN_REQUIRED · INSUFFICIENT_SCOPE Error
404 NOT_FOUND Error
429 RATE_LIMITED Error
502 RENDER_FAILED Error
GET /v1/flows/{flowId}/shares flows.shares.list

List a flow's guests

Clients and guests with access to this flow. Guests never occupy a seat, on any plan.

Scopes: flows:read

Path

flowId required string

Responses

200 SUCCESS { total, items: Share[] }
401 UNAUTHORIZED Error
403 FORBIDDEN · PLAN_REQUIRED · INSUFFICIENT_SCOPE Error
404 NOT_FOUND Error
429 RATE_LIMITED Error
POST /v1/flows/{flowId}/shares flows.shares.create

Share a flow with a guest

Gives one person access to this flow only — they see its progress and nothing else in the workspace.

Scopes: flows:write · Role: member or higher

Path

flowId required string

Body application/json

email required string
role string viewer or commenter.

Responses

201 CREATED Share
400 INVALID_EMAIL Error
401 UNAUTHORIZED Error
403 FORBIDDEN Error
404 NOT_FOUND Error
429 RATE_LIMITED Error
DELETE /v1/flows/{flowId}/shares/{shareId} flows.shares.remove

Remove a guest from a flow

Scopes: flows:write · Role: member or higher

Path

flowId required string
shareId required string

Responses

204 REMOVED
401 UNAUTHORIZED Error
403 FORBIDDEN · PLAN_REQUIRED · INSUFFICIENT_SCOPE Error
404 NOT_FOUND Error
429 RATE_LIMITED Error

tasks

The work, its dependencies, and moving it along.

GET /v1/flows/{flowId}/tasks tasks.list

List a flow's tasks

Every task in the flow with its derived status. Filter by status to get just what is ready, or just what is blocked.

Scopes: flows:read

Path

flowId required string

Query

status string blocked, ready, in_progress or done.
assigneeId string Only tasks assigned to this member.

Responses

200 SUCCESS { total, items: Task[] }
401 UNAUTHORIZED Error
403 FORBIDDEN · PLAN_REQUIRED · INSUFFICIENT_SCOPE Error
404 NOT_FOUND Error
429 RATE_LIMITED Error
POST /v1/flows/{flowId}/tasks tasks.create

Add a task to a flow

Creates a task and links it in the same call. after are the tasks that must finish first; before are the tasks that now wait on this one — use it to insert a step into an existing chain.

Scopes: flows:write · Role: member or higher

Path

flowId required string

Body application/json

name required string
description string
after string[] Task ids this depends on.
before string[] Task ids that should depend on this.
estimateHours number
assigneeId string

Responses

201 CREATED Task
400 VALIDATION_FAILED · CIRCULAR_DEPENDENCY Error
401 UNAUTHORIZED Error
403 FORBIDDEN · SUBSCRIPTION_PAST_DUE Error
404 NOT_FOUND Error
429 RATE_LIMITED Error
GET /v1/tasks/{taskId} tasks.get

Get a task

Scopes: flows:read

Path

taskId required string

Responses

200 SUCCESS Task
401 UNAUTHORIZED Error
403 FORBIDDEN · PLAN_REQUIRED · INSUFFICIENT_SCOPE Error
404 NOT_FOUND Error
429 RATE_LIMITED Error
PATCH /v1/tasks/{taskId} tasks.update

Update a task

Name, description and estimate. Status is not settable here — see the transition endpoints. Refused once work has started, the same rule the canvas applies.

Scopes: flows:write · Role: member or higher

Path

taskId required string

Body application/json

name string
description string
estimateHours number

Responses

200 SUCCESS Task
401 UNAUTHORIZED Error
403 FORBIDDEN · SUBSCRIPTION_PAST_DUE Error
404 NOT_FOUND Error
429 RATE_LIMITED Error
DELETE /v1/tasks/{taskId} tasks.delete

Delete a task

Bridges the gap it leaves: whatever depended on this task now depends on what it depended on, so removing a step from the middle leaves a plan that still runs end to end.

Scopes: flows:write · Role: member or higher

Path

taskId required string

Responses

204 DELETED
401 UNAUTHORIZED Error
403 FORBIDDEN · SUBSCRIPTION_PAST_DUE Error
404 NOT_FOUND Error
429 RATE_LIMITED Error
POST /v1/tasks/{taskId}/subtasks tasks.subtasks.create

Add a sub-task

A breakdown of one task. A task that has sub-tasks completes through them rather than directly.

Scopes: flows:write · Role: member or higher

Path

taskId required string

Body application/json

name required string
assigneeId string
estimateHours number

Responses

201 CREATED SubTask
401 UNAUTHORIZED Error
403 FORBIDDEN · PLAN_REQUIRED · INSUFFICIENT_SCOPE Error
404 NOT_FOUND Error
429 RATE_LIMITED Error
POST /v1/tasks/{taskId}/dependencies tasks.dependencies.add

Add a dependency

Makes this task wait on another. Refuses anything that would create a cycle.

Scopes: flows:write · Role: member or higher

Path

taskId required string

Body application/json

dependsOn required string The task that must finish first.

Responses

200 SUCCESS Task
400 CIRCULAR_DEPENDENCY Error
401 UNAUTHORIZED Error
403 FORBIDDEN · PLAN_REQUIRED · INSUFFICIENT_SCOPE Error
404 NOT_FOUND Error
429 RATE_LIMITED Error
DELETE /v1/tasks/{taskId}/dependencies/{dependsOnId} tasks.dependencies.remove

Remove a dependency

Scopes: flows:write · Role: member or higher

Path

taskId required string
dependsOnId required string

Responses

200 SUCCESS Task
401 UNAUTHORIZED Error
403 FORBIDDEN · PLAN_REQUIRED · INSUFFICIENT_SCOPE Error
404 NOT_FOUND Error
429 RATE_LIMITED Error
POST /v1/tasks/{taskId}/start tasks.start

Start a task

Ready → in progress. Refused while anything it depends on is unfinished, and assigned work only moves for its assignee.

Scopes: work:write · Role: member or higher

Path

taskId required string

Responses

200 SUCCESS Task
401 UNAUTHORIZED Error
403 FORBIDDEN · PLAN_REQUIRED · INSUFFICIENT_SCOPE Error
404 NOT_FOUND Error
409 TASK_BLOCKED · ALREADY_DONE Error
429 RATE_LIMITED Error
POST /v1/tasks/{taskId}/complete tasks.complete

Complete a task

In progress → done, and the response says what that unblocked — which is the next question every time. Refused if sub-tasks are outstanding or a required deliverable has not been handed in.

Scopes: work:write · Role: member or higher

Path

taskId required string

Body application/json

comment string Optional note recorded on the completion.

Responses

200 SUCCESS
401 UNAUTHORIZED Error
403 FORBIDDEN · PLAN_REQUIRED · INSUFFICIENT_SCOPE Error
404 NOT_FOUND Error
409 TASK_BLOCKED · SUBTASKS_OUTSTANDING · DELIVERABLE_REQUIRED · ALREADY_DONE Error
429 RATE_LIMITED Error

Example

curl -X POST 'https://api.flowral.app/v1/tasks/tsk_2/complete' \
  -H 'Authorization: Bearer fl_sk_live_…'
{
  "task": {
    "id": "tsk_2",
    "name": "Design",
    "status": "done",
    "criticalPath": true
  },
  "unblocked": [
    {
      "id": "tsk_3",
      "name": "Client approval",
      "status": "ready"
    }
  ]
}
POST /v1/tasks/{taskId}/reopen tasks.reopen

Reopen a task

Done → in progress. Refused once anything downstream has been started: undoing a completion that the next person already acted on would rewrite work that is under way.

Scopes: work:write · Role: member or higher

Path

taskId required string

Responses

200 SUCCESS Task
401 UNAUTHORIZED Error
403 FORBIDDEN · PLAN_REQUIRED · INSUFFICIENT_SCOPE Error
404 NOT_FOUND Error
409 DOWNSTREAM_STARTED · NOT_DONE Error
429 RATE_LIMITED Error
PUT /v1/tasks/{taskId}/assignee tasks.assign

Assign a task

Sets the owner. Send null to unassign.

Scopes: work:write · Role: member or higher

Path

taskId required string

Body application/json

assigneeId string Member id, or null.

Responses

200 SUCCESS Task
401 UNAUTHORIZED Error
403 FORBIDDEN · PLAN_REQUIRED · INSUFFICIENT_SCOPE Error
404 NOT_FOUND · MEMBER_NOT_FOUND Error
429 RATE_LIMITED Error
GET /v1/tasks/{taskId}/comments tasks.comments.list

List comments

Scopes: flows:read

Path

taskId required string

Responses

200 SUCCESS { total, items: Comment[] }
401 UNAUTHORIZED Error
403 FORBIDDEN · PLAN_REQUIRED · INSUFFICIENT_SCOPE Error
404 NOT_FOUND Error
429 RATE_LIMITED Error
POST /v1/tasks/{taskId}/comments tasks.comments.create

Comment on a task

The one write a viewer credential can make, and the one that is never frozen by work having started.

Scopes: work:write

Path

taskId required string

Body application/json

body required string

Responses

201 CREATED Comment
401 UNAUTHORIZED Error
403 FORBIDDEN · PLAN_REQUIRED · INSUFFICIENT_SCOPE Error
404 NOT_FOUND Error
429 RATE_LIMITED Error
POST /v1/tasks/{taskId}/attachments tasks.attachments.create

Attach a file

A reference file on the task — a brief, a spec, a screenshot. multipart/form-data.

Scopes: flows:write · Role: member or higher

Path

taskId required string

Body multipart/form-data

file required string · binary Images, video, PDF and Word, up to 50 MB.

Responses

201 CREATED Attachment
400 MISSING_FILE · FILE_TOO_LARGE · UNSUPPORTED_FILE_TYPE Error
401 UNAUTHORIZED Error
403 FORBIDDEN · PLAN_REQUIRED · INSUFFICIENT_SCOPE Error
404 NOT_FOUND Error
429 RATE_LIMITED Error
POST /v1/tasks/{taskId}/deliverables tasks.deliverables.submit

Hand in a deliverable

Satisfies a file requirement on the task, which is what lets it complete. Different from an attachment: this one is the thing being asked for.

Scopes: work:write

Path

taskId required string

Body multipart/form-data

file required string · binary
requirementId string Which requirement this satisfies. Optional when the task has exactly one.

Responses

201 CREATED Attachment
400 MISSING_FILE · FILE_TOO_LARGE Error
401 UNAUTHORIZED Error
403 FORBIDDEN · PLAN_REQUIRED · INSUFFICIENT_SCOPE Error
404 NOT_FOUND · REQUIREMENT_NOT_FOUND Error
429 RATE_LIMITED Error

work

Ready, blocked and in-progress work across every flow.

GET /v1/work/available work.available

What is ready to start

Every unblocked, unstarted task across the workspace, critical-path work first. The single most useful call in this API.

Scopes: flows:read

Query

flowId string Restrict to one flow.
assigneeId string Restrict to one person. Unassigned work is always included.
size integer default 25

Responses

200 SUCCESS { total, items: WorkItem[] }
401 UNAUTHORIZED Error
403 PLAN_REQUIRED · INSUFFICIENT_SCOPE Error
429 RATE_LIMITED Error

Example

curl 'https://api.flowral.app/v1/work/available' \
  -H 'Authorization: Bearer fl_sk_live_…'
{
  "total": 2,
  "items": [
    {
      "taskId": "tsk_3",
      "taskName": "Client approval",
      "flowId": "flw_2b8e10",
      "flowName": "Acme: Website relaunch",
      "status": "ready",
      "assigneeId": null,
      "criticalPath": true
    },
    {
      "taskId": "tsk_9",
      "taskName": "Write launch copy",
      "flowId": "flw_2b8e10",
      "flowName": "Acme: Website relaunch",
      "status": "ready",
      "assigneeId": "usr_41",
      "criticalPath": false
    }
  ]
}
GET /v1/work/mine work.mine

What is assigned to the caller

Ready and in-progress work assigned to this credential's identity. For a service credential, pass assigneeId to /work/available instead — a credential is not a person and has nothing assigned to it.

Scopes: flows:read

Query

size integer default 25

Responses

200 SUCCESS { total, items: WorkItem[] }
401 UNAUTHORIZED Error
403 FORBIDDEN · PLAN_REQUIRED · INSUFFICIENT_SCOPE Error
429 RATE_LIMITED Error
GET /v1/work/blocked work.blocked

What is stuck, and on what

Blocked work with the unfinished tasks holding it up, ranked so the task blocking the most appears first. This is the "where is delivery actually stalling" call.

Scopes: flows:read

Query

flowId string
size integer default 25

Responses

200 SUCCESS { total, items: WorkItem[] }
401 UNAUTHORIZED Error
403 FORBIDDEN · PLAN_REQUIRED · INSUFFICIENT_SCOPE Error
429 RATE_LIMITED Error
GET /v1/work/in-progress work.inProgress

What is under way

Scopes: flows:read

Query

flowId string
assigneeId string
size integer default 25

Responses

200 SUCCESS { total, items: WorkItem[] }
401 UNAUTHORIZED Error
403 FORBIDDEN · PLAN_REQUIRED · INSUFFICIENT_SCOPE Error
429 RATE_LIMITED Error
GET /v1/work/overview work.overview

The whole workspace in one call

Per-flow counts, workload per member and the flows at risk of their due date — so a dashboard does not have to fetch twelve flows to render.

Scopes: flows:read

Responses

200 SUCCESS
401 UNAUTHORIZED Error
403 FORBIDDEN · PLAN_REQUIRED · INSUFFICIENT_SCOPE Error
429 RATE_LIMITED Error

team

Members and invitations.

GET /v1/team/members team.members

List members

People in the workspace and their roles. Guests are not members and are not listed here — they belong to a flow (see /v1/flows/{flowId}/shares).

Scopes: team:read

Responses

200 SUCCESS { total, items: Member[] }
401 UNAUTHORIZED Error
403 FORBIDDEN · PLAN_REQUIRED · INSUFFICIENT_SCOPE Error
429 RATE_LIMITED Error
GET /v1/team/invites team.invites.list

List pending invitations

Scopes: team:read

Responses

200 SUCCESS { total, items: Invite[] }
401 UNAUTHORIZED Error
403 FORBIDDEN · PLAN_REQUIRED · INSUFFICIENT_SCOPE Error
429 RATE_LIMITED Error
POST /v1/team/invites team.invites.create

Invite someone

Sends an invitation. A pending invite occupies a seat for the purposes of the plan limit, which is why this can be refused on a full workspace before anyone has accepted.

Scopes: team:write · Role: admin or higher

Body application/json

email required string
role string admin, member or viewer.

Responses

201 CREATED Invite
400 INVALID_EMAIL · ALREADY_MEMBER Error
401 UNAUTHORIZED Error
403 FORBIDDEN · PLAN_LIMIT_REACHED Error
429 RATE_LIMITED Error
PATCH /v1/team/members/{memberId} team.members.role

Change a member's role

Scopes: team:write · Role: admin or higher

Path

memberId required string

Body application/json

role required string admin, member or viewer.

Responses

200 SUCCESS Member
401 UNAUTHORIZED Error
403 FORBIDDEN Error
404 NOT_FOUND Error
429 RATE_LIMITED Error

templates

Saved flow shapes.

GET /v1/templates templates.list

List templates

Saved flow shapes in this workspace.

Scopes: templates:read

Query

category string

Responses

200 SUCCESS { total, items: Template[] }
401 UNAUTHORIZED Error
403 FORBIDDEN · PLAN_REQUIRED · INSUFFICIENT_SCOPE Error
429 RATE_LIMITED Error
GET /v1/templates/{templateId} templates.get

Get a template

The template with the task graph it will produce, so an integration can show what it is about to create.

Scopes: templates:read

Path

templateId required string

Responses

200 SUCCESS Template
401 UNAUTHORIZED Error
403 FORBIDDEN · PLAN_REQUIRED · INSUFFICIENT_SCOPE Error
404 NOT_FOUND Error
429 RATE_LIMITED Error
POST /v1/templates/{templateId}/flows templates.use

Start a flow from a template

Creates a new flow from the template and returns it, graph and all. Using a template is available on every plan; saving new ones is not.

Scopes: flows:write templates:read · Role: member or higher

Path

templateId required string

Body application/json

name string Defaults to the template's name.
dueDate string ISO 8601.

Responses

201 CREATED Flow
401 UNAUTHORIZED Error
403 FORBIDDEN · SUBSCRIPTION_PAST_DUE Error
404 NOT_FOUND Error
429 RATE_LIMITED Error

notifications

GET /v1/notifications/preferences notifications.preferences.get

What a person is subscribed to

The settings that actually apply, after the account defaults and any flow overrides have been resolved. This is the answer, not the stored row — a true here means mail will be sent.

Scopes: notifications:read

Query

memberId required string The person these settings belong to. A credential has no inbox, so this is never optional.

Responses

400 VALIDATION_FAILED Error
401 UNAUTHORIZED Error
403 FORBIDDEN · PLAN_REQUIRED · INSUFFICIENT_SCOPE Error
404 MEMBER_NOT_FOUND Error
429 RATE_LIMITED Error

Example

curl 'https://api.flowral.app/v1/notifications/preferences?memberId=usr_41' \
  -H 'Authorization: Bearer fl_sk_live_…'
{
  "memberId": "usr_41",
  "flowId": null,
  "email": true,
  "muted": false,
  "events": {
    "assigned": true,
    "unlocked": true,
    "digest": true
  },
  "timezone": "Europe/Paris",
  "digestHour": 8
}
PUT /v1/notifications/preferences notifications.preferences.set

Change what a person is subscribed to

Sets the account defaults. Only the keys you send are changed; sending null for one resets it to the built-in default rather than turning it off.

Scopes: notifications:write · Role: admin or higher

Body application/json

memberId required string The person these settings belong to.
email boolean The master switch for this scope. null means "inherit".
muted boolean Flow scope only: silence everything about this flow.
events object Per-event switches: assigned, unlocked, digest. Omit a key to leave it alone; send null to reset it to inherited.
timezone string Account scope only. IANA zone name — the digest is sent in this person's morning, not the server's.
digestHour integer Account scope only. 0–23, local. Default 8.

Responses

400 VALIDATION_FAILED Error
401 UNAUTHORIZED Error
403 FORBIDDEN · INSUFFICIENT_SCOPE Error
404 MEMBER_NOT_FOUND Error
429 RATE_LIMITED Error

Example

curl -X PUT 'https://api.flowral.app/v1/notifications/preferences' \
  -H 'Authorization: Bearer fl_sk_live_…' \
  -H 'Content-Type: application/json' \
  -d '{"memberId":"usr_41","timezone":"America/New_York","digestHour":7}'
{
  "memberId": "usr_41",
  "flowId": null,
  "email": true,
  "muted": false,
  "events": {
    "assigned": true,
    "unlocked": true,
    "digest": true
  },
  "timezone": "America/New_York",
  "digestHour": 7
}
GET /v1/notifications/preferences/flows/{flowId} notifications.preferences.flow.get

What a person is subscribed to on one flow

The account settings with this flow's override applied.

Scopes: notifications:read

Path

flowId required string

Query

memberId required string The person these settings belong to. A credential has no inbox, so this is never optional.

Responses

400 VALIDATION_FAILED Error
401 UNAUTHORIZED Error
403 FORBIDDEN · PLAN_REQUIRED · INSUFFICIENT_SCOPE Error
404 NOT_FOUND · MEMBER_NOT_FOUND Error
429 RATE_LIMITED Error
PUT /v1/notifications/preferences/flows/{flowId} notifications.preferences.flow.set

Override a person's settings for one flow

The usual use is {"muted": true} — silence one noisy flow without touching anything else this person gets.

Scopes: notifications:write · Role: admin or higher

Path

flowId required string

Body application/json

memberId required string The person these settings belong to.
email boolean The master switch for this scope. null means "inherit".
muted boolean Flow scope only: silence everything about this flow.
events object Per-event switches: assigned, unlocked, digest. Omit a key to leave it alone; send null to reset it to inherited.
timezone string Account scope only. IANA zone name — the digest is sent in this person's morning, not the server's.
digestHour integer Account scope only. 0–23, local. Default 8.

Responses

400 VALIDATION_FAILED Error
401 UNAUTHORIZED Error
403 FORBIDDEN · INSUFFICIENT_SCOPE Error
404 NOT_FOUND · MEMBER_NOT_FOUND Error
429 RATE_LIMITED Error
DELETE /v1/notifications/preferences/flows/{flowId} notifications.preferences.flow.clear

Remove a flow override

The flow follows the person's account settings again. Not the same as turning everything off for it.

Scopes: notifications:write · Role: admin or higher

Path

flowId required string

Query

memberId required string The person these settings belong to. A credential has no inbox, so this is never optional.

Responses

401 UNAUTHORIZED Error
403 FORBIDDEN · INSUFFICIENT_SCOPE Error
404 NOT_FOUND · MEMBER_NOT_FOUND Error
429 RATE_LIMITED Error
GET /v1/notifications/log notifications.log

What was actually sent

One row per email that left the building, newest first — batches and digests alike. Use it to answer "did they get told", and to see what was suppressed because they had unsubscribed.

Scopes: notifications:read

Query

size integer default 25
memberId required string The person these settings belong to. A credential has no inbox, so this is never optional.

Responses

200 SUCCESS { total, items: NotificationDelivery[] }
401 UNAUTHORIZED Error
403 FORBIDDEN · PLAN_REQUIRED · INSUFFICIENT_SCOPE Error
404 MEMBER_NOT_FOUND Error
429 RATE_LIMITED Error
GET /v1/notifications/digest notifications.digest.preview

This person's digest, without sending it

What tomorrow morning's mail would say, built by the real builder and delivered to nobody. Also the cleanest per-person view of "what should I work on today" across every flow they can see — /v1/work/available answers that per workspace, this answers it per person.

Scopes: notifications:read

Query

memberId required string The person these settings belong to. A credential has no inbox, so this is never optional.

Responses

200 SUCCESS NotificationDigest
401 UNAUTHORIZED Error
403 FORBIDDEN · PLAN_REQUIRED · INSUFFICIENT_SCOPE Error
404 MEMBER_NOT_FOUND Error
429 RATE_LIMITED Error

Example

curl 'https://api.flowral.app/v1/notifications/digest?memberId=usr_41' \
  -H 'Authorization: Bearer fl_sk_live_…'
{
  "memberId": "usr_41",
  "wouldSend": true,
  "counts": {
    "ready": 2,
    "inProgress": 1,
    "blocked": 1,
    "available": 3,
    "doneByMe": 1,
    "doneByTeam": 4,
    "atRisk": 1,
    "flows": 3
  },
  "ready": [
    {
      "questId": "flw_2b8e10",
      "questName": "Acme: Website relaunch",
      "nodeId": "tsk_9",
      "nodeTitle": "Write launch copy",
      "hours": 8,
      "criticalPath": true
    }
  ],
  "atRisk": [
    {
      "questId": "flw_2b8e10",
      "questName": "Acme: Website relaunch",
      "dueDate": "2026-09-14",
      "projectedFinish": "2026-09-21",
      "workdaysOver": 5
    }
  ]
}

objects

The shapes every endpoint returns.

Error

Every non-2xx response has this shape.

error required object

Context

Who the credential is and what it may do — the first call to make when wiring an integration.

workspaceId required string
workspaceName required string
credentialId string The key or client this token belongs to.
credentialName string
role required admin | member | viewer The credential’s role in the workspace. It decides what every other call may do.admin member viewer
scopes required string[]
plan required free | pro free pro

Token

access_token required string
token_type required string
expires_in required integer Seconds.
scope string Space-separated, and never more than the credential was granted.

FlowSummary

A flow without its tasks — what list endpoints return.

id required string
name required string
description string | null
archived required boolean
dueDate string · date-time The date the flow is meant to land, if one is set.
taskCount integer
doneCount integer
readyCount integer Tasks that can be started right now.
createdAt string · date-time
updatedAt string · date-time

Flow

A flow with its full task graph and derived state.

Everything in FlowSummary, plus:

tasks required Task[]

Task

A unit of work in a flow. Tasks are joined by dependencies: one is ready only when everything it depends on is done, and one with sub-tasks completes through them.

id required string
flowId required string
name required string
description string | null
status required blocked | ready | in_progress | done Derived from the graph on read, never stored. Work moves ready → in_progress → done; nothing jumps straight to done.blocked ready in_progress done
dependsOn required string[] Task ids that must be done first.
blockedBy string[] Of those, the ones not yet done. Empty when status is not "blocked".
assigneeId string | null
estimateHours number | null
criticalPath boolean True when this task is on the chain that decides the flow's finish date.
subtasks SubTask[]
requiredDeliverables object[] Files that have to be handed in before the task can complete.

SubTask

id required string
name required string
done required boolean
assigneeId string | null Workspace member id.
estimateHours number | null

WorkItem

A task in the context of "what should be worked on", flattened so a caller does not have to hold the flow to make sense of it.

taskId required string
taskName required string
flowId required string
flowName required string
status required blocked | ready | in_progress | done blocked ready in_progress done
assigneeId string | null
estimateHours number | null
criticalPath boolean
blockedBy object[] On blocked work, the unfinished tasks holding it up, most-blocking first.

Schedule

flowId required string
dueDate string | null The day the flow is meant to land, YYYY-MM-DD.
projectedFinish string | null Working day it lands at the current shape and estimates, YYYY-MM-DD.
onTrack boolean | null Null when there is no due date to be on track against.
entries required ScheduleEntry[]

ScheduleEntry

One task on the critical-path pass. Dates are computed from estimates and dependencies; nothing here is stored.

taskId required string
taskName required string
earliestStart string Working day, YYYY-MM-DD.
earliestFinish string Working day, YYYY-MM-DD.
criticalPath required boolean On the chain that decides the finish date — no slack.
estimated boolean False when the span is a one-day guess because nothing was estimated.

Report

The delivery health of a flow: what is done, what is left, what is holding it up, and who is carrying it.

flowId required string
flowName string
completion required number 0–1.
tasksTotal required integer
tasksDone integer
tasksReady integer
tasksBlocked integer
hoursEstimated number | null
hoursRemaining number | null
unestimatedTasks string[] Task ids with no estimate — the reason a projection is soft.
orphanedTasks string[] Tasks nothing depends on and that depend on nothing.
workload object[]
topBlockers object[]

Comment

id required string
taskId required string
authorId string
authorName string
body required string
createdAt required string · date-time

Attachment

id required string
name required string
mimeType string
sizeBytes integer
kind required reference | deliverable reference deliverable
uploadedAt string · date-time

Member

id required string
name string
email required string · email
role required owner | admin | member | viewer owner admin member viewer

Invite

id required string
email required string · email
role required admin | member | viewer admin member viewer
status required pending | accepted | revoked pending accepted revoked
createdAt string · date-time

Share

A client or guest with access to one flow. Guests never occupy a seat.

id required string
email required string · email
role required viewer | commenter viewer commenter
flowId required string
createdAt string · date-time

Template

A saved flow shape, ready to start a new flow from.

id required string
name required string
description string | null
category string | null
taskCount integer
usedCount integer

SearchResult

id required string
type required flow | task | template flow task template
title required string
flowId string | null
url string Where it lives in the app, for a link back.

NotificationPreferences

What one person is emailed about, resolved. When flowId is set, this is the account settings with that flow's override already applied.

memberId required string
flowId string | null Null for the account-level answer.
email required boolean The master switch. False means no notification email at all, whatever the per-event flags say.
muted boolean Set by a flow-level override to silence one flow.
events required object Per-event switches.
timezone string IANA zone. The digest is sent in this person's morning.
digestHour integer 0–23, local.

NotificationDelivery

One email that was sent — or deliberately not sent, which is recorded too.

id required string
kind required string batch (assignments and unblocked work, grouped) or digest.
status required string sent, failed, skipped, skipped-empty, or claimed while in flight.
sentAt string · date-time When the send was claimed.
subject string | null
itemCount integer How many events one batch covered. Notifications are batched after a quiet period, so this is usually more than one.
flowIds string[]
suppressed integer Events dropped because the person had unsubscribed since they were queued.

NotificationDigest

One person's day: what is on them, what is free to pick up, what moved. Spans every flow they can see, including flows shared with them from workspaces they are not a member of.

memberId required string
generatedAt string · date-time When this was built.
wouldSend required boolean False when there is nothing worth mailing — an empty digest is never sent.
counts required object Totals BEFORE the per-section caps, so a truncated list still reports honestly.
ready object[] Assigned to them and unblocked.
inProgress object[]
blocked object[] Theirs, waiting on someone else.
available object[] Unassigned and ready — anyone could pick it up.
completed object[] Finished in the last 24 hours, across their flows.
atRisk object[] Flows projected to finish after their due date.