openapi: 3.0.0
info:
  title: NexusFlow API - Complete Specification
  description: |
    Comprehensive REST API for the NexusFlow workflow management system covering all functional areas:
    
    ## API Areas:
    - **User**: User authorization and subscription status
    - **Activity**: Activity timeline for flow instances
    - **Inbox**: User and digiworker inbox messages
    - **Messages**: Workflow message creation, retrieval, and management
    - **Flow Instances**: Flow instance querying and status updates
    - **Reporting**: Flow execution data for visualization and reporting
    - **Resource Management**: Assignments and digiworkers (human users and groups are managed via SoS Admin)
    - **Digiworkers**: Service account management (internal to NexusFlow)
    - **Assignments**: Step assignment management for users, groups, and digiworkers
    - **Properties**: Tenant-scoped property definitions used to assign values to flows and steps
    - **Property values**: Enumerated values for a property
    
    ## Authentication
    All endpoints require authentication via OIDC bearer token. Some orchestrator-style operations require tokens with specific `client_id` claims.
    
    ## Server URLs
    All APIs are deployed across 5 environments (dev, qa, stage, cte, production) accessible via the same base paths.
  version: 1.0.0
  contact:
    name: Product Team
    email: productTeam@concentrix.com
servers:
  - url: https://dev.nexusflow.cloud.gradera.ai/nexus-flow/v1
    description: Development server
  - url: https://qa.nexusflow.cloud.gradera.ai/nexus-flow/v1
    description: Quality Assurance server
  - url: https://np.nexusflow.cloud.gradera.ai/nexus-flow/v1
    description: Staging server
  - url: https://nexusflow.cloud.gradera.ai/nexus-flow/v1
    description: Production server

tags:
  - name: User
    description: Operations for the authenticated user's status and metadata.
  - name: Inbox
    description: Operations related to user inbox messages.
  - name: Messages
    description: Operations for creating, retrieving, and updating workflow messages.
  - name: Flows
    description: Operations for managing flow definitions (workflows).
  - name: FlowInstances
    description: Operations for querying and updating flow instances.
  - name: Reporting
    description: Operations for retrieving flow execution data for visualization and reporting as part of the prototype QA tool.

  - name: Digiworkers (Service Accounts)
    description: Operations for creating, reading, updating, and deleting Digiworker service accounts. Digiworkers are internal to NexusFlow.
  - name: Assignments
    description: Operations for managing step assignments (binding users, groups, and digiworkers to steps with roles).
  - name: Properties
    description: Operations for managing tenant properties (definitions used to scope flows and steps).
  - name: PropertyValues
    description: Operations for enumerated values belonging to a property.
