Flowral Notifications API
The notifications API from Flowral — 4 operation(s) for notifications.
The notifications API from Flowral — 4 operation(s) for notifications.
Every API here is available over the APIs.io API and to AI agents over MCP.
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
find_apisBrowse and filter every API in the catalog.get_api_artifactsOne API's artifacts, grouped by type.get_openapiThe primary OpenAPI for this API.find_similar_apisAPIs that look like this one.apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.resolveTurn a domain, URL or GitHub org into the provider it belongs to.find_cohortsEvery scored population of providers in the catalog.curl "https://apis.io/api/v1/apis/flowral-app:flowral-app-notifications-api"
curl "https://apis.io/api/v1/apis?limit=25"
Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.
Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.
A second provider on the same verified email joins the account you already have.
openapi: 3.2.0
info:
title: Flowral Notifications API
version: 1.0.0
summary: Client projects as dependency graphs — over HTTP.
description: '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.
**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.'
contact:
name: Flowral
email: hello@flowral.app
url: https://flowral.app/api
license:
name: Proprietary
url: https://flowral.app/terms
servers:
- url: https://api.flowral.app
description: Production
security:
- apiKey: []
- oauth2: []
tags:
- name: Notifications
paths:
/v1/notifications/preferences:
get:
operationId: notifications.preferences.get
summary: What a person is subscribed to
description: 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.
tags:
- Notifications
parameters:
- name: memberId
in: query
required: true
description: The person these settings belong to. A credential has no inbox, so this is never optional.
schema:
type: string
description: The person these settings belong to. A credential has no inbox, so this is never optional.
responses:
'200':
description: SUCCESS
content:
application/json:
schema:
$ref: '#/components/schemas/NotificationPreferences'
'400':
description: VALIDATION_FAILED
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error:
code: VALIDATION_FAILED
message: Human-readable explanation.
'401':
description: UNAUTHORIZED
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error:
code: UNAUTHORIZED
message: Human-readable explanation.
'403':
description: FORBIDDEN · PLAN_REQUIRED · INSUFFICIENT_SCOPE
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error:
code: FORBIDDEN
message: Human-readable explanation.
'404':
description: MEMBER_NOT_FOUND
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error:
code: MEMBER_NOT_FOUND
message: Human-readable explanation.
'429':
description: RATE_LIMITED
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error:
code: RATE_LIMITED
message: Human-readable explanation.
security:
- apiKey: []
- oauth2:
- notifications:read
x-codeSamples:
- lang: cURL
source: "curl '{{API_URL}}/v1/notifications/preferences?memberId=usr_41' \\\n -H 'Authorization: Bearer {{API_KEY}}'"
x-examples:
- request:
curl: "curl '{{API_URL}}/v1/notifications/preferences?memberId=usr_41' \\\n -H 'Authorization: Bearer {{API_KEY}}'"
response:
status: 200
body:
memberId: usr_41
flowId: null
email: true
muted: false
events:
assigned: true
unlocked: true
digest: true
timezone: Europe/Paris
digestHour: 8
put:
operationId: notifications.preferences.set
summary: Change what a person is subscribed to
description: 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.
tags:
- Notifications
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
memberId:
type: string
description: The person these settings belong to.
email:
type: boolean
description: The master switch for this scope. `null` means "inherit".
muted:
type: boolean
description: 'Flow scope only: silence everything about this flow.'
events:
type: object
description: 'Per-event switches: `assigned`, `unlocked`, `digest`. Omit a key to leave it alone; send `null` to reset it to inherited.'
timezone:
type: string
description: Account scope only. IANA zone name — the digest is sent in this person's morning, not the server's.
digestHour:
type: integer
description: Account scope only. 0–23, local. Default 8.
required:
- memberId
responses:
'200':
description: SUCCESS
content:
application/json:
schema:
$ref: '#/components/schemas/NotificationPreferences'
'400':
description: VALIDATION_FAILED
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error:
code: VALIDATION_FAILED
message: Human-readable explanation.
'401':
description: UNAUTHORIZED
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error:
code: UNAUTHORIZED
message: Human-readable explanation.
'403':
description: FORBIDDEN · INSUFFICIENT_SCOPE
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error:
code: FORBIDDEN
message: Human-readable explanation.
'404':
description: MEMBER_NOT_FOUND
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error:
code: MEMBER_NOT_FOUND
message: Human-readable explanation.
'429':
description: RATE_LIMITED
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error:
code: RATE_LIMITED
message: Human-readable explanation.
security:
- apiKey: []
- oauth2:
- notifications:write
x-minRole: admin
x-codeSamples:
- lang: cURL
source: "curl -X PUT '{{API_URL}}/v1/notifications/preferences' \\\n -H 'Authorization: Bearer {{API_KEY}}' \\\n -H 'Content-Type: application/json' \\\n -d '{\"memberId\":\"usr_41\",\"timezone\":\"America/New_York\",\"digestHour\":7}'"
x-examples:
- request:
curl: "curl -X PUT '{{API_URL}}/v1/notifications/preferences' \\\n -H 'Authorization: Bearer {{API_KEY}}' \\\n -H 'Content-Type: application/json' \\\n -d '{\"memberId\":\"usr_41\",\"timezone\":\"America/New_York\",\"digestHour\":7}'"
response:
status: 200
body:
memberId: usr_41
flowId: null
email: true
muted: false
events:
assigned: true
unlocked: true
digest: true
timezone: America/New_York
digestHour: 7
/v1/notifications/preferences/flows/{flowId}:
get:
operationId: notifications.preferences.flow.get
summary: What a person is subscribed to on one flow
description: The account settings with this flow's override applied.
tags:
- Notifications
parameters:
- name: flowId
in: path
required: true
schema:
type: string
- name: memberId
in: query
required: true
description: The person these settings belong to. A credential has no inbox, so this is never optional.
schema:
type: string
description: The person these settings belong to. A credential has no inbox, so this is never optional.
responses:
'200':
description: SUCCESS
content:
application/json:
schema:
$ref: '#/components/schemas/NotificationPreferences'
'400':
description: VALIDATION_FAILED
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error:
code: VALIDATION_FAILED
message: Human-readable explanation.
'401':
description: UNAUTHORIZED
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error:
code: UNAUTHORIZED
message: Human-readable explanation.
'403':
description: FORBIDDEN · PLAN_REQUIRED · INSUFFICIENT_SCOPE
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error:
code: FORBIDDEN
message: Human-readable explanation.
'404':
description: NOT_FOUND · MEMBER_NOT_FOUND
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error:
code: NOT_FOUND
message: Human-readable explanation.
'429':
description: RATE_LIMITED
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error:
code: RATE_LIMITED
message: Human-readable explanation.
security:
- apiKey: []
- oauth2:
- notifications:read
put:
operationId: notifications.preferences.flow.set
summary: Override a person's settings for one flow
description: 'The usual use is `{"muted": true}` — silence one noisy flow without touching anything else this person gets.'
tags:
- Notifications
parameters:
- name: flowId
in: path
required: true
schema:
type: string
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
memberId:
type: string
description: The person these settings belong to.
email:
type: boolean
description: The master switch for this scope. `null` means "inherit".
muted:
type: boolean
description: 'Flow scope only: silence everything about this flow.'
events:
type: object
description: 'Per-event switches: `assigned`, `unlocked`, `digest`. Omit a key to leave it alone; send `null` to reset it to inherited.'
timezone:
type: string
description: Account scope only. IANA zone name — the digest is sent in this person's morning, not the server's.
digestHour:
type: integer
description: Account scope only. 0–23, local. Default 8.
required:
- memberId
responses:
'200':
description: SUCCESS
content:
application/json:
schema:
$ref: '#/components/schemas/NotificationPreferences'
'400':
description: VALIDATION_FAILED
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error:
code: VALIDATION_FAILED
message: Human-readable explanation.
'401':
description: UNAUTHORIZED
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error:
code: UNAUTHORIZED
message: Human-readable explanation.
'403':
description: FORBIDDEN · INSUFFICIENT_SCOPE
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error:
code: FORBIDDEN
message: Human-readable explanation.
'404':
description: NOT_FOUND · MEMBER_NOT_FOUND
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error:
code: NOT_FOUND
message: Human-readable explanation.
'429':
description: RATE_LIMITED
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error:
code: RATE_LIMITED
message: Human-readable explanation.
security:
- apiKey: []
- oauth2:
- notifications:write
x-minRole: admin
delete:
operationId: notifications.preferences.flow.clear
summary: Remove a flow override
description: The flow follows the person's account settings again. Not the same as turning everything off for it.
tags:
- Notifications
parameters:
- name: flowId
in: path
required: true
schema:
type: string
- name: memberId
in: query
required: true
description: The person these settings belong to. A credential has no inbox, so this is never optional.
schema:
type: string
description: The person these settings belong to. A credential has no inbox, so this is never optional.
responses:
'200':
description: SUCCESS
content:
application/json:
schema:
$ref: '#/components/schemas/NotificationPreferences'
'401':
description: UNAUTHORIZED
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error:
code: UNAUTHORIZED
message: Human-readable explanation.
'403':
description: FORBIDDEN · INSUFFICIENT_SCOPE
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error:
code: FORBIDDEN
message: Human-readable explanation.
'404':
description: NOT_FOUND · MEMBER_NOT_FOUND
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error:
code: NOT_FOUND
message: Human-readable explanation.
'429':
description: RATE_LIMITED
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error:
code: RATE_LIMITED
message: Human-readable explanation.
security:
- apiKey: []
- oauth2:
- notifications:write
x-minRole: admin
/v1/notifications/log:
get:
operationId: notifications.log
summary: What was actually sent
description: 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.
tags:
- Notifications
parameters:
- name: size
in: query
required: false
schema:
type: integer
default: 25
- name: memberId
in: query
required: true
description: The person these settings belong to. A credential has no inbox, so this is never optional.
schema:
type: string
description: The person these settings belong to. A credential has no inbox, so this is never optional.
responses:
'200':
description: SUCCESS
content:
application/json:
schema:
type: object
properties:
total:
type: integer
description: Total matching, not the length of `items`.
items:
type: array
items:
$ref: '#/components/schemas/NotificationDelivery'
required:
- total
- items
'401':
description: UNAUTHORIZED
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error:
code: UNAUTHORIZED
message: Human-readable explanation.
'403':
description: FORBIDDEN · PLAN_REQUIRED · INSUFFICIENT_SCOPE
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error:
code: FORBIDDEN
message: Human-readable explanation.
'404':
description: MEMBER_NOT_FOUND
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error:
code: MEMBER_NOT_FOUND
message: Human-readable explanation.
'429':
description: RATE_LIMITED
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error:
code: RATE_LIMITED
message: Human-readable explanation.
security:
- apiKey: []
- oauth2:
- notifications:read
/v1/notifications/digest:
get:
operationId: notifications.digest.preview
summary: This person's digest, without sending it
description: 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.
tags:
- Notifications
parameters:
- name: memberId
in: query
required: true
description: The person these settings belong to. A credential has no inbox, so this is never optional.
schema:
type: string
description: The person these settings belong to. A credential has no inbox, so this is never optional.
responses:
'200':
description: SUCCESS
content:
application/json:
schema:
$ref: '#/components/schemas/NotificationDigest'
'401':
description: UNAUTHORIZED
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error:
code: UNAUTHORIZED
message: Human-readable explanation.
'403':
description: FORBIDDEN · PLAN_REQUIRED · INSUFFICIENT_SCOPE
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error:
code: FORBIDDEN
message: Human-readable explanation.
'404':
description: MEMBER_NOT_FOUND
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error:
code: MEMBER_NOT_FOUND
message: Human-readable explanation.
'429':
description: RATE_LIMITED
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error:
code: RATE_LIMITED
message: Human-readable explanation.
security:
- apiKey: []
- oauth2:
- notifications:read
x-codeSamples:
- lang: cURL
source: "curl '{{API_URL}}/v1/notifications/digest?memberId=usr_41' \\\n -H 'Authorization: Bearer {{API_KEY}}'"
x-examples:
- request:
curl: "curl '{{API_URL}}/v1/notifications/digest?memberId=usr_41' \\\n -H 'Authorization: Bearer {{API_KEY}}'"
response:
status: 200
body:
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
components:
schemas:
NotificationDelivery:
type: object
description: One email that was sent — or deliberately not sent, which is recorded too.
properties:
id:
type: string
kind:
type: string
description: '`batch` (assignments and unblocked work, grouped) or `digest`.'
status:
type: string
description: '`sent`, `failed`, `skipped`, `skipped-empty`, or `claimed` while in flight.'
sentAt:
type: string
format: date-time
description: When the send was claimed.
subject:
type:
- string
- 'null'
itemCount:
type: integer
description: How many events one batch covered. Notifications are batched after a quiet period, so this is usually more than one.
flowIds:
type: array
items:
type: string
suppressed:
type: integer
description: Events dropped because the person had unsubscribed since they were queued.
required:
- id
- kind
- status
NotificationDigest:
type: object
description: '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.'
properties:
memberId:
type: string
generatedAt:
type: string
format: date-time
description: When this was built.
wouldSend:
type: boolean
description: False when there is nothing worth mailing — an empty digest is never sent.
counts:
type: object
additionalProperties:
type: integer
description: Totals BEFORE the per-section caps, so a truncated list still reports honestly.
ready:
type: array
items:
type: object
properties:
questId:
type: string
description: The flow id.
questName:
type: string
nodeId:
type: string
description: The task id.
nodeTitle:
type: string
hours:
type:
- number
- 'null'
criticalPath:
type: boolean
description: Assigned to them and unblocked.
inProgress:
type: array
items:
type: object
properties:
questId:
type: string
description: The flow id.
questName:
type: string
nodeId:
type: string
description: The task id.
nodeTitle:
type: string
hours:
type:
- number
- 'null'
criticalPath:
type: boolean
blocked:
type: array
items:
type: object
properties:
questId:
type: string
description: The flow id.
questName:
type: string
nodeId:
type: string
description: The task id.
nodeTitle:
type: string
hours:
type:
- number
- 'null'
criticalPath:
type: boolean
description: Theirs, waiting on someone else.
available:
type: array
items:
type: object
properties:
questId:
type: string
description: The flow id.
questName:
type: string
nodeId:
type: string
description: The task id.
nodeTitle:
type: string
hours:
type:
- number
- 'null'
criticalPath:
type: boolean
description: Unassigned and ready — anyone could pick it up.
completed:
type: array
items:
type: object
description: Finished in the last 24 hours, across their flows.
atRisk:
type: array
items:
type: object
description: Flows projected to finish after their due date.
required:
- memberId
- wouldSend
- counts
NotificationPreferences:
type: object
description: What one person is emailed about, resolved. When `flowId` is set, this is the account settings with that flow's override already applied.
properties:
memberId:
type: string
flowId:
type:
- string
- 'null'
description: Null for the account-level answer.
email:
type: boolean
description: The master switch. False means no notification email at all, whatever the per-event flags say.
muted:
type: boolean
description: Set by a flow-level override to silence one flow.
events:
type: object
description: Per-event switches.
properties:
assigned:
type: boolean
description: A task was assigned to them.
unlocked:
type: boolean
description: A task of theirs stopped being blocked.
digest:
type: boolean
description: The morning summary. Account level only.
timezone:
type: string
description: IANA zone. The digest is sent in this person's morning.
examples:
- Europe/Paris
digestHour:
type: integer
description: 0–23, local.
required:
- memberId
- email
- events
Error:
type: object
description: Every non-2xx response has this shape.
properties:
error:
type: object
properties:
code:
type: string
description: Stable, machine-readable. Switch on this, not on the message.
examples:
- NOT_FOUND
message:
type: string
description: Human-readable. May change; never parse it.
details:
type: object
additionalProperties: true
description: Present when the code alone is not actionable — which task blocked, which deliverable is missing, which limit was hit.
required:
- code
- message
required:
- error
securitySchemes:
apiKey:
type: http
scheme: bearer
description: 'An API key from the app: `Authorization: Bearer fl_sk_live_…`.'
oauth2:
type: oauth2
description: Client credentials, for anything long-lived.
flows:
clientCredentials:
tokenUrl: https://api.flowral.app/v1/oauth/token
scopes:
flows:read: Read flows, tasks, schedules and reports
flows:write: Create and change flows and tasks
work:write: Start, complete, assign and comment on work
team:read: Read members and invitations
team:write: Invite people and change roles
templates:read: Read templates and start flows from them
notifications:read: Read what people are emailed about
notifications:write: Change what people are emailed about