Flowral Auth API

Getting a token.

GitHub OpenAPI

Operations 3

GET /v1/oauth/authorize Start a first-party sign-in (authorization code + PKCE) #
POST /v1/oauth/token Exchange credentials for an access token #
POST /v1/oauth/revoke Revoke an access token #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • 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.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/flowral-app:flowral-app-auth-api"
All apis
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.

Get an API 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 Specification

flowral-app-auth-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Flowral Auth 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: Auth
  description: Getting a token.
paths:
  /v1/oauth/authorize:
    get:
      operationId: auth.authorize
      summary: Start a first-party sign-in (authorization code + PKCE)
      description: Browser-facing, not meant to be called by anything but a user-agent. Validates the request and redirects to the Flowral app's own sign-in screen; once the person signs in and allows the connection there, the app redirects on to `redirect_uri` with a one-time `code`. POST /oauth/token's `authorization_code` grant exchanges it for a token. `client_id` must name a registered first-party client (currently `flowral-vscode`) and `redirect_uri` must be a loopback address it owns.
      tags:
      - Auth
      parameters:
      - name: client_id
        in: query
        required: true
        schema:
          type: string
      - name: redirect_uri
        in: query
        required: true
        description: A loopback address, e.g. `http://127.0.0.1:<port>/callback`.
        schema:
          type: string
          description: A loopback address, e.g. `http://127.0.0.1:<port>/callback`.
      - name: response_type
        in: query
        required: true
        description: Must be `code`.
        schema:
          type: string
          description: Must be `code`.
      - name: code_challenge
        in: query
        required: true
        description: PKCE, S256 of a code_verifier the client keeps.
        schema:
          type: string
          description: PKCE, S256 of a code_verifier the client keeps.
      - name: code_challenge_method
        in: query
        required: true
        description: Must be `S256`.
        schema:
          type: string
          description: Must be `S256`.
      - name: state
        in: query
        required: false
        schema:
          type: string
      - name: scope
        in: query
        required: false
        description: Space-separated. Defaults to everything the signed-in person can do; naming scopes here can only narrow it.
        schema:
          type: string
          description: Space-separated. Defaults to everything the signed-in person can do; naming scopes here can only narrow it.
      responses:
        '302':
          description: Redirects to the app's sign-in screen.
      security: []
  /v1/oauth/token:
    post:
      operationId: auth.token
      summary: Exchange credentials for an access token
      description: 'Three grants, all `application/x-www-form-urlencoded`:


        - `client_credentials` — a workspace credential''s id/secret. May also be sent as HTTP Basic.

        - `authorization_code` — the code from `/oauth/authorize`, with the PKCE `code_verifier`. Issues a token that acts as the person who signed in, not a workspace credential — plus a `refresh_token`, since a person has no secret to silently re-present an hour later.

        - `refresh_token` — trades the refresh token from an `authorization_code` grant for a new access/refresh pair. Rotated: the old refresh token stops working the moment this succeeds. Pass `org_id` to move to a DIFFERENT workspace the same person belongs to, without a second trip through the browser — checked fresh against live membership, same as a new sign-in would be.


        Either way 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.'
      tags:
      - Auth
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              properties:
                grant_type:
                  type: string
                  description: '`client_credentials`, `authorization_code`, or `refresh_token`.'
                client_id:
                  type: string
                  description: Required for client_credentials and authorization_code.
                client_secret:
                  type: string
                  description: client_credentials only.
                code:
                  type: string
                  description: authorization_code only — from /oauth/authorize.
                code_verifier:
                  type: string
                  description: authorization_code only — PKCE.
                redirect_uri:
                  type: string
                  description: authorization_code only — must match the value sent to /oauth/authorize.
                refresh_token:
                  type: string
                  description: refresh_token only.
                org_id:
                  type: string
                  description: refresh_token only. Switch to a different workspace the person belongs to; omit to just refresh the current one.
                scope:
                  type: string
                  description: Space-separated. Defaults to everything the credential (or, for authorization_code, the person) was granted; naming scopes here can only narrow it.
              required:
              - grant_type
      responses:
        '200':
          description: SUCCESS
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Token'
        '400':
          description: UNSUPPORTED_GRANT_TYPE · INVALID_SCOPE · invalid_grant · invalid_request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                error:
                  code: UNSUPPORTED_GRANT_TYPE
                  message: Human-readable explanation.
        '401':
          description: INVALID_CLIENT
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                error:
                  code: INVALID_CLIENT
                  message: Human-readable explanation.
        '403':
          description: invalid_target
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                error:
                  code: invalid_target
                  message: Human-readable explanation.
      security: []
      x-codeSamples:
      - lang: cURL
        source: "curl -X POST '{{API_URL}}/v1/oauth/token' \\\n  -H 'Content-Type: application/x-www-form-urlencoded' \\\n  -d 'grant_type=client_credentials&client_id={{CLIENT_ID}}&client_secret={{CLIENT_SECRET}}'"
      x-examples:
      - request:
          curl: "curl -X POST '{{API_URL}}/v1/oauth/token' \\\n  -H 'Content-Type: application/x-www-form-urlencoded' \\\n  -d 'grant_type=client_credentials&client_id={{CLIENT_ID}}&client_secret={{CLIENT_SECRET}}'"
        response:
          status: 200
          body:
            access_token: fl_at_9c2f…
            token_type: Bearer
            expires_in: 3600
            scope: flows:read flows:write work:write
  /v1/oauth/revoke:
    post:
      operationId: auth.revoke
      summary: Revoke an access token
      description: 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.
      tags:
      - Auth
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              properties:
                token:
                  type: string
              required:
              - token
      responses:
        '200':
          description: SUCCESS — also returned for a token that was already invalid, per the RFC.
        '401':
          description: INVALID_CLIENT
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                error:
                  code: INVALID_CLIENT
                  message: Human-readable explanation.
      security: []
components:
  schemas:
    Token:
      type: object
      properties:
        access_token:
          type: string
        token_type:
          type: string
          examples:
          - Bearer
        expires_in:
          type: integer
          description: Seconds.
          examples:
          - 3600
        scope:
          type: string
          description: Space-separated, and never more than the credential was granted.
      required:
      - access_token
      - token_type
      - expires_in
    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