paths:
  /user/status:
    get:
      tags:
        - User
      summary: Get authenticated user's authorization status
      operationId: getUserStatus
      description: |
        Returns whether the authenticated user is authorized to use NexusFlow, and lists of step slugs
        the user is subscribed to (`subscriber`) and step slugs the user is allowed to publish to (`publisher`).
      security:
        - oauth2_oidc: []
      responses:
        '200':
          description: User authorization summary
          headers:
            API-Version:
              $ref: '#/components/headers/API-Version'
            Correlation-Key:
              $ref: '#/components/headers/Correlation-Key'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserStatus'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '500':
          $ref: '#/components/responses/InternalServiceError'

  /inbox:
    get:
      summary: Retrieve active messages in the user's inbox
      operationId: getInboxMessages
      tags:
        - Inbox
      description: |
        Retrieves all active (unlocked and not in progress) messages currently assigned to the authenticated user.
        Messages are part of workflow instances and represent tasks requiring user action.
        
        **Use Case**:  
        As a User, I want to retrieve all of the active messages assigned to me, so that I can choose one to act upon.

        - Returns a paginated list of inbox items.
        - If offset is not included int he query, it defaults to 0.
        - If limit is not included in the query, it defaults to 25.
        - Each item includes metadata about the workflow, current step, and message location.
        - Full message details are retrieved via the `messageLocation` URI.
        - Inbox can be filtered by flow by using the `flow-slug` query parameter.
      security:
        - oauth2_oidc: []
      parameters:
        - $ref: '#/components/parameters/offset'
        - $ref: '#/components/parameters/limit'
        - $ref: '#/components/parameters/flow-slug'
      responses:
        '200':
          description: A paginated list of active inbox messages assigned to the user.
          headers:
            API-Version:
              $ref: "#/components/headers/API-Version"
            Correlation-Key:
              $ref: "#/components/headers/Correlation-Key"
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Paged-InboxMessages'
              examples:
                digiworker-basic:
                  $ref: '#/components/examples/digiworker-inbox-basic'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RequestLimitExceeded'
        '500':
          $ref: '#/components/responses/InternalServiceError'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'

  /inbox/digiworker:
    get:
      summary: Retrieve active messages for digiworker subscriber steps
      operationId: getDigiworkerInbox
      tags:
        - Inbox
      description: |
        Retrieves active messages assigned to steps where the subscriber type is `digiworker`.
        This endpoint is designed for orchestrator clients and optionally expands message details and subscriber callbacks.
      security:
        - oauth2_oidc: []
      parameters:
        - $ref: '#/components/parameters/offset'
        - $ref: '#/components/parameters/limit'
        - $ref: '#/components/parameters/flow-slug'
        - $ref: '#/components/parameters/expand'
      responses:
        '200':
          description: Paginated list of digiworker inbox items
          headers:
            API-Version:
              $ref: '#/components/headers/API-Version'
            Correlation-Key:
              $ref: '#/components/headers/Correlation-Key'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Paged-DigiworkerInboxMessages'
              examples:
                digiworker-basic:
                  $ref: '#/components/examples/digiworker-inbox-basic'
                digiworker-with-message:
                  $ref: '#/components/examples/digiworker-inbox-with-message'
                digiworker-with-subscribers:
                  $ref: '#/components/examples/digiworker-inbox-with-subscribers'
                digiworker-with-message-and-subscribers:
                  $ref: '#/components/examples/digiworker-inbox-with-message-and-subscribers'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RequestLimitExceeded'
        '500':
          $ref: '#/components/responses/InternalServiceError'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'

  /messages:
    post:
      tags:
        - Messages
      summary: Create a new message
      operationId: createMessage
      description: |
        Creates a new message in the workflow system. Supports both human users and digiworkers; all callers authenticate with bearer tokens issued by the OIDC provider.
        - Input uses `MessageCreateInput` — excludes system-managed fields.
        - On success, returns `201 Created` with `Location` header.
        - **Publisher Permission Enforcement**: The authenticated actor (from bearer token `sub` claim) must be authorized to publish to the target step:
          - **Human users**: Must be a member of a group that has a publisher assignment for the target step.
          - **Digiworkers**: Must have a direct publisher assignment for the target step.
        - The created message will have `created_by_user_idxid` set to the actor's `user_idxid`.
      security:
        - oauth2_oidc: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MessageCreateInput'
      responses:
        '201':
          description: Message created successfully
          headers:
            API-Version:
              $ref: '#/components/headers/API-Version'
            Correlation-Key:
              $ref: '#/components/headers/Correlation-Key'
            Location:
              $ref: '#/components/headers/Location'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/RequestLimitExceeded'
        '500':
          $ref: '#/components/responses/InternalServiceError'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'

  /messages/{messageId}:
    get:
      tags:
        - Messages
      summary: Get message by ID
      operationId: getMessage
      description: |
        Retrieves full message by `messageId`.
        - **Users** (OIDC): Can access messages they own.
        - **Digiworkers**: authenticate with bearer tokens and may access messages per their assignments/roles.
        - When a message is viewed by a human user, the message is automatically locked for that user for one hour to prevent concurrent processing.
        - Locks (i.e. `properties.lockedUserId` and `properties.lockedTime`) are only set when the message's lifecycle `header.status` is `active`. If a message has a non-active lifecycle value (for example `completed`, `failed`, or `dead`), lock fields will not be populated and will remain null.
        - Optional query param `expand=subscribers` includes subscriber callback information in the response (`subscribers` array on the message envelope). This expansion is restricted to callers with the orchestrator privilege (token `client_id` claim matching `OIDC_DIGIWORKER_ORCHESTRATOR_CLIENT_ID`). Non-privileged callers will receive a 403 if `expand` is requested.
      security:
        - oauth2_oidc: []
      parameters:
        - $ref: '#/components/parameters/messageId'
        - $ref: '#/components/parameters/expand'
      responses:
        '200':
          description: Message details
          headers:
            API-Version:
              $ref: '#/components/headers/API-Version'
            Correlation-Key:
              $ref: '#/components/headers/Correlation-Key'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Message'
              examples:
                bug-duplicate-review:
                  $ref: '#/components/examples/bug-ticket-comparison'
                bug-ticket-comparison:
                  $ref: '#/components/examples/bug-ticket-comparison'
                completed-message-no-locks:
                  $ref: '#/components/examples/completed-message-no-locks'
                bug-severity:
                  $ref: '#/components/examples/bug-severity'
                new-ticket:
                  $ref: '#/components/examples/new-ticket'
                new-ticket-with-subscribers:
                  $ref: '#/components/examples/new-ticket-with-subscribers'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RequestLimitExceeded'
        '500':
          $ref: '#/components/responses/InternalServiceError'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'

    patch:
      tags:
        - Messages
      operationId: patchMessage
      summary: Partially update a message
      description: |
        Partially updates mutable message fields. Supported updates:
        - `header.status` — must be one of `active`, `superseded`, `completed`, `failed`, or `dead`.
        - `properties.lockedUserId` — UUID of the user claiming/locking the message (maps to messages.locked_user_id).
        - `properties.lockedTime` — ISO 8601 timestamp for when the message was locked (maps to messages.locked_dts).

        Note: Locks are managed via `properties.lockedUserId` and `properties.lockedTime` — not via the lifecycle status. To lock a message provide both lock fields with non-null values. To clear locks set both fields to `null` in the same request; partial clearing (only one null) is invalid.

        Security requirements:
        - OIDC tokens (authorization code flow) must include a `client_id` claim matching the environment variable `OIDC_DIGIWORKER_ORCHESTRATOR_CLIENT_ID` to perform orchestrator-style lock operations.
        - Orchestrator-style operations require a token that includes the orchestrator `client_id` claim (matching `OIDC_DIGIWORKER_ORCHESTRATOR_CLIENT_ID`).

      security:
        - oauth2_oidc: []
      parameters:
        - $ref: '#/components/parameters/messageId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MessagePatchInput'
      responses:
        '200':
          description: Message updated successfully
          headers:
            API-Version:
              $ref: '#/components/headers/API-Version'
            Correlation-Key:
              $ref: '#/components/headers/Correlation-Key'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Message'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RequestLimitExceeded'
        '500':
          $ref: '#/components/responses/InternalServiceError'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'

  /messages/{messageId}/locks:
    delete:
      tags:
        - Messages
      summary: Release a message (unlock for others)
      operationId: releaseMessage
      description: |
        Releases (unlocks) the message identified by `messageId`, making it available for another user to claim or process.
        - Only allowed for authorized users or digiworkers.
        - Clears lock information by setting both `properties.lockedUserId` and `properties.lockedTime` to `null`, and sets `header.status` to `active`.
        - If the client supplies an `If-Unmodified-Since` header containing the exact `properties.lockedTime` value the client previously observed, the server will only clear the lock when the stored `lockedTime` equals that value. If they don't match (stale), the server returns a structured result describing the current lock rather than clearing it. The header is optional and omitted by older clients.
      security:
        - oauth2_oidc: []
      parameters:
        - $ref: '#/components/parameters/messageId'
        - $ref: '#/components/parameters/If-Unmodified-Since'
      responses:
        '204':
          description: Message released successfully. No content returned.
          headers:
            API-Version:
              $ref: '#/components/headers/API-Version'
            Correlation-Key:
              $ref: '#/components/headers/Correlation-Key'
        '200':
          description: Unlock request accepted but no change made (stale lock) — returns current lock info.
          headers:
            API-Version:
              $ref: '#/components/headers/API-Version'
            Correlation-Key:
              $ref: '#/components/headers/Correlation-Key'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnlockResult'
              examples:
                staleUnlockExample:
                  summary: Stale unlock no-op
                  value:
                    cleared: false
                    reason: stale_lock
                    currentLock:
                      lockedUserId: 2938edf7-6903-4e19-b285-a1855ffe75ef
                      lockedTime: 2026-02-12T15:34:14.342Z
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RequestLimitExceeded'
        '500':
          $ref: '#/components/responses/InternalServiceError'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'

  /messages/{messageId}/subscribers:
    get:
      tags:
        - Messages
      summary: Get subscribers for message's step (orchestrator)
      operationId: getMessageSubscribers
      description: |
        Returns the list of subscribers (users) assigned to the step of the specified message, including callback URIs.
        Only tokens that include the orchestrator `client_id` claim (matching `OIDC_DIGIWORKER_ORCHESTRATOR_CLIENT_ID`) are allowed.
      security:
        - oauth2_oidc: []
      parameters:
        - $ref: '#/components/parameters/messageId'
      responses:
        '200':
          description: List of subscribers assigned to the step
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Subscriber'
              examples:
                one-digiworker:
                  $ref: '#/components/examples/subscribers-one-digiworker'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalServiceError'

  /flows:
    get:
      tags:
        - Flows
      summary: List all flows
      operationId: listFlows
      description: |
        Retrieves a paginated list of all flows with filtering and sorting options.
        
        **Filtering**:
        - `status`: Filter by flow status (active, inactive, archived)
        - `search`: Full-text search by display_name or cname
        
        **Sorting**:
        - `sort`: Sort key and direction (created_dts|desc, created_dts|asc, display_name|asc, display_name|desc, status|asc, status|desc)
        
        Results are paginated; use `limit` and `offset` query parameters to navigate.
      security:
        - oauth2_oidc: []
      parameters:
        - $ref: '#/components/parameters/limit'
        - $ref: '#/components/parameters/offset'
        - $ref: '#/components/parameters/sort'
        - name: status
          in: query
          required: false
          description: Filter by flow status
          schema:
            type: string
            enum: [active, inactive, archived]
          example: active
        - name: search
          in: query
          required: false
          description: Search keyword (matches display_name or cname)
          schema:
            type: string
          example: claims
      responses:
        '200':
          description: Paginated list of flows
          headers:
            API-Version:
              $ref: '#/components/headers/API-Version'
            Correlation-Key:
              $ref: '#/components/headers/Correlation-Key'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FlowList'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalServiceError'

    post:
      tags:
        - Flows
      summary: Create a new flow
      operationId: createFlow
      description: |
        Creates a new flow definition.
        
        **Authorization**: Requires admin privileges.
        
        **Input Requirements**:
        - `cname`: Canonical name (unique, alphanumeric + hyphens, required)
        - `slug`: URL-friendly identifier (unique, required)
        - `displayName`: Human-readable name (required)
        - `description`: Optional detailed description
        - `status`: Initial status (defaults to 'active')
      security:
        - oauth2_oidc: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/FlowCreateInput'
      responses:
        '201':
          description: Flow created successfully
          headers:
            API-Version:
              $ref: '#/components/headers/API-Version'
            Correlation-Key:
              $ref: '#/components/headers/Correlation-Key'
            Location:
              $ref: '#/components/headers/Location'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Flow'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '409':
          description: Conflict - cname or slug already exists
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Errors'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '500':
          $ref: '#/components/responses/InternalServiceError'

  /flows/{flowId}:
    get:
      tags:
        - Flows
      summary: Get flow by ID
      operationId: getFlow
      description: |
        Retrieves detailed information about a specific flow, including associated steps.
      security:
        - oauth2_oidc: []
      parameters:
        - name: flowId
          in: path
          required: true
          description: Flow GUID (public identifier)
          schema:
            type: string
            format: uuid
          example: ad0bab1d-f102-400a-a0f3-144a13f1021d
      responses:
        '200':
          description: Flow details
          headers:
            API-Version:
              $ref: '#/components/headers/API-Version'
            Correlation-Key:
              $ref: '#/components/headers/Correlation-Key'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Flow'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalServiceError'

    put:
      tags:
        - Flows
      summary: Update flow properties
      operationId: updateFlow
      description: |
        Updates an existing flow's properties (displayName, description, status).
        
        **Authorization**: Requires admin privileges.
        
        **Mutable Fields**:
        - `displayName`: Human-readable name
        - `description`: Detailed description
        - `status`: Flow lifecycle state (active, inactive, archived)
        
        **Immutable Fields**: `cname`, `slug`, `guid` cannot be changed.
      security:
        - oauth2_oidc: []
      parameters:
        - name: flowId
          in: path
          required: true
          description: Flow GUID (public identifier)
          schema:
            type: string
            format: uuid
          example: ad0bab1d-f102-400a-a0f3-144a13f1021d
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/FlowUpdateInput'
      responses:
        '200':
          description: Flow updated successfully
          headers:
            API-Version:
              $ref: '#/components/headers/API-Version'
            Correlation-Key:
              $ref: '#/components/headers/Correlation-Key'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Flow'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '500':
          $ref: '#/components/responses/InternalServiceError'

    delete:
      tags:
        - Flows
      summary: Delete flow
      operationId: deleteFlow
      description: |
        Deletes a flow definition.
        
        **Authorization**: Requires admin privileges.
        
        **Constraints**: Flow must not have active flow instances or steps.
        Remove all associated steps and instances before attempting deletion.
      security:
        - oauth2_oidc: []
      parameters:
        - name: flowId
          in: path
          required: true
          description: Flow GUID (public identifier)
          schema:
            type: string
            format: uuid
          example: ad0bab1d-f102-400a-a0f3-144a13f1021d
      responses:
        '204':
          description: Flow deleted successfully (no content)
          headers:
            API-Version:
              $ref: '#/components/headers/API-Version'
            Correlation-Key:
              $ref: '#/components/headers/Correlation-Key'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: Conflict - Flow has active instances or steps and cannot be deleted
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Errors'
              example:
                - id: 550e8400-e29b-41d4-a716-446655440000
                  code: ADMIN_FLOW_HAS_QUEUES_409
                  title: Conflict
                  detail: Cannot delete flow with active steps. Remove steps first.
        '500':
          $ref: '#/components/responses/InternalServiceError'

  /flows/{flowId}/flow-instances:
    get:
      tags:
        - FlowInstances
      summary: List flow instances for a flow
      operationId: listFlowInstancesForFlow
      description: |
        Returns a paginated list of flow instances for the specified `flowId`.
        Results are paginated using `limit` and `offset` query parameters.
        
        Filtering by status or subject content allows you to efficiently find relevant instances.
        Sorting options include creation date, update date, or subject.
        Use expand to optionally include step assignees or message statistics for each instance.
      security:
        - oauth2_oidc: []
      parameters:
        - $ref: '#/components/parameters/flowId'
        - name: status
          in: query
          required: false
          description: |
            Filter by instance status. Comma-separated list of statuses.
            Example: ?status=active,failed
          schema:
            type: string
          example: active,completed
        - name: subject_contains
          in: query
          required: false
          description: |
            Filter instances by subject (case-insensitive substring match).
            Minimum length 2 characters. Example: ?subject_contains=CLAIM-2026
          schema:
            type: string
            minLength: 2
          example: CLAIM
        - name: sort
          in: query
          required: false
          description: |
            Sort field and direction. Allowed values:
            - created_dts|asc, created_dts|desc (default)
            - updated_dts|asc, updated_dts|desc
            - subject|asc, subject|desc
          schema:
            type: string
          example: created_dts|desc
        - name: expand
          in: query
          required: false
          description: |
            Optional expansion of related entities. Comma-separated values are supported.
            - `step-assignees`: Include subscribers and publishers for each instance's current step
            - `message-stats`: Include message counts by step for each instance
          schema:
            type: string
          example: step-assignees
        - $ref: '#/components/parameters/limit'
        - $ref: '#/components/parameters/offset'
      responses:
        '200':
          description: Paginated list of flow instances
          headers:
            API-Version:
              $ref: '#/components/headers/API-Version'
            Correlation-Key:
              $ref: '#/components/headers/Correlation-Key'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FlowInstanceList'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalServiceError'

  /flows/{flowId}/flow-instances/{flowInstanceId}:
    get:
      tags:
        - FlowInstances
      summary: Retrieve a single flow instance by ID
      operationId: getFlowInstanceById
      description: |
        Retrieve the full flow instance envelope for the specified `flowInstanceId`, including
        the current step context and optional step assignees and message statistics.
        
        Use `expand=step-assignees` to include the subscribers and publishers for the current step.
        Use `expand=message-stats` to include message counts by step.
      security:
        - oauth2_oidc: []
      parameters:
        - $ref: '#/components/parameters/flowId'
        - $ref: '#/components/parameters/flowInstanceId'
        - name: expand
          in: query
          required: false
          description: |
            Optional expansion of related entities. Comma-separated values are supported.
            - `step-assignees`: Include subscribers and publishers for the current step
            - `message-stats`: Include message counts by step and status
          schema:
            type: string
          example: step-assignees,message-stats
      responses:
        '200':
          description: Flow instance retrieved successfully
          headers:
            API-Version:
              $ref: '#/components/headers/API-Version'
            Correlation-Key:
              $ref: '#/components/headers/Correlation-Key'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FlowInstance'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalServiceError'

    patch:
      tags:
        - FlowInstances
      summary: Partially update a flow instance
      operationId: patchFlowInstance
      description: |
        Partially updates mutable flow-instance fields. Currently only `header.status` is supported.
        Orchestrator or service clients must authenticate with a bearer token (OIDC).
      security:
        - oauth2_oidc: []
      parameters:
        - $ref: '#/components/parameters/flowId'
        - $ref: '#/components/parameters/flowInstanceId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/FlowInstancePatchInput'
      responses:
        '200':
          description: Flow instance updated successfully
          headers:
            API-Version:
              $ref: '#/components/headers/API-Version'
            Correlation-Key:
              $ref: '#/components/headers/Correlation-Key'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FlowInstance'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/RequestLimitExceeded'
        '500':
          $ref: '#/components/responses/InternalServiceError'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'

  /flows/{flowId}/flow-instances/{flowInstanceId}/activities:
    get:
      summary: Get activity timeline for a flow instance (flow-scoped)
      operationId: getFlowInstanceActivityByFlow
      description: >
        Same response as the NexusFlow-prefixed endpoint, but scoped under a flow.
        The API returns 404 when either the flow does not exist or the flow instance does not belong to the provided flow.
      tags:
        - FlowInstances
      security:
        - oauth2_oidc: []
      parameters:
        - $ref: '#/components/parameters/flowId'
        - $ref: '#/components/parameters/flowInstanceId'
        - $ref: '#/components/parameters/offset'
        - $ref: '#/components/parameters/limit'
        - $ref: '#/components/parameters/sort'
      responses:
        '200':
          description: Paged activity items
          headers:
            API-Version:
              $ref: '#/components/headers/API-Version'
            Correlation-Key:
              $ref: '#/components/headers/Correlation-Key'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PagedActivityItems'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RequestLimitExceeded'
        '500':
          $ref: '#/components/responses/InternalServiceError'


  /reporting/flow-instances:
    get:
      tags:
        - Reporting
      summary: List flow instances for reporting
      operationId: listFlowInstancesReporting
      description: |
        Retrieves a paginated list of flow instances with optional filtering and sorting.
        
        Supports filtering by flow slug and status, with customizable pagination and sorting.
        
        **Access Control:**
        - Users can list flow instances in their assigned tenant
      security:
        - oauth2_oidc: []
      parameters:
        - name: offset
          in: query
          description: Pagination offset (default 0)
          schema:
            type: integer
            minimum: 0
            default: 0
        - name: limit
          in: query
          description: Page size (default 25, max 250)
          schema:
            type: integer
            minimum: 1
            maximum: 250
            default: 25
        - name: sort
          in: query
          description: Sort field and direction (e.g., 'created_dts|desc', 'status|asc')
          schema:
            type: string
            example: 'created_dts|desc'
        - name: flowSlug
          in: query
          description: Filter by flow slug
          schema:
            type: string
            example: 'vbt-ado-bug-intake'
        - name: status
          in: query
          description: Filter by flow instance status
          schema:
            type: string
            enum: ['active', 'completed', 'failed']
      responses:
        '200':
          description: Paginated list of flow instances
          headers:
            API-Version:
              $ref: '#/components/headers/API-Version'
            Correlation-Key:
              $ref: '#/components/headers/Correlation-Key'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PagedFlowInstances'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RequestLimitExceeded'
        '500':
          $ref: '#/components/responses/InternalServiceError'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'

  /reporting/flow-instances/{flowInstanceId}:
    get:
      tags:
        - Reporting
      summary: Get flow instance execution data for reporting
      operationId: getFlowInstanceReporting
      description: |
        Retrieves complete flow instance data including the flow definition, instance details, all messages in sequence, and computed metrics (node visits, edge usage, durations).
        
        This endpoint is optimized for visualization and reporting purposes, returning all necessary data in a single request to minimize client-side processing.
        
        **Access Control:**
        - Users can access flow instances in their assigned tenant
        - Returns 404 if the flow instance does not exist or is not accessible to the caller's tenant
      security:
        - oauth2_oidc: []
      parameters:
        - $ref: '#/components/parameters/flowInstanceIdReporting'
      responses:
        '200':
          description: Flow instance execution data for reporting
          headers:
            API-Version:
              $ref: '#/components/headers/API-Version'
            Correlation-Key:
              $ref: '#/components/headers/Correlation-Key'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FlowInstanceReporting'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RequestLimitExceeded'
        '500':
          $ref: '#/components/responses/InternalServiceError'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'

  /digiworkers:
    get:
      tags:
        - Digiworkers (Service Accounts)
      summary: List all Digiworker records (Service Accounts)
      operationId: listDigiworkers
      description: |
        Retrieves a paginated list of all Digiworker records (service accounts) managed by NexusFlow.
        **SERVICE ACCOUNTS ONLY**: This endpoint returns only async webhook handlers, not human users.
        **Humans**: Human user records are managed via the SoS Admin API (not listed here).
        
        This is a read-only reference endpoint for enriching assignment responses and audit queries.
        **To create, update, or delete Digiworkers, call the NexusFlow API (this endpoint handles CRUD).**
        - Results are paginated; use `limit` and `offset` query parameters to navigate.
        - Response includes HATEOS links for navigation and pagination.
      security:
        - oauth2_oidc: []
      parameters:
        - $ref: '#/components/parameters/limit'
        - $ref: '#/components/parameters/offset'
      responses:
        '200':
          description: Paginated list of Digiworkers (service accounts managed by NexusFlow)
          headers:
            API-Version:
              $ref: '#/components/headers/API-Version'
            Correlation-Key:
              $ref: '#/components/headers/Correlation-Key'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DigiworkerList'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalServiceError'
    post:
      tags:
        - Digiworkers (Service Accounts)
      summary: Create a new Digiworker (Service Account)
      operationId: createDigiworker
      description: |
        Creates a new Digiworker service account with a callback URI for async message delivery.
        
        **Authorization**: Requires admin privileges.
        **Note**: To update or delete Digiworkers, use PUT or DELETE endpoints.
      security:
        - oauth2_oidc: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateDigiworkerRequest'
      responses:
        '201':
          description: Digiworker created successfully
          headers:
            API-Version:
              $ref: '#/components/headers/API-Version'
            Correlation-Key:
              $ref: '#/components/headers/Correlation-Key'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Digiworker'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '409':
          description: Conflict - displayName already exists or other conflict
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Errors'
        '500':
          $ref: '#/components/responses/InternalServiceError'

  /digiworkers/{digiworkerGuid}:
    get:
      tags:
        - Digiworkers (Service Accounts)
      summary: Get Digiworker by GUID (Service Account)
      operationId: getDigiworker
      description: |
        Retrieves detailed information about a specific Digiworker, including callback URI and step assignments.
        **SERVICE ACCOUNTS ONLY**: This endpoint returns only service accounts. For human users, use the SoS Admin API.
        
        This is a read-only reference endpoint. Digiworker creation and assignment management is handled by NexusFlow.
        **To update or delete this Digiworker, use PUT or DELETE on this same endpoint.**
      security:
        - oauth2_oidc: []
      parameters:
        - $ref: '#/components/parameters/digiworkerGuid'
      responses:
        '200':
          description: Digiworker details (service account managed by NexusFlow)
          headers:
            API-Version:
              $ref: '#/components/headers/API-Version'
            Correlation-Key:
              $ref: '#/components/headers/Correlation-Key'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DigiworkerDetail'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalServiceError'
    put:
      tags:
        - Digiworkers (Service Accounts)
      summary: Update Digiworker details
      operationId: updateDigiworker
      description: |
        Updates an existing Digiworker's displayName and/or callbackUri.
        
        **Authorization**: Requires admin privileges.
        **Note**: At least one field (displayName or callbackUri) must be provided.
      security:
        - oauth2_oidc: []
      parameters:
        - $ref: '#/components/parameters/digiworkerGuid'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateDigiworkerRequest'
      responses:
        '200':
          description: Digiworker updated successfully
          headers:
            API-Version:
              $ref: '#/components/headers/API-Version'
            Correlation-Key:
              $ref: '#/components/headers/Correlation-Key'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Digiworker'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: Conflict - displayName already exists or other conflict
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Errors'
        '500':
          $ref: '#/components/responses/InternalServiceError'
    delete:
      tags:
        - Digiworkers (Service Accounts)
      summary: Delete Digiworker
      operationId: deleteDigiworker
      description: |
        Deletes a Digiworker service account.
        
        **Authorization**: Requires admin privileges.
        **Constraint**: Digiworker must have no active step assignments. Remove all assignments before deletion.
      security:
        - oauth2_oidc: []
      parameters:
        - $ref: '#/components/parameters/digiworkerGuid'
      responses:
        '204':
          description: Digiworker deleted successfully (no content)
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: Conflict - Digiworker has active step assignments and cannot be deleted
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Errors'
              example:
                - id: 550e8400-e29b-41d4-a716-446655440000
                  code: ADMIN_DIGIWORKER_HAS_ASSIGNMENTS_409
                  title: Conflict
                  detail: Cannot delete Digiworker with active step assignments. Remove assignments first.
        '500':
          $ref: '#/components/responses/InternalServiceError'

  /assignments:
    post:
      tags:
        - Assignments
      summary: Create a new assignment
      operationId: createAssignment
      description: |
        Creates a new step/flow assignment for a group (from SoS Admin) or Digiworker (from NexusFlow).
        - **Admin Authorization Required**: Caller must have admin privileges.
        - **Group Assignments**: Groups enable multi-user access; humans access steps exclusively through group membership.
        - **Digiworker Assignments**: Digiworkers receive async webhook deliveries; they cannot participate in groups.
        - Input uses `AssignmentCreateInput` — specifies assignee type/ID and step reference.
        - On success, returns `201 Created` with `Location` header pointing to the new assignment resource.
        - Response includes enriched group or Digiworker details.
        - **Error Codes**:
          - `ADMIN_ASSIGNMENT_DUPLICATE_409`: Assignment already exists (same assignee and step)
          - `SOS_ADMIN_GROUP_NOT_FOUND_404`: Group UUID does not exist in SoS Admin
          - `ADMIN_DIGIWORKER_NOT_FOUND_404`: Digiworker UUID does not exist in NexusFlow
          - `ADMIN_QUEUE_NOT_FOUND_404`: Step does not exist in NexusFlow
          - `ADMIN_INVALID_INPUT_400`: Invalid input (validation failure)
      security:
        - oauth2_oidc: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AssignmentCreateInput'
      responses:
        '201':
          description: Assignment created successfully
          headers:
            API-Version:
              $ref: '#/components/headers/API-Version'
            Correlation-Key:
              $ref: '#/components/headers/Correlation-Key'
            Location:
              $ref: '#/components/headers/Location'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Assignment'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '500':
          $ref: '#/components/responses/InternalServiceError'

    get:
      tags:
        - Assignments
      summary: List all assignments
      operationId: listAssignments
      description: |
        Retrieves a paginated list of all step and flow assignments with enriched group and Digiworker details.
        - **Admin Authorization Required**: Caller must have admin privileges.
        - Results are paginated; use `limit` and `offset` query parameters to navigate.
        - Response includes HATEOS links for navigation and pagination.
        - Each assignment includes enriched group data (from SoS Admin) or Digiworker data (from NexusFlow).
      security:
        - oauth2_oidc: []
      parameters:
        - $ref: '#/components/parameters/limit'
        - $ref: '#/components/parameters/offset'
      responses:
        '200':
          description: Paginated list of assignments with SoS Admin enrichment
          headers:
            API-Version:
              $ref: '#/components/headers/API-Version'
            Correlation-Key:
              $ref: '#/components/headers/Correlation-Key'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AssignmentList'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '500':
          $ref: '#/components/responses/InternalServiceError'

  /assignments/{assignmentGuid}:
    get:
      tags:
        - Assignments
      summary: Get assignment by GUID
      operationId: getAssignment
      description: |
        Retrieves detailed information about a specific assignment, including enriched group or Digiworker data.
        - **Admin Authorization Required**: Caller must have admin privileges.
      security:
        - oauth2_oidc: []
      parameters:
        - $ref: '#/components/parameters/assignmentGuid'
      responses:
        '200':
          description: Assignment details with SoS Admin enrichment
          headers:
            API-Version:
              $ref: '#/components/headers/API-Version'
            Correlation-Key:
              $ref: '#/components/headers/Correlation-Key'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Assignment'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalServiceError'

    delete:
      tags:
        - Assignments
      summary: Delete assignment
      operationId: deleteAssignment
      description: |
        Deletes an assignment, revoking step/flow access.
        - **Admin Authorization Required**: Caller must have admin privileges.
        - Returns `204 No Content` on success.
        - **Error Codes**:
          - `ADMIN_ASSIGNMENT_NOT_FOUND_404`: Assignment with the specified GUID does not exist
      security:
        - oauth2_oidc: []
      parameters:
        - $ref: '#/components/parameters/assignmentGuid'
      responses:
        '204':
          description: Assignment deleted successfully. No content returned.
          headers:
            API-Version:
              $ref: '#/components/headers/API-Version'
            Correlation-Key:
              $ref: '#/components/headers/Correlation-Key'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalServiceError'

  /properties:
    get:
      tags:
        - Properties
      summary: List all properties
      operationId: listProperties
      description: Retrieves a paginated list of all properties.
      security:
        - oauth2_oidc: []
      parameters:
        - $ref: '#/components/parameters/offset'
        - $ref: '#/components/parameters/limit'
      responses:
        '200':
          description: A paginated list of properties.
          headers:
            API-Version:
              $ref: '#/components/headers/API-Version'
            Correlation-Key:
              $ref: '#/components/headers/Correlation-Key'
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Property'
                  paging:
                    $ref: '#/components/schemas/Paging'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RequestLimitExceeded'
        '500':
          $ref: '#/components/responses/InternalServiceError'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
    post:
      tags:
        - Properties
      summary: Create a new property
      operationId: createProperty
      description: Creates a new property with a given name and description.
      security:
        - oauth2_oidc: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PropertyCreate'
      responses:
        '201':
          description: Property created successfully.
          headers:
            API-Version:
              $ref: '#/components/headers/API-Version'
            Correlation-Key:
              $ref: '#/components/headers/Correlation-Key'
            Location:
              $ref: '#/components/headers/Location'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Property'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '422':
          $ref: '#/components/responses/UnprocessableEntity'
        '429':
          $ref: '#/components/responses/RequestLimitExceeded'
        '500':
          $ref: '#/components/responses/InternalServiceError'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'

  /properties/{propertyId}:
    parameters:
      - $ref: '#/components/parameters/propertyId'
    get:
      tags:
        - Properties
      summary: Get property by ID
      operationId: getPropertyById
      description: Retrieves a single property by its unique identifier.
      security:
        - oauth2_oidc: []
      responses:
        '200':
          description: The requested property.
          headers:
            API-Version:
              $ref: '#/components/headers/API-Version'
            Correlation-Key:
              $ref: '#/components/headers/Correlation-Key'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Property'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RequestLimitExceeded'
        '500':
          $ref: '#/components/responses/InternalServiceError'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
    put:
      tags:
        - Properties
      summary: Update a property
      operationId: updateProperty
      description: Updates the name and description of a property.
      security:
        - oauth2_oidc: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PropertyCreate'
      responses:
        '200':
          description: Property updated successfully.
          headers:
            API-Version:
              $ref: '#/components/headers/API-Version'
            Correlation-Key:
              $ref: '#/components/headers/Correlation-Key'
            Location:
              $ref: '#/components/headers/Location'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Property'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/UnprocessableEntity'
        '429':
          $ref: '#/components/responses/RequestLimitExceeded'
        '500':
          $ref: '#/components/responses/InternalServiceError'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
    delete:
      tags:
        - Properties
      summary: Delete a property
      operationId: deleteProperty
      description: Deletes a specific property record.
      security:
        - oauth2_oidc: []
      responses:
        '204':
          description: Property deleted successfully.
          headers:
            API-Version:
              $ref: '#/components/headers/API-Version'
            Correlation-Key:
              $ref: '#/components/headers/Correlation-Key'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RequestLimitExceeded'
        '500':
          $ref: '#/components/responses/InternalServiceError'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'

  /properties/{propertyId}/propertyValues:
    parameters:
      - $ref: '#/components/parameters/propertyId'
    get:
      tags:
        - PropertyValues
      summary: List property values
      operationId: listPropertyValues
      description: Retrieves a paginated list of property values for a given property.
      security:
        - oauth2_oidc: []
      parameters:
        - $ref: '#/components/parameters/offset'
        - $ref: '#/components/parameters/limit'
      responses:
        '200':
          description: A paginated list of property values.
          headers:
            API-Version:
              $ref: '#/components/headers/API-Version'
            Correlation-Key:
              $ref: '#/components/headers/Correlation-Key'
          content:
            application/json:
              schema:
                type: object
                properties:
                  paging:
                    $ref: '#/components/schemas/Paging'
                  results:
                    type: array
                    items:
                      $ref: '#/components/schemas/PropertyValue'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RequestLimitExceeded'
        '500':
          $ref: '#/components/responses/InternalServiceError'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
    post:
      tags:
        - PropertyValues
      summary: Create property value
      operationId: createPropertyValue
      description: Creates a new property value for a given property.
      security:
        - oauth2_oidc: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PropertyValueCreate'
      responses:
        '201':
          description: The property value was created successfully.
          headers:
            API-Version:
              $ref: '#/components/headers/API-Version'
            Correlation-Key:
              $ref: '#/components/headers/Correlation-Key'
            Location:
              $ref: '#/components/headers/Location'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PropertyValue'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RequestLimitExceeded'
        '500':
          $ref: '#/components/responses/InternalServiceError'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'

  /propertyValues/{propertyValueId}:
    parameters:
      - $ref: '#/components/parameters/propertyValueId'
    get:
      tags:
        - PropertyValues
      summary: Get property value
      operationId: getPropertyValue
      description: Retrieves a single property value by its ID.
      security:
        - oauth2_oidc: []
      responses:
        '200':
          description: The requested property value.
          headers:
            API-Version:
              $ref: '#/components/headers/API-Version'
            Correlation-Key:
              $ref: '#/components/headers/Correlation-Key'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PropertyValue'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RequestLimitExceeded'
        '500':
          $ref: '#/components/responses/InternalServiceError'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
    put:
      tags:
        - PropertyValues
      summary: Update property value
      operationId: updatePropertyValue
      description: Updates an existing property value.
      security:
        - oauth2_oidc: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PropertyValueCreate'
      responses:
        '200':
          description: The property value was updated successfully.
          headers:
            API-Version:
              $ref: '#/components/headers/API-Version'
            Correlation-Key:
              $ref: '#/components/headers/Correlation-Key'
            Location:
              $ref: '#/components/headers/Location'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PropertyValue'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RequestLimitExceeded'
        '500':
          $ref: '#/components/responses/InternalServiceError'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
    delete:
      tags:
        - PropertyValues
      summary: Delete property value
      operationId: deletePropertyValue
      description: Deletes a property value. If the value has assignments, it will be archived instead of deleted.
      security:
        - oauth2_oidc: []
      responses:
        '204':
          description: The property value was deleted or archived successfully.
          headers:
            API-Version:
              $ref: '#/components/headers/API-Version'
            Correlation-Key:
              $ref: '#/components/headers/Correlation-Key'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RequestLimitExceeded'
        '500':
          $ref: '#/components/responses/InternalServiceError'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'

