> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sahlfinancial.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Update a flow

> **Who can call it:** `tenant_admin`, `tenant_reviewer`, `tenant_api_manager` or `platform_admin`. A `tenant_viewer` is read-only and gets 403.

Saves name, description, graph. Each save bumps `version`. A flow that is the Partner API's KYC policy can be changed by an admin only (403).

**Auth:** dashboard session (`Authorization: Bearer <access token>`). Not available with a partner API key.



## OpenAPI

````yaml /openapi-console.json put /v1/flows/{flow_id}
openapi: 3.1.0
info:
  title: Sahl Console API
  version: 0.1.0
  description: >-
    **Used by the Sahl console. Not available with partner API keys. Contact
    Sahl if you need programmatic access.**


    These are the routes the Sahl console calls with a signed-in user's session.
    They are documented so that a developer or a workspace admin can see what
    the console does, with the real request and response models. They are
    **not** part of the [Partner API](/api-reference/introduction): an API key
    is refused here (401), there is no try-it, and the shapes can change with
    the console.


    Authentication: `Authorization: Bearer <access token>`, the JWT the console
    gets from `POST /v1/auth/login` (30 minutes; renewed with `POST
    /v1/auth/refresh`, refresh token valid 7 days). There is no cookie. The role
    of the user is read from the user's record on every call, not from the
    token. Every call is scoped to the workspace of the user.


    Common answers: 401 (no or bad session), 403 `Insufficient permissions`
    (role), 422 (validation), 429 (100 requests per minute and IP). Error bodies
    have the same three shapes as the Partner API: see [Errors](/errors).
servers:
  - url: https://app.sahlfinancial.com/api
    description: Console host. Same host as the Partner API.
security:
  - dashboardSession: []
tags:
  - name: Bank connections
    description: Simulated in the Sandbox; 409 `bank_connect_unavailable` elsewhere.
  - name: Cases
    description: Client files that group documents, a score and a decision.
  - name: Documents
    description: Upload, status, result and download.
  - name: Webhooks
    description: Manage the endpoints that receive events.
  - name: API keys
    description: Create, rotate and revoke the keys used with the Partner API.
  - name: Scoring policies
    description: Weights and thresholds of the credit score.
  - name: Flows
    description: Verification flows (graphs) and their simulation.
paths:
  /v1/flows/{flow_id}:
    put:
      tags:
        - Flows
      summary: Update a flow
      description: >-
        **Who can call it:** `tenant_admin`, `tenant_reviewer`,
        `tenant_api_manager` or `platform_admin`. A `tenant_viewer` is read-only
        and gets 403.


        Saves name, description, graph. Each save bumps `version`. A flow that
        is the Partner API's KYC policy can be changed by an admin only (403).


        **Auth:** dashboard session (`Authorization: Bearer <access token>`).
        Not available with a partner API key.
      operationId: updateFlow
      parameters:
        - name: flow_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
            title: Flow Id
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/FlowUpdateRequest'
            example:
              name: Salaried borrower v2
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FlowResponse'
              example:
                id: d6a3f9b8-2c5e-4170-b8d1-4e0a7c3f5b29
                tenant_id: 0b6f5a3e-7c1d-4e0a-9d2f-3a1c5e8b7f10
                name: Salaried borrower v2
                description: null
                use_case: lending
                kind: kyc
                country: MA
                nodes:
                  - id: start
                    type: start
                    position:
                      x: 0
                      'y': 0
                    data:
                      label: Start
                  - id: end
                    type: end
                    position:
                      x: 240
                      'y': 0
                    data:
                      label: End
                edges:
                  - id: e1
                    source: start
                    target: end
                version: 2
                is_published: false
                status: draft
                environment: sandbox
                created_at: '2026-10-07T09:14:22Z'
                updated_at: '2026-10-07T09:14:22Z'
                published_at: null
                country_support: null
        '401':
          description: No session, expired token, ended session or inactive account.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                detail: Invalid or expired token
        '403':
          description: >-
            The role is not allowed, MFA enrolment is pending, or the
            environment is refused.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                detail: Insufficient permissions
        '404':
          description: Unknown flow.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                detail: Flow not found
        '422':
          description: >-
            The graph is not valid, or a rule of publishing is broken on a
            published flow.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                detail:
                  message: Flow failed validation
                  errors:
                    - Flow has no Start node.
        '429':
          description: >-
            More than 100 requests per minute from one IP address. The body has
            no `detail`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RateLimitError'
              example:
                code: rate_limit_exceeded
                message: Too many requests. Please slow down.
          headers:
            Retry-After:
              schema:
                type: integer
              description: Seconds to wait.
components:
  schemas:
    FlowUpdateRequest:
      properties:
        name:
          anyOf:
            - type: string
            - type: 'null'
        description:
          anyOf:
            - type: string
            - type: 'null'
        use_case:
          anyOf:
            - type: string
            - type: 'null'
        kind:
          anyOf:
            - type: string
              enum:
                - kyc
                - kyb
            - type: 'null'
        country:
          anyOf:
            - type: string
              maxLength: 2
            - type: 'null'
        nodes:
          anyOf:
            - items:
                $ref: '#/components/schemas/FlowNode'
              type: array
            - type: 'null'
        edges:
          anyOf:
            - items:
                $ref: '#/components/schemas/FlowEdge'
              type: array
            - type: 'null'
        allow_manual_steps:
          anyOf:
            - type: boolean
            - type: 'null'
      type: object
    FlowResponse:
      properties:
        id:
          type: string
          format: uuid
        tenant_id:
          type: string
          format: uuid
        name:
          type: string
        description:
          anyOf:
            - type: string
            - type: 'null'
        use_case:
          type: string
        kind:
          type: string
          enum:
            - kyc
            - kyb
        country:
          anyOf:
            - type: string
            - type: 'null'
        nodes:
          items:
            additionalProperties: true
            type: object
          type: array
        edges:
          items:
            additionalProperties: true
            type: object
          type: array
        version:
          type: integer
        is_published:
          type: boolean
        status:
          type: string
        environment:
          type: string
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        published_at:
          anyOf:
            - type: string
              format: date-time
            - type: 'null'
        country_support:
          anyOf:
            - type: string
            - type: 'null'
          description: >-
            ``"ga"``, ``"beta"`` (CI, SN, TN, EG, SA) or ``None`` for the flow's
            country.
          readOnly: true
      type: object
      required:
        - id
        - tenant_id
        - name
        - description
        - use_case
        - kind
        - nodes
        - edges
        - version
        - is_published
        - status
        - environment
        - created_at
        - updated_at
        - published_at
        - country_support
    Error:
      type: object
      description: >-
        FastAPI error body. `detail` is a string, a list (422), or an object
        with `code` and `message` (extra keys possible).
      properties:
        detail:
          oneOf:
            - type: string
            - items:
                additionalProperties: true
                type: object
              type: array
            - type: object
              additionalProperties: true
              properties:
                code:
                  type: string
                message:
                  type: string
      required:
        - detail
    RateLimitError:
      type: object
      description: Top-level `code` and `message`, no `detail`.
      properties:
        code:
          type: string
        message:
          type: string
      required:
        - code
        - message
    FlowNode:
      properties:
        id:
          type: string
        type:
          type: string
        position:
          additionalProperties:
            type: number
          type: object
        data:
          $ref: '#/components/schemas/FlowNodeData'
        deletable:
          anyOf:
            - type: boolean
            - type: 'null'
      additionalProperties: true
      type: object
      required:
        - id
        - type
        - position
        - data
    FlowEdge:
      properties:
        id:
          type: string
        source:
          type: string
        target:
          type: string
        animated:
          anyOf:
            - type: boolean
            - type: 'null'
          default: true
        style:
          anyOf:
            - additionalProperties: true
              type: object
            - type: 'null'
        data:
          anyOf:
            - additionalProperties: true
              type: object
            - type: 'null'
      additionalProperties: true
      type: object
      required:
        - id
        - source
        - target
    FlowNodeData:
      properties:
        label:
          type: string
        stepType:
          anyOf:
            - type: string
            - type: 'null'
        category:
          anyOf:
            - type: string
            - type: 'null'
        condition:
          anyOf:
            - additionalProperties: true
              type: object
            - type: 'null'
      additionalProperties: true
      type: object
      required:
        - label
  securitySchemes:
    dashboardSession:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: >-
        The access token (JWT) of a signed-in console user, from `POST
        /v1/auth/login`. It lasts 30 minutes. It is not an API key: a partner
        API key is refused here. There is no cookie.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.