components:
  parameters:
    flowId:
      name: flowId
      in: path
      required: true
      description: Flow GUID (public identifier)
      schema:
        type: string
        format: uuid
      example: ad0bab1d-f102-400a-a0f3-144a13f1021d
    flowInstanceId:
      name: flowInstanceId
      in: path
      required: true
      description: Flow instance GUID (public identifier)
      schema:
        type: string
        format: uuid
      example: 10540421-2a14-4a11-a89b-fa11b4900000
    flowInstanceIdReporting:
      name: flowInstanceId
      in: path
      required: true
      description: Flow instance ID (UUID format)
      schema:
        type: string
        format: uuid
      example: 419e62c0-212f-4a32-8ec4-396576d1fdba
    offset:
      name: offset
      in: query
      required: false
      description: Zero-based record offset
      schema:
        type: integer
        minimum: 0
        default: 0
      example: 0
    limit:
      name: limit
      in: query
      required: false
      description: Page size (default 25, max 1000)
      schema:
        type: integer
        minimum: 1
        maximum: 1000
        default: 25
      example: 25
    sort:
      name: sort
      in: query
      required: false
      description: "Sort key and direction (allowed: created_dts|desc or created_dts|asc)"
      schema:
        type: string
        default: created_dts|desc
      example: created_dts|desc
    flow-slug:
      name: flow-slug
      in: query
      description: The unique human-readable identifier (slug) for the flow to filter inbox messages by.
      required: false
      example: bug-intake
      schema:
        type: string
        format: slug
    expand:
      name: expand
      in: query
      required: false
      description: >-
        Optional expansion of related entities. Canonical values are `message` (singular) and `subscribers` (plural).
        - `message`: include the full message envelope for each inbox item.
        - `subscribers`: include the list of subscribers for the step.
        Multiple values may be comma-separated, for example: `expand=message,subscribers`.
        Note: Any expand value requires an orchestrator client token by design; non-orchestrator callers receive 403.
      schema:
        type: string
      example: message,subscribers
    messageId:
      name: messageId
      in: path
      required: true
      schema:
        type: string
        format: uuid
      example: c7def7f8-ef4e-4219-bf46-e0c3010a6317
    If-Unmodified-Since:
      name: If-Unmodified-Since
      in: header
      required: false
      description: |
        Optional ISO 8601 timestamp value equal to the `properties.lockedTime` previously observed by the client.
        When present, the server will only clear the lock if the stored
      schema:
        type: string
        format: date-time
      example: 2026-02-12T15:34:14.342Z
    assignmentGuid:
      name: assignmentGuid
      in: path
      required: true
      schema:
        type: string
        format: uuid
      description: Assignment GUID
      example: aa0e7600-d40c-52e5-c827-557766551122
    digiworkerGuid:
      name: digiworkerGuid
      in: path
      required: true
      schema:
        type: string
        format: uuid
      description: Digiworker GUID
      example: 660e8400-e29b-41d4-a716-446655440000
    propertyId:
      name: propertyId
      in: path
      required: true
      description: Property UUID
      schema:
        type: string
        format: uuid
      example: 41e2cbac-280a-46bc-840f-fecec4bf1752
    propertyValueId:
      name: propertyValueId
      in: path
      required: true
      description: Property value UUID
      schema:
        type: string
        format: uuid
      example: 1c20e1a8-31ab-4b25-aed8-36ed5a684430

  headers:
    API-Version:
      description: API version
      required: true
      schema:
        type: string
        example: 1.0.0
    Correlation-Key:
      description: Request trace ID
      required: true
      schema:
        type: string
        format: uuid
    Location:
      description: URI of created resource
      required: true
      schema:
        type: string
        format: uri

  schemas:
    UserStatus:
      type: object
      required:
        - authorized
        - subscriber
        - publisher
      properties:
        authorized:
          type: boolean
          description: Whether the authenticated user is recognized and allowed by the API
          example: true
        subscriber:
          type: array
          description: Array of step slugs the user is subscribed to (may be empty)
          items:
            type: string
          example: ["vbt-duplicate-analysis", "vbt-integrity-analysis"]
        publisher:
          type: array
          description: Array of step slugs the user can publish to (may be empty)
          items:
            type: string
          example: ["vbt-ado-update"]

    UserSummary:
      type: object
      description: Minimal user info for activity rows (may be null)
      additionalProperties: false
      properties:
        firstName:
          type: string
          nullable: true
        lastName:
          type: string
          nullable: true
        email:
          type: string
          format: email
          nullable: true
        userGuid:
          type: string
          format: uuid
          nullable: true

    ActivityItem:
      type: object
      description: Single activity row derived from a message
      additionalProperties: false
      required:
        - id
        - timestamp
        - title
        - messageGuid
        - stepSlug
        - messageTypeSlug
        - metadata
      properties:
        id:
          type: string
          description: Activity id (use message.guid)
          format: uuid
        icon:
          type: string
          description: Short icon key for UI mapping (e.g., robot, person, check, warning)
          nullable: true
        timestamp:
          type: string
          format: date-time
          description: ISO8601 timestamp (message.created_dts)
        title:
          type: string
          description: Short title for the activity row
        description:
          type: string
          nullable: true
          description: Short description or summary
        user:
          allOf:
            - $ref: '#/components/schemas/UserSummary'
          nullable: true
        messageGuid:
          type: string
          format: uuid
          description: The originating message GUID
        stepSlug:
          type: string
          description: Step slug for the message
        messageTypeSlug:
          type: string
          description: Message type slug
        metadata:
          type: object
          nullable: true
          description: "Optional additional free-form metadata extracted from message body (v1: null)"

    Links:
      type: object
      additionalProperties: false
      properties:
        self:
          type: string
          format: uri
        first:
          type: string
          format: uri
        prev:
          type: string
          format: uri
          nullable: true
        next:
          type: string
          format: uri
          nullable: true
        last:
          type: string
          format: uri

    PagedActivityItems:
      type: object
      description: Paged response wrapper for activity items (aligned to existing API)
      additionalProperties: false
      required:
        - offset
        - limit
        - total
        - _links
        - data
      properties:
        offset:
          type: integer
        limit:
          type: integer
        total:
          type: integer
        _links:
          $ref: '#/components/schemas/Links'
        data:
          type: array
          items:
            $ref: '#/components/schemas/ActivityItem'

    InboxMessage:
      type: object
      description: A single active message in the user's inbox.
      properties:
        flowName:
          type: string
          description: Human-readable name of the workflow or process.
          minLength: 1
          maxLength: 100
          example: "Bug Intake"
        stepSlug:
          type: string
          description: Machine-readable step identifier (slug).
          example: "duplicate-review"
        stepName:
          type: string
          description: Human-readable name of the step.
          example: "Duplicate Review Step"
        instanceSubject:
          type: string
          description: Brief, human-readable summary or subject line for the message.
          minLength: 1
          maxLength: 256
          example: "iOS - Hourly Rainfall Value Displayed as Daily Total"
        instanceMetadata:
          type: object
          description: Optional JSON metadata for the flow instance.
          nullable: true
        messageGuid:
          type: string
          format: uuid
          description: Unique identifier (UUID) for this specific message.
          pattern: ^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$
          example: "c7def7f8-ef4e-4219-bf46-e0c3010a6317"
        messageCreatedTime:
          type: string
          format: date-time
          description: Message creation timestamp (ISO 8601).
          example: "2025-06-05T09:00:00Z"
        messageLocation:
          type: string
          format: uri
          description: API path to retrieve full message details.
          example: "https://api.gradera.ai/nexusFlow/flow/v1/messages/c7def7f8-ef4e-4219-bf46-e0c3010a6317"
        messageTypeSlug:
          type: string
          description: Machine-readable identifier (slug) for the message type.
          minLength: 5
          maxLength: 50
          pattern: ^[a-z0-9-]+$
          example: "duplicate-review"
        subscriberType:
          type: string
          enum: [human, digiworker]
          description: Present on digiworker inbox items; indicates subscriber channel for the step.
          example: digiworker
        message:
          type: object
          description: Full message envelope; present when expand=message (digiworker inbox).
          additionalProperties: true
        subscribers:
          type: array
          description: Subscribers for the step; present when expand=subscribers (digiworker inbox).
          items:
            $ref: '#/components/schemas/Subscriber'
        messageMetadata:
          type: object
          description: Optional metadata about the message, such as lock information.
          nullable: true
          properties:
            lock:
              type: object
              description: Lock information if the message is locked by another user.
              nullable: true
              properties:
                locked_dts:
                  type: string
                  format: date-time
                  description: Timestamp when the message was locked (ISO 8601).
                  example: "2025-06-05T10:15:30Z"
                locked_display_name:
                  type: string
                  description: Display name of the user who locked the message.
                  nullable: true
                  example: "John Doe"
                locked_by_guid:
                  type: string
                  format: uuid
                  description: GUID of the user who locked the message.
                  pattern: ^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$
                  example: "a1b2c3d4-e5f6-4a5b-8c9d-0e1f2a3b4c5d"
              required:
                - locked_dts
                - locked_display_name
      required:
        - flowName
        - instanceSubject
        - messageGuid
        - messageCreatedTime
        - messageLocation
        - messageTypeSlug
      additionalProperties: false

    Paged-InboxMessages:
      allOf:
        - $ref: '#/components/schemas/Paging'
      type: object
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/InboxMessage'
      required:
        - data

    DigiworkerInboxMessage:
      allOf:
        - $ref: '#/components/schemas/InboxMessage'
        - type: object
          description: Minimal inbox item for digiworker steps; optionally includes message and subscribers when requested via expand

    Paged-DigiworkerInboxMessages:
      allOf:
        - $ref: '#/components/schemas/Paging'
      type: object
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/DigiworkerInboxMessage'
      required:
        - data

    Subscriber:
      type: object
      description: Subscriber information for a step; used by orchestrator to route callbacks.
      properties:
        userGuid:
          type: string
          format: uuid
          description: Subscriber user GUID
        userIdxid:
          type: integer
          format: int64
          description: Numeric database identifier for subscriber user
        displayName:
          type: string
          description: Subscriber display name
        callbackUri:
          type: string
          format: uri
          description: Callback URL for the subscriber (digiworker) or null for human
        assigneeType:
          type: string
          enum: [user, group]
        role:
          type: string
          enum: [subscriber, publisher]
        userType:
          type: string
          enum: [human, digiworker]
          description: The type of the user (human or digiworker)

    Paging:
      type: object
      description: Pagination metadata for the result set.
      properties:
        offset:
          type: integer
          description: Number of records skipped.
          example: 0
        limit:
          type: integer
          description: Number of records returned in this page.
          example: 25
        total:
          type: integer
          description: Total number of matching records across all pages.
          example: 47
        _links:
          type: object
          description: HATEOAS navigation links for pagination.
          properties:
            self:
              type: string
              format: uri
              example: "https://api.gradera.ai/nexusFlow/inbox/v1/inbox?offset=25&limit=25"
            first:
              type: string
              format: uri
              example: "https://api.gradera.ai/nexusFlow/inbox/v1/inbox?offset=0&limit=25"
            prev:
              type: string
              format: uri
              nullable: true
              example: "https://api.gradera.ai/nexusFlow/inbox/v1/inbox?offset=0&limit=25"
            next:
              type: string
              format: uri
              nullable: true
              example: "https://api.gradera.ai/nexusFlow/inbox/v1/inbox?offset=50&limit=25"
            last:
              type: string
              format: uri
              example: "https://api.gradera.ai/nexusFlow/inbox/v1/inbox?offset=25&limit=25"
      required:
        - offset
        - limit
        - total
        - _links

    Header:
      type: object
      description: Message header metadata.
      required:
        - status
        - ttl
      additionalProperties: false
      properties:
        status:
          type: string
          enum: [active, superseded, completed, failed, dead]
          description: Current lifecycle status of the message. Use these lifecycle values to indicate message progress; locks are represented separately via `properties.lockedUserId` and `properties.lockedTime`.
          example: active 
        ttl:
          type: string
          format: date-time
          description: ISO 8601 timestamp when the message expires (UTC).
          example: 2025-11-01T16:04:49Z

    CreateProperties:
      type: object
      description: Properties required when creating a new message.
      required:
        - tenantId
        - stepSlug
        - messageTypeSlug
        - sequence
        - subject
      properties:
        tenantId:
          type: string
          format: uuid
          example: d2dc0315-853c-4d15-916f-1e47ffec8af4
        flowInstanceId:
          type: string
          format: uuid
          description: Optional existing flow instance GUID. If omitted and the step is the first in the flow a new flow instance may be created.
          example: 8415c2f8-5c48-4319-8c13-8fa3bd68cf48
        flowId:
          type: string
          format: uuid
          description: Optional flow GUID. Either `flowId` or `flowInstanceId` should be provided when applicable.
          example: 4da73b93-e85c-43c9-b0ff-395a33c73b87
        messageId:
          type: string
          format: uuid
          readOnly: true
        stepSlug:
          type: string
          pattern: '^[a-z0-9-]+$'
          example: duplicate-review
        sequence:
          type: integer
          minimum: 0
          default: 0
        messageTypeSlug:
          type: string
          pattern: '^[a-z0-9-]+$'
          example: duplicate-review
        actorJWT:
          type: string
          description: JWT token of the user or digiworker acting on this message.
        to:
          type: string
          format: uri
          example: nf://prod/vmt/bug-intake/5/duplicate-review
        subject:
          type: string
          minLength: 1
          example: iOS - Hourly Rainfall Value Displayed as Daily Total

    Properties:
      allOf:
        - $ref: '#/components/schemas/CreateProperties'
        - type: object
          properties:
            messageId:
              type: string
              format: uuid
              readOnly: true
            creationTime:
              type: string
              format: date-time
              readOnly: true
            flowInstanceId:
              type: string
              format: uuid
              readOnly: true
              description: Flow instance GUID associated with this message, when available.
            flowId:
              type: string
              format: uuid
              readOnly: true
              description: Parent flow GUID, when available.
            lockedUserId:
              type: string
              format: uuid
              nullable: true
              readOnly: true
              description: User ID of the user who has locked the message for processing (maps to messages.locked_user_id). This field is only populated when the message `header.status` is `active`.
              example: 3c1c6122-5496-4c77-84eb-417a31c30f5c
            lockedTime:
              type: string
              format: date-time
              nullable: true
              readOnly: true
              description: ISO 8601 timestamp when the message was locked for processing (UTC) (maps to messages.locked_dts). This field is only populated when the message `header.status` is `active`.
              example: 2025-10-28T15:04:49Z
            messageType:
              type: string
              description: Resolved message type slug returned by the system (response field).
              example: duplicate-review

    MessagePatchInput:
      type: object
      description: Partial update payload. Only the fields listed are mutable via this endpoint `header.status`, `properties.lockedUserId`, `properties.lockedTime`.
      additionalProperties: false
      properties:
        header:
          type: object
          properties:
            status:
              type: string
              enum: [active, superseded, completed, failed, dead]
              description: New lifecycle status for the message. Locks are managed via `properties.lockedUserId` and `properties.lockedTime` rather than the lifecycle status.
        properties:
          type: object
          properties:
            lockedUserId:
              type: string
              format: uuid
              nullable: true
              description: UUID of the user claiming/locking the message (maps to messages.locked_user_id). To clear locks set this to `null`.
            lockedTime:
              type: string
              format: date-time
              nullable: true
              description: ISO 8601 timestamp for when the message was locked (maps to messages.locked_dts). To clear locks set this to `null`.
      example:
        header:
          status: active
        properties:
          lockedUserId: 3c1c6122-5496-4c77-84eb-417a31c30f5c
          lockedTime: 2025-10-28T15:04:49Z

    UnlockResult:
      type: object
      required:
        - cleared
      properties:
        cleared:
          type: boolean
          description: True when the server actually cleared the lock.
        reason:
          type: string
          nullable: true
          description: Machine-readable reason when `cleared` is false (e.g., `stale_lock`, `not_locked`, `not_owner`).
          example: stale_lock
        currentLock:
          type: object
          nullable: true
          description: Current lock information on the message when no clear was performed.
          properties:
            lockedUserId:
              type: string
              format: uuid
              nullable: true
              description: GUID of the user who currently owns the lock.
              example: 2938edf7-6903-4e19-b285-a1855ffe75ef
            lockedTime:
              type: string
              format: date-time
              nullable: true
              description: ISO 8601 timestamp when the current lock was taken.
              example: 2026-02-12T15:34:14.342Z

    MessageEnvelope:
      type: object
      description: Full message envelope including header, properties, and body. Optional `subscribers` is included when `expand=subscribers` is used and the caller has orchestrator privileges.
      required:
        - header
        - properties
        - body
      additionalProperties: false
      properties:
        header:
          $ref: '#/components/schemas/Header'
        properties:
          $ref: '#/components/schemas/Properties'
        body:
          type: object
          description: The actual payload. Structure depends on `properties.messageType`.
          additionalProperties: true
        subscribers:
          type: array
          description: Subscriber callbacks for the step; only present when `expand=subscribers` is used and caller has orchestrator privileges.
          items:
            $ref: '#/components/schemas/Subscriber'

    MessageCreateInput:
      type: object
      description: Input for creating a new message. Excludes system-managed fields.
      required:
        - properties
        - body
      additionalProperties: false
      properties:
        properties:
          $ref: '#/components/schemas/CreateProperties'
        body:
          type: object
          additionalProperties: true

    Message:
      allOf:
        - $ref: '#/components/schemas/MessageEnvelope'
      description: Complete message object returned by the API.

    FlowInstanceHeader:
      type: object
      required:
        - status
      additionalProperties: false
      properties:
        status:
          type: string
          enum: [running, paused, completed, failed, cancelled, active]
          description: Lifecycle status of the flow instance.
          example: running

    FlowInstancePatchInput:
      type: object
      description: Partial update payload. Only `header.status` is mutable.
      additionalProperties: false
      properties:
        header:
          type: object
          properties:
            status:
              type: string
              enum: [running, paused, completed, failed, cancelled, active]
              description: New lifecycle status for the flow instance.
      example:
        header:
          status: completed

    AssigneeInfo:
      type: object
      description: Reference to a user or group assignee with basic display info.
      required:
        - guid
        - type
        - displayName
      additionalProperties: false
      properties:
        guid:
          type: string
          format: uuid
          description: The unique identifier for the user or group.
        type:
          type: string
          enum: [user, group]
          description: Whether this is a user or group.
        displayName:
          type: string
          description: Human-readable name for display in UIs.

    CurrentStep:
      type: object
      description: |-
        Current step context for a flow instance.
        Represents the step where the instance's active/latest message currently resides.
      required:
        - stepId
        - stepSlug
        - stepName
        - sequence
      additionalProperties: false
      properties:
        stepId:
          type: string
          format: uuid
          description: The GUID of the step.
        stepSlug:
          type: string
          description: URL-friendly identifier for the step within the flow.
        stepName:
          type: string
          description: Human-readable display name for the step.
        sequence:
          type: integer
          minimum: 0
          description: Sequential position of this step within the flow (0-indexed).

    CurrentStepAssignees:
      type: object
      description: |-
        Subscribers and publishers assigned to the current step.
        Includes SoS Admin users/groups with subscriber/publisher roles on the step.
      required:
        - subscribers
        - publishers
      additionalProperties: false
      properties:
        subscribers:
          type: array
          description: Users and groups who can subscribe (receive) messages from this step.
          items:
            $ref: '#/components/schemas/AssigneeInfo'
        publishers:
          type: array
          description: Users and groups who can publish (send) messages to this step.
          items:
            $ref: '#/components/schemas/AssigneeInfo'

    MessageStatsByStep:
      type: object
      description: Message count summary for a single step.
      required:
        - stepSlug
        - count
      additionalProperties: false
      properties:
        stepSlug:
          type: string
          description: URL-friendly identifier for the step.
        count:
          type: integer
          minimum: 0
          description: Number of messages in this step for the flow instance.
        status:
          type: string
          enum: [active, completed, failed, dead, closed]
          description: Optional status filter (if included, count reflects this status only).

    MessageStats:
      type: object
      description: |-
        Message statistics for a flow instance across all steps.
        Shows message distribution and counts to indicate progress through the flow.
      required:
        - totalActiveMessages
        - byStep
      additionalProperties: false
      properties:
        totalActiveMessages:
          type: integer
          minimum: 0
          description: Total count of messages with active status across all steps.
        byStep:
          type: array
          description: Message counts grouped by step.
          items:
            $ref: '#/components/schemas/MessageStatsByStep'

    FlowInstance:
      type: object
      description: Flow instance object returned by the API.
      required:
        - header
        - properties
        - currentStep
      additionalProperties: false
      properties:
        header:
          $ref: '#/components/schemas/FlowInstanceHeader'
        properties:
          type: object
          additionalProperties: false
          properties:
            flowId:
              type: string
              format: uuid
            flowInstanceId:
              type: string
              format: uuid
            tenantId:
              type: string
              format: uuid
            creationTime:
              type: string
              format: date-time
            sequence:
              type: integer
              minimum: 0
            subject:
              type: string
              description: The subject/identifier being processed (e.g., claim ID, invoice number).
            status:
              type: string
              enum: [active, completed, failed, dead, closed]
              description: Current lifecycle status of the instance.
        currentStep:
          description: The step where the instance's active/latest message currently resides.
          allOf:
            - $ref: '#/components/schemas/CurrentStep'
        currentStepAssignees:
          description: Subscribers and publishers for the current step (included when expand=step-assignees).
          allOf:
            - $ref: '#/components/schemas/CurrentStepAssignees'
        messageStats:
          description: Message statistics across steps (included when expand=message-stats).
          allOf:
            - $ref: '#/components/schemas/MessageStats'
      example:
        header:
          status: active
        properties:
          flowId: 4da73b93-e85c-43c9-b0ff-395a33c73b87
          flowInstanceId: 8415c2f8-5c48-4319-8c13-8fa3bd68cf48
          tenantId: d2dc0315-853c-4d15-916f-1e47ffec8af4
          creationTime: 2025-06-05T09:00:00Z
          sequence: 0
          subject: CLAIM-2026-00123
          status: active
        currentStep:
          stepId: 550e8400-e29b-41d4-a716-446655440000
          stepSlug: claims-review
          stepName: Claims Review
          sequence: 2
        currentStepAssignees:
          subscribers:
            - guid: 660e8400-e29b-41d4-a716-446655440001
              type: group
              displayName: Claims Processors
            - guid: 770e8400-e29b-41d4-a716-446655440002
              type: user
              displayName: John Smith
          publishers:
            - guid: 880e8400-e29b-41d4-a716-446655440003
              type: user
              displayName: Automated Claims System
        messageStats:
          totalActiveMessages: 1
          byStep:
            - stepSlug: intake
              count: 1
              status: completed
            - stepSlug: validation
              count: 0
              status: active
            - stepSlug: claims-review
              count: 1
              status: active

    FlowInstanceSummary:
      type: object
      required:
        - header
        - properties
      additionalProperties: false
      properties:
        header:
          $ref: '#/components/schemas/FlowInstanceHeader'
        properties:
          type: object
          additionalProperties: false
          properties:
            flowInstanceId:
              type: string
              format: uuid
            flowId:
              type: string
              format: uuid
            tenantId:
              type: string
              format: uuid
            creationTime:
              type: string
              format: date-time

    FlowInstanceList:
      type: object
      required:
        - data
        - pagination
      additionalProperties: false
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/FlowInstanceSummary'
        pagination:
          $ref: '#/components/schemas/PaginationMeta'

    PaginationMeta:
      type: object
      required:
        - total
        - limit
        - offset
        - hasMore
        - _links
      additionalProperties: false
      properties:
        total:
          type: integer
          description: Total number of matching resources
          example: 145
        limit:
          type: integer
          example: 20
        offset:
          type: integer
          example: 0
        hasMore:
          type: boolean
          example: true
        _links:
          type: object
          description: HATEOAS links for pagination
          additionalProperties: false
          properties:
            self:
              type: string
              description: Link to this flow instance's detail view
              example: /nexusFlow/flow/v1/reporting/flow-instances/419e62c0-212f-4a32-8ec4-396576d1fdba
            list:
              type: string
              description: Link to the flow instances list
              example: /nexusFlow/flow/v1/reporting/flow-instances

    HateoasLink:
      type: object
      additionalProperties: false
      required:
        - href
      properties:
        href:
          type: string
          format: uri
        method:
          type: string
          enum: [GET, POST, PUT, PATCH, DELETE]
          default: GET
        title:
          type: string

    HateoasLinks:
      type: object
      additionalProperties:
        $ref: '#/components/schemas/HateoasLink'

    PagedFlowInstances:
      type: object
      description: Paginated list of flow instances with links for navigation.
      required:
        - offset
        - limit
        - total
        - data
      additionalProperties: false
      properties:
        offset:
          type: integer
          minimum: 0
          description: Current pagination offset
          example: 0
        limit:
          type: integer
          minimum: 1
          description: Page size
          example: 25
        total:
          type: integer
          minimum: 0
          description: Total number of flow instances available
          example: 150
        data:
          type: array
          description: Array of flow instances for this page
          items:
            $ref: '#/components/schemas/ReportingFlowInstanceSummary'
        _links:
          type: object
          description: HATEOAS links for pagination
          additionalProperties: false
          properties:
            first:
              type: string
              nullable: true
              description: Link to first page
            prev:
              type: string
              nullable: true
              description: Link to previous page
            next:
              type: string
              nullable: true
              description: Link to next page
            last:
              type: string
              nullable: true
              description: Link to last page

    ReportingFlowInstanceSummary:
      type: object
      description: Summary of a flow instance for list views with latest message details.
      required:
        - guid
        - flow_slug
        - flow_name
        - subject
        - status
        - created_dts
      additionalProperties: false
      properties:
        guid:
          type: string
          format: uuid
          description: Unique identifier for the flow instance
          example: 419e62c0-212f-4a32-8ec4-396576d1fdba
        flow_slug:
          type: string
          description: URL-friendly identifier for the flow
          example: vbt-ado-bug-intake
        flow_name:
          type: string
          description: Human-readable name of the flow
          example: VBT ADO Bug Intake
        subject:
          type: string
          description: Subject or title of this flow instance
          example: Sensor Data Blank
        status:
          type: string
          enum: ['active', 'completed', 'failed']
          description: Current status of the flow instance
          example: completed
        metadata:
          type: object
          nullable: true
          description: Optional metadata associated with the instance
          example: { "workItemId": 131 }
        created_dts:
          type: string
          format: date-time
          description: ISO 8601 timestamp when the instance was created
          example: '2026-01-23T21:20:53.161Z'
        latest_message:
          allOf:
            - $ref: '#/components/schemas/LatestMessage'
          nullable: true
          description: Details of the latest message in the flow instance
        _links:
          type: object
          description: HATEOAS link to this flow instance detail
          additionalProperties: false
          properties:
            self:
              type: string
              description: Link to this flow instance's detail view
              example: /nexusFlow/flow/v1/reporting/flow-instances/419e62c0-212f-4a32-8ec4-396576d1fdba

    FlowDefinition:
      type: object
      description: Flow structure and configuration.
      required:
        - flow_idxid
        - guid
        - cname
        - slug
        - display_name
        - status
        - created_dts
      additionalProperties: false
      properties:
        flow_idxid:
          type: integer
          format: int64
          description: Numeric database identifier for the flow
          example: 1
        guid:
          type: string
          format: uuid
          description: Global unique identifier for the flow
          example: ad0bab1d-f102-400a-a0f3-144a13f1021d
        cname:
          type: string
          description: Canonical name (fully qualified identifier)
          example: ai.gradera.valmont.bugtracker.ado-bug-intake
        slug:
          type: string
          description: URL-friendly identifier for the flow
          example: vbt-ado-bug-intake
        display_name:
          type: string
          description: Human-readable flow name
          example: VBT ADO Bug Intake
        description:
          type: string
          nullable: true
          description: Detailed description of the flow's purpose
          example: A flow for analyzing, categorizing and suggesting improvements to defect tickets from ADO
        status:
          type: string
          enum: [active, inactive, archived]
          description: Current status of the flow
          example: active
        created_dts:
          type: string
          format: date-time
          description: ISO 8601 timestamp when the flow was created (UTC)
          example: 2026-01-23T20:51:19.232Z
        created_by:
          type: string
          nullable: true
          description: User or system that created the flow
          example: seed

    FlowStep:
      type: object
      description: Step summary within a flow (lite representation for response nesting).
      required:
        - guid
        - slug
        - displayName
        - sequence
      additionalProperties: false
      properties:
        guid:
          type: string
          format: uuid
          description: Step GUID (public identifier)
          example: 550e8400-e29b-41d4-a716-446655440000
        slug:
          type: string
          description: URL-friendly step identifier
          example: claims-review
        displayName:
          type: string
          description: Human-readable step name
          example: Claims Review
        description:
          type: string
          nullable: true
          description: Optional step description
          example: Review and approve claims
        sequence:
          type: integer
          minimum: 0
          description: Ordinal position of step within flow
          example: 2
        messageTypeSlug:
          type: string
          description: Message type slug processed by this step
          example: claims-submission
        subscriberType:
          type: string
          enum: [human, digiworker, any]
          nullable: true
          description: Expected subscriber user type for this step
          example: human
        publisherType:
          type: string
          enum: [human, digiworker, any]
          nullable: true
          description: Expected publisher user type for this step
          example: digiworker

    Flow:
      type: object
      description: Flow resource returned by the API.
      required:
        - guid
        - cname
        - slug
        - displayName
        - status
        - createdDts
      additionalProperties: false
      properties:
        guid:
          type: string
          format: uuid
          description: Global unique identifier for the flow
          example: ad0bab1d-f102-400a-a0f3-144a13f1021d
        cname:
          type: string
          description: Canonical name (machine-readable, unique identifier)
          example: claims-processing
          pattern: '^[a-z0-9-]+(\.[a-z0-9-]+)*$'
        slug:
          type: string
          description: URL-friendly identifier (unique)
          example: claims-processing
          pattern: '^[a-z0-9-]+$'
        displayName:
          type: string
          description: Human-readable flow name
          example: Claims Processing Flow
        description:
          type: string
          nullable: true
          description: Detailed description of flow purpose and use cases
          example: Processes insurance claims through validation, review, and settlement stages
        status:
          type: string
          enum: [active, inactive, archived]
          description: Flow lifecycle status
          example: active
        createdDts:
          type: string
          format: date-time
          readOnly: true
          description: ISO 8601 timestamp when flow was created (UTC)
          example: 2026-01-23T20:51:19.232Z
        updatedDts:
          type: string
          format: date-time
          readOnly: true
          nullable: true
          description: ISO 8601 timestamp when flow was last updated (UTC)
          example: 2026-03-10T14:30:45.678Z
        createdBy:
          type: string
          nullable: true
          description: User or system identifier that created this flow
          example: admin-user-123
        steps:
          type: array
          description: Associated steps in this flow (ordered by sequence)
          items:
            $ref: '#/components/schemas/FlowStep'
        _links:
          $ref: '#/components/schemas/HateoasLinks'

    FlowSummary:
      type: object
      description: Minimal flow representation for list views.
      required:
        - guid
        - cname
        - slug
        - displayName
        - status
        - createdDts
      additionalProperties: false
      properties:
        guid:
          type: string
          format: uuid
          description: Global unique identifier for the flow
          example: ad0bab1d-f102-400a-a0f3-144a13f1021d
        cname:
          type: string
          description: Canonical name (machine-readable identifier)
          example: claims-processing
        slug:
          type: string
          description: URL-friendly identifier
          example: claims-processing
        displayName:
          type: string
          description: Human-readable flow name
          example: Claims Processing Flow
        status:
          type: string
          enum: [active, inactive, archived]
          description: Flow lifecycle status
          example: active
        description:
          type: string
          nullable: true
          description: Brief description
          example: Processes insurance claims
        createdDts:
          type: string
          format: date-time
          description: ISO 8601 timestamp when flow was created (UTC)
          example: 2026-01-23T20:51:19.232Z
        stepCount:
          type: integer
          minimum: 0
          description: Number of steps in this flow
          example: 3
        _links:
          type: object
          description: HATEOAS links
          additionalProperties: false
          properties:
            self:
              type: string
              description: Link to this flow's detail view
              example: /flows/ad0bab1d-f102-400a-a0f3-144a13f1021d

    FlowList:
      allOf:
        - $ref: '#/components/schemas/PaginationMeta'
      type: object
      description: Paginated list of flows.
      properties:
        data:
          type: array
          description: Array of flows for this page
          items:
            $ref: '#/components/schemas/FlowSummary'
      required:
        - data

    FlowCreateInput:
      type: object
      description: Request payload for creating a new flow.
      required:
        - cname
        - slug
        - displayName
      additionalProperties: false
      properties:
        cname:
          type: string
          description: Canonical name (unique, machine-readable identifier, alphanumeric + hyphens + dots)
          example: claims-processing
          pattern: '^[a-z0-9-]+(\.[a-z0-9-]+)*$'
          minLength: 1
          maxLength: 128
        slug:
          type: string
          description: URL-friendly identifier (unique, alphanumeric + hyphens)
          example: claims-processing
          pattern: '^[a-z0-9-]+$'
          minLength: 1
          maxLength: 100
        displayName:
          type: string
          description: Human-readable flow name
          example: Claims Processing Flow
          minLength: 1
          maxLength: 128
        description:
          type: string
          nullable: true
          description: Detailed description of flow purpose
          example: Processes insurance claims through validation, review, and settlement
          maxLength: 1000
        status:
          type: string
          enum: [active, inactive, archived]
          default: active
          description: Initial flow status (defaults to active)
          example: active

    FlowUpdateInput:
      type: object
      description: Request payload for updating a flow. All fields are optional.
      additionalProperties: false
      properties:
        displayName:
          type: string
          description: New human-readable flow name
          example: Claims Processing Flow v2
          minLength: 1
          maxLength: 128
        description:
          type: string
          nullable: true
          description: Updated flow description
          example: Enhanced claims processing with additional validation
          maxLength: 1000
        status:
          type: string
          enum: [active, inactive, archived]
          description: New flow status
          example: inactive

    ReportingFlowInstance:
      type: object
      description: Execution instance of a flow.
      required:
        - flowinst_idxid
        - guid
        - flow_idxid
        - subject
        - status
        - created_dts
      additionalProperties: false
      properties:
        flowinst_idxid:
          type: integer
          format: int64
          description: Numeric database identifier for the flow instance
          example: 3
        guid:
          type: string
          format: uuid
          description: Global unique identifier for the flow instance
          example: 419e62c0-212f-4a32-8ec4-396576d1fdba
        flow_idxid:
          type: integer
          format: int64
          description: Reference to the parent flow
          example: 1
        subject:
          type: string
          description: Title or subject of the flow instance (usually from the ticket)
          example: Sensor Data Blank
        status:
          type: string
          enum: [active, completed, failed]
          description: Current lifecycle status of the flow instance
          example: active
        metadata:
          type: object
          nullable: true
          additionalProperties: true
          description: Arbitrary metadata associated with the flow instance
          example:
            workItemId: 131
        summary:
          type: string
          nullable: true
          description: Summary of the flow instance execution
        created_dts:
          type: string
          format: date-time
          description: ISO 8601 timestamp when the flow instance was created (UTC)
          example: 2026-01-23T21:20:53.161Z

    ReportingMessage:
      type: object
      description: Message data in execution timeline.
      required:
        - message_idxid
        - guid
        - step_slug
        - step_idxid
        - status
        - sequence
        - created_dts
      additionalProperties: false
      properties:
        message_idxid:
          type: integer
          format: int64
          example: 8
        guid:
          type: string
          format: uuid
          example: e871b5d8-75ee-4484-b0b3-2217cf49b40e
        cname:
          type: string
          example: ai.gradera.valmont.nexusflow.e871b5d8-75ee-4484-b0b3-2217cf49b40e
        subject:
          type: string
          example: Sensor Data Blank
        step_idxid:
          type: integer
          format: int64
          example: 1
        step_slug:
          type: string
          description: Step identifier for this message
          example: vbt-duplicate-analysis
        status:
          type: string
          enum: [active, completed, superseded, failed, dead]
          description: Lifecycle status of the message
          example: completed
        ttl:
          type: integer
          description: Time-to-live in seconds
          example: 3600
        sequence:
          type: integer
          description: Position in the message sequence within the flow instance
          example: 1
        created_dts:
          type: string
          format: date-time
          description: ISO 8601 timestamp when the message was created (UTC)
          example: 2026-01-23T21:20:53.171Z
        locked_user_id:
          type: string
          format: uuid
          nullable: true
          description: User UUID if the message is locked for processing
        locked_dts:
          type: string
          format: date-time
          nullable: true
          description: ISO 8601 timestamp when the message was locked (UTC)
        created_by_user_idxid:
          type: integer
          format: int64
          nullable: true
          description: User index ID of the actor who created this message
        body:
          type: object
          nullable: true
          additionalProperties: true
          description: Message payload content

    LatestMessage:
      type: object
      description: Latest message details in a flow instance for list views.
      additionalProperties: false
      properties:
        guid:
          type: string
          format: uuid
          description: Unique identifier for the message
          example: e871b5d8-75ee-4484-b0b3-2217cf49b40e
        step_slug:
          type: string
          description: Step identifier for this message
          example: vbt-duplicate-analysis
        status:
          type: string
          enum: [active, completed, superseded, failed, dead]
          description: Current lifecycle status of the message
          example: completed
        is_locked:
          type: boolean
          description: Whether the message is currently locked for processing
          example: false
        sequence:
          type: string
          description: Position in the message sequence within the flow instance
          example: '42'
        created_dts:
          type: string
          format: date-time
          description: ISO 8601 timestamp when the message was created (UTC)
          example: 2026-01-23T21:20:53.171Z
        created_by_user:
          type: object
          nullable: true
          description: User who created this message
          additionalProperties: false
          properties:
            guid:
              type: string
              format: uuid
              description: Unique identifier for the user
              example: 550e8400-e29b-41d4-a716-446655440000
            display_name:
              type: string
              description: Display name of the user
              example: John Doe
            type:
              type: string
              enum: [human, digiworker]
              nullable: true
              description: Type of user (human or digiworker)
              example: human

    ExecutionStats:
      type: object
      description: Pre-computed metrics for flow execution visualization.
      required:
        - node_visits
        - edge_usage
        - node_durations
      additionalProperties: false
      properties:
        node_visits:
          type: object
          description: Map of step slug to number of visits
          additionalProperties:
            type: integer
          example:
            "vbt-duplicate-analysis": 1
            "vbt-integrity-analysis": 1
            "vbt-severity-analysis": 1
            "vbt-integrity-review-pm": 2
            "vbt-integrity-review-tl": 1
            "vbt-ado-update": 1
        edge_usage:
          type: object
          description: Map of edge ID to number of times the edge was traversed
          additionalProperties:
            type: integer
          example:
            "e-1-2": 1
            "e-2-3": 1
            "e-3-4": 1
            "e-4-5": 1
            "e-5-6": 1
            "e-6-5": 1
            "e-6-7": 1
            "e-7-8": 1
        node_durations:
          type: object
          description: Map of step slug to cumulative duration in seconds
          additionalProperties:
            type: number
            format: double
          example:
            "vbt-duplicate-analysis": 0.01
            "vbt-integrity-analysis": 0.903
            "vbt-severity-analysis": 14.852
            "vbt-integrity-review-pm": 138.1
            "vbt-integrity-review-tl": 138.066
            "vbt-ado-update": 0

    FlowInstanceReporting:
      type: object
      description: Complete flow instance data for reporting and visualization.
      required:
        - flow
        - flow_instance
        - messages
        - stats
      additionalProperties: false
      properties:
        flow:
          $ref: '#/components/schemas/FlowDefinition'
        flow_instance:
          $ref: '#/components/schemas/ReportingFlowInstance'
        messages:
          type: array
          description: All messages in sequence order
          items:
            $ref: '#/components/schemas/ReportingMessage'
        stats:
          $ref: '#/components/schemas/ExecutionStats'
        _links:
          type: object
          description: HATEOAS links for navigation
          additionalProperties: false
          properties:
            self:
              type: string
              description: Link to this flow instance
              example: /nexusFlow/flow/v1/reporting/flow-instances/419e62c0-212f-4a32-8ec4-396576d1fdba
            list:
              type: string
              description: Link to the flow instances list
              example: /nexusFlow/flow/v1/reporting/flow-instances

    Assignment:
      type: object
      description: Step/flow assignment resource with enriched group or Digiworker details.
      required:
        - assignmentId
        - assigneeType
        - assigneeId
        - stepId
      additionalProperties: false
      properties:
        assignmentId:
          type: string
          format: uuid
          description: Unique identifier (GUID) for the assignment
          example: aa0e7600-d40c-52e5-c827-557766551122
        assigneeType:
          type: string
          enum: [group, digiworker]
          description: Type of assignee (group for SoS Admin groups, digiworker for NexusFlow Digiworkers)
          example: group
        assigneeId:
          type: string
          format: uuid
          description: |
            GUID of the group (from SoS Admin) or Digiworker (from NexusFlow) to assign.
            This UUID must exist in the appropriate source. NexusFlow will validate the reference.
          example: 770e9500-f40c-52e5-c827-557766551111
        stepId:
          type: string
          format: uuid
          description: GUID of the step or flow being assigned
          example: bb1f8701-e40d-63f6-d938-668877662333
        role:
          type: string
          enum: [subscriber, publisher]
          description: Role/permission level for this assignment
          example: subscriber
        createdDts:
          type: string
          format: date-time
          readOnly: true
          description: ISO 8601 timestamp when this assignment was created
          example: 2026-02-28T10:15:00Z
        assignee:
          description: |
            Enriched assignee data. Type is determined by assigneeType field:
            - If assigneeType='group', this is an EnrichedGroup object (from SoS Admin)
            - If assigneeType='digiworker', this is an EnrichedDigiworker object (from NexusFlow)
          nullable: true
          oneOf:
            - $ref: '#/components/schemas/EnrichedGroup'
            - $ref: '#/components/schemas/EnrichedDigiworker'
        _links:
          description: Item-level links (self, delete operations)
          allOf:
            - $ref: '#/components/schemas/HateoasLinks'
          example:
            self:
              href: /assignments/aa0e7600-d40c-52e5-c827-557766551122
            delete:
              href: /assignments/aa0e7600-d40c-52e5-c827-557766551122
              method: DELETE

    EnrichedGroup:
      type: object
      description: Group details enriched from SoS Admin for assignment context.
      required:
        - groupId
        - displayName
      additionalProperties: false
      properties:
        groupId:
          type: string
          format: uuid
          description: Group GUID
          example: 770e9500-f40c-52e5-c827-557766551111
        displayName:
          type: string
          description: Human-readable display name
          example: Claims Processors
        slug:
          type: string
          nullable: true
          description: URL-friendly slug for the group
          example: claims-processors
        memberCount:
          type: integer
          description: Number of members in this group
          example: 12

    EnrichedDigiworker:
      type: object
      description: Digiworker details enriched from NexusFlow for assignment context.
      required:
        - digiworkerId
        - displayName
        - callbackUri
      additionalProperties: false
      properties:
        digiworkerId:
          type: string
          format: uuid
          description: Digiworker GUID
          example: 880f0611-e51d-47a7-9949-668888773444
        displayName:
          type: string
          description: Human-readable display name
          example: ADO Sync Service
        callbackUri:
          type: string
          format: uri
          description: HTTPS webhook endpoint for async message delivery
          example: https://ado-sync.gradera.ai/webhook/messages

    AssignmentList:
      allOf:
        - $ref: '#/components/schemas/PaginationMeta'
      type: object
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/Assignment'
      required:
        - data

    AssignmentCreateInput:
      type: object
      description: |
        Input for creating a new step/flow assignment.
        Assigns access to a step or flow for a group (from SoS Admin) or Digiworker (from NexusFlow).
        **IMPORTANT**: assigneeId must be a UUID from the appropriate source (SoS Admin for groups, NexusFlow for Digiworkers).
      required:
        - assigneeType
        - assigneeId
        - stepId
        - role
      additionalProperties: false
      properties:
        assigneeType:
          type: string
          enum: [group, digiworker]
          description: Type of assignee (group for SoS Admin groups, digiworker for NexusFlow Digiworkers)
          example: group
        assigneeId:
          type: string
          format: uuid
          description: |
            GUID of the group (from SoS Admin) or Digiworker (from NexusFlow) to assign.
            This UUID must exist in the appropriate source. NexusFlow will validate the reference.
          example: 770e9500-f40c-52e5-c827-557766551111
        stepId:
          type: string
          format: uuid
          description: GUID of the step or flow to assign
          example: bb1f8701-e40d-63f6-d938-668877662333
        role:
          type: string
          enum: [subscriber, publisher]
          description: Role/permission level for this assignment (subscriber or publisher)
          example: subscriber

    CreateDigiworkerRequest:
      type: object
      description: Request payload for creating a new Digiworker service account.
      required:
        - displayName
        - callbackUri
      additionalProperties: false
      properties:
        displayName:
          type: string
          minLength: 1
          maxLength: 255
          description: Human-readable display name for the Digiworker (e.g., service name)
          example: claims-bot-v2
        callbackUri:
          type: string
          format: uri
          description: Webhook URI where NexusFlow will deliver messages to this Digiworker (must be http or https)
          example: https://bot-service.internal/webhook/messages

    UpdateDigiworkerRequest:
      type: object
      description: Request payload for replacing a Digiworker service account with a full record. Both fields are required.
      required:
        - displayName
        - callbackUri
      additionalProperties: false
      properties:
        displayName:
          type: string
          minLength: 1
          maxLength: 255
          description: New human-readable display name for the Digiworker
          example: claims-bot-v2-prod
        callbackUri:
          type: string
          format: uri
          description: New webhook URI for message delivery (must be http or https)
          example: https://bot-prod.internal/webhook

    Digiworker:
      type: object
      description: Digiworker service account resource representation for API responses.
      required:
        - digiworkerId
        - displayName
        - callbackUri
        - createdDts
      additionalProperties: false
      properties:
        digiworkerId:
          type: string
          format: uuid
          description: Unique identifier (GUID) for the Digiworker service account
          example: 660e8400-e29b-41d4-a716-446655440000
        displayName:
          type: string
          description: Human-readable display name
          example: claims-bot-1
        callbackUri:
          type: string
          format: uri
          description: Webhook URI where NexusFlow delivers messages for this Digiworker
          example: https://bot1.internal/webhook
        createdDts:
          type: string
          format: date-time
          readOnly: true
          description: ISO 8601 timestamp when this Digiworker was created
          example: 2026-01-15T10:00:00Z
        updatedDts:
          type: string
          format: date-time
          readOnly: true
          nullable: true
          description: ISO 8601 timestamp when this Digiworker was last updated
          example: 2026-04-09T10:15:00Z
        _links:
          $ref: '#/components/schemas/HateoasLinks'

    DigiworkerDetail:
      allOf:
        - $ref: '#/components/schemas/Digiworker'
        - type: object
          properties:
            assignments:
              type: array
              description: Step assignments for this Digiworker (which steps it can access and in what role)
              items:
                $ref: '#/components/schemas/DigiworkerAssignment'

    DigiworkerAssignment:
      type: object
      description: |
        Step assignment that grants a Digiworker access to a step.
        Specifies the role (publisher | subscriber) for this access.
      required:
        - assignmentId
        - stepId
        - role
      additionalProperties: false
      properties:
        assignmentId:
          type: string
          format: uuid
          readOnly: true
          description: Unique identifier for this assignment
          example: 770e8400-e29b-41d4-a716-446655440000
        stepId:
          type: string
          format: uuid
          description: Step UUID this Digiworker can access
          example: 880e8400-e29b-41d4-a716-446655440000
        role:
          type: string
          enum: [subscriber, publisher]
          description: Role in this step (subscriber receives messages, publisher sends messages)
          example: subscriber
        createdDts:
          type: string
          format: date-time
          readOnly: true
          description: ISO 8601 timestamp when this assignment was created
          example: 2026-01-15T10:05:00Z

    DigiworkerList:
      allOf:
        - $ref: '#/components/schemas/PaginationMeta'
      type: object
      properties:
        data:
          type: array
          description: List of Digiworkers (service accounts only)
          items:
            $ref: '#/components/schemas/Digiworker'
      required:
        - data

    PropertyCreate:
      type: object
      description: Fields required to create or update a property definition.
      required:
        - name
        - description
      properties:
        name:
          type: string
          description: Human-readable name of the property.
          example: Product
        description:
          type: string
          description: Brief description of the property and its purpose.
          example: Products that are managed by our company

    Property:
      description: Tenant property definition (isolates which items a user may be assigned).
      allOf:
        - $ref: '#/components/schemas/PropertyCreate'
        - type: object
          properties:
            propertyId:
              type: string
              format: uuid
              readOnly: true
              description: Unique identifier for this property.
              example: 41e2cbac-280a-46bc-840f-fecec4bf1752

    PropertyValueCreate:
      type: object
      description: Fields required to create or update a property value.
      required:
        - propertyName
        - value
      properties:
        propertyName:
          type: string
          description: Name of the property.
          example: Products
        value:
          type: string
          description: String value for this property.
          example: ACME Customer iOS Application

    PropertyValue:
      description: A value assigned to a named property, with stable identifiers for the value and parent property.
      allOf:
        - $ref: '#/components/schemas/PropertyValueCreate'
        - type: object
          required:
            - propertyValueId
            - propertyId
          properties:
            propertyValueId:
              type: string
              format: uuid
              readOnly: true
              description: Unique identifier for this property value.
              example: 1c20e1a8-31ab-4b25-aed8-36ed5a684430
            propertyId:
              type: string
              format: uuid
              readOnly: true
              description: Property this value belongs to.
              example: cce6ad8f-b04c-4eb9-8bb8-f20bfce1e5e4

    Error:
      type: object
      required:
        - code
        - title
      properties:
        id:
          type: string
          format: uuid
          description: Unique identifier for this error occurrence
        code:
          type: string
          description: Application-specific error code
        title:
          type: string
          description: Short summary of the problem
        detail:
          type: string
          description: Detailed explanation of this occurrence

    Errors:
      type: array
      items:
        $ref: '#/components/schemas/Error'

  responses:
    BadRequest:
      description: Invalid request
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Errors'
    Unauthorized:
      description: Missing or invalid authentication
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Errors'
    Forbidden:
      description: Insufficient permissions
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Errors'
    Conflict:
      description: Resource conflict (e.g., duplicate assignment)
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Errors'
    NotFound:
      description: Resource not found
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Errors'
    UnprocessableEntity:
      description: Request was well-formed but could not be applied (validation or semantic error)
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Errors'
    RequestLimitExceeded:
      description: Rate limit exceeded
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Errors'
    InternalServiceError:
      description: Server error
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Errors'
    ServiceUnavailable:
      description: Service unavailable
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Errors'
    UnsupportedMediaType:
      description: Unsupported media type
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Errors'

  examples:
    digiworker-inbox-basic:
      summary: Simple Digiworker inbox list
      value:
        offset: 0
        limit: 25
        total: 1
        _links:
          self: https://api.gradera.ai/nexus-flow/v1/inbox/digiworker?offset=0&limit=25
          first: https://api.gradera.ai/nexus-flow/v1/inbox/digiworker?offset=0&limit=25
          prev: null
          next: null
          last: https://api.gradera.ai/nexus-flow/v1/inbox/digiworker?offset=0&limit=25
        data:
          - flowName: Bug Intake
            instanceSubject: iOS - Hourly Rainfall Value Displayed as Daily Total
            messageGuid: c7def7f8-ef4e-4219-bf46-e0c3010a6317
            messageLocation: https://api.gradera.ai/nexus-flow/v1/messages/c7def7f8-ef4e-4219-bf46-e0c3010a6317
            messageTypeSlug: duplicate-review
            subscriberType: digiworker
            stepSlug: duplicate-review
            stepName: Duplicate Review Step
            instanceMetadata: null
            messageCreatedTime: 2025-06-05T09:00:00Z
            messageMetadata: null

    digiworker-inbox-with-message:
      summary: Digiworker inbox with message envelope expanded
      value:
        offset: 0
        limit: 25
        total: 1
        _links:
          self: https://api.gradera.ai/nexus-flow/v1/inbox/digiworker?offset=0&limit=25&expand=message
          first: https://api.gradera.ai/nexus-flow/v1/inbox/digiworker?offset=0&limit=25&expand=message
          prev: null
          next: null
          last: https://api.gradera.ai/nexus-flow/v1/inbox/digiworker?offset=0&limit=25&expand=message
        data:
          - flowName: Bug Intake
            instanceSubject: iOS - Hourly Rainfall Value Displayed as Daily Total
            messageGuid: c7def7f8-ef4e-4219-bf46-e0c3010a6317
            messageLocation: https://api.gradera.ai/nexus-flow/v1/messages/c7def7f8-ef4e-4219-bf46-e0c3010a6317
            messageTypeSlug: duplicate-review
            subscriberType: digiworker
            stepSlug: duplicate-review
            stepName: Duplicate Review Step
            instanceMetadata: null
            messageCreatedTime: 2025-06-05T09:00:00Z
            messageMetadata: null
            message:
              header:
                status: active
                ttl: 2025-11-01T16:04:49Z
              properties:
                messageId: c7def7f8-ef4e-4219-bf46-e0c3010a6317
                stepId: be186ec4-0977-4b29-a33d-ad56873bf15f
                stepSlug: digiworker-step
                sequence: 1
                messageType: duplicate-review
                creationTime: 2025-06-05T09:00:00Z
              body:
                ticket:
                  System.Title: iOS - Hourly Rainfall Value Displayed as Daily Total

    digiworker-inbox-with-subscribers:
      summary: Digiworker inbox with subscribers expanded
      value:
        offset: 0
        limit: 25
        total: 1
        _links:
          self: https://api.gradera.ai/nexus-flow/v1/inbox/digiworker?offset=0&limit=25&expand=subscribers
          first: https://api.gradera.ai/nexus-flow/v1/inbox/digiworker?offset=0&limit=25&expand=subscribers
          prev: null
          next: null
          last: https://api.gradera.ai/nexus-flow/v1/inbox/digiworker?offset=0&limit=25&expand=subscribers
        data:
          - flowName: Bug Intake
            stepSlug: duplicate-review
            stepName: Duplicate Review Step
            instanceSubject: iOS - Hourly Rainfall Value Displayed as Daily Total
            instanceMetadata: null
            messageGuid: c7def7f8-ef4e-4219-bf46-e0c3010a6317
            messageLocation: https://api.gradera.ai/nexus-flow/v1/messages/c7def7f8-ef4e-4219-bf46-e0c3010a6317
            messageTypeSlug: duplicate-review
            messageCreatedTime: 2025-06-05T09:00:00Z
            subscriberType: digiworker
            messageMetadata: null
            subscribers:
              - userGuid: 4c3faed2-1234-4d3f-8a13-1a2b3c4d5e6f
                userIdxid: 900
                displayName: Digiworker Ticket Processor
                callbackUri: https://digiworker.prod.gradera.ai/api/v1/callbacks/messages
                assigneeType: user
                role: subscriber
                userType: digiworker

    digiworker-inbox-with-message-and-subscribers:
      summary: Digiworker inbox with both message and subscribers expanded
      value:
        offset: 0
        limit: 25
        total: 1
        _links:
          self: https://api.gradera.ai/nexus-flow/v1/inbox/digiworker?offset=0&limit=25&expand=message,subscribers
          first: https://api.gradera.ai/nexus-flow/v1/inbox/digiworker?offset=0&limit=25&expand=message,subscribers
          prev: null
          next: null
          last: https://api.gradera.ai/nexus-flow/v1/inbox/digiworker?offset=0&limit=25&expand=message,subscribers
        data:
          - flowName: Bug Intake
            stepSlug: duplicate-review
            stepName: Duplicate Review Step
            instanceSubject: iOS - Hourly Rainfall Value Displayed as Daily Total
            instanceMetadata: null
            messageGuid: c7def7f8-ef4e-4219-bf46-e0c3010a6317
            messageLocation: https://api.gradera.ai/nexus-flow/v1/messages/c7def7f8-ef4e-4219-bf46-e0c3010a6317
            messageTypeSlug: duplicate-review
            messageCreatedTime: 2025-06-05T09:00:00Z
            subscriberType: digiworker
            messageMetadata:
              lock:
                locked_dts: "2025-06-05T10:15:30Z"
                locked_display_name: "John Doe"
                locked_by_guid: "a1b2c3d4-e5f6-4a5b-8c9d-0e1f2a3b4c5d"
            message:
              header:
                status: active
                ttl: 2025-11-01T16:04:49Z
              properties:
                messageId: c7def7f8-ef4e-4219-bf46-e0c3010a6317
                stepId: be186ec4-0977-4b29-a33d-ad56873bf15f
                stepSlug: digiworker-step
                sequence: 1
                messageType: duplicate-review
                creationTime: 2025-06-05T09:00:00Z
              body:
                ticket:
                  System.Title: iOS - Hourly Rainfall Value Displayed as Daily Total
            subscribers:
              - userGuid: 4c3faed2-1234-4d3f-8a13-1a2b3c4d5e6f
                userIdxid: 900
                displayName: Digiworker Ticket Processor
                callbackUri: https://digiworker.prod.gradera.ai/api/v1/callbacks/messages
                assigneeType: user
                role: subscriber

    subscribers-one-digiworker:
      summary: Subscribers - single digiworker
      value:
        - userGuid: 4c3faed2-1234-4d3f-8a13-1a2b3c4d5e6f
          userIdxid: 900
          displayName: Digiworker Ticket Processor
          callbackUri: https://digiworker.prod.gradera.ai/api/v1/callbacks/messages
          assigneeType: user
          role: subscriber
          userType: digiworker

    bug-ticket-comparison: 
      summary: Bug Duplicate Review Message
      value:
        header:
          status: active
          ttl: 2025-11-01T16:04:49Z
        properties:
          tenantId: d2dc0315-853c-4d15-916f-1e47ffec8af4
          flowInstanceId: 8415c2f8-5c48-4319-8c13-8fa3bd68cf48
          creationTime: 2025-06-05T09:00:00Z
          flowId: 4da73b93-e85c-43c9-b0ff-395a33c73b87
          stepId: be186ec4-0977-4b29-a33d-ad56873bf15f
          stepSlug: duplicate-review
          sequence: 0
          messageType: duplicate-review
          messageTypeSlug: duplicate-review
          userId: 8074c00d-7bd1-4912-a755-5fc53d8aac64
          actorJWT: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzMWIzMjRjZS01ZDlkLTQxOGItYWUzZC04NGM1YzIwOTEzMjgiLCJ0ZW5hbnQiOiIyZjA3MWQ0MC0yOTNlLTQ2YzAtOWIxNy05MGYwYjhlODdhZDAiLCJuYW1lIjoiTmF2aW4gSm9obnNvbiIsImFkbWluIjp0cnVlLCJpYXQiOjE3NjA5MDkyNDUsImV4cCI6MTc2MDkxMjg0NX0.U1VERaWLrqsC3D7Nt3sBndooIG7316fST47vp7yk-AU
          to: nf://prod/vmt/bug-intake/5/duplicate-review
          subject: iOS - Hourly Rainfall Value Displayed as Daily Total
          lockedUserId: 3c1c6122-5496-4c77-84eb-417a31c30f5c
          lockedTime: 2025-10-28T15:04:49Z
        body:
          messageDesc: Digital worker Sarah has updated the ticket to improve the quality of the data and has identified data that needs to be completed.
          instructions: Review the submitted ticket and the modified ticke and then click the "Submit" button.

    completed-message-no-locks:
      summary: Completed message (locks cleared)
      value:
        header:
          status: completed
          ttl: 2025-11-01T16:04:49Z
        properties:
          messageId: c7def7f8-ef4e-4219-bf46-e0c3010a6317
          tenantId: d2dc0315-853c-4d15-916f-1e47ffec8af4
          flowInstanceId: 8415c2f8-5c48-4319-8c13-8fa3bd68cf48
          creationTime: 2025-06-05T09:00:00Z
          flowId: 4da73b93-e85c-43c9-b0ff-395a33c73b87
          stepId: be186ec4-0977-4b29-a33d-ad56873bf15f
          stepSlug: duplicate-review
          sequence: 0
          messageType: duplicate-review
          messageTypeSlug: duplicate-review
          userId: 8074c00d-7bd1-4912-a755-5fc53d8aac64
          actorJWT: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzMWIzMjRjZS01ZDlkLTQxOGItYWUzZC04NGM1YzIwOTEzMjgiLCJ0ZW5hbnQiOiIyZjA3MWQ0MC0yOTNlLTQ2YzAtOWIxNy05MGYwYjhlODdhZDAiLCJuYW1lIjoiTmF2aW4gSm9obnNvbiIsImFkbWluIjp0cnVlLCJpYXQiOjE3NjA5MDkyNDUsImV4cCI6MTc2MDkxMjg0NX0.U1VERaWLrqsC3D7Nt3sBndooIG7316fST47vp7yk-AU
          to: nf://prod/vmt/bug-intake/5/duplicate-review
          subject: iOS - Hourly Rainfall Value Displayed as Daily Total
          lockedUserId: null
          lockedTime: null
        body:
          messageDesc: This message has been completed and lock fields are cleared.

    bug-severity: 
      summary: Bug Severity Assessment Message
      value:
        header:
          status: active
          ttl: 2025-11-01T16:04:49Z
        properties:
          tenantId: d2dc0315-853c-4d15-916f-1e47ffec8af4
          flowInstanceId: 8415c2f8-5c48-4319-8c13-8fa3bd68cf48
          creationTime: 2025-06-05T09:00:00Z
          flowId: 4da73b93-e85c-43c9-b0ff-395a33c73b87
          stepId: be186ec4-0977-4b29-a33d-ad56873bf15f  
          stepSlug: severity-assessment
          sequence: 0         
          messageType: severity-assessment
          messageTypeSlug: severity-assessment
          userId: 8074c00d-7bd1-4912-a755-5fc53d8aac64
          actorJWT: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzMWIzMjRjZS01ZDlkLTQxOGItYWUzZC04NGM1YzIwOTEzMjgiLCJ0ZW5hbnQiOiIyZjA3MWQ0MC0yOTNlLTQ2YzAtOWIxNy05MGYwYjhlODdhZDAiLCJuYW1lIjoiTmF2aW4gSm9obnNvbiIsImFkbWluIjp0cnVlLCJpYXQiOjE3NjA5MDkyNDUsImV4cCI6MTc2MDkxMjg0NX0.U1VERaWLrqsC3D7Nt3sBndooIG7316fST47vp7yk-AU
          to: nf://prod/vmt/bug-intake/5/severity-assessment
          subject: iOS - Hourly Rainfall Value Displayed as Daily Total
          lockedUserId: 3c1c6122-5496-4c77-84eb-417a31c30f5c
          lockedTime: 2025-10-28T15:04:49Z
        body:
          messageDesc: Digital worker Johan has updated the ticket to recommend a severity level of 2
          instructions: Review the recommended severity and accept it or edit it and then submit it.
          severity_assessment:
            severity: 2
            severityMetrics:
              numberOfUsers: Few Users
              numberOfUsersDesc: The system has 3 reports of this type of error.
              failureType: crash/hanging
              failureTypeDesc: The application stops responding when I click the update button.

    new-ticket: 
      summary: New Ticket submitted to NexusFlow
      value: 
        header:
          status: active
          ttl: 2025-11-01T16:04:49Z
        properties:
          messageId: c7def7f8-ef4e-4219-bf46-e0c3010a6317
          tenantId: d2dc0315-853c-4d15-916f-1e47ffec8af4
          flowInstanceId: 8415c2f8-5c48-4319-8c13-8fa3bd68cf48
          creationTime: 2025-06-05T09:00:00Z
          flowId: 4da73b93-e85c-43c9-b0ff-395a33c73b87
          stepId: be186ec4-0977-4b29-a33d-ad56873bf15f
          stepSlug: new-ticket
          sequence: 0
          messageType: new-ticket
          messageTypeSlug: new-ticket
          userId: 8074c00d-7bd1-4912-a755-5fc53d8aac64
          actorJWT: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzMWIzMjRjZS01ZDlkLTQxOGItYWUzZC04NGM1YzIwOTEzMjgiLCJ0ZW5hbnQiOiIyZjA3MWQ0MC0yOTNlLTQ2YzAtOWIxNy05MGYwYjhlODdhZDAiLCJuYW1lIjoiTmF2aW4gSm9obnNvbiIsImFkbWluIjp0cnVlLCJpYXQiOjE3NjA5MDkyNDUsImV4cCI6MTc2MDkxMjg0NX0.U1VERaWLrqsC3D7Nt3sBndooIG7316fST47vp7yk-AU
          to: nf://prod/vmt/bug-intake/5/new-ticket
          subject: iOS - Hourly Rainfall Value Displayed as Daily Total
          lockedUserId: 3c1c6122-5496-4c77-84eb-417a31c30f5c
          lockedTime: 2025-10-28T15:04:49Z
        body:
          ticket:
            System.Title: iOS - Hourly Rainfall Value Displayed as Daily Total

    new-ticket-with-subscribers:
      summary: New Ticket (with subscribers expansion)
      value: 
        header:
          status: active
          ttl: 2025-11-01T16:04:49Z
        properties:
          messageId: c7def7f8-ef4e-4219-bf46-e0c3010a6317
          tenantId: d2dc0315-853c-4d15-916f-1e47ffec8af4
          flowInstanceId: 8415c2f8-5c48-4319-8c13-8fa3bd68cf48
          creationTime: 2025-06-05T09:00:00Z
          flowId: 4da73b93-e85c-43c9-b0ff-395a33c73b87
          stepId: be186ec4-0977-4b29-a33d-ad56873bf15f
          stepSlug: new-ticket
          sequence: 0
          messageType: new-ticket
          messageTypeSlug: new-ticket
          userId: 8074c00d-7bd1-4912-a755-5fc53d8aac64
          subject: iOS - Hourly Rainfall Value Displayed as Daily Total
        body:
          ticket:
            System.Title: iOS - Hourly Rainfall Value Displayed as Daily Total
        subscribers:
          - userGuid: 4c3faed2-1234-4d3f-8a13-1a2b3c4d5e6f
            userIdxid: 900
            displayName: Digiworker Ticket Processor
            callbackUri: https://digiworker.prod.gradera.ai/api/v1/callbacks/messages
            assigneeType: user
            role: subscriber
            userType: digiworker

  securitySchemes:
    oauth2_oidc:
      type: oauth2
      flows:
        authorizationCode:
          authorizationUrl: https://auth.gradera.ai/oauth/authorize
          tokenUrl: https://auth.gradera.ai/oauth/token
          scopes: {}
      description: |
        User authentication (OIDC). Certain orchestrator-style operations require the token to include a `client_id` claim that matches the environment variable `OIDC_DIGIWORKER_ORCHESTRATOR_CLIENT_ID`.
