openapi: 3.1.0
info:
  title: Funkel AI API
  version: 2026-09-19
  description: >-
    REST capabilities for Funkel AI. Create a personal access token in the app under Settings → Security and send
    Authorization: Bearer YOUR_API_TOKEN. These routes also accept valid session JWT bearer tokens. MCP OAuth tokens
    authenticate the MCP transport, not these REST routes. Resource ownership, workspace permissions, plan entitlements,
    and available credits still apply. Manage purchases, subscriptions, checkout, and invoices in the Funkel AI app.


    Write operations can create resources, consume credits, start asynchronous work, or send messages. Review each
    operation and its error responses. REST operations do not inherit MCP browser approval. There is no universal
    idempotency guarantee: use only keys documented by an operation. API routes may change with product releases.
servers:
  - url: https://api.funkel.ai
x-handler-source-sha256:
  backend/internal/handlers/agent.go: f0761e2aab699a9962dcc061a9193d3029f64b27dbeb1c494239f0aceee022b5
  backend/internal/handlers/agent_attachments.go: 1f1905d4bbf0e637e2b116449c9bea563d5c5192a14b2577ac466f511a72f0ae
  backend/internal/handlers/agent_sources.go: 41318f26c1848741247781c1dd97b9a895c349f81ea56d49ac0e32b1b7442b8a
  backend/internal/handlers/agent_threads.go: 570726d58f44293ff025e7894b3084d5f4d503627313aa3f6837afe69887988c
  backend/internal/handlers/agents.go: a81b1540f8a1883165cb3db14827ced929602fd3ad671328f7b76d26a1d051d7
  backend/internal/handlers/agents_rejected_profiles.go: 5b38aa054f2172d88750210f870081d7ac50cd925a09d662c0d36ed12f8e14b8
  backend/internal/handlers/analytics.go: e2a8ddb7c0d350c8ca7af994db6cfd557ce2a0985dd8a87735827aa6ad2678a4
  backend/internal/handlers/announcements.go: 9da63acc33da9af2cfa91cfdf7201d4a717f3970d7971830a774235f295963e9
  backend/internal/handlers/api_tokens.go: fe914be00f6fb7ce41131bc0b2d2e44b89b502b3aa13d2daab7185216cb673b3
  backend/internal/handlers/apps.go: e5ecdd5c580dbd7f24a83f748ea9cf982ee3507822ff304bdad939cda11e4910
  backend/internal/handlers/auth.go: 4319ac223ec7caf05c014bd6129de9981a4f805dd6d328f9fc3d182e02770a9e
  backend/internal/handlers/billing.go: 5743c69d0274238a0f74d1249c71f4db96da5736d9493c53f8f79de762b01dc4
  backend/internal/handlers/booking_provider_events.go: c015906b64472f64aad2a0fc4ab0bd13c18b9db0c434cff3af0785b14db86595
  backend/internal/handlers/booking_providers.go: ffb0a4ccc399bb0d2b0e02daa3c68fc5bdb3b32aad9f99f579666a13cbd2b803
  backend/internal/handlers/campaign_message_review.go: faa63128a215126001bc6336663c274f57429b25ab0e9046e1d125b9aaaab0f9
  backend/internal/handlers/campaigns.go: 8548d2c452b2207b94f83bf03eaf2499cc92748732ba7e24fdd466f51fbfd576
  backend/internal/handlers/clay_inbound.go: cfe1fe8fd3b016bd4bd8e3a7882c9e1b5fe20da08c209e107f603d5841f0b376
  backend/internal/handlers/clay_install.go: 7252122accdb6e244a1243fd8b680271eb523bfeda18f4a416df02d2d755d053
  backend/internal/handlers/company_decision_maker_import.go: e77ca1c6cb52f926154c4cda64640ac072a596e6eb6b5f8eef2dfd330832b76f
  backend/internal/handlers/contacts.go: 84510cb8d8d50409f64984a9b828c13f6cec5a754bc30c941a2595186997afd9
  backend/internal/handlers/csv_import.go: 74409f77e5502290b7bc22faf1e650a4140e185347547d6d6f7a56b7e7ddb551
  backend/internal/handlers/dashboard.go: c3ef4442b9337d2310311763c6112bb90b0872e40572cb5b3931c22897fdc92b
  backend/internal/handlers/discovered_leads.go: 1ba076cf8cae1a0aac68f6a699f034f3c555eb88a2a5d77e67a64a45ab98b8d9
  backend/internal/handlers/enrichment.go: 323ecfef96f8be2d489a448c78e3baacfeeec9464b7e86db991eac7ec28f79c5
  backend/internal/handlers/entitlement.go: 578adb8357ebeab410547175f576783f726be57978a5d29ead170f067c9c81b0
  backend/internal/handlers/follow_ups.go: 412d624fc27e9d7b194e4bc06d40647a62146cfd314951437cbc539debe7804e
  backend/internal/handlers/ideas.go: b959acc89886a71b880473b8140577348770b348eb7cf2a9750a8fc5d60299d6
  backend/internal/handlers/inbox.go: c3336ad21c2dff32732942009612a23aa15338a9381ec5a7e10d73c7432e4700
  backend/internal/handlers/insights.go: c9f06ca65e100a177b6b80c09949b569936c3733c6b0945ec4f76995cfaa390d
  backend/internal/handlers/integrations.go: 4acef6ec2afe6a12386b2a734fdc98fa8dfbeb3a23d113103ce065af637af73c
  backend/internal/handlers/lead_lists.go: 63341e7b53af8785674da1692da888ec795fd806b1f70aeac228cd1084d1dd42
  backend/internal/handlers/leads.go: 6f51fa5a0106964db79b368c2f21997061820409240f2373c7db5d1a9a338340
  backend/internal/handlers/mcp.go: 50668332356a6f390c605d83b398d3b1055cc811bf88ce46d2ab86dc3c71c8b3
  backend/internal/handlers/meetings.go: 5558982077a9028445c9320835e32884f968e9316801704b08bf8e71025fa259
  backend/internal/handlers/notifications.go: 20613fa5b3c107a21587998dcc6445d5fa9f24db0474b911cc0735e3743337c8
  backend/internal/handlers/oauth.go: b33c6e0f59730d7d74dffbb83e06c386b1a3e3fdbaa238138aa6d63a80bf88d2
  backend/internal/handlers/offers.go: 4c2e813f4adc98d928bc2486eb793cabca2d139c73454c69e084da09a7bebe23
  backend/internal/handlers/onboarding.go: 9fb08e32ad6e5978017fa3fc2a529a0b3b3d4c4785227b86d2017d36502b1083
  backend/internal/handlers/operator_capabilities.go: 1d89034e398dc362877acf8d9eca13732eb1d691fa00c2bc24d566d3111e13b8
  backend/internal/handlers/operator_memory.go: a33b8902c5bf1fc67eb559c26979703ba432ef23247529cb8221562d6cbe3e7b
  backend/internal/handlers/operator_outcomes.go: a7841580d2f641ee7cbc247cce99a2ed865643661327e80acc737eca395b1b42
  backend/internal/handlers/operator_tasks.go: aab2b48bc24e82a745eb9c093a4d07aaa42591687996bb466c977cf65b5d1dc9
  backend/internal/handlers/people_csv_import.go: 4ad580fac38fd02e267c3cdca068c181284f662c12b258ec1baf064273cafd7d
  backend/internal/handlers/platform_accounts.go: 9ec35a1d3a274f6e4b6986ebcda9bb6743fb2914e9047e010a82a9ab40a9e408
  backend/internal/handlers/scheduled_actions.go: 291ebc7a6d98b01526980c4c0b21f32dd1c9ad5ed4b69027a65758723a8928c3
  backend/internal/handlers/specialist_activity.go: 7ccf0be6e91b69e024a33534d87908fc3164196f9d295a7f8f8233fbf31de6de
  backend/internal/handlers/table_views.go: 3c62430d175ed7ad2910b5b5f9de995f2e2f0b25dfa4904635cfad79fd2ab790
  backend/internal/handlers/teams.go: 92e8ebe35dd508af8d8ab32509f8d22317ebb4bfb3cb26700da9a9eaf0b3114d
  backend/internal/handlers/user_sessions.go: 5ad2cad67f99c64f4db5097483954d4f4eeee1f78de5f1f70277195325d1f53c
  backend/internal/handlers/users.go: 8b3cd32e8c0b6ca48f54371b358a6e701cc2730a6508c57affbc1446dc833eca
  backend/internal/handlers/webhooks_outbound.go: dca0740a54c736ed3cec422b0177e25b0ad5fc21ea9b37b29bd1cf0544c12ef1
  backend/internal/handlers/workflow_steps.go: ebad933adcc42a29ef2cad9087622e3b5f1f2c0d77868a40c836a58af67daf23
tags:
  - name: API tokens
  - name: Account usage
  - name: Agent
  - name: Analytics
  - name: Auth
  - name: Booking Provider Events
  - name: Booking Providers
  - name: Campaigns
  - name: Company Decision Maker Imports
  - name: Contacts
  - name: Dashboard
  - name: Discovered Leads
  - name: Discovery agents
  - name: Enrichment
  - name: Features
  - name: Follow Ups
  - name: Ideas
  - name: Inbox
  - name: Insights
  - name: Integrations
  - name: Lead lists
  - name: Leads
  - name: Mcp
  - name: Me
  - name: Meetings
  - name: Message review
  - name: Onboarding
  - name: Operator
  - name: Outbound webhooks
  - name: Platform Accounts
  - name: Products
  - name: Scheduled Actions
  - name: Table Views
  - name: Team
  - name: Users
paths:
  /api/agent/attachments:
    post:
      description: >-
        handles POST /api/agent/attachments without calling an AI provider.


        Uses the effective workspace identity and enforces resource ownership.


        Rate limit: 20 requests per minute, shared with routes in this middleware group, keyed by authenticated user, or
        IP when unauthenticated.
      operationId: post_api_agent_attachments
      requestBody:
        content:
          multipart/form-data:
            schema:
              properties:
                app_id:
                  type: string
                file:
                  format: binary
                  type: string
                thread_id:
                  type: string
              type: object
        required: true
      responses:
        "201":
          content:
            application/json:
              schema:
                properties:
                  content_type:
                    type: string
                  extraction_status:
                    type: string
                  file_id:
                    type: string
                  filename:
                    type: string
                  size_bytes:
                    type: integer
                  thread_id:
                    type: string
                type: object
          description: Created
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_app: valid app_id is required

            invalid_file: unsupported or invalid file

            invalid_thread: invalid thread id

            invalid_user: invalid user id

            missing_file: file field is required
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "402":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: ": Operator is for paid plans. Upgrade to use it."
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            app_not_owned: app does not belong to user

            demo_read_only: attachments are not available in Demo

            not_assigned: this app is not assigned to you
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "thread_not_found: thread does not belong to selected app"
        "413":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            file_too_large: file exceeds 2 MiB

            file_too_large: file exceeds a supported limit

            invalid_upload: file too large or invalid multipart form
        "422":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "pdf_extraction_unavailable: PDF extraction is unavailable on this host"
        "429":
          description: Rate limit exceeded. Wait before retrying.
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            thread_create_failed: couldn't create thread

            upload_failed: couldn't store attachment
      security:
        - bearerAuth: []
      summary: Upload
      tags:
        - Agent
      x-source: backend/internal/handlers/agent_attachments.go:35
      x-source-registration: backend/cmd/api/main.go:1949
      x-handler: agentAttachmentsHandler.Upload
      parameters:
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
  /api/agent/capabilities:
    get:
      description: |-
        exposes the same registered tools as Operator, without tool schemas or
        internal dispatch details. Product ownership is checked before enumeration.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: get_api_agent_capabilities
      parameters:
        - in: query
          name: app_id
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  app_id:
                    type: string
                  capabilities:
                    items:
                      $ref: "#/components/schemas/handlers_operatorCapabilityView"
                    type: array
                type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_scope: valid app_id required"
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: product not found"
        "503":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unavailable: Operator capability guide is unavailable"
      security:
        - bearerAuth: []
      summary: List operator capabilities
      tags:
        - Agent
      x-source: backend/internal/handlers/operator_capabilities.go:31
      x-source-registration: backend/cmd/api/main.go:1952
      x-handler: operatorCapabilitiesHandler.List
  /api/agent/messages:
    post:
      description: Send a message to the Funkel AI agent and receive a server-sent event stream. The account needs agent
        access and available credits. Supply the product and thread context. The stream reports message, tool, approval,
        completion, and error events. This endpoint shares its 20 requests per minute user limit with attachment
        uploads.
      operationId: post_api_agent_messages
      requestBody:
        content:
          application/json:
            schema:
              properties:
                app_id:
                  type: string
                attachment_ids:
                  items:
                    type: string
                  type: array
                confirmation_action:
                  type: string
                confirmation_token:
                  type: string
                message:
                  type: string
                thread_id:
                  type: string
              type: object
              required:
                - app_id
        required: true
      responses:
        "200":
          content:
            text/event-stream:
              schema:
                type: string
          description: Server-sent event stream.
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_app: invalid app id

            invalid_attachments: attachment_ids must be unique UUIDs

            invalid_attachments: up to three attachments require an existing thread and cannot confirm an action

            invalid_body: app_id is required

            invalid_body: confirmation_action must be confirm or cancel

            invalid_body: confirmation_token is required to cancel

            invalid_body: malformed request body

            invalid_body: message and confirmation_token cannot be combined

            invalid_body: message is required

            invalid_thread: invalid thread id

            invalid_user: invalid user id

            thread_app_mismatch: thread does not belong to selected app
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "402":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: ": Operator is for paid plans. Upgrade to use it."
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            app_not_owned: app does not belong to user

            demo_read_only: Operator is not available in Demo

            not_assigned: this app is not assigned to you
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            attachment_not_found: attachment does not belong to this thread

            thread_not_found: thread does not belong to user
        "422":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "attachment_unavailable: attachment has no text for Operator chat"
        "429":
          description: Rate limit exceeded. Wait before retrying.
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "thread_create_failed: couldn't create thread"
      security:
        - bearerAuth: []
      summary: Stream
      tags:
        - Agent
      x-required-input-fields:
        - app_id
      x-source: backend/internal/handlers/agent.go:84
      x-source-registration: backend/cmd/api/main.go:1948
      x-handler: agentHandler.Stream
      parameters:
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
  /api/agent/threads:
    get:
      description: |-
        handles GET /api/agent/threads?app_id=...
        Returns a (possibly empty) slice of AgentThread rows scoped to
        (current_user, app_id).

        Uses the effective workspace identity and enforces resource ownership.
      operationId: get_api_agent_threads
      parameters:
        - in: query
          name: app_id
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: "#/components/schemas/sqlc_AgentThread"
                type: array
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_app: app_id query required

            invalid_app: invalid app id

            invalid_user: invalid user id
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: couldn't load threads"
      security:
        - bearerAuth: []
      summary: List agent threads
      tags:
        - Agent
      x-source: backend/internal/handlers/agent_threads.go:52
      x-source-registration: backend/cmd/api/main.go:1951
      x-handler: agentThreadsHandler.List
    post:
      description: |-
        handles POST /api/agent/threads.
        Body: {app_id, title?}. Verifies app ownership before insert.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: post_api_agent_threads
      requestBody:
        content:
          application/json:
            schema:
              properties:
                app_id:
                  type: string
                title:
                  type: string
              type: object
              required:
                - app_id
        required: true
      responses:
        "201":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/sqlc_AgentThread"
          description: Created
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_app: app_id is required

            invalid_app: invalid app id

            invalid_body: malformed request body

            invalid_user: invalid user id
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "app_not_owned: app does not belong to user"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: couldn't create thread"
      security:
        - bearerAuth: []
      summary: Create agent threads
      tags:
        - Agent
      x-required-input-fields:
        - app_id
      x-source: backend/internal/handlers/agent_threads.go:236
      x-source-registration: backend/cmd/api/main.go:1953
      x-handler: agentThreadsHandler.Create
      parameters:
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
  /api/agent/threads/{threadId}:
    delete:
      description: |-
        handles DELETE /api/agent/threads/{threadId}.
        CASCADE FK from Plan 01 cleans up agent_messages + agent_confirmations
        + agent_turn_costs as part of the same transaction.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: delete_api_agent_threads_threadId
      parameters:
        - in: path
          name: threadId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "204":
          description: No content.
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_thread: invalid thread id

            invalid_user: invalid user id
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: thread not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: couldn't delete thread"
      security:
        - bearerAuth: []
      summary: Delete agent threads
      tags:
        - Agent
      x-source: backend/internal/handlers/agent_threads.go:283
      x-source-registration: backend/cmd/api/main.go:1956
      x-handler: agentThreadsHandler.Delete
    get:
      description: |-
        handles GET /api/agent/threads/{threadId}.
        Returns the single thread row if owned by the current user, else 404.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: get_api_agent_threads_threadId
      parameters:
        - in: path
          name: threadId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/sqlc_AgentThread"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_thread: invalid thread id

            invalid_user: invalid user id
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: thread not found"
      security:
        - bearerAuth: []
      summary: Get agent threads
      tags:
        - Agent
      x-source: backend/internal/handlers/agent_threads.go:88
      x-source-registration: backend/cmd/api/main.go:1954
      x-handler: agentThreadsHandler.Get
    patch:
      description: |-
        handles PATCH /api/agent/threads/{threadId}. The only field
        the client may change is title. Trims whitespace; rejects an empty
        resulting title (use the rail's "New thread" fallback instead, by
        not setting one at all). User-scoping happens at the SQL layer via
        the WHERE user_id = $2 clause; we additionally GetAgentThreadForUser
        first so a cross-tenant rename returns 404 instead of a silent
        no-op.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: patch_api_agent_threads_threadId
      parameters:
        - in: path
          name: threadId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                title:
                  type: string
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/sqlc_AgentThread"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_body: malformed request body

            invalid_thread: invalid thread id

            invalid_title: title cannot be empty

            invalid_user: invalid user id

            title_too_long: 
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: thread not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: couldn't update title"
      security:
        - bearerAuth: []
      summary: Update agent threads
      tags:
        - Agent
      x-source: backend/internal/handlers/agent_threads.go:337
      x-source-registration: backend/cmd/api/main.go:1955
      x-handler: agentThreadsHandler.Update
  /api/agent/threads/{threadId}/messages:
    get:
      description: |-
        handles GET /api/agent/threads/{threadId}/messages.
        Returns the FULL chronological history for UI scrollback. The
        orchestrator only sees the last 20 turns via GetAgentThreadWindow; the
        UI sees everything via this endpoint so users can review prior turns.

        User-scoping happens at the SQL layer via the JOIN to agent_threads.
        A thread that does not belong to the current user returns an empty
        slice (existence not leaked).

        Uses the effective workspace identity and enforces resource ownership.
      operationId: get_api_agent_threads_threadId_messages
      parameters:
        - in: path
          name: threadId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: "#/components/schemas/handlers_AgentMessageView"
                type: array
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_thread: invalid thread id

            invalid_user: invalid user id
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: thread not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: couldn't load messages"
      security:
        - bearerAuth: []
      summary: List Messages
      tags:
        - Agent
      x-source: backend/internal/handlers/agent_threads.go:191
      x-source-registration: backend/cmd/api/main.go:1957
      x-handler: agentThreadsHandler.ListMessages
  /api/agents/{agentId}:
    delete:
      description: |-
        handles DELETE /api/agents/{agentId}.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: delete_api_agents_agentId
      parameters:
        - in: path
          name: agentId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/demosubmit_Response"
          description: OK
        "204":
          description: No content.
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid agent ID

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            Missing, invalid, expired, or revoked bearer credential.

            unauthorized: not authenticated
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_assigned: this app is not assigned to you"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: agent not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to create Demo simulation response

            internal: failed to delete agent
        "503":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "workspace_policy_unavailable: workspace policy is unavailable"
      security:
        - bearerAuth: []
      summary: Delete agents
      tags:
        - Discovery agents
      x-source: backend/internal/handlers/agents.go:785
      x-source-registration: backend/cmd/api/main.go:1625
      x-handler: agentsHandler.Delete
    get:
      description: |-
        handles GET /api/agents/{agentId}.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: get_api_agents_agentId
      parameters:
        - in: path
          name: agentId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/handlers_agentResponse"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid agent ID

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: agent not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: failed to fetch agent passport"
      security:
        - bearerAuth: []
      summary: Get agents
      tags:
        - Discovery agents
      x-source: backend/internal/handlers/agents.go:284
      x-source-registration: backend/cmd/api/main.go:1623
      x-handler: agentsHandler.Get
    put:
      description: |-
        handles PUT /api/agents/{agentId}.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: put_api_agents_agentId
      parameters:
        - in: path
          name: agentId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                b2b:
                  anyOf:
                    - type: boolean
                    - type: "null"
                config:
                  description: JSON value stored by the server; shape depends on this resource's configuration.
                icp_description:
                  anyOf:
                    - type: string
                    - type: "null"
                icp_filters:
                  description: JSON value stored by the server; shape depends on this resource's configuration.
                list_id:
                  anyOf:
                    - type: string
                    - type: "null"
                max_signals:
                  anyOf:
                    - type: integer
                    - type: "null"
                name:
                  anyOf:
                    - type: string
                    - type: "null"
                require_current_employer:
                  anyOf:
                    - type: boolean
                    - type: "null"
                status:
                  anyOf:
                    - type: string
                    - type: "null"
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                anyOf:
                  - $ref: "#/components/schemas/demosubmit_Response"
                  - $ref: "#/components/schemas/sqlc_Agent"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_body: invalid request body

            invalid_id: invalid agent ID

            invalid_list_id: invalid list ID

            invalid_list_id: list must belong to the agent app

            invalid_transition: 

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            Missing, invalid, expired, or revoked bearer credential.

            unauthorized: not authenticated
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            not_assigned: this app is not assigned to you

            subscription_required: Start your subscription to use this feature. Visit Settings → Billing to continue.
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            not_found: agent not found

            not_found: agent not found or update failed
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: couldn't verify subscription state

            internal: failed to create Demo simulation response

            internal: failed to validate signals
        "503":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "workspace_policy_unavailable: workspace policy is unavailable"
      security:
        - bearerAuth: []
      summary: Update agents
      tags:
        - Discovery agents
      x-source: backend/internal/handlers/agents.go:508
      x-source-registration: backend/cmd/api/main.go:1624
      x-handler: agentsHandler.Update
  /api/agents/{agentId}/discovery-funnel:
    get:
      description: |-
        handles GET /api/agents/{agentId}/discovery-funnel.
        It keeps the recent qualification cohort separate from current list and
        campaign totals, while enforcing ownership before every aggregate read.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: get_api_agents_agentId_discovery_funnel
      parameters:
        - in: path
          name: agentId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/handlers_agentDiscoveryFunnelResponse"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid agent ID

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: agent not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            db_error: Failed to fetch agent signals

            db_error: Failed to fetch discovery results
      security:
        - bearerAuth: []
      summary: Get Discovery Funnel
      tags:
        - Discovery agents
      x-source: backend/internal/handlers/agents.go:1381
      x-source-registration: backend/cmd/api/main.go:1638
      x-handler: agentsHandler.GetDiscoveryFunnel
  /api/agents/{agentId}/generate-keywords:
    post:
      description: |-
        handles POST /api/agents/{agentId}/generate-keywords.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: post_api_agents_agentId_generate_keywords
      parameters:
        - in: path
          name: agentId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  keywords:
                    type: string
                type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid agent ID

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: agent not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            ai_error: failed to generate keywords

            internal: failed to fetch app
      security:
        - bearerAuth: []
      summary: Generate Keywords
      tags:
        - Discovery agents
      x-source: backend/internal/handlers/agents.go:991
      x-source-registration: backend/cmd/api/main.go:1635
      x-handler: agentsHandler.GenerateKeywords
  /api/agents/{agentId}/next-launches:
    get:
      description: |-
        handles GET /api/agents/{agentId}/next-launches.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: get_api_agents_agentId_next_launches
      parameters:
        - in: path
          name: agentId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: "#/components/schemas/sqlc_ListNextLaunchesRow"
                type: array
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid agent ID

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: agent not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: failed to fetch launches"
      security:
        - bearerAuth: []
      summary: Next Launches
      tags:
        - Discovery agents
      x-source: backend/internal/handlers/agents.go:1123
      x-source-registration: backend/cmd/api/main.go:1637
      x-handler: agentsHandler.NextLaunches
  /api/agents/{agentId}/preview:
    post:
      description: |-
        handles POST /api/agents/{agentId}/preview (signal_id optional
        in request body or query). Runs search → dedup → cheap filter against
        Unipile and returns the survivors WITHOUT enriching, scoring, or inserting.
        Lets users sanity-check what an agent would find before activating it.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: post_api_agents_agentId_preview
      parameters:
        - in: path
          name: agentId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                anyOf:
                  - $ref: "#/components/schemas/engine_PreviewResult"
                  - type: "null"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_body: 

            invalid_id: invalid agent ID

            invalid_signal_id: invalid signal ID

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: agent not found"
        "413":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "request_too_large: "
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "preview_failed: "
        "503":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "linkedin_unavailable: preview is unavailable — LinkedIn integration is not configured"
      security:
        - bearerAuth: []
      summary: Preview Signal
      tags:
        - Discovery agents
      x-source: backend/internal/handlers/agents.go:1567
      x-source-registration: backend/cmd/api/main.go:1645
      x-handler: agentsHandler.PreviewSignal
  /api/agents/{agentId}/rejected-profiles:
    get:
      description: |-
        handles
        GET /api/agents/{agentId}/rejected-profiles?reason=&limit=&offset=.
        Returns the individual rejected profiles behind one rejection-class card
        on the agent edit page's Filtered tab — same agent, same 7-day/200-row
        window, same classifyRejection() classification GetRejectionBreakdown uses
        for the card's count, so the dialog's contents can never disagree with the
        card.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: get_api_agents_agentId_rejected_profiles
      parameters:
        - in: query
          name: reason
          schema:
            type: string
        - in: query
          name: limit
          schema:
            type: string
        - in: query
          name: offset
          schema:
            type: string
        - in: path
          name: agentId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/handlers_agentRejectedProfilesResponse"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid agent ID

            invalid_user_id: invalid user ID

            missing_reason: reason query param is required
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: agent not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "db_error: Failed to fetch rejected profiles"
      security:
        - bearerAuth: []
      summary: Get Rejected Profiles For Class
      tags:
        - Discovery agents
      x-source: backend/internal/handlers/agents_rejected_profiles.go:120
      x-source-registration: backend/cmd/api/main.go:1640
      x-handler: agentsHandler.GetRejectedProfilesForClass
  /api/agents/{agentId}/rejected-profiles/bulk-promote:
    post:
      description: |-
        queues several rejected profiles for manual
        promotion. Each outcome stays independent so one bad row does not hide the
        successful queue requests.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: post_api_agents_agentId_rejected_profiles_bulk_promote
      parameters:
        - in: path
          name: agentId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                ids:
                  items:
                    type: string
                  type: array
              type: object
              required:
                - ids
        required: true
      responses:
        "202":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/handlers_batchPromoteRejectedProfilesResponse"
          description: Accepted
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_body: invalid request body

            invalid_id: invalid agent ID

            invalid_id: invalid raw profile ID

            invalid_ids: ids must contain between 1 and 50 profiles

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: agent not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to load rejected profiles

            internal: promote unavailable
      security:
        - bearerAuth: []
      summary: Batch Promote Rejected Profiles
      tags:
        - Discovery agents
      x-required-input-fields:
        - ids
      x-source: backend/internal/handlers/agents_rejected_profiles.go:296
      x-source-registration: backend/cmd/api/main.go:1641
      x-handler: agentsHandler.BatchPromoteRejectedProfiles
  /api/agents/{agentId}/rejected-profiles/{rawId}/promote:
    post:
      description: |-
        queues one rejected profile for manual promotion.
        The worker performs enrichment and campaign enrollment asynchronously.
      operationId: post_api_agents_agentId_rejected_profiles_rawId_promote
      parameters:
        - in: path
          name: agentId
          required: true
          schema:
            type: string
        - in: path
          name: rawId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "202":
          content:
            application/json:
              schema:
                properties:
                  status:
                    type: string
                type: object
          description: Accepted
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid agent ID

            invalid_id: invalid raw profile ID

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            not_found: agent not found

            not_found: profile not found
        "409":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_rejected: this profile isn't in a rejected state"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to queue promote

            internal: promote unavailable
      security:
        - bearerAuth: []
      summary: Promote Rejected Profile
      tags:
        - Discovery agents
      x-source: backend/internal/handlers/agents_rejected_profiles.go:273
      x-source-registration: backend/cmd/api/main.go:1642
      x-handler: agentsHandler.PromoteRejectedProfile
  /api/agents/{agentId}/rejected-profiles/{rawId}/promote-status:
    get:
      description: |-
        returns the live state of one manual
        promotion. It does not infer completion from a paginated list response.
      operationId: get_api_agents_agentId_rejected_profiles_rawId_promote_status
      parameters:
        - in: path
          name: agentId
          required: true
          schema:
            type: string
        - in: path
          name: rawId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/handlers_promoteRejectedProfileStatusResponse"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid agent ID

            invalid_id: invalid raw profile ID

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            not_found: agent not found

            not_found: profile not found
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: failed to read promote status"
      security:
        - bearerAuth: []
      summary: Promote Rejected Profile Status
      tags:
        - Discovery agents
      x-source: backend/internal/handlers/agents_rejected_profiles.go:388
      x-source-registration: backend/cmd/api/main.go:1643
      x-handler: agentsHandler.PromoteRejectedProfileStatus
  /api/agents/{agentId}/rejections:
    get:
      description: |-
        handles GET /api/agents/{agentId}/rejections.
        Returns the last-24h rejection breakdown grouped into user-facing classes,
        with a sample AI reasoning per class when available. Used by the
        "Why leads aren't reaching you" panel on the agent detail page.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: get_api_agents_agentId_rejections
      parameters:
        - in: path
          name: agentId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: "#/components/schemas/handlers_rejectionClass"
                type: array
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid agent ID

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: agent not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "db_error: Failed to fetch rejections"
      security:
        - bearerAuth: []
      summary: Get Rejection Breakdown
      tags:
        - Discovery agents
      x-source: backend/internal/handlers/agents.go:1262
      x-source-registration: backend/cmd/api/main.go:1639
      x-handler: agentsHandler.GetRejectionBreakdown
  /api/agents/{agentId}/signals:
    get:
      description: |-
        handles GET /api/agents/{agentId}/signals.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: get_api_agents_agentId_signals
      parameters:
        - in: path
          name: agentId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: "#/components/schemas/sqlc_AgentSignal"
                type: array
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid agent ID

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: agent not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: failed to fetch signals"
      security:
        - bearerAuth: []
      summary: List Signals
      tags:
        - Discovery agents
      x-source: backend/internal/handlers/agents.go:838
      x-source-registration: backend/cmd/api/main.go:1628
      x-handler: agentsHandler.ListSignals
    put:
      description: |-
        handles PUT /api/agents/{agentId}/signals.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: put_api_agents_agentId_signals
      parameters:
        - in: path
          name: agentId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                signals:
                  items:
                    properties:
                      config:
                        description: JSON value stored by the server; shape depends on this resource's configuration.
                      description:
                        type: string
                      enabled:
                        type: boolean
                      estimated_matches:
                        type: integer
                      label:
                        type: string
                      platform:
                        type: string
                      signal_category:
                        type: string
                      signal_key:
                        type: string
                    type: object
                  type: array
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                anyOf:
                  - $ref: "#/components/schemas/demosubmit_Response"
                  - items:
                      $ref: "#/components/schemas/sqlc_AgentSignal"
                    type: array
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_body: invalid request body

            invalid_id: invalid agent ID

            invalid_signal: 

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            Missing, invalid, expired, or revoked bearer credential.

            unauthorized: not authenticated
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_assigned: this app is not assigned to you"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: agent not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to create Demo simulation response

            internal: failed to begin transaction

            internal: failed to commit signals

            internal: failed to fetch updated signals

            internal: failed to prune signals

            internal: failed to upsert signal
        "503":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "workspace_policy_unavailable: workspace policy is unavailable"
      security:
        - bearerAuth: []
      summary: Update Signals
      tags:
        - Discovery agents
      x-source: backend/internal/handlers/agents.go:878
      x-source-registration: backend/cmd/api/main.go:1629
      x-handler: agentsHandler.UpdateSignals
  /api/agents/{agentId}/signals/{signalId}/launch:
    post:
      description: |-
        handles POST /api/agents/{agentId}/signals/{signalId}/launch.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: post_api_agents_agentId_signals_signalId_launch
      parameters:
        - in: path
          name: agentId
          required: true
          schema:
            type: string
        - in: path
          name: signalId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                anyOf:
                  - $ref: "#/components/schemas/demosubmit_Response"
                  - properties:
                      status:
                        type: string
                    type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            agent_not_active: agent must be active to launch signals

            invalid_id: invalid agent ID

            invalid_signal_id: invalid signal ID

            invalid_user_id: invalid user ID

            signal_disabled: signal must be enabled to launch
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            Missing, invalid, expired, or revoked bearer credential.

            unauthorized: not authenticated
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_assigned: this app is not assigned to you"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            not_found: agent not found

            not_found: signal not found
        "429":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "manual_launch_cooldown: This signal was launched recently. Try again after the cooldown."
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to create Demo simulation response

            internal: failed to enqueue signal launch
        "503":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            workspace_policy_unavailable: workspace policy is unavailable

            launch_unavailable: signal launch queue is unavailable
      security:
        - bearerAuth: []
      summary: Launch Signal
      tags:
        - Discovery agents
      x-source: backend/internal/handlers/agents.go:1459
      x-source-registration: backend/cmd/api/main.go:1644
      x-handler: agentsHandler.LaunchSignal
  /api/agents/{agentId}/sources:
    get:
      description: handles GET /api/agents/{agentId}/sources.
      operationId: get_api_agents_agentId_sources
      parameters:
        - in: path
          name: agentId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: "#/components/schemas/handlers_agentSourceResponse"
                type: array
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid agent ID

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: agent not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to fetch agent sources

            internal: failed to fetch source rule status

            internal: failed to fetch source usage
      security:
        - bearerAuth: []
      summary: List Sources
      tags:
        - Discovery agents
      x-source: backend/internal/handlers/agent_sources.go:118
      x-source-registration: backend/cmd/api/main.go:1633
      x-handler: agentsHandler.ListSources
  /api/agents/{agentId}/sources/hackernews/generate-queries:
    post:
      description: |-
        handles POST
        /api/agents/{agentId}/sources/hackernews/generate-queries. Reuses the
        same agent/app context GenerateKeywords uses (product name/description +
        ICP description) plus a freeform user prompt describing who they want to
        find. No credit consumption: this is setup/config tooling, not discovery
        or messaging.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: post_api_agents_agentId_sources_hackernews_generate_queries
      parameters:
        - in: path
          name: agentId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                prompt:
                  type: string
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  queries:
                    items:
                      type: string
                    type: array
                type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_body: invalid JSON body

            invalid_id: invalid agent ID

            invalid_user_id: invalid user ID

            missing_prompt: missing 'prompt' field
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: agent not found"
        "429":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "rate_limited: Too many generation requests for this agent. Wait a moment and try again."
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            ai_error: failed to generate hacker news queries

            internal: failed to fetch app
      security:
        - bearerAuth: []
      summary: Generate Hacker News Queries
      tags:
        - Discovery agents
      x-source: backend/internal/handlers/agents.go:1050
      x-source-registration: backend/cmd/api/main.go:1636
      x-handler: agentsHandler.GenerateHackerNewsQueries
  /api/agents/{agentId}/sources/{source}:
    put:
      description: |-
        handles PUT /api/agents/{agentId}/sources/{source}.
        Upserts the agent's listening config for one source. Both 'x' and
        'hackernews' are supported today; the route validates so a typo cannot
        create an orphan source the collector will never read.
      operationId: put_api_agents_agentId_sources_source
      parameters:
        - in: path
          name: agentId
          required: true
          schema:
            type: string
        - in: path
          name: source
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                auto_resolve_linkedin:
                  description: |-
                    AutoResolveLinkedin (Part 4, hackernews-only): unlike DailyPollBudget
                    (preserved-from-existing, UI doesn't manage it), this field follows
                    the same handling as Queries -- the UI sends it explicitly on every
                    PUT, so it's a user-controlled field here, never preserved from the
                    prior row. Ignored for the "x" source.
                  type: boolean
                enabled:
                  type: boolean
                queries:
                  items:
                    type: string
                  type: array
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                anyOf:
                  - $ref: "#/components/schemas/demosubmit_Response"
                  - $ref: "#/components/schemas/handlers_agentSourceResponse"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid agent ID

            invalid_user_id: invalid user ID

            invalid_body: invalid request body

            invalid_source: unknown source: only 'x' and 'hackernews' are supported

            no_queries: add at least one search before enabling the source

            too_many_queries: an agent can listen on at most 10 searches per source; remove some before adding more
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            unauthorized: not authenticated

            Missing, invalid, expired, or revoked bearer credential.

            Missing, invalid, expired, or revoked bearer credential.

            Missing, invalid, expired, or revoked bearer credential.
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_assigned: this app is not assigned to you"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: agent not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to create Demo simulation response

            internal: failed to encode source config

            internal: failed to fetch source rule status

            internal: failed to fetch source usage

            internal: failed to read agent source

            internal: failed to reconcile source rules

            internal: failed to save agent source
        "503":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "workspace_policy_unavailable: workspace policy is unavailable"
      security:
        - bearerAuth: []
      summary: Update Source
      tags:
        - Discovery agents
      x-source: backend/internal/handlers/agent_sources.go:154
      x-source-registration: backend/cmd/api/main.go:1634
      x-handler: agentsHandler.UpdateSource
  /api/analytics/daily:
    get:
      description: handles GET /api/analytics/daily
      operationId: get_api_analytics_daily
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: "#/components/schemas/handlers_dailyActivityRow"
                type: array
          description: OK
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: "
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: failed to fetch daily activity"
      security:
        - bearerAuth: []
      summary: Daily Activity
      tags:
        - Analytics
      x-source: backend/internal/handlers/analytics.go:199
      x-source-registration: backend/cmd/api/main.go:1817
      x-handler: analyticsHandler.DailyActivity
      parameters:
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
  /api/analytics/daily-overview:
    get:
      description: handles GET /api/analytics/daily-overview
      operationId: get_api_analytics_daily_overview
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: "#/components/schemas/handlers_dailyOverviewRow"
                type: array
          description: OK
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: "
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: failed to fetch daily overview"
      security:
        - bearerAuth: []
      summary: Daily Overview
      tags:
        - Analytics
      x-source: backend/internal/handlers/analytics.go:357
      x-source-registration: backend/cmd/api/main.go:1822
      x-handler: analyticsHandler.DailyOverview
      parameters:
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
  /api/analytics/funnel:
    get:
      description: handles GET /api/analytics/funnel
      operationId: get_api_analytics_funnel
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/handlers_funnelAnalysisResponse"
          description: OK
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: "
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to fetch funnel overview

            internal: failed to fetch funnel steps
      security:
        - bearerAuth: []
      summary: Funnel Analysis
      tags:
        - Analytics
      x-source: backend/internal/handlers/analytics.go:301
      x-source-registration: backend/cmd/api/main.go:1821
      x-handler: analyticsHandler.FunnelAnalysis
      parameters:
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
  /api/analytics/overview:
    get:
      description: handles GET /api/analytics/overview
      operationId: get_api_analytics_overview
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/handlers_analyticsOverviewResponse"
          description: OK
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: "
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: failed to fetch analytics overview"
      security:
        - bearerAuth: []
      summary: Overview
      tags:
        - Analytics
      x-source: backend/internal/handlers/analytics.go:182
      x-source-registration: backend/cmd/api/main.go:1816
      x-handler: analyticsHandler.Overview
      parameters:
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
  /api/analytics/signals:
    get:
      description: handles GET /api/analytics/signals
      operationId: get_api_analytics_signals
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: "#/components/schemas/handlers_signalStatsRow"
                type: array
          description: OK
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: "
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: failed to fetch signal stats"
      security:
        - bearerAuth: []
      summary: Signal Stats
      tags:
        - Analytics
      x-source: backend/internal/handlers/analytics.go:251
      x-source-registration: backend/cmd/api/main.go:1819
      x-handler: analyticsHandler.SignalStats
      parameters:
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
  /api/analytics/sources:
    get:
      description: |-
        handles GET /api/analytics/sources — the signal-first minimal
        funnel (SPEC FR-13): per-source discovery/route/review counts, sent/replied
        outcomes for signal-sourced leads, and today's X read budget usage so the
        UI can show when collection is paused.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: get_api_analytics_sources
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  lead_outcomes:
                    items:
                      $ref: "#/components/schemas/sqlc_CountSignalFirstLeadOutcomesRow"
                    type: array
                  lead_source_credits:
                    properties:
                      allowance:
                        type: integer
                      remaining:
                        type: integer
                      used:
                        type: integer
                    type: object
                  route_counts:
                    items:
                      $ref: "#/components/schemas/sqlc_CountSourceRouteOutcomesRow"
                    type: array
                  x_accounts:
                    items:
                      additionalProperties:
                        description: JSON value; the server permits arbitrary JSON at this field.
                      type: object
                    type: array
                  x_reads_today:
                    type: integer
                type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_user_id: invalid user ID"
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to load lead outcomes

            internal: failed to load source counts
      security:
        - bearerAuth: []
      summary: Source Stats
      tags:
        - Analytics
      x-source: backend/internal/handlers/analytics.go:658
      x-source-registration: backend/cmd/api/main.go:1820
      x-handler: analyticsHandler.SourceStats
      parameters:
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
  /api/analytics/steps:
    get:
      description: handles GET /api/analytics/steps
      operationId: get_api_analytics_steps
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: "#/components/schemas/handlers_stepStatsRow"
                type: array
          description: OK
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: "
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: failed to fetch step stats"
      security:
        - bearerAuth: []
      summary: Step Stats
      tags:
        - Analytics
      x-source: backend/internal/handlers/analytics.go:234
      x-source-registration: backend/cmd/api/main.go:1818
      x-handler: analyticsHandler.StepStats
      parameters:
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
  /api/apps:
    get:
      description: |-
        handles GET /api/apps -- returns all apps for the authenticated user.
        When acting-as a workspace owner via X-Acting-As, returns only the apps
        assigned to the team member.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: get_api_apps
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: "#/components/schemas/handlers_appResponse"
                type: array
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_user_id: invalid user ID"
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to fetch agent passports

            internal: failed to fetch apps
      security:
        - bearerAuth: []
      summary: List apps
      tags:
        - Products
      x-source: backend/internal/handlers/apps.go:172
      x-source-registration: backend/cmd/api/main.go:1605
      x-handler: appsHandler.List
      parameters:
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
    post:
      description: |-
        handles POST /api/apps -- creates a new app for the authenticated user.
        Refuses when acting-as a team member: only the workspace owner creates apps.

        Uses the effective workspace identity and enforces resource ownership.

        Workspace delegation restrictions apply; see the documented 403 errors.
      operationId: post_api_apps
      requestBody:
        content:
          application/json:
            schema:
              properties:
                description:
                  type: string
                icp_description:
                  type: string
                icp_filters:
                  description: JSON value stored by the server; shape depends on this resource's configuration.
                language:
                  type: string
                logo_url:
                  type: string
                name:
                  type: string
                product_brain:
                  description: JSON value stored by the server; shape depends on this resource's configuration.
                url:
                  type: string
              type: object
              required:
                - name
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/demosubmit_Response"
          description: OK
        "201":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/sqlc_App"
          description: Created
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_body: invalid request body

            invalid_language: unsupported outreach language

            invalid_product_brain: product_brain must be a JSON object

            invalid_user_id: invalid user ID

            missing_name: name is required
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            Missing, invalid, expired, or revoked bearer credential.

            unauthorized: not authenticated
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "owner_only: only the workspace owner can create apps"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            simulation_error: failed to build Demo simulation

            internal: failed to create app
        "503":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "workspace_policy_unavailable: workspace policy is unavailable"
      security:
        - bearerAuth: []
      summary: Create apps
      tags:
        - Products
      x-required-input-fields:
        - name
      x-source: backend/internal/handlers/apps.go:221
      x-source-registration: backend/cmd/api/main.go:1608
      x-handler: appsHandler.Create
      parameters:
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
  /api/apps/analyze-text:
    post:
      description: |-
        handles POST /api/apps/analyze-text -- uses Claude to extract app info from a description.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: post_api_apps_analyze_text
      requestBody:
        content:
          application/json:
            schema:
              properties:
                description:
                  type: string
              type: object
              required:
                - description
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                anyOf:
                  - $ref: "#/components/schemas/ai_AppSuggestion"
                  - type: "null"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_body: invalid request body

            missing_description: description is required
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "ai_error: failed to analyze description"
      security:
        - bearerAuth: []
      summary: Analyze Text
      tags:
        - Products
      x-required-input-fields:
        - description
      x-source: backend/internal/handlers/apps.go:754
      x-source-registration: backend/cmd/api/main.go:1614
      x-handler: appsHandler.AnalyzeText
      parameters:
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
  /api/apps/analyze-url:
    post:
      description: |-
        handles POST /api/apps/analyze-url -- uses Claude to analyze a URL.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: post_api_apps_analyze_url
      requestBody:
        content:
          application/json:
            schema:
              properties:
                url:
                  type: string
              type: object
              required:
                - url
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                anyOf:
                  - $ref: "#/components/schemas/ai_AppSuggestion"
                  - type: "null"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_body: invalid request body

            missing_url: url is required
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "ai_error: failed to analyze URL"
      security:
        - bearerAuth: []
      summary: Analyze URL
      tags:
        - Products
      x-required-input-fields:
        - url
      x-source: backend/internal/handlers/apps.go:726
      x-source-registration: backend/cmd/api/main.go:1613
      x-handler: appsHandler.AnalyzeURL
      parameters:
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
  /api/apps/stats:
    get:
      description: |-
        handles GET /api/apps/stats — returns per-app aggregate stats.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: get_api_apps_stats
      responses:
        "200":
          content:
            application/json:
              schema:
                additionalProperties:
                  additionalProperties:
                    type: integer
                  type: object
                type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_user_id: invalid user ID"
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: failed to fetch app stats"
      security:
        - bearerAuth: []
      summary: Stats apps
      tags:
        - Products
      x-source: backend/internal/handlers/apps.go:880
      x-source-registration: backend/cmd/api/main.go:1606
      x-handler: appsHandler.Stats
      parameters:
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
  /api/apps/{appId}/agents:
    get:
      description: |-
        handles GET /api/apps/{appId}/agents.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: get_api_apps_appId_agents
      parameters:
        - in: path
          name: appId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: "#/components/schemas/handlers_agentResponse"
                type: array
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid app ID

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to fetch agent passport

            internal: failed to fetch agents
      security:
        - bearerAuth: []
      summary: List agents
      tags:
        - Discovery agents
      x-source: backend/internal/handlers/agents.go:241
      x-source-registration: backend/cmd/api/main.go:1621
      x-handler: agentsHandler.List
    post:
      description: |-
        handles POST /api/apps/{appId}/agents.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: post_api_apps_appId_agents
      parameters:
        - in: path
          name: appId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                config:
                  description: JSON value stored by the server; shape depends on this resource's configuration.
                max_signals:
                  anyOf:
                    - type: integer
                    - type: "null"
                name:
                  type: string
                type:
                  type: string
              type: object
              required:
                - name
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/demosubmit_Response"
          description: OK
        "201":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/sqlc_Agent"
          description: Created
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_body: invalid request body

            invalid_id: invalid app ID

            invalid_type: type must be 'signal' or 'lookalike'

            invalid_user_id: invalid user ID

            missing_name: name is required
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            Missing, invalid, expired, or revoked bearer credential.

            unauthorized: not authenticated
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_assigned: this app is not assigned to you"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: app not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to create Demo simulation response

            internal: failed to create agent

            internal: failed to fetch app
        "503":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "workspace_policy_unavailable: workspace policy is unavailable"
      security:
        - bearerAuth: []
      summary: Create agents
      tags:
        - Discovery agents
      x-required-input-fields:
        - name
      x-source: backend/internal/handlers/agents.go:367
      x-source-registration: backend/cmd/api/main.go:1622
      x-handler: agentsHandler.Create
  /api/apps/{appId}/campaigns:
    get:
      description: |-
        handles GET /api/apps/{appId}/campaigns.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: get_api_apps_appId_campaigns
      parameters:
        - in: path
          name: appId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: "#/components/schemas/handlers_campaignWithStats"
                type: array
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid app ID

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: failed to fetch campaigns"
      security:
        - bearerAuth: []
      summary: List campaigns
      tags:
        - Campaigns
      x-source: backend/internal/handlers/campaigns.go:558
      x-source-registration: backend/cmd/api/main.go:1701
      x-handler: campaignsHandler.List
    post:
      description: |-
        handles POST /api/apps/{appId}/campaigns.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: post_api_apps_appId_campaigns
      parameters:
        - in: path
          name: appId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                account_assignments:
                  additionalProperties:
                    anyOf:
                      - type: string
                      - type: "null"
                  description: |-
                    AccountAssignments: optional create-time per-platform sender assignment.
                    Used by mixed LinkedIn + email campaigns so the first touch can use
                    LinkedIn while later email steps use a selected mailbox.
                  type: object
                account_id:
                  description: |-
                    AccountID: when provided, the user picks which connected sender account
                    sends. Invite/InMail campaigns expect LinkedIn; email campaigns expect an
                    email account.
                  type: string
                agent_id:
                  type: string
                clone_from_list:
                  description: |-
                    CloneFromList: when true (and ListID is set), the new campaign starts
                    pre-populated with copies of every non-removed lead currently in that
                    list. Used by the "Use existing list" branch in campaign creation so a
                    BYO list can seed multiple campaigns without re-uploading the CSV.
                  type: boolean
                cold_outreach_mode:
                  description: |-
                    ColdOutreachMode: 'invite' (default), 'inmail', or 'email'. Written
                    into campaigns.settings so cloneListIntoCampaign + the engine's lead
                    intake can pick up the cold-touch type without a follow-up PATCH.
                    The signal-first campaign species and its 'x_dm' mode are gone
                    (agent-anchored revision): X discovery is agent source config, and
                    campaign_mode / signal_sources / review_mode in the request body are
                    ignored, so the old species cannot be created.
                  type: string
                creation_idempotency_key:
                  type: string
                list_id:
                  type: string
                message_review_mode:
                  type: string
                name:
                  type: string
                workflow_steps:
                  description: |-
                    WorkflowSteps: optional user-defined sequence. When non-empty, exactly
                    these steps are seeded (one is_final required, >=1 step, valid types
                    per cold_outreach_mode). When empty, the default 4-step seed runs
                    (invite/inmail + 3 messages, or 4 emails, last marked is_final).
                  items:
                    properties:
                      delay_unit:
                        type: string
                      delay_value:
                        type: integer
                      is_final:
                        type: boolean
                      message_mode:
                        type: string
                      message_template:
                        type: string
                      step_type:
                        type: string
                      subject_template:
                        type: string
                    type: object
                  type: array
              type: object
              required:
                - name
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                anyOf:
                  - $ref: "#/components/schemas/demosubmit_Response"
                  - $ref: "#/components/schemas/handlers_createCampaignResponse"
          description: OK
        "201":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/handlers_createCampaignResponse"
          description: Created
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            account_mismatch: account_id must match the primary sender assignment

            account_not_connected: 

            account_not_connected: x sender must have valid OAuth tokens

            agent_app_mismatch: agent does not belong to the selected app

            contact_route_mismatch: list contact route does not match cold_outreach_mode

            invalid_account_id: 

            invalid_account_id: invalid account ID

            invalid_agent_id: invalid agent ID

            invalid_body: invalid request body

            invalid_cold_outreach_mode: cold_outreach_mode must be 'invite', 'inmail', or 'email'

            invalid_creation_idempotency_key: 

            invalid_id: invalid app ID

            invalid_list_id: invalid list ID

            invalid_mode: invalid message review mode

            invalid_platform: 

            invalid_user_id: invalid user ID

            invalid_workflow_steps: 

            list_app_mismatch: list does not belong to the selected app

            missing_list_id: list_id is required when clone_from_list is true

            missing_name: name is required

            platform_mismatch: 
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            Missing, invalid, expired, or revoked bearer credential.

            unauthorized: not authenticated
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_assigned: this app is not assigned to you"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            not_found: 

            not_found: agent not found

            not_found: app not found

            not_found: list not found
        "409":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "idempotency_key_reused: creation_idempotency_key was already used for different campaign intent"
        "422":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "missing_sender_channels: "
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to build Demo simulation

            internal: 

            internal: failed to assign sender account

            internal: failed to auto-assign LinkedIn account

            internal: failed to auto-assign email account

            internal: failed to check campaign retry key

            internal: failed to commit campaign

            internal: failed to count planned campaign actions

            internal: failed to create campaign

            internal: failed to create default lead list

            internal: failed to enroll list contacts

            internal: failed to finalize campaign

            internal: failed to fingerprint campaign request

            internal: failed to inspect list contacts

            internal: failed to load campaign retry

            internal: failed to prepare campaign schedule

            internal: failed to restore campaign enrollment summary

            internal: failed to save message review mode

            internal: failed to seed default signals

            internal: failed to seed workflow steps

            internal: failed to start campaign transaction

            internal: failed to store campaign enrollment summary

            internal: planned campaign action count exceeds server integer range
        "503":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "workspace_policy_unavailable: workspace policy is unavailable"
      security:
        - bearerAuth: []
      summary: Create campaigns
      tags:
        - Campaigns
      x-required-input-fields:
        - name
      x-source: backend/internal/handlers/campaigns.go:745
      x-source-registration: backend/cmd/api/main.go:1703
      x-handler: campaignsHandler.Create
  /api/apps/{appId}/campaigns/preflight:
    post:
      description: |-
        handles POST /api/apps/{appId}/campaigns/preflight.
        It reports every sender channel needed by the selected list and workflow.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: post_api_apps_appId_campaigns_preflight
      parameters:
        - in: path
          name: appId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                account_assignments:
                  additionalProperties:
                    anyOf:
                      - type: string
                      - type: "null"
                  description: |-
                    AccountAssignments: optional create-time per-platform sender assignment.
                    Used by mixed LinkedIn + email campaigns so the first touch can use
                    LinkedIn while later email steps use a selected mailbox.
                  type: object
                account_id:
                  description: |-
                    AccountID: when provided, the user picks which connected sender account
                    sends. Invite/InMail campaigns expect LinkedIn; email campaigns expect an
                    email account.
                  type: string
                agent_id:
                  type: string
                clone_from_list:
                  description: |-
                    CloneFromList: when true (and ListID is set), the new campaign starts
                    pre-populated with copies of every non-removed lead currently in that
                    list. Used by the "Use existing list" branch in campaign creation so a
                    BYO list can seed multiple campaigns without re-uploading the CSV.
                  type: boolean
                cold_outreach_mode:
                  description: |-
                    ColdOutreachMode: 'invite' (default), 'inmail', or 'email'. Written
                    into campaigns.settings so cloneListIntoCampaign + the engine's lead
                    intake can pick up the cold-touch type without a follow-up PATCH.
                    The signal-first campaign species and its 'x_dm' mode are gone
                    (agent-anchored revision): X discovery is agent source config, and
                    campaign_mode / signal_sources / review_mode in the request body are
                    ignored, so the old species cannot be created.
                  type: string
                creation_idempotency_key:
                  type: string
                list_id:
                  type: string
                message_review_mode:
                  type: string
                name:
                  type: string
                workflow_steps:
                  description: |-
                    WorkflowSteps: optional user-defined sequence. When non-empty, exactly
                    these steps are seeded (one is_final required, >=1 step, valid types
                    per cold_outreach_mode). When empty, the default 4-step seed runs
                    (invite/inmail + 3 messages, or 4 emails, last marked is_final).
                  items:
                    properties:
                      delay_unit:
                        type: string
                      delay_value:
                        type: integer
                      is_final:
                        type: boolean
                      message_mode:
                        type: string
                      message_template:
                        type: string
                      step_type:
                        type: string
                      subject_template:
                        type: string
                    type: object
                  type: array
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/handlers_campaignCreationPreflightResponse"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            contact_route_mismatch: list contact route does not match cold_outreach_mode

            invalid_body: invalid request body

            invalid_cold_outreach_mode: cold_outreach_mode must be 'invite', 'inmail', or 'email'

            invalid_id: invalid app ID

            invalid_list_id: invalid list ID

            invalid_user_id: invalid user ID

            invalid_workflow_steps: 

            list_app_mismatch: list does not belong to the selected app

            missing_list_id: list_id is required when clone_from_list is true
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            not_found: app not found

            not_found: list not found
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: failed to inspect list contacts"
      security:
        - bearerAuth: []
      summary: Preflight Create
      tags:
        - Campaigns
      x-source: backend/internal/handlers/campaigns.go:673
      x-source-registration: backend/cmd/api/main.go:1702
      x-handler: campaignsHandler.PreflightCreate
  /api/apps/{appId}/lists:
    get:
      description: |-
        handles GET /api/apps/{appId}/lists

        Uses the effective workspace identity and enforces resource ownership.
      operationId: get_api_apps_appId_lists
      parameters:
        - in: path
          name: appId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: "#/components/schemas/handlers_leadListWithReviewState"
                type: array
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid app ID

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_assigned: this app is not assigned to you"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            db_error: failed to list lead lists

            db_error: failed to load review state
      security:
        - bearerAuth: []
      summary: List By App
      tags:
        - Lead lists
      x-source: backend/internal/handlers/lead_lists.go:36
      x-source-registration: backend/cmd/api/main.go:1659
      x-handler: leadListsHandler.ListByApp
    post:
      description: |-
        handles POST /api/apps/{appId}/lists

        Uses the effective workspace identity and enforces resource ownership.
      operationId: post_api_apps_appId_lists
      parameters:
        - in: path
          name: appId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                description:
                  type: string
                name:
                  type: string
              type: object
              required:
                - name
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/demosubmit_Response"
          description: OK
        "201":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/sqlc_LeadList"
          description: Created
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_body: invalid request body

            invalid_id: invalid app ID

            invalid_list: list name or description is invalid

            invalid_user_id: invalid user ID

            missing_name: name is required
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            Missing, invalid, expired, or revoked bearer credential.

            unauthorized: not authenticated
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_assigned: this app is not assigned to you"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: app not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            simulation_error: failed to build Demo simulation

            db_error: failed to create list
        "503":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "workspace_policy_unavailable: workspace policy is unavailable"
      security:
        - bearerAuth: []
      summary: Create lead lists
      tags:
        - Lead lists
      x-required-input-fields:
        - name
      x-source: backend/internal/handlers/lead_lists.go:119
      x-source-registration: backend/cmd/api/main.go:1660
      x-handler: leadListsHandler.Create
  /api/apps/{appId}/lists/import:
    post:
      description: |-
        handles POST /api/apps/{appId}/lists/import.

        Multipart form fields:
          - file (required): CSV with at minimum linkedin_url or email. Optional
            columns: first_name, last_name, company, headline, already_connected,
            notes.
          - name (required unless list_id is set): becomes the new list's name.
          - list_id (optional): an existing lead list (owned by this user, scoped
            to this app) to append the import into instead of creating a new one.
          - campaign_id (optional): an existing campaign (owned by this user,
            scoped to this app). When set, each row is additionally required to
            carry the field(s) matching the campaign's assigned channel(s) —
            linkedin_url for a linkedin-channel campaign, email for an
            email-channel campaign. Precondition: the target campaign must
            already have at least one campaign_account_assignments row, or the
            whole request is rejected with 422 no_sender_account before the CSV
            file is opened or anything is created.

        No campaign is created by this endpoint itself. Account assignment +
        cold-outreach mode happen at campaign-creation time (BYO campaign on the
        resulting list), or already exist when campaign_id is supplied. Within-list
        dedupe runs against the UNIQUE (list_id, dedup_fingerprint) constraint —
        duplicates are silently skipped and counted. Bad rows (malformed URL/email,
        missing both linkedin_url and email, or missing the campaign-required
        field) land in failures.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: post_api_apps_appId_lists_import
      parameters:
        - in: path
          name: appId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      requestBody:
        content:
          multipart/form-data:
            schema:
              properties:
                campaign_id:
                  type: string
                file:
                  format: binary
                  type: string
                list_id:
                  type: string
                name:
                  type: string
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                anyOf:
                  - $ref: "#/components/schemas/demosubmit_Response"
                  - $ref: "#/components/schemas/handlers_csvImportResponse"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_body: file too large or invalid multipart form

            invalid_campaign_id: invalid campaign ID

            invalid_csv: 

            invalid_id: invalid app ID

            invalid_list_id: invalid list ID

            invalid_user_id: invalid user ID

            missing_file: file field is required

            missing_name: name is required
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_assigned: this app is not assigned to you"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            not_found: app not found

            not_found: campaign not found

            not_found: list not found
        "422":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "no_sender_account: This campaign has no sender account assigned yet. Assign one before importing leads
            directly into it."
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to build Demo simulation

            internal: failed to commit import

            internal: failed to create list

            internal: failed to load campaign assignments

            internal: failed to load campaign workflow

            internal: failed to lock import destination

            internal: failed to start import

            internal: failed to validate import duplicates
      security:
        - bearerAuth: []
      summary: Import
      tags:
        - Lead lists
      x-source: backend/internal/handlers/csv_import.go:91
      x-source-registration: backend/cmd/api/main.go:1676
      x-handler: csvImportHandler.Import
  /api/apps/{appId}/lists/import-drafts/preview:
    post:
      description: Preview people csvimport for this resource.
      operationId: post_api_apps_appId_lists_import_drafts_preview
      parameters:
        - in: path
          name: appId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      requestBody:
        content:
          multipart/form-data:
            schema:
              properties:
                campaign_id:
                  type: string
                file:
                  format: binary
                  type: string
                list_id:
                  type: string
                name:
                  type: string
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                anyOf:
                  - $ref: "#/components/schemas/demosubmit_Response"
                  - $ref: "#/components/schemas/handlers_peopleCSVResponse"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid app ID

            invalid_body: file too large

            invalid_body: file too large or invalid multipart form

            invalid_csv: 

            invalid_csv: CSV has no rows

            invalid_csv: duplicate CSV header

            invalid_csv: malformed CSV row

            invalid_csv: missing CSV header

            invalid_csv: missing linkedin_url or email

            invalid_csv: too many rows

            invalid_destination: provide exactly one of name or list_id and no campaign_id

            invalid_list_id: invalid list ID

            missing_file: file field is required
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_assigned: this app is not assigned to you"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            not_found: app not found

            not_found: list not found
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to check duplicates

            internal: failed to save preview

            internal: failed to stage import

            internal: failed to stage row

            internal: failed to start preview
      security:
        - bearerAuth: []
      summary: Preview people csvimport
      tags:
        - Lead lists
      x-source: backend/internal/handlers/people_csv_import.go:140
      x-source-registration: backend/cmd/api/main.go:1678
      x-handler: peopleCSVImportHandler.Preview
  /api/apps/{appId}/lists/import-drafts/{importId}:
    get:
      description: reloads a single scoped import and its stable row page.
      operationId: get_api_apps_appId_lists_import_drafts_importId
      parameters:
        - in: query
          name: offset
          schema:
            type: string
        - in: query
          name: limit
          schema:
            type: string
        - in: path
          name: appId
          required: true
          schema:
            type: string
        - in: path
          name: importId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/handlers_peopleCSVResponse"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid app ID

            invalid_id: invalid import ID

            invalid_page: invalid limit

            invalid_page: invalid offset
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            unauthorized: not authenticated

            Missing, invalid, expired, or revoked bearer credential.
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_assigned: this app is not assigned to you"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: import not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: failed to reload import"
      security:
        - bearerAuth: []
      summary: Get people csvimport
      tags:
        - Lead lists
      x-source: backend/internal/handlers/people_csv_import.go:402
      x-source-registration: backend/cmd/api/main.go:1679
      x-handler: peopleCSVImportHandler.Get
  /api/apps/{appId}/lists/import-drafts/{importId}/commit:
    post:
      description: locks the immutable draft. A response retry reads saved outcomes.
      operationId: post_api_apps_appId_lists_import_drafts_importId_commit
      parameters:
        - in: path
          name: appId
          required: true
          schema:
            type: string
        - in: path
          name: importId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                campaign_id:
                  type: string
                selection_fingerprint:
                  type: string
              type: object
              required:
                - selection_fingerprint
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/handlers_peopleCSVResponse"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid app ID

            invalid_id: invalid import ID

            invalid_request: selection_fingerprint is required; campaign_id is not accepted
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            unauthorized: not authenticated

            Missing, invalid, expired, or revoked bearer credential.

            Missing, invalid, expired, or revoked bearer credential.
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_assigned: this app is not assigned to you"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            not_found: app not found

            not_found: import not found

            not_found: list not found
        "409":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "stale_selection: selection fingerprint changed"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to check duplicates

            internal: failed to commit import

            internal: failed to complete import

            internal: failed to create list

            internal: failed to import member

            internal: failed to lock destination

            internal: failed to read rows

            internal: failed to record destination

            internal: failed to record row

            internal: failed to reload import

            internal: failed to start import
      security:
        - bearerAuth: []
      summary: Commit
      tags:
        - Lead lists
      x-required-input-fields:
        - selection_fingerprint
      x-source: backend/internal/handlers/people_csv_import.go:441
      x-source-registration: backend/cmd/api/main.go:1680
      x-handler: peopleCSVImportHandler.Commit
  /api/apps/{appId}/specialist-activity:
    get:
      description: Uses the effective workspace identity and enforces resource ownership.
      operationId: get_api_apps_appId_specialist_activity
      parameters:
        - in: query
          name: specialist
          schema:
            type: string
        - in: query
          name: limit
          schema:
            type: string
        - in: path
          name: appId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/handlers_specialistActivityResponse"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid app ID

            invalid_specialist: unknown specialist

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: product not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: failed to load specialist activity"
      security:
        - bearerAuth: []
      summary: List specialist activity
      tags:
        - Products
      x-source: backend/internal/handlers/specialist_activity.go:116
      x-source-registration: backend/cmd/api/main.go:1616
      x-handler: specialistActivityHandler.List
  /api/apps/{appId}/specialist-runs:
    get:
      description: Uses the effective workspace identity and enforces resource ownership.
      operationId: get_api_apps_appId_specialist_runs
      parameters:
        - in: query
          name: specialist
          schema:
            type: string
        - in: query
          name: limit
          schema:
            type: string
        - in: path
          name: appId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/handlers_specialistRunsResponse"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid app ID

            invalid_specialist: unknown specialist

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: product not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to load specialist run steps

            internal: failed to load specialist runs
      security:
        - bearerAuth: []
      summary: List Runs
      tags:
        - Products
      x-source: backend/internal/handlers/specialist_activity.go:157
      x-source-registration: backend/cmd/api/main.go:1617
      x-handler: specialistActivityHandler.ListRuns
  /api/apps/{appId}/specialist-runs/{runId}:
    get:
      description: Uses the effective workspace identity and enforces resource ownership.
      operationId: get_api_apps_appId_specialist_runs_runId
      parameters:
        - in: path
          name: appId
          required: true
          schema:
            type: string
        - in: path
          name: runId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/handlers_specialistRunDetailResponse"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid app ID

            invalid_run_id: invalid run ID

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            not_found: product not found

            not_found: specialist run not found
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: failed to load specialist run steps"
      security:
        - bearerAuth: []
      summary: Get Run
      tags:
        - Products
      x-source: backend/internal/handlers/specialist_activity.go:210
      x-source-registration: backend/cmd/api/main.go:1618
      x-handler: specialistActivityHandler.GetRun
  /api/apps/{id}:
    get:
      description: |-
        handles GET /api/apps/{id} -- returns a single app by ID for the authenticated user.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: get_api_apps_id
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/handlers_appResponse"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid app ID

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_assigned: this app is not assigned to you"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: app not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: failed to fetch agent passport"
      security:
        - bearerAuth: []
      summary: Get apps
      tags:
        - Products
      x-source: backend/internal/handlers/apps.go:341
      x-source-registration: backend/cmd/api/main.go:1609
      x-handler: appsHandler.Get
    put:
      description: |-
        handles PUT /api/apps/{id} -- updates app fields for the authenticated user.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: put_api_apps_id
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                description:
                  anyOf:
                    - type: string
                    - type: "null"
                icp_description:
                  anyOf:
                    - type: string
                    - type: "null"
                icp_filters:
                  description: JSON value stored by the server; shape depends on this resource's configuration.
                language:
                  anyOf:
                    - type: string
                    - type: "null"
                logo_url:
                  anyOf:
                    - type: string
                    - type: "null"
                name:
                  anyOf:
                    - type: string
                    - type: "null"
                product_brain:
                  description: JSON value stored by the server; shape depends on this resource's configuration.
                propagate_icp_to_agent_ids:
                  items:
                    type: string
                  type: array
                url:
                  anyOf:
                    - type: string
                    - type: "null"
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                anyOf:
                  - anyOf:
                      - $ref: "#/components/schemas/demosubmit_Response"
                      - $ref: "#/components/schemas/sqlc_App"
                  - $ref: "#/components/schemas/sqlc_App"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_agent_selection: 

            invalid_agent_selection: icp_filters is required when updating selected agents

            invalid_agent_selection: selected agent ID is invalid

            invalid_body: invalid request body

            invalid_id: invalid app ID

            invalid_language: unsupported outreach language

            invalid_product_brain: product_brain must be a JSON object

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            Missing, invalid, expired, or revoked bearer credential.

            unauthorized: not authenticated
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_assigned: this app is not assigned to you"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            not_found: app not found

            not_found: app not found or update failed
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            simulation_error: failed to build Demo simulation

            internal: agent propagation is unavailable

            internal: failed to begin app update

            internal: failed to commit app update

            internal: failed to load agents

            internal: failed to update selected agents
        "503":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "workspace_policy_unavailable: workspace policy is unavailable"
      security:
        - bearerAuth: []
      summary: Update apps
      tags:
        - Products
      x-source: backend/internal/handlers/apps.go:435
      x-source-registration: backend/cmd/api/main.go:1610
      x-handler: appsHandler.Update
  /api/apps/{id}/generate-icp:
    post:
      description: |-
        handles POST /api/apps/{id}/generate-icp -- uses Claude to generate ICP.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: post_api_apps_id_generate_icp
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                description:
                  type: string
              type: object
              required:
                - description
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                anyOf:
                  - $ref: "#/components/schemas/ai_ICPSuggestion"
                  - type: "null"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_body: invalid request body

            missing_description: description is required
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "ai_error: failed to generate ICP"
      security:
        - bearerAuth: []
      summary: Generate ICP
      tags:
        - Products
      x-required-input-fields:
        - description
      x-source: backend/internal/handlers/apps.go:782
      x-source-registration: backend/cmd/api/main.go:1615
      x-handler: appsHandler.GenerateICP
  /api/auth/login:
    post:
      description: |-
        handles POST /api/auth/login.
        Validates credentials and returns JWT tokens.

        Rate limit: 5 requests per minute, shared with routes in this middleware group, keyed by client IP.
      operationId: post_api_auth_login
      requestBody:
        content:
          application/json:
            schema:
              properties:
                cadence:
                  description: |-
                    Cadence — passed from the marketing-site pricing page via
                    ?cadence=monthly|quarterly|annual on the signup URL. Empty / unknown
                    values fall back to monthly in resolveCadence.
                  type: string
                email:
                  type: string
                first_name:
                  description: |-
                    Phase 16: SPEC Req 13 — first/last name required at password signup,
                    optional in login (login won't read them). Empty strings on login
                    requests are ignored; signup validates non-empty.
                  type: string
                last_name:
                  type: string
                password:
                  type: string
                utm_campaign:
                  type: string
                utm_content:
                  type: string
                utm_medium:
                  type: string
                utm_source:
                  type: string
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/handlers_authResponse"
          description: OK
          headers:
            Set-Cookie:
              description: Sets the refresh_token cookie with HttpOnly, SameSite=Lax, and Secure outside local development.
              schema:
                type: string
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_body: invalid request body"
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_credentials: invalid credentials"
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "demo_workspace_login_disabled: Demo workspace login is disabled"
        "429":
          description: Rate limit exceeded. Wait before retrying.
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: failed to generate tokens"
        "503":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "workspace_policy_unavailable: workspace policy is unavailable"
      security: []
      summary: Login
      tags:
        - Auth
      x-source: backend/internal/handlers/auth.go:388
      x-source-registration: backend/cmd/api/main.go:1498
      x-handler: authHandler.Login
  /api/auth/logout:
    post:
      description: |-
        handles POST /api/auth/logout.
        Clears the refresh token cookie.
      operationId: post_api_auth_logout
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  message:
                    type: string
                type: object
          description: OK
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: Missing, invalid, expired, or revoked bearer credential.
      security:
        - bearerAuth: []
      summary: Logout
      tags:
        - Auth
      x-source: backend/internal/handlers/auth.go:510
      x-source-registration: backend/cmd/api/main.go:1539
      x-handler: authHandler.Logout
      parameters:
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
  /api/auth/oauth/{provider}/start:
    get:
      description: |-
        handles GET /api/auth/oauth/{provider}/start.
        Sets the oauth_state cookie + 302s to the provider AuthCodeURL.
      operationId: get_api_auth_oauth_provider_start
      parameters:
        - in: query
          name: cadence
          schema:
            type: string
        - in: query
          name: utm_source
          schema:
            type: string
        - in: query
          name: utm_medium
          schema:
            type: string
        - in: query
          name: utm_campaign
          schema:
            type: string
        - in: query
          name: utm_content
          schema:
            type: string
        - in: query
          name: tracekit_visitor_id
          schema:
            type: string
        - in: query
          name: tracekit_session_id
          schema:
            type: string
        - in: path
          name: provider
          required: true
          schema:
            type: string
      responses:
        "302":
          description: Redirect to the resource URL.
          headers:
            Location:
              schema:
                format: uri
                type: string
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: failed to sign oauth state"
        "503":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "oauth_provider_unavailable: OAuth provider not configured"
      security: []
      summary: Start
      tags:
        - Auth
      x-source: backend/internal/handlers/oauth.go:191
      x-source-registration: backend/cmd/api/main.go:1472
      x-handler: oauthHandler.Start
  /api/auth/refresh:
    post:
      description: |-
        handles POST /api/auth/refresh.
        Validates the refresh token cookie and returns a new token pair.

        Rate limit: 30 requests per minute, shared with routes in this middleware group, keyed by client IP.
      operationId: post_api_auth_refresh
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  access_token:
                    type: string
                  user:
                    properties:
                      email:
                        type: string
                      id:
                        type: string
                    type: object
                type: object
          description: OK
          headers:
            Set-Cookie:
              description: Sets the refresh_token cookie with HttpOnly, SameSite=Lax, and Secure outside local development.
              schema:
                type: string
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_refresh_token: invalid or expired refresh token

            no_refresh_token: no refresh token provided

            user_not_found: user no longer exists
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "demo_workspace_login_disabled: Demo workspace login is disabled"
        "429":
          description: Rate limit exceeded. Wait before retrying.
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: failed to generate tokens"
        "503":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "workspace_policy_unavailable: workspace policy is unavailable"
      security:
        - refreshCookie: []
      summary: Refresh
      tags:
        - Auth
      x-source: backend/internal/handlers/auth.go:450
      x-source-registration: backend/cmd/api/main.go:1502
      x-handler: authHandler.Refresh
  /api/auth/signup:
    post:
      description: |-
        handles POST /api/auth/signup.
        Creates a new user with bcrypt-hashed password and returns JWT tokens.

        Phase 16 changes (plan 16-04):
          - SPEC Req 13: first_name + last_name required + persisted
          - SPEC Req 14: free-provider domains rejected with 403 free_email_blocked
            UNLESS the email is in allowed_emails (beta override)
          - SPEC Req 3: beta_grandfather flag is set to IsEmailAllowed(email) at
            creation time and re-checked on every Login + Refresh + OAuth callback
          - SPEC Req 4: response includes checkout_url (placeholder; plan 16-05
            wires real Stripe URL via billing.LockCohortAndCreateCheckout)

        Rate limit: 5 requests per minute, shared with routes in this middleware group, keyed by client IP.
      operationId: post_api_auth_signup
      requestBody:
        content:
          application/json:
            schema:
              properties:
                cadence:
                  description: |-
                    Cadence — passed from the marketing-site pricing page via
                    ?cadence=monthly|quarterly|annual on the signup URL. Empty / unknown
                    values fall back to monthly in resolveCadence.
                  type: string
                email:
                  type: string
                first_name:
                  description: |-
                    Phase 16: SPEC Req 13 — first/last name required at password signup,
                    optional in login (login won't read them). Empty strings on login
                    requests are ignored; signup validates non-empty.
                  type: string
                last_name:
                  type: string
                password:
                  type: string
                utm_campaign:
                  type: string
                utm_content:
                  type: string
                utm_medium:
                  type: string
                utm_source:
                  type: string
              type: object
        required: true
      responses:
        "201":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/handlers_authResponse"
          description: Created
          headers:
            Set-Cookie:
              description: Sets the refresh_token cookie with HttpOnly, SameSite=Lax, and Secure outside local development.
              schema:
                type: string
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_body: invalid request body

            invalid_email: invalid email address

            missing_name: first and last name are required

            weak_password: password must be at least 8 characters
        "409":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "signup_failed: could not create account"
        "429":
          description: Rate limit exceeded. Wait before retrying.
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to generate tokens

            internal: failed to process password
      security: []
      summary: Signup
      tags:
        - Auth
      x-source: backend/internal/handlers/auth.go:198
      x-source-registration: backend/cmd/api/main.go:1497
      x-handler: authHandler.Signup
  /api/billing/beta-rewards:
    get:
      description: |-
        handles GET /api/billing/beta-rewards. Returns the per-user
        status of all reward events (earned/not, granted_at, credits per event)
        plus the static label/credits map. Frontend hides the card when
        eligible=false (non-beta users).
      operationId: get_api_billing_beta_rewards
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/billing_BetaRewardStatus"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_user_id: invalid user ID"
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            Missing, invalid, expired, or revoked bearer credential.

            unauthorized: not authenticated
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "billing_admin_required: this workspace's billing is owner/admin-only. Switch to your own workspace or ask
            the owner to grant you the Manage Billing capability."
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "user_not_found: user not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: failed to load beta rewards"
      security:
        - bearerAuth: []
      summary: Beta Rewards
      tags:
        - Account usage
      x-source: backend/internal/handlers/billing.go:1228
      x-source-registration: backend/cmd/api/main.go:1882
      x-handler: billingHandler.BetaRewards
      parameters:
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
  /api/billing/catalog:
    get:
      description: |-
        handles GET /api/billing/catalog. The catalog is safe for all
        authenticated workspace members; only the purchase capability is gated.
      operationId: get_api_billing_catalog
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/handlers_creditCatalogResponse"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_user_id: invalid user ID"
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "user_not_found: user not found"
      security:
        - bearerAuth: []
      summary: Credit Catalog
      tags:
        - Account usage
      x-source: backend/internal/handlers/billing.go:95
      x-source-registration: backend/cmd/api/main.go:1881
      x-handler: billingHandler.CreditCatalog
      parameters:
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
  /api/billing/current-plan:
    get:
      description: Get the current plan, credit balances, capacity, usage, subscription state, and next billing date. Requires
        the workspace owner or the Manage Billing capability when acting for another workspace. Other members can use
        /api/me/entitlement. This read does not change the subscription. Use the Funkel AI app for payments and
        subscription changes.
      operationId: get_api_billing_current_plan
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/handlers_currentPlanResponse"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_user_id: invalid user ID"
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            Missing, invalid, expired, or revoked bearer credential.

            unauthorized: not authenticated

            Missing, invalid, expired, or revoked bearer credential.
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: >-
            billing_admin_required: this workspace's billing is owner/admin-only. Switch to your own workspace or ask
            the owner to grant you the Manage Billing capability.


            demo_external_read_blocked: external reads are unavailable in the Demo workspace
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "user_not_found: user not found"
        "503":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "workspace_policy_unavailable: workspace policy is unavailable"
      security:
        - bearerAuth: []
      summary: Current Plan
      tags:
        - Account usage
      x-source: backend/internal/handlers/billing.go:234
      x-source-registration: backend/cmd/api/main.go:1880
      x-handler: billingHandler.CurrentPlan
      parameters:
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
  /api/booking-provider-events/unmatched:
    get:
      description: List Unmatched Events for this resource.
      operationId: get_api_booking_provider_events_unmatched
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/handlers_unmatchedBookingEventsResponse"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_user_id: invalid user ID"
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "402":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "calendar_sync_upgrade_required: calendar sync is included in Growth"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: failed to fetch unmatched bookings"
        "503":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "calendar_sync_disabled: calendar sync is not enabled"
      security:
        - bearerAuth: []
      summary: List Unmatched Events
      tags:
        - Booking Provider Events
      x-source: backend/internal/handlers/booking_provider_events.go:158
      x-source-registration: backend/cmd/api/main.go:1844
      x-handler: bookingProvidersHandler.ListUnmatchedEvents
      parameters:
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
  /api/booking-provider-events/{id}/attach:
    post:
      description: Attach Provider Event for this resource.
      operationId: post_api_booking_provider_events_id_attach
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                lead_id:
                  type: string
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  meeting:
                    $ref: "#/components/schemas/handlers_meetingResponse"
                type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_user_id: invalid user ID

            invalid_event_id: invalid booking event ID

            invalid_body: invalid request body

            invalid_lead_id: invalid lead ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            unauthorized: not authenticated

            Missing, invalid, expired, or revoked bearer credential.

            Missing, invalid, expired, or revoked bearer credential.
        "402":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "calendar_sync_upgrade_required: calendar sync is included in Growth"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: meeting not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to load meeting

            internal: failed to attach booking
        "503":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "calendar_sync_disabled: calendar sync is not enabled"
      security:
        - bearerAuth: []
      summary: Attach Provider Event
      tags:
        - Booking Provider Events
      x-source: backend/internal/handlers/booking_provider_events.go:186
      x-source-registration: backend/cmd/api/main.go:1845
      x-handler: bookingProvidersHandler.AttachProviderEvent
  /api/booking-provider-events/{id}/dismiss:
    post:
      description: Dismiss Provider Event for this resource.
      operationId: post_api_booking_provider_events_id_dismiss
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "204":
          description: No content.
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_user_id: invalid user ID

            invalid_event_id: invalid booking event ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            unauthorized: not authenticated

            Missing, invalid, expired, or revoked bearer credential.
        "402":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "calendar_sync_upgrade_required: calendar sync is included in Growth"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: meeting not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to load meeting

            internal: failed to dismiss booking
        "503":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "calendar_sync_disabled: calendar sync is not enabled"
      security:
        - bearerAuth: []
      summary: Dismiss Provider Event
      tags:
        - Booking Provider Events
      x-source: backend/internal/handlers/booking_provider_events.go:235
      x-source-registration: backend/cmd/api/main.go:1846
      x-handler: bookingProvidersHandler.DismissProviderEvent
  /api/booking-providers:
    get:
      description: List booking providers for this resource.
      operationId: get_api_booking_providers
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/handlers_bookingProvidersResponse"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_user_id: invalid user ID"
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: failed to fetch booking providers"
      security:
        - bearerAuth: []
      summary: List booking providers
      tags:
        - Booking Providers
      x-source: backend/internal/handlers/booking_providers.go:186
      x-source-registration: backend/cmd/api/main.go:1837
      x-handler: bookingProvidersHandler.List
      parameters:
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
  /api/booking-providers/calcom/api-key:
    post:
      description: Connect Calcom APIKey for this resource.
      operationId: post_api_booking_providers_calcom_api_key
      requestBody:
        content:
          application/json:
            schema:
              properties:
                api_base_url:
                  type: string
                api_key:
                  type: string
                force_reconnect:
                  type: boolean
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  account:
                    $ref: "#/components/schemas/handlers_bookingProviderAccountDTO"
                type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_user_id: invalid user ID

            invalid_api_base_url: Cal API host is not supported

            invalid_body: invalid request body

            missing_api_key: Cal.com API key is required

            provider_verification_failed: Cal API key could not be verified
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            unauthorized: not authenticated

            Missing, invalid, expired, or revoked bearer credential.
        "402":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "calendar_sync_upgrade_required: calendar sync is included in Growth"
        "409":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "already_connected: booking provider is already connected"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to check booking provider connection

            encryption_not_configured: integration encryption is not configured

            internal: failed to store Cal.com account
        "503":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "calendar_sync_disabled: calendar sync is not enabled"
      security:
        - bearerAuth: []
      summary: Connect Calcom APIKey
      tags:
        - Booking Providers
      x-source: backend/internal/handlers/booking_providers.go:338
      x-source-registration: backend/cmd/api/main.go:1838
      x-handler: bookingProvidersHandler.ConnectCalcomAPIKey
      parameters:
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
  /api/booking-providers/{id}:
    delete:
      description: Disconnect for this resource.
      operationId: delete_api_booking_providers_id
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  account:
                    $ref: "#/components/schemas/handlers_bookingProviderAccountDTO"
                type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_user_id: invalid user ID

            invalid_account_id: invalid provider account ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            unauthorized: not authenticated

            Missing, invalid, expired, or revoked bearer credential.
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: booking provider account not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to fetch booking provider account

            internal: failed to disconnect booking provider
      security:
        - bearerAuth: []
      summary: Disconnect
      tags:
        - Booking Providers
      x-source: backend/internal/handlers/booking_providers.go:406
      x-source-registration: backend/cmd/api/main.go:1843
      x-handler: bookingProvidersHandler.Disconnect
  /api/booking-providers/{id}/backfill:
    post:
      description: Backfill Provider Bookings for this resource.
      operationId: post_api_booking_providers_id_backfill
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/handlers_bookingProviderBackfillResult"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_user_id: invalid user ID

            invalid_account_id: invalid provider account ID

            backfill_failed: booking provider backfill failed
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            unauthorized: not authenticated

            Missing, invalid, expired, or revoked bearer credential.
        "402":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "calendar_sync_upgrade_required: calendar sync is included in Growth"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: booking provider account not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: failed to fetch booking provider account"
        "503":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "calendar_sync_disabled: calendar sync is not enabled"
      security:
        - bearerAuth: []
      summary: Backfill Provider Bookings
      tags:
        - Booking Providers
      x-source: backend/internal/handlers/booking_provider_events.go:253
      x-source-registration: backend/cmd/api/main.go:1841
      x-handler: bookingProvidersHandler.BackfillProviderBookings
  /api/booking-providers/{id}/verify:
    post:
      description: Verify for this resource.
      operationId: post_api_booking_providers_id_verify
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  account:
                    $ref: "#/components/schemas/handlers_bookingProviderAccountDTO"
                type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_user_id: invalid user ID

            invalid_account_id: invalid provider account ID

            : booking provider could not be verified
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            unauthorized: not authenticated

            Missing, invalid, expired, or revoked bearer credential.
        "402":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "calendar_sync_upgrade_required: calendar sync is included in Growth"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: booking provider account not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to fetch booking provider account

            internal: failed to update booking provider
        "503":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "calendar_sync_disabled: calendar sync is not enabled"
      security:
        - bearerAuth: []
      summary: Verify
      tags:
        - Booking Providers
      x-source: backend/internal/handlers/booking_providers.go:434
      x-source-registration: backend/cmd/api/main.go:1842
      x-handler: bookingProvidersHandler.Verify
  /api/booking-providers/{id}/webhook/register:
    post:
      description: Register Webhook for this resource.
      operationId: post_api_booking_providers_id_webhook_register
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  account:
                    $ref: "#/components/schemas/handlers_bookingProviderAccountDTO"
                type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_user_id: invalid user ID

            invalid_account_id: invalid provider account ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            unauthorized: not authenticated

            Missing, invalid, expired, or revoked bearer credential.
        "402":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "calendar_sync_upgrade_required: calendar sync is included in Growth"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: booking provider account not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: failed to fetch booking provider account"
        "503":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "calendar_sync_disabled: calendar sync is not enabled"
        default:
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: ": booking provider webhook could not be registered"
      security:
        - bearerAuth: []
      summary: Register Webhook
      tags:
        - Booking Providers
      x-source: backend/internal/handlers/booking_providers.go:464
      x-source-registration: backend/cmd/api/main.go:1840
      x-handler: bookingProvidersHandler.RegisterWebhook
  /api/booking-providers/{provider}/oauth/start:
    post:
      description: Start OAuth for this resource.
      operationId: post_api_booking_providers_provider_oauth_start
      parameters:
        - in: query
          name: force_reconnect
          schema:
            type: string
        - in: path
          name: provider
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/handlers_bookingProviderOAuthStartResponse"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_user_id: invalid user ID

            invalid_provider: unsupported booking provider
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            unauthorized: not authenticated

            Missing, invalid, expired, or revoked bearer credential.
        "402":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "calendar_sync_upgrade_required: calendar sync is included in Growth"
        "409":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "already_connected: booking provider is already connected"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to check booking provider connection

            encryption_not_configured: integration encryption is not configured

            internal: failed to create oauth state

            internal: failed to create oauth verifier
        "503":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            calendar_sync_disabled: calendar sync is not enabled

            provider_not_configured: booking provider OAuth is not configured
      security:
        - bearerAuth: []
      summary: Start OAuth
      tags:
        - Booking Providers
      x-source: backend/internal/handlers/booking_providers.go:222
      x-source-registration: backend/cmd/api/main.go:1839
      x-handler: bookingProvidersHandler.StartOAuth
  /api/campaigns/{campaignId}/enroll-list-contacts:
    post:
      description: |-
        handles selected members through a durable receipt.
        Every eligible lead and its item commit in the same per-member transaction.
        Callers without an operation ID receive a fresh receipt for compatibility.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: post_api_campaigns_campaignId_enroll_list_contacts
      parameters:
        - in: path
          name: campaignId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                member_ids:
                  items:
                    type: string
                  type: array
                operation_id:
                  type: string
              type: object
              required:
                - member_ids
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/handlers_enrollListContactsResponse"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_body: member_ids is required

            invalid_id: invalid campaign ID

            invalid_operation_id: operation_id must be a UUID

            invalid_user_id: invalid user ID

            too_many_members: member_ids exceeds 500
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            Missing, invalid, expired, or revoked bearer credential.

            unauthorized: not authenticated
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_assigned: this app is not assigned to you"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: campaign not found"
        "409":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "operation_conflict: operation_id has a different selection"
        "422":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "no_sender_account: This campaign has no sender account assigned yet. Assign one before importing leads
            directly into it."
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: db pool not available

            simulation_error: failed to build Demo simulation

            internal: failed to complete receipt

            internal: failed to create receipt

            internal: failed to fetch campaign-bound leads

            internal: failed to fetch list members

            internal: failed to load receipt

            internal: failed to renew receipt

            internal: failed to save receipt

            internal: failed to save receipt item

            internal: failed to load campaign assignments

            internal: failed to validate list members
        "503":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "workspace_policy_unavailable: workspace policy is unavailable"
      security:
        - bearerAuth: []
      summary: Enroll List Contacts
      tags:
        - Campaigns
      x-required-input-fields:
        - member_ids
      x-source: backend/internal/handlers/scheduled_actions.go:1327
      x-source-registration: backend/cmd/api/main.go:1777
      x-handler: scheduledActionsHandler.EnrollListContacts
  /api/campaigns/{campaignId}/enroll-list-contacts/operations/{operationId}:
    get:
      description: Uses the effective workspace identity and enforces resource ownership.
      operationId: get_api_campaigns_campaignId_enroll_list_contacts_operations_operationId
      parameters:
        - in: path
          name: campaignId
          required: true
          schema:
            type: string
        - in: path
          name: operationId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/handlers_enrollListContactsResponse"
          description: OK
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: receipt not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: failed to load receipt"
      security:
        - bearerAuth: []
      summary: Get Selected Enrollment Receipt
      tags:
        - Campaigns
      x-source: backend/internal/handlers/scheduled_actions.go:1840
      x-source-registration: backend/cmd/api/main.go:1779
      x-handler: scheduledActionsHandler.GetSelectedEnrollmentReceipt
  /api/campaigns/{campaignId}/enroll-list-contacts/receipts/{receiptId}:
    get:
      description: Uses the effective workspace identity and enforces resource ownership.
      operationId: get_api_campaigns_campaignId_enroll_list_contacts_receipts_receiptId
      parameters:
        - in: path
          name: campaignId
          required: true
          schema:
            type: string
        - in: path
          name: receiptId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/handlers_enrollListContactsResponse"
          description: OK
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: receipt not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: failed to load receipt"
      security:
        - bearerAuth: []
      summary: Get Selected Enrollment Receipt
      tags:
        - Campaigns
      x-source: backend/internal/handlers/scheduled_actions.go:1840
      x-source-registration: backend/cmd/api/main.go:1778
      x-handler: scheduledActionsHandler.GetSelectedEnrollmentReceipt
  /api/campaigns/{campaignId}/in-flight-impact:
    get:
      description: |-
        handles GET /api/campaigns/{campaignId}/in-flight-impact.

        Drives the cascade-impact confirmation modal in the editor: returns a
        campaign-wide pending+executing total plus a per-step three-bucket breakdown
        scoped to active (non-soft-deleted) steps. Bulk-flip sums message-step
        pending, delay edits use campaign_pending, per-step Save pulls its own row.
      operationId: get_api_campaigns_campaignId_in_flight_impact
      parameters:
        - in: path
          name: campaignId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  campaign_executing:
                    type: integer
                  campaign_pending:
                    type: integer
                  per_step:
                    items:
                      properties:
                        executing:
                          type: integer
                        history:
                          type: integer
                        pending:
                          type: integer
                        step_id:
                          type: string
                        step_type:
                          type: string
                      required:
                        - step_id
                        - step_type
                        - pending
                        - executing
                        - history
                      type: object
                    type: array
                type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid campaign ID

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_assigned: this app is not assigned to you"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: campaign not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: failed to count actions"
      security:
        - bearerAuth: []
      summary: In Flight Impact
      tags:
        - Campaigns
      x-source: backend/internal/handlers/workflow_steps.go:721
      x-source-registration: backend/cmd/api/main.go:1741
      x-handler: workflowStepsHandler.InFlightImpact
  /api/campaigns/{campaignId}/leads:
    post:
      description: |-
        handles POST /api/campaigns/{campaignId}/leads.
        Adds a lead to the campaign and pre-schedules all workflow steps per D-11.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: post_api_campaigns_campaignId_leads
      parameters:
        - in: path
          name: campaignId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                company:
                  type: string
                first_name:
                  type: string
                headline:
                  type: string
                last_name:
                  type: string
                metadata:
                  description: JSON value stored by the server; shape depends on this resource's configuration.
                platform_account_id:
                  type: string
                profile_picture_url:
                  type: string
                profile_url:
                  type: string
                provider_id:
                  type: string
              type: object
              required:
                - provider_id
        required: true
      responses:
        "201":
          content:
            application/json:
              schema:
                anyOf:
                  - $ref: "#/components/schemas/sqlc_Lead"
                  - type: "null"
          description: Created
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            country_restricted: 

            invalid_body: invalid request body

            invalid_id: invalid campaign ID

            invalid_id: invalid platform_account_id

            invalid_user_id: invalid user ID

            missing_provider_id: provider_id is required
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            Missing, invalid, expired, or revoked bearer credential.

            unauthorized: not authenticated
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: campaign not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: "
      security:
        - bearerAuth: []
      summary: Add Lead
      tags:
        - Campaigns
      x-required-input-fields:
        - provider_id
      x-source: backend/internal/handlers/scheduled_actions.go:1216
      x-source-registration: backend/cmd/api/main.go:1775
      x-handler: scheduledActionsHandler.AddLead
  /api/campaigns/{campaignId}/leads/{leadId}:
    delete:
      description: |-
        handles DELETE /api/campaigns/{campaignId}/leads/{leadId}.
        Per D-02: cancels ALL pending actions for that lead immediately.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: delete_api_campaigns_campaignId_leads_leadId
      parameters:
        - in: path
          name: campaignId
          required: true
          schema:
            type: string
        - in: path
          name: leadId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  cancelled_count:
                    type: integer
                type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid campaign ID

            invalid_id: invalid lead ID

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: campaign not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to cancel pending actions

            internal: failed to count cancelled actions

            internal: failed to remove lead
      security:
        - bearerAuth: []
      summary: Remove Lead
      tags:
        - Campaigns
      x-source: backend/internal/handlers/scheduled_actions.go:869
      x-source-registration: backend/cmd/api/main.go:1776
      x-handler: scheduledActionsHandler.RemoveLead
  /api/campaigns/{campaignId}/message-review:
    get:
      description: List message review for this resource.
      operationId: get_api_campaigns_campaignId_message_review
      parameters:
        - in: query
          name: page
          schema:
            type: string
        - in: query
          name: page_size
          schema:
            type: string
        - in: query
          name: step_id
          schema:
            type: string
        - in: query
          name: status
          schema:
            type: string
        - in: query
          name: channel
          schema:
            type: string
        - in: path
          name: campaignId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  eligible_people:
                    type: integer
                  examples:
                    items:
                      additionalProperties:
                        description: Arbitrary JSON value.
                      type: object
                    type: array
                  items:
                    items:
                      additionalProperties:
                        description: Arbitrary JSON value.
                      type: object
                    type: array
                  mode:
                    type: string
                  page:
                    type: integer
                  page_size:
                    type: integer
                  revision:
                    type: integer
                  status_counts:
                    properties:
                      approved:
                        type: integer
                      changed:
                        type: integer
                      failed:
                        type: integer
                      needs_review:
                        type: integer
                    type: object
                  total_items:
                    type: integer
                type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_campaign: invalid campaign ID

            invalid_channel: invalid review channel

            invalid_page: page must be a positive number

            invalid_page_size: page_size must be between 1 and 50

            invalid_status: invalid review status

            invalid_step: invalid workflow step ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: campaign not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to count people for review

            internal: failed to load examples

            internal: failed to load message review

            internal: failed to prepare message review

            internal: failed to read examples

            internal: failed to read message review
      security:
        - bearerAuth: []
      summary: List message review
      tags:
        - Message review
      x-source: backend/internal/handlers/campaign_message_review.go:102
      x-source-registration: backend/cmd/api/main.go:1721
      x-handler: messageReviewHandler.List
  /api/campaigns/{campaignId}/message-review/drafts/generate:
    post:
      description: Generate or revise campaign messages for the selected records. The server reserves credits, settles
        completed generation, and releases unused credits. A repeated request key returns the existing operation; a
        different selection returns 409. A still-running operation returns 202. Inspect settled_credits,
        released_credits, and maximum_credits in the response.
      operationId: post_api_campaigns_campaignId_message_review_drafts_generate
      parameters:
        - in: path
          name: campaignId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                idempotency_key:
                  type: string
                  minLength: 8
                  maxLength: 200
                  description: Reuse only for the same selection. Replays return the stored result. Use a new key after
                    generation_expired.
                revision_instruction:
                  type: string
                selected_ids:
                  items:
                    type: string
                  type: array
                  minItems: 1
                  maxItems: 8
                selection_fingerprint:
                  type: string
                step_id:
                  type: string
              type: object
              required:
                - idempotency_key
                - selection_fingerprint
                - selected_ids
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                anyOf:
                  - properties:
                      maximum_credits:
                        type: integer
                      released_credits:
                        type: integer
                      replayed:
                        type: boolean
                      results:
                        description: JSON value stored by the server; shape depends on this resource's configuration.
                      settled_credits:
                        type: integer
                    type: object
                  - properties:
                      maximum_credits:
                        type: integer
                      released_credits:
                        type: integer
                      results:
                        items:
                          $ref: "#/components/schemas/handlers_reviewGenerationResult"
                        type: array
                      settled_credits:
                        type: integer
                      wallet_balance:
                        type: integer
                    type: object
          description: OK
        "202":
          content:
            application/json:
              schema:
                properties:
                  retry_after_seconds:
                    type: integer
                  status:
                    type: string
                type: object
          description: Accepted
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_campaign: invalid campaign ID

            invalid_request: invalid request body

            invalid_request: select messages and provide a request key

            invalid_selection: select 3 or 4 contacts
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            unauthorized: not authenticated

            Missing, invalid, expired, or revoked bearer credential.

            Missing, invalid, expired, or revoked bearer credential.
        "402":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "insufficient_credits: not enough credits for selected messages"
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "demo_no_effect: generation is unavailable in Demo workspace"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            not_found: campaign not found

            not_found: message selection not found
        "409":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            generation_expired: generation expired; retry with a new request key

            request_key_reused: request key has a different selection

            stale_quote: get a new credit quote
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to check generation request

            internal: failed to finish generation

            internal: failed to gate selected actions

            internal: failed to gate selected messages

            internal: failed to release expired generation

            internal: failed to replay generation

            internal: failed to reserve credits

            internal: failed to reserve generation credits

            internal: failed to save generation result

            internal: failed to settle generation credits

            internal: failed to start generation

            internal: generation saved no output; retry this request key
        "503":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "workspace_policy_unavailable: workspace policy is unavailable"
      security:
        - bearerAuth: []
      summary: Generate Drafts
      tags:
        - Message review
      x-required-input-fields:
        - selected_ids
      x-source: backend/internal/handlers/campaign_message_review.go:967
      x-source-registration: backend/cmd/api/main.go:1725
      x-handler: messageReviewHandler.GenerateDrafts
  /api/campaigns/{campaignId}/message-review/drafts/{itemID}/approve:
    post:
      description: Approve Draft for this resource.
      operationId: post_api_campaigns_campaignId_message_review_drafts_itemID_approve
      parameters:
        - in: path
          name: campaignId
          required: true
          schema:
            type: string
        - in: path
          name: itemID
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                body:
                  type: string
                context_fingerprint:
                  type: string
                expected_version_id:
                  type: string
                subject:
                  type: string
              type: object
              required:
                - context_fingerprint
        required: true
      responses:
        "204":
          description: No content.
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_campaign: invalid campaign ID

            invalid_request: invalid request body

            invalid_message: message text is required

            invalid_approval: version and context are required

            invalid_item: invalid message ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            unauthorized: not authenticated

            Missing, invalid, expired, or revoked bearer credential.

            Missing, invalid, expired, or revoked bearer credential.
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: campaign not found"
        "409":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            not_ready: message is not ready for approval

            stale_context: message context changed

            not_ready: assign a sender before approval

            not_ready: email subject and recipient are required

            not_ready: message is not ready for approval

            not_ready: message route changed

            stale_context: message context changed

            stale_version: message version changed
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to approve action

            internal: failed to approve message
      security:
        - bearerAuth: []
      summary: Approve Draft
      tags:
        - Message review
      x-required-input-fields:
        - context_fingerprint
      x-source: backend/internal/handlers/campaign_message_review.go:769
      x-source-registration: backend/cmd/api/main.go:1731
      x-handler: messageReviewHandler.ApproveDraft
  /api/campaigns/{campaignId}/message-review/drafts/{itemID}/edit:
    put:
      description: Edit Draft for this resource.
      operationId: put_api_campaigns_campaignId_message_review_drafts_itemID_edit
      parameters:
        - in: path
          name: campaignId
          required: true
          schema:
            type: string
        - in: path
          name: itemID
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                body:
                  type: string
                expected_version_id:
                  type: string
                subject:
                  type: string
              type: object
              required:
                - body
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  context_fingerprint:
                    type: string
                  version:
                    type: integer
                  version_id:
                    format: uuid
                    type:
                      - string
                      - "null"
                type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_campaign: invalid campaign ID

            invalid_request: invalid request body

            invalid_item: invalid message ID

            invalid_message: message text is required
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            unauthorized: not authenticated

            Missing, invalid, expired, or revoked bearer credential.
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: campaign not found"
        "409":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            stale_context: message context changed

            stale_version: message version changed
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: failed to edit message"
      security:
        - bearerAuth: []
      summary: Edit Draft
      tags:
        - Message review
      x-required-input-fields:
        - body
      x-source: backend/internal/handlers/campaign_message_review.go:652
      x-source-registration: backend/cmd/api/main.go:1730
      x-handler: messageReviewHandler.EditDraft
  /api/campaigns/{campaignId}/message-review/drafts/{itemID}/retry:
    post:
      description: Retry Draft for this resource.
      operationId: post_api_campaigns_campaignId_message_review_drafts_itemID_retry
      parameters:
        - in: path
          name: campaignId
          required: true
          schema:
            type: string
        - in: path
          name: itemID
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "202":
          content:
            application/json:
              schema:
                properties:
                  status:
                    type: string
                type: object
          description: Accepted
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_campaign: invalid campaign ID

            invalid_item: invalid message ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: campaign not found"
        "409":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_retryable: message is not ready for retry"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: failed to retry message"
      security:
        - bearerAuth: []
      summary: Retry Draft
      tags:
        - Message review
      x-source: backend/internal/handlers/campaign_message_review.go:387
      x-source-registration: backend/cmd/api/main.go:1727
      x-handler: messageReviewHandler.RetryDraft
  /api/campaigns/{campaignId}/message-review/drafts/{itemID}/revise:
    post:
      description: Generate or revise campaign messages for the selected records. The server reserves credits, settles
        completed generation, and releases unused credits. A repeated request key returns the existing operation; a
        different selection returns 409. A still-running operation returns 202. Inspect settled_credits,
        released_credits, and maximum_credits in the response.
      operationId: post_api_campaigns_campaignId_message_review_drafts_itemID_revise
      parameters:
        - in: path
          name: campaignId
          required: true
          schema:
            type: string
        - in: path
          name: itemID
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                idempotency_key:
                  type: string
                  minLength: 8
                  maxLength: 200
                  description: Reuse only for the same selection. Replays return the stored result. Use a new key after
                    generation_expired.
                revision_instruction:
                  type: string
                selected_ids:
                  items:
                    type: string
                  type: array
                  minItems: 1
                  maxItems: 8
                selection_fingerprint:
                  type: string
                step_id:
                  type: string
              type: object
              required:
                - idempotency_key
                - selection_fingerprint
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                anyOf:
                  - properties:
                      maximum_credits:
                        type: integer
                      released_credits:
                        type: integer
                      replayed:
                        type: boolean
                      results:
                        description: JSON value stored by the server; shape depends on this resource's configuration.
                      settled_credits:
                        type: integer
                    type: object
                  - properties:
                      maximum_credits:
                        type: integer
                      released_credits:
                        type: integer
                      results:
                        items:
                          $ref: "#/components/schemas/handlers_reviewGenerationResult"
                        type: array
                      settled_credits:
                        type: integer
                      wallet_balance:
                        type: integer
                    type: object
          description: OK
        "202":
          content:
            application/json:
              schema:
                properties:
                  retry_after_seconds:
                    type: integer
                  status:
                    type: string
                type: object
          description: Accepted
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_campaign: invalid campaign ID

            invalid_request: invalid request body

            invalid_request: select messages and provide a request key

            invalid_selection: select 3 or 4 contacts
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            unauthorized: not authenticated

            Missing, invalid, expired, or revoked bearer credential.

            Missing, invalid, expired, or revoked bearer credential.
        "402":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "insufficient_credits: not enough credits for selected messages"
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "demo_no_effect: generation is unavailable in Demo workspace"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            not_found: campaign not found

            not_found: message selection not found
        "409":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            generation_expired: generation expired; retry with a new request key

            request_key_reused: request key has a different selection

            stale_quote: get a new credit quote
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to check generation request

            internal: failed to finish generation

            internal: failed to gate selected actions

            internal: failed to gate selected messages

            internal: failed to release expired generation

            internal: failed to replay generation

            internal: failed to reserve credits

            internal: failed to reserve generation credits

            internal: failed to save generation result

            internal: failed to settle generation credits

            internal: failed to start generation

            internal: generation saved no output; retry this request key
        "503":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "workspace_policy_unavailable: workspace policy is unavailable"
      security:
        - bearerAuth: []
      summary: Revise Draft
      tags:
        - Message review
      x-required-input-fields:
        - selected_ids
      x-source: backend/internal/handlers/campaign_message_review.go:971
      x-source-registration: backend/cmd/api/main.go:1726
      x-handler: messageReviewHandler.ReviseDraft
  /api/campaigns/{campaignId}/message-review/examples/generate:
    post:
      description: Generate or revise campaign messages for the selected records. The server reserves credits, settles
        completed generation, and releases unused credits. A repeated request key returns the existing operation; a
        different selection returns 409. A still-running operation returns 202. Inspect settled_credits,
        released_credits, and maximum_credits in the response.
      operationId: post_api_campaigns_campaignId_message_review_examples_generate
      parameters:
        - in: path
          name: campaignId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                idempotency_key:
                  type: string
                  minLength: 8
                  maxLength: 200
                  description: Reuse only for the same selection. Replays return the stored result. Use a new key after
                    generation_expired.
                revision_instruction:
                  type: string
                selected_ids:
                  items:
                    type: string
                  type: array
                  minItems: 3
                  maxItems: 4
                selection_fingerprint:
                  type: string
                step_id:
                  type: string
              type: object
              required:
                - idempotency_key
                - selection_fingerprint
                - selected_ids
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                anyOf:
                  - properties:
                      maximum_credits:
                        type: integer
                      released_credits:
                        type: integer
                      replayed:
                        type: boolean
                      results:
                        description: JSON value stored by the server; shape depends on this resource's configuration.
                      settled_credits:
                        type: integer
                    type: object
                  - properties:
                      maximum_credits:
                        type: integer
                      released_credits:
                        type: integer
                      results:
                        items:
                          $ref: "#/components/schemas/handlers_reviewGenerationResult"
                        type: array
                      settled_credits:
                        type: integer
                      wallet_balance:
                        type: integer
                    type: object
          description: OK
        "202":
          content:
            application/json:
              schema:
                properties:
                  retry_after_seconds:
                    type: integer
                  status:
                    type: string
                type: object
          description: Accepted
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_campaign: invalid campaign ID

            invalid_request: invalid request body

            invalid_request: select messages and provide a request key

            invalid_selection: select 3 or 4 contacts
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            unauthorized: not authenticated

            Missing, invalid, expired, or revoked bearer credential.

            Missing, invalid, expired, or revoked bearer credential.
        "402":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "insufficient_credits: not enough credits for selected messages"
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "demo_no_effect: generation is unavailable in Demo workspace"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            not_found: campaign not found

            not_found: message selection not found
        "409":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            generation_expired: generation expired; retry with a new request key

            request_key_reused: request key has a different selection

            stale_quote: get a new credit quote
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to check generation request

            internal: failed to finish generation

            internal: failed to gate selected actions

            internal: failed to gate selected messages

            internal: failed to release expired generation

            internal: failed to replay generation

            internal: failed to reserve credits

            internal: failed to reserve generation credits

            internal: failed to save generation result

            internal: failed to settle generation credits

            internal: failed to start generation

            internal: generation saved no output; retry this request key
        "503":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "workspace_policy_unavailable: workspace policy is unavailable"
      security:
        - bearerAuth: []
      summary: Generate Examples
      tags:
        - Message review
      x-required-input-fields:
        - selected_ids
      x-source: backend/internal/handlers/campaign_message_review.go:975
      x-source-registration: backend/cmd/api/main.go:1724
      x-handler: messageReviewHandler.GenerateExamples
  /api/campaigns/{campaignId}/message-review/examples/save:
    post:
      description: Save Examples for this resource.
      operationId: post_api_campaigns_campaignId_message_review_examples_save
      parameters:
        - in: path
          name: campaignId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                examples:
                  items:
                    properties:
                      body:
                        type: string
                      context:
                        description: JSON value stored by the server; shape depends on this resource's configuration.
                      id:
                        type: string
                      source_lead_id:
                        type: string
                      subject:
                        type: string
                    type: object
                    required:
                      - body
                  type: array
                step_id:
                  type: string
              type: object
        required: true
      responses:
        "204":
          description: No content.
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_campaign: invalid campaign ID

            invalid_request: invalid request body

            invalid_examples: each example needs message text

            invalid_examples: save 3 or 4 examples for one step

            invalid_lead: invalid source contact
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            unauthorized: not authenticated

            Missing, invalid, expired, or revoked bearer credential.
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            not_found: campaign not found

            not_found: source contact not found

            not_found: workflow step not found
        "409":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "wrong_mode: select Review examples first"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: failed to save examples"
      security:
        - bearerAuth: []
      summary: Save Examples
      tags:
        - Message review
      x-required-input-fields:
        - body
      x-source: backend/internal/handlers/campaign_message_review.go:536
      x-source-registration: backend/cmd/api/main.go:1728
      x-handler: messageReviewHandler.SaveExamples
  /api/campaigns/{campaignId}/message-review/examples/{exampleID}:
    put:
      description: Edit Example for this resource.
      operationId: put_api_campaigns_campaignId_message_review_examples_exampleID
      parameters:
        - in: path
          name: campaignId
          required: true
          schema:
            type: string
        - in: path
          name: exampleID
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                body:
                  type: string
                expected_version:
                  type: integer
                subject:
                  type: string
              type: object
              required:
                - body
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  version:
                    type: integer
                type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_campaign: invalid campaign ID

            invalid_request: invalid request body

            invalid_example: invalid example ID

            invalid_example: message text is required
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            unauthorized: not authenticated

            Missing, invalid, expired, or revoked bearer credential.
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: campaign not found"
        "409":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "stale_example: example changed"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: failed to edit example"
      security:
        - bearerAuth: []
      summary: Edit Example
      tags:
        - Message review
      x-required-input-fields:
        - body
      x-source: backend/internal/handlers/campaign_message_review.go:611
      x-source-registration: backend/cmd/api/main.go:1729
      x-handler: messageReviewHandler.EditExample
  /api/campaigns/{campaignId}/message-review/policy:
    put:
      description: Set Policy for this resource.
      operationId: put_api_campaigns_campaignId_message_review_policy
      parameters:
        - in: path
          name: campaignId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                confirm_release:
                  type: boolean
                expected_revision:
                  type: integer
                mode:
                  type: string
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  mode:
                    type: string
                  revision:
                    type: integer
                type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_campaign: invalid campaign ID

            invalid_request: invalid request body

            invalid_mode: invalid review mode
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            unauthorized: not authenticated

            Missing, invalid, expired, or revoked bearer credential.
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: campaign not found"
        "409":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            campaign_must_be_paused: pause the campaign before changing review mode

            confirmation_required: confirm the release of unsent messages

            stale_policy: review mode changed
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to gate messages

            internal: failed to invalidate approvals

            internal: failed to update policy
      security:
        - bearerAuth: []
      summary: Set Policy
      tags:
        - Message review
      x-source: backend/internal/handlers/campaign_message_review.go:301
      x-source-registration: backend/cmd/api/main.go:1722
      x-handler: messageReviewHandler.SetPolicy
  /api/campaigns/{campaignId}/message-review/quote:
    post:
      description: Quote for this resource.
      operationId: post_api_campaigns_campaignId_message_review_quote
      parameters:
        - in: path
          name: campaignId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                kind:
                  type: string
                selected_ids:
                  items:
                    type: string
                  type: array
                step_id:
                  type: string
              type: object
              required:
                - selected_ids
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  maximum_credits:
                    type: integer
                  selection_fingerprint:
                    type: string
                  wallet_balance:
                    type: integer
                type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_campaign: invalid campaign ID

            invalid_request: invalid request body

            invalid_kind: invalid generation kind

            invalid_selection: invalid or repeated ID

            invalid_selection: select 1 to 8 messages

            invalid_selection: select 3 or 4 contacts

            invalid_step: select a workflow step
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            unauthorized: not authenticated

            Missing, invalid, expired, or revoked bearer credential.
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            not_found: campaign not found

            not_found: message selection not found
        "409":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "stale_selection: message selection changed"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to quote messages

            internal: failed to quote wallet
      security:
        - bearerAuth: []
      summary: Quote
      tags:
        - Message review
      x-required-input-fields:
        - selected_ids
      x-source: backend/internal/handlers/campaign_message_review.go:445
      x-source-registration: backend/cmd/api/main.go:1723
      x-handler: messageReviewHandler.Quote
  /api/campaigns/{campaignId}/scheduled-actions:
    get:
      description: |-
        handles GET /api/campaigns/{campaignId}/scheduled-actions.
        Returns actions grouped into today/tomorrow/this_week/later with lead data inline.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: get_api_campaigns_campaignId_scheduled_actions
      parameters:
        - in: path
          name: campaignId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/handlers_GroupedActions"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid campaign ID

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: campaign not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to fetch draft contacts

            internal: failed to fetch scheduled actions

            internal: failed to load message preparation
      security:
        - bearerAuth: []
      summary: List Grouped
      tags:
        - Campaigns
      x-source: backend/internal/handlers/scheduled_actions.go:207
      x-source-registration: backend/cmd/api/main.go:1762
      x-handler: scheduledActionsHandler.ListGrouped
  /api/campaigns/{campaignId}/scheduled-actions/batch-approve:
    post:
      description: |-
        handles POST /api/campaigns/{campaignId}/scheduled-actions/batch-approve.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: post_api_campaigns_campaignId_scheduled_actions_batch_approve
      parameters:
        - in: path
          name: campaignId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                time_group:
                  type: string
              type: object
        required: true
      responses:
        "204":
          description: No content.
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_body: invalid request body

            invalid_id: invalid campaign ID

            invalid_time_group: time_group must be one of: overdue, today, tomorrow, this_week, later

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: campaign not found"
        "409":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "lead_excluded: "
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to batch approve actions

            internal: failed to check latest exclusions
      security:
        - bearerAuth: []
      summary: Batch Approve
      tags:
        - Campaigns
      x-source: backend/internal/handlers/scheduled_actions.go:419
      x-source-registration: backend/cmd/api/main.go:1763
      x-handler: scheduledActionsHandler.BatchApprove
  /api/campaigns/{campaignId}/scheduled-actions/cancel-country-restricted:
    post:
      description: |-
        handles POST /api/campaigns/{campaignId}/scheduled-actions/cancel-country-restricted.
        It cancels every pending/approved/waiting action whose lead now violates the
        campaign's country restrictions.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: post_api_campaigns_campaignId_scheduled_actions_cancel_country_restricted
      parameters:
        - in: path
          name: campaignId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/handlers_countryRestrictionImpactResponse"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid campaign ID

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: campaign not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: db pool not available

            internal: failed to cancel planned actions

            internal: failed to finish cancellation

            internal: failed to inspect planned actions

            internal: failed to start cancellation
      security:
        - bearerAuth: []
      summary: Cancel Country Restricted Planned
      tags:
        - Campaigns
      x-source: backend/internal/handlers/scheduled_actions.go:747
      x-source-registration: backend/cmd/api/main.go:1767
      x-handler: scheduledActionsHandler.CancelCountryRestrictedPlanned
  /api/campaigns/{campaignId}/scheduled-actions/country-restriction-impact:
    get:
      description: |-
        handles GET /api/campaigns/{campaignId}/scheduled-actions/country-restriction-impact.
        It counts unsent planned actions that would be blocked by the campaign's
        current country restriction settings.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: get_api_campaigns_campaignId_scheduled_actions_country_restriction_impact
      parameters:
        - in: path
          name: campaignId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/handlers_countryRestrictionImpactResponse"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid campaign ID

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: campaign not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: db pool not available

            internal: failed to inspect planned actions
      security:
        - bearerAuth: []
      summary: Country Restriction Impact
      tags:
        - Campaigns
      x-source: backend/internal/handlers/scheduled_actions.go:707
      x-source-registration: backend/cmd/api/main.go:1766
      x-handler: scheduledActionsHandler.CountryRestrictionImpact
  /api/campaigns/{campaignId}/scheduled-actions/reschedule-overdue:
    post:
      description: |-
        handles POST /api/campaigns/{campaignId}/scheduled-actions/reschedule-overdue.
        Bumps scheduled_at to the next campaign send window for every actionable
        past-due action in the campaign so SchedulerWorker can pick them up safely.
        Skips waiting_for_connection (those are blocked on a real condition) and terminal states.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: post_api_campaigns_campaignId_scheduled_actions_reschedule_overdue
      parameters:
        - in: path
          name: campaignId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                anyOf:
                  - $ref: "#/components/schemas/demosubmit_Response"
                  - properties:
                      rescheduled_count:
                        type: integer
                    type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid campaign ID

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            Missing, invalid, expired, or revoked bearer credential.

            unauthorized: not authenticated
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_assigned: this app is not assigned to you"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: campaign not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: db pool not available

            internal: failed to build Demo simulation

            internal: db pool not available

            internal: failed to inspect overdue actions

            internal: failed to reschedule overdue actions
        "503":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "workspace_policy_unavailable: workspace policy is unavailable"
      security:
        - bearerAuth: []
      summary: Reschedule Overdue
      tags:
        - Campaigns
      x-source: backend/internal/handlers/scheduled_actions.go:506
      x-source-registration: backend/cmd/api/main.go:1764
      x-handler: scheduledActionsHandler.RescheduleOverdue
  /api/campaigns/{campaignId}/scheduled-actions/reschedule-planned:
    post:
      description: |-
        handles POST /api/campaigns/{campaignId}/scheduled-actions/reschedule-planned.
        It snaps every pending/approved action's current scheduled_at forward into
        the campaign's active send window, preserving existing spacing where possible.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: post_api_campaigns_campaignId_scheduled_actions_reschedule_planned
      parameters:
        - in: path
          name: campaignId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                anyOf:
                  - $ref: "#/components/schemas/demosubmit_Response"
                  - $ref: "#/components/schemas/handlers_reschedulePlannedResponse"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid campaign ID

            invalid_user_id: invalid user ID

            schedule_missing: campaign has no send window configured
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            Missing, invalid, expired, or revoked bearer credential.

            unauthorized: not authenticated
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_assigned: this app is not assigned to you"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: campaign not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: db pool not available

            internal: failed to build Demo simulation

            internal: db pool not available

            internal: failed to fetch planned actions

            internal: failed to finish reschedule

            internal: failed to inspect planned actions

            internal: failed to read planned action

            internal: failed to read planned actions

            internal: failed to reschedule planned actions

            internal: failed to start reschedule
        "503":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "workspace_policy_unavailable: workspace policy is unavailable"
      security:
        - bearerAuth: []
      summary: Reschedule Planned
      tags:
        - Campaigns
      x-source: backend/internal/handlers/scheduled_actions.go:582
      x-source-registration: backend/cmd/api/main.go:1765
      x-handler: scheduledActionsHandler.ReschedulePlanned
  /api/campaigns/{campaignId}/steps:
    get:
      description: handles GET /api/campaigns/{campaignId}/steps.
      operationId: get_api_campaigns_campaignId_steps
      parameters:
        - in: path
          name: campaignId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: "#/components/schemas/sqlc_WorkflowStep"
                type: array
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid campaign ID

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_assigned: this app is not assigned to you"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: campaign not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: failed to fetch steps"
      security:
        - bearerAuth: []
      summary: List workflow steps
      tags:
        - Campaigns
      x-source: backend/internal/handlers/workflow_steps.go:198
      x-source-registration: backend/cmd/api/main.go:1737
      x-handler: workflowStepsHandler.List
    post:
      description: handles POST /api/campaigns/{campaignId}/steps.
      operationId: post_api_campaigns_campaignId_steps
      parameters:
        - in: path
          name: campaignId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                delay_unit:
                  type: string
                delay_value:
                  type: integer
                message_template:
                  type: string
                step_type:
                  type: string
                subject_template:
                  type: string
              type: object
              required:
                - step_type
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/demosubmit_Response"
          description: OK
        "201":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/sqlc_WorkflowStep"
          description: Created
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid campaign ID

            invalid_user_id: invalid user ID

            invalid_user_id: invalid user ID

            invalid_body: invalid request body

            invalid_delay_unit: delay_unit must be hours or days

            invalid_delay_value: delay_value must be non-negative

            invalid_step_type: 

            invalid_step_type: step_type must be invite, message, follow_up, inmail, email, or follow

            missing_subject: manual email steps require a subject
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            unauthorized: not authenticated

            Missing, invalid, expired, or revoked bearer credential.

            Missing, invalid, expired, or revoked bearer credential.
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_assigned: this app is not assigned to you"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: campaign not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to build Demo simulation

            internal: failed to count steps

            internal: failed to create step
        "503":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "workspace_policy_unavailable: workspace policy is unavailable"
      security:
        - bearerAuth: []
      summary: Create workflow steps
      tags:
        - Campaigns
      x-required-input-fields:
        - step_type
      x-source: backend/internal/handlers/workflow_steps.go:214
      x-source-registration: backend/cmd/api/main.go:1738
      x-handler: workflowStepsHandler.Create
  /api/campaigns/{campaignId}/steps/message-mode:
    put:
      description: |-
        handles PUT /api/campaigns/{campaignId}/steps/message-mode.

        Flips every non-invite step in the campaign to the requested mode and cascades
        the change to every in-flight scheduled_actions row tied to those steps. Invite
        steps stay 'manual' (there is no AI generation on connection requests).

        Pre-validation: when flipping to 'manual', every email step must already have
        a subject_template — the per-step Update handler rejects manual emails with a
        blank subject, so the bulk endpoint mirrors that rule to avoid creating state
        the editor itself would refuse.

        Runs in a single transaction so a mid-loop failure on any step or cascade
        rolls back the entire flip; the editor never sees a half-flipped workflow.
      operationId: put_api_campaigns_campaignId_steps_message_mode
      parameters:
        - in: path
          name: campaignId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                message_mode:
                  type: string
                message_review_mode:
                  type: string
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                anyOf:
                  - properties:
                      error:
                        type: string
                      message:
                        type: string
                      step_ids:
                        items:
                          type: string
                        type: array
                    type: object
                  - items:
                      $ref: "#/components/schemas/sqlc_WorkflowStep"
                    type: array
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid campaign ID

            invalid_user_id: invalid user ID

            invalid_mode: 

            invalid_body: invalid request body

            invalid_message_mode: message_mode must be 'manual' or 'ai_personalized'
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            unauthorized: not authenticated

            Missing, invalid, expired, or revoked bearer credential.
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_assigned: this app is not assigned to you"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: campaign not found"
        "409":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            campaign_must_be_paused: 

            review_mode_already_chosen: 
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to save message review mode

            internal: failed to cascade

            internal: failed to commit

            internal: failed to fetch updated steps

            internal: failed to gate review messages

            internal: failed to load steps

            internal: failed to start transaction

            internal: failed to update step
      security:
        - bearerAuth: []
      summary: Bulk Set Message Mode
      tags:
        - Campaigns
      x-source: backend/internal/handlers/workflow_steps.go:594
      x-source-registration: backend/cmd/api/main.go:1740
      x-handler: workflowStepsHandler.BulkSetMessageMode
  /api/campaigns/{campaignId}/steps/reorder:
    put:
      description: |-
        handles PUT /api/campaigns/{campaignId}/steps/reorder.

        Transactional: parses + validates the request, opens a tx on the pool,
        asserts the submitted IDs are an exact permutation of the campaign's ACTIVE
        steps (rejects partial sets, duplicates, and extras), runs a two-phase
        step_order update to sidestep partial-unique-index conflicts on swaps,
        recomputes scheduled_at for every pending action with per-flow semantics,
        commits. A failure anywhere rolls back so the editor never observes a
        half-reordered workflow or stale scheduled_at after a partial UPDATE.

        Two-phase update rationale: a row-by-row UPDATE that swaps step 2 and step 3
        momentarily creates two rows at step_order = 2 (or 3) inside the tx, which
        trips the partial unique index `idx_workflow_steps_campaign_step_order_active`
        (Postgres checks per-statement, not per-tx). Phase A writes negative
        sentinels (no collision with positive active rows); Phase B writes final
        values. Both phases share the tx, so a Phase-B failure rolls back A too.
      operationId: put_api_campaigns_campaignId_steps_reorder
      parameters:
        - in: path
          name: campaignId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                step_ids:
                  items:
                    type: string
                  type: array
              type: object
              required:
                - step_ids
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                anyOf:
                  - $ref: "#/components/schemas/demosubmit_Response"
                  - items:
                      $ref: "#/components/schemas/sqlc_WorkflowStep"
                    type: array
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid campaign ID

            invalid_user_id: invalid user ID

            invalid_user_id: invalid user ID

            invalid_body: invalid request body

            invalid_id: 

            invalid_permutation: 

            invalid_permutation: step_ids must be an exact permutation of the campaign's active steps

            missing_step_ids: step_ids array is required
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            unauthorized: not authenticated

            Missing, invalid, expired, or revoked bearer credential.

            Missing, invalid, expired, or revoked bearer credential.
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_assigned: this app is not assigned to you"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: campaign not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            simulation_error: failed to build Demo simulation

            internal: failed to commit

            internal: failed to count affected actions

            internal: failed to fetch reordered steps

            internal: failed to load steps

            internal: failed to recompute schedule

            internal: failed to reorder steps

            internal: failed to start transaction
        "503":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "workspace_policy_unavailable: workspace policy is unavailable"
      security:
        - bearerAuth: []
      summary: Reorder
      tags:
        - Campaigns
      x-required-input-fields:
        - step_ids
      x-source: backend/internal/handlers/workflow_steps.go:1081
      x-source-registration: backend/cmd/api/main.go:1739
      x-handler: workflowStepsHandler.Reorder
  /api/campaigns/{campaignId}/steps/{stepId}:
    delete:
      description: Uses the effective workspace identity and enforces resource ownership.
      operationId: delete_api_campaigns_campaignId_steps_stepId
      parameters:
        - in: query
          name: expected_pending
          schema:
            type: string
        - in: query
          name: confirmed
          schema:
            type: string
        - in: path
          name: campaignId
          required: true
          schema:
            type: string
        - in: path
          name: stepId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                anyOf:
                  - anyOf:
                      - anyOf:
                          - $ref: "#/components/schemas/demosubmit_Response"
                          - properties:
                              already_deleted:
                                type: boolean
                              cancelled:
                                type: integer
                              executing_left:
                                type: integer
                              history_preserved:
                                type: integer
                              soft_deleted:
                                type: boolean
                            type: object
                      - properties:
                          error:
                            type: string
                          executing:
                            type: integer
                          history:
                            type: integer
                          message:
                            type: string
                          pending:
                            type: integer
                        type: object
                  - properties:
                      cancelled:
                        type: integer
                      executing_left:
                        type: integer
                      history_preserved:
                        type: integer
                      soft_deleted:
                        type: boolean
                    type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid campaign ID

            invalid_user_id: invalid user ID

            invalid_user_id: invalid user ID

            invalid_id: invalid step ID

            invalid_params: confirmed must be 'true' when provided

            invalid_params: expected_pending and confirmed must both be provided or both omitted

            invalid_params: expected_pending must be a non-negative integer

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            unauthorized: not authenticated

            Missing, invalid, expired, or revoked bearer credential.

            Missing, invalid, expired, or revoked bearer credential.
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_assigned: this app is not assigned to you"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            not_found: campaign not found

            not_found: step not found
        "409":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "required_step: the first invitation or follow step cannot be removed"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to build Demo simulation

            internal: failed to cancel pending actions

            internal: failed to commit

            internal: failed to count actions

            internal: failed to lock pending actions

            internal: failed to renumber steps

            internal: failed to soft-delete step

            internal: failed to start transaction
        "503":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "workspace_policy_unavailable: workspace policy is unavailable"
      security:
        - bearerAuth: []
      summary: Delete workflow steps
      tags:
        - Campaigns
      x-source: backend/internal/handlers/workflow_steps.go:837
      x-source-registration: backend/cmd/api/main.go:1744
      x-handler: workflowStepsHandler.Delete
    put:
      description: handles PUT /api/campaigns/{campaignId}/steps/{stepId}.
      operationId: put_api_campaigns_campaignId_steps_stepId
      parameters:
        - in: path
          name: campaignId
          required: true
          schema:
            type: string
        - in: path
          name: stepId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                delay_unit:
                  anyOf:
                    - type: string
                    - type: "null"
                delay_value:
                  anyOf:
                    - type: integer
                    - type: "null"
                message_mode:
                  anyOf:
                    - type: string
                    - type: "null"
                message_review_mode:
                  type: string
                message_template:
                  anyOf:
                    - type: string
                    - type: "null"
                step_type:
                  anyOf:
                    - type: string
                    - type: "null"
                subject_template:
                  anyOf:
                    - type: string
                    - type: "null"
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                anyOf:
                  - $ref: "#/components/schemas/demosubmit_Response"
                  - $ref: "#/components/schemas/sqlc_WorkflowStep"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid campaign ID

            invalid_user_id: invalid user ID

            invalid_user_id: invalid user ID

            invalid_mode: 

            invalid_body: invalid request body

            invalid_delay_unit: delay_unit must be hours or days

            invalid_delay_value: delay_value must be non-negative

            invalid_id: invalid step ID

            invalid_message_mode: message_mode must be manual or ai_personalized

            invalid_step_type: 

            invalid_step_type: step_type must be invite, message, follow_up, inmail, email, or follow

            missing_subject: manual email steps require a subject
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            unauthorized: not authenticated

            Missing, invalid, expired, or revoked bearer credential.

            Missing, invalid, expired, or revoked bearer credential.

            Missing, invalid, expired, or revoked bearer credential.
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_assigned: this app is not assigned to you"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            not_found: campaign not found

            not_found: step not found

            not_found: step not found or update failed
        "409":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            campaign_must_be_paused: 

            review_mode_already_chosen: 
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to build Demo simulation

            internal: failed to save message review mode

            internal: failed to cascade message change

            internal: failed to cascade mode change

            internal: failed to cascade subject change

            internal: failed to count affected actions

            internal: failed to gate review messages

            internal: failed to re-gate X messages

            internal: failed to update step
        "503":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "workspace_policy_unavailable: workspace policy is unavailable"
      security:
        - bearerAuth: []
      summary: Update workflow steps
      tags:
        - Campaigns
      x-source: backend/internal/handlers/workflow_steps.go:296
      x-source-registration: backend/cmd/api/main.go:1743
      x-handler: workflowStepsHandler.Update
  /api/campaigns/{campaignId}/steps/{stepId}/affected-counts:
    get:
      description: |-
        handles GET /api/campaigns/{campaignId}/steps/{stepId}/affected-counts.
        Drives the delete-confirmation prompt in the editor: pending sends that will
        be cancelled, executing sends that will finish naturally, and history rows
        that the new ON DELETE RESTRICT FK protects regardless.
      operationId: get_api_campaigns_campaignId_steps_stepId_affected_counts
      parameters:
        - in: path
          name: campaignId
          required: true
          schema:
            type: string
        - in: path
          name: stepId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  executing:
                    type: integer
                  history:
                    type: integer
                  pending:
                    type: integer
                type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid campaign ID

            invalid_user_id: invalid user ID

            invalid_id: invalid step ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_assigned: this app is not assigned to you"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            not_found: campaign not found

            not_found: step not found
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: failed to count actions"
      security:
        - bearerAuth: []
      summary: Affected Counts
      tags:
        - Campaigns
      x-source: backend/internal/handlers/workflow_steps.go:767
      x-source-registration: backend/cmd/api/main.go:1742
      x-handler: workflowStepsHandler.AffectedCounts
  /api/campaigns/{campaignId}/steps/{stepId}/attachment:
    delete:
      description: handles DELETE /api/campaigns/{campaignId}/steps/{stepId}/attachment.
      operationId: delete_api_campaigns_campaignId_steps_stepId_attachment
      parameters:
        - in: path
          name: campaignId
          required: true
          schema:
            type: string
        - in: path
          name: stepId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/sqlc_WorkflowStep"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid campaign ID

            invalid_user_id: invalid user ID

            invalid_id: invalid step ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_assigned: this app is not assigned to you"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            not_found: campaign not found

            not_found: step not found
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: failed to clear attachment"
        "503":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "s3_not_configured: file storage is not configured"
      security:
        - bearerAuth: []
      summary: Delete Attachment
      tags:
        - Campaigns
      x-source: backend/internal/handlers/workflow_steps.go:1352
      x-source-registration: backend/cmd/api/main.go:1746
      x-handler: workflowStepsHandler.DeleteAttachment
    post:
      description: handles POST /api/campaigns/{campaignId}/steps/{stepId}/attachment.
      operationId: post_api_campaigns_campaignId_steps_stepId_attachment
      parameters:
        - in: path
          name: campaignId
          required: true
          schema:
            type: string
        - in: path
          name: stepId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      requestBody:
        content:
          multipart/form-data:
            schema:
              properties:
                file:
                  format: binary
                  type: string
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/sqlc_WorkflowStep"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid campaign ID

            invalid_user_id: invalid user ID

            file_too_large: attachment must be 10MB or less

            invalid_body: file too large or invalid multipart form

            invalid_id: invalid step ID

            missing_file: file field is required
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_assigned: this app is not assigned to you"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            not_found: campaign not found

            not_found: step not found
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to look up app

            internal: failed to look up campaign

            internal: failed to read file

            internal: failed to save attachment metadata

            upload_failed: failed to upload file to storage
        "503":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "s3_not_configured: file storage is not configured"
      security:
        - bearerAuth: []
      summary: Upload Attachment
      tags:
        - Campaigns
      x-source: backend/internal/handlers/workflow_steps.go:1240
      x-source-registration: backend/cmd/api/main.go:1745
      x-handler: workflowStepsHandler.UploadAttachment
  /api/campaigns/{id}:
    get:
      description: |-
        handles GET /api/campaigns/{id}.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: get_api_campaigns_id
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/handlers_campaignWithAssignments"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid campaign ID

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: campaign not found"
      security:
        - bearerAuth: []
      summary: Get campaigns
      tags:
        - Campaigns
      x-source: backend/internal/handlers/campaigns.go:1253
      x-source-registration: backend/cmd/api/main.go:1706
      x-handler: campaignsHandler.Get
    put:
      description: |-
        handles PUT /api/campaigns/{id}.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: put_api_campaigns_id
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                account_assignments:
                  additionalProperties:
                    anyOf:
                      - type: string
                      - type: "null"
                  type: object
                agent_id:
                  anyOf:
                    - type: string
                    - type: "null"
                enroll_members:
                  anyOf:
                    - type: boolean
                    - type: "null"
                  description: |-
                    Map keys: "linkedin" | "x" | "reddit" | "email"; value is account UUID string,
                    or nil to clear that platform's assignment (sets platform_account_id to NULL).
                    EnrollMembers, when true, materializes not-yet-enrolled members of the
                    resolved list (ListID if provided, else the campaign's existing list)
                    into real leads after the update commits. See EnrollListMembersIntoCampaign.
                list_id:
                  anyOf:
                    - type: string
                    - type: "null"
                name:
                  anyOf:
                    - type: string
                    - type: "null"
                settings:
                  description: JSON value stored by the server; shape depends on this resource's configuration.
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                anyOf:
                  - anyOf:
                      - anyOf:
                          - $ref: "#/components/schemas/demosubmit_Response"
                          - $ref: "#/components/schemas/sqlc_Campaign"
                      - $ref: "#/components/schemas/sqlc_Campaign"
                  - $ref: "#/components/schemas/handlers_updateCampaignResponse"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            agent_app_mismatch: agent does not belong to the campaign app

            contact_route_mismatch: list contact route does not match cold_outreach_mode

            invalid_account_id: 

            invalid_agent_id: invalid agent ID

            invalid_body: invalid request body

            invalid_id: invalid campaign ID

            invalid_list_id: invalid list ID

            invalid_platform: 

            invalid_settings: 

            invalid_user_id: invalid user ID

            list_app_mismatch: list does not belong to the campaign app

            missing_name: name cannot be empty

            no_list_to_enroll: campaign has no list to enroll from

            platform_mismatch: 
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            Missing, invalid, expired, or revoked bearer credential.

            unauthorized: not authenticated
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_assigned: this app is not assigned to you"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            not_found: 

            not_found: agent not found

            not_found: campaign not found

            not_found: campaign not found or update failed

            not_found: list not found
        "422":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "no_sender_account: This campaign has no sender account assigned yet. Assign one before enrolling members."
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to build Demo simulation

            internal: failed to inspect campaign details

            internal: failed to inspect campaign schedule

            internal: failed to load campaign assignments

            internal: failed to prepare draft campaign actions

            internal: failed to save assignment
        "503":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "workspace_policy_unavailable: workspace policy is unavailable"
      security:
        - bearerAuth: []
      summary: Update campaigns
      tags:
        - Campaigns
      x-source: backend/internal/handlers/campaigns.go:1373
      x-source-registration: backend/cmd/api/main.go:1707
      x-handler: campaignsHandler.Update
  /api/campaigns/{id}/duplicate:
    post:
      description: |-
        handles POST /api/campaigns/{id}/duplicate.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: post_api_campaigns_id_duplicate
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "201":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/sqlc_Campaign"
          description: Created
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid campaign ID

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: campaign not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: failed to duplicate campaign"
      security:
        - bearerAuth: []
      summary: Duplicate
      tags:
        - Campaigns
      x-source: backend/internal/handlers/campaigns.go:2730
      x-source-registration: backend/cmd/api/main.go:1714
      x-handler: campaignsHandler.Duplicate
  /api/campaigns/{id}/enrollment-preview:
    get:
      description: |-
        handles GET /api/campaigns/{id}/enrollment-preview?list_id=X.
        Drives the list-switch confirmation dialog: how many of the candidate
        list's members are not yet enrolled (materialized into leads) under this
        campaign. This is a live snapshot only — the actual enrollment count at
        confirm time can be higher if members are added to the list in between.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: get_api_campaigns_id_enrollment_preview
      parameters:
        - in: query
          name: list_id
          schema:
            type: string
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  not_enrolled_count:
                    type: integer
                type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid campaign ID

            invalid_list_id: invalid list ID

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            not_found: campaign not found

            not_found: list not found
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: failed to count list members"
      security:
        - bearerAuth: []
      summary: Enrollment Preview
      tags:
        - Campaigns
      x-source: backend/internal/handlers/campaigns.go:1326
      x-source-registration: backend/cmd/api/main.go:1708
      x-handler: campaignsHandler.EnrollmentPreview
  /api/campaigns/{id}/generate-all-messages:
    post:
      description: |-
        handles POST /api/campaigns/{id}/generate-all-messages.
        Loops the campaign's message-bearing workflow steps in order and asks
        Claude to generate each one, feeding previously-generated messages
        forward as PreviousSteps so later messages stay sequence-aware.
        Mode "overwrite" regenerates every message step; "fill_empty" skips
        steps that already have content. Invites are always skipped (their
        300-char note is generated on a different path). On per-step Claude
        failure the loop continues and the failure is reported in that step's
        result; the client decides how to surface partial success.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: post_api_campaigns_id_generate_all_messages
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                mode:
                  type: string
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  steps:
                    items:
                      $ref: "#/components/schemas/handlers_generatedStepResult"
                    type: array
                type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_body: invalid request body

            invalid_id: invalid campaign ID

            invalid_mode: mode must be 'overwrite' or 'fill_empty'

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            not_found: app not found

            not_found: campaign not found
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to load workflow steps

            specialist_audit_failed: failed to complete Message Writer audit

            specialist_audit_failed: failed to record Message Writer action audit

            specialist_audit_failed: failed to record Message Writer context audit

            specialist_audit_failed: failed to record Message Writer model audit

            specialist_audit_failed: failed to start Message Writer audit
      security:
        - bearerAuth: []
      summary: Generate All Messages
      tags:
        - Campaigns
      x-source: backend/internal/handlers/campaigns.go:2153
      x-source-registration: backend/cmd/api/main.go:1733
      x-handler: campaignsHandler.GenerateAllMessages
  /api/campaigns/{id}/generate-message:
    post:
      description: |-
        handles POST /api/campaigns/{id}/generate-message.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: post_api_campaigns_id_generate_message
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                step_order:
                  type: integer
                step_type:
                  type: string
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  message:
                    type: string
                  rationale:
                    $ref: "#/components/schemas/ai_MessageAgentRationale"
                  subject:
                    type: string
                  subject_template:
                    type: string
                type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_body: invalid request body

            invalid_id: invalid campaign ID

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            not_found: app not found

            not_found: campaign not found
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            ai_error: failed to generate message

            specialist_audit_failed: failed to complete Message Writer audit

            specialist_audit_failed: failed to record Message Writer action audit

            specialist_audit_failed: failed to record Message Writer context audit

            specialist_audit_failed: failed to record Message Writer model audit

            specialist_audit_failed: failed to start Message Writer audit
      security:
        - bearerAuth: []
      summary: Generate Message
      tags:
        - Campaigns
      x-source: backend/internal/handlers/campaigns.go:1846
      x-source-registration: backend/cmd/api/main.go:1732
      x-handler: campaignsHandler.GenerateMessage
  /api/campaigns/{id}/signals:
    get:
      description: |-
        handles GET /api/campaigns/{id}/signals.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: get_api_campaigns_id_signals
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: "#/components/schemas/sqlc_CampaignSignal"
                type: array
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid campaign ID

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: campaign not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: failed to fetch signals"
      security:
        - bearerAuth: []
      summary: List Signals
      tags:
        - Campaigns
      x-source: backend/internal/handlers/campaigns.go:2481
      x-source-registration: backend/cmd/api/main.go:1717
      x-handler: campaignsHandler.ListSignals
    put:
      description: |-
        handles PUT /api/campaigns/{id}/signals.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: put_api_campaigns_id_signals
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      requestBody:
        content:
          application/json:
            schema:
              items:
                properties:
                  enabled:
                    type: boolean
                  id:
                    type: string
                type: object
              type: array
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: "#/components/schemas/sqlc_CampaignSignal"
                type: array
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_body: invalid request body

            invalid_id: 

            invalid_id: invalid campaign ID

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: campaign not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to fetch updated signals

            internal: failed to update signal
      security:
        - bearerAuth: []
      summary: Update Signals
      tags:
        - Campaigns
      x-source: backend/internal/handlers/campaigns.go:2521
      x-source-registration: backend/cmd/api/main.go:1718
      x-handler: campaignsHandler.UpdateSignals
  /api/campaigns/{id}/stats:
    get:
      description: |-
        handles GET /api/campaigns/{id}/stats.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: get_api_campaigns_id_stats
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                anyOf:
                  - $ref: "#/components/schemas/handlers_campaignStatsResponse"
                  - type: "null"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid campaign ID

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: campaign not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to count planned actions

            internal: failed to fetch campaign stats
      security:
        - bearerAuth: []
      summary: Stats campaigns
      tags:
        - Campaigns
      x-source: backend/internal/handlers/campaigns.go:2664
      x-source-registration: backend/cmd/api/main.go:1712
      x-handler: campaignsHandler.Stats
  /api/campaigns/{id}/status:
    patch:
      description: |-
        handles PATCH /api/campaigns/{id}/status.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: patch_api_campaigns_id_status
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                status:
                  type: string
              type: object
              required:
                - status
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                anyOf:
                  - $ref: "#/components/schemas/demosubmit_Response"
                  - $ref: "#/components/schemas/sqlc_Campaign"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_body: invalid request body

            invalid_id: invalid campaign ID

            invalid_transition: 

            invalid_user_id: invalid user ID

            missing_status: status is required
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            Missing, invalid, expired, or revoked bearer credential.

            unauthorized: not authenticated
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            not_assigned: this app is not assigned to you

            subscription_required: Start your subscription to use this feature. Visit Settings → Billing to continue.
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: campaign not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to build Demo simulation

            internal: couldn't verify subscription state

            internal: failed to inspect campaign details

            internal: failed to update status
        "503":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "workspace_policy_unavailable: workspace policy is unavailable"
      security:
        - bearerAuth: []
      summary: Update Status
      tags:
        - Campaigns
      x-required-input-fields:
        - status
      x-source: backend/internal/handlers/campaigns.go:1702
      x-source-registration: backend/cmd/api/main.go:1709
      x-handler: campaignsHandler.UpdateStatus
  /api/campaigns/{id}/step-replies:
    get:
      description: |-
        handles GET /api/campaigns/{id}/step-replies.
        Returns per-step reply counts and the leads who replied at each step.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: get_api_campaigns_id_step_replies
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                additionalProperties:
                  properties:
                    count:
                      type: integer
                    leads:
                      items:
                        properties:
                          first_name:
                            type: string
                          id:
                            type: string
                          last_name:
                            type: string
                          profile_picture_url:
                            anyOf:
                              - type: string
                              - type: "null"
                        required:
                          - id
                          - first_name
                          - last_name
                          - profile_picture_url
                        type: object
                      type: array
                  required:
                    - count
                    - leads
                  type: object
                type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid campaign ID

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: campaign not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: failed to fetch reply counts"
      security:
        - bearerAuth: []
      summary: Step Replies
      tags:
        - Campaigns
      x-source: backend/internal/handlers/campaigns.go:2991
      x-source-registration: backend/cmd/api/main.go:1713
      x-handler: campaignsHandler.StepReplies
  /api/company-decision-maker-imports/events:
    post:
      description: |-
        records the non-mutating company-import discovery event emitted when
        the company-to-person mode is shown. The allowlist keeps this endpoint from
        becoming a free-form analytics sink.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: post_api_company_decision_maker_imports_events
      requestBody:
        content:
          application/json:
            schema:
              properties:
                event:
                  type: string
              type: object
        required: true
      responses:
        "204":
          description: No content.
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_event: unsupported company import event"
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
      security:
        - bearerAuth: []
      summary: Event
      tags:
        - Company Decision Maker Imports
      x-source: backend/internal/handlers/company_decision_maker_import.go:123
      x-source-registration: backend/cmd/api/main.go:1684
      x-handler: companyDecisionMakerImportHandler.Event
      parameters:
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
  /api/company-decision-maker-imports/preview:
    post:
      description: Preview company decision maker import for this resource.
      operationId: post_api_company_decision_maker_imports_preview
      parameters:
        - description: Idempotency key for this request.
          in: header
          name: Idempotency-Key
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      requestBody:
        content:
          multipart/form-data:
            schema:
              properties:
                app_id:
                  type: string
                email_fallback_enabled:
                  type: string
                file:
                  format: binary
                  type: string
                max_email_fallbacks:
                  type: string
                max_people_per_company:
                  type: string
                roles:
                  type: string
                search_channel:
                  type: string
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/decisionmakers_PreviewResponse"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            Bad Request

            invalid_request: invalid company import request

            too_many_companies: Select up to 250 companies for one search.

            invalid_request: invalid company import request

            too_many_companies: Select up to 250 companies for one search.

            invalid_app_id: invalid app ID

            invalid_body: file too large or invalid multipart form

            invalid_body: file too large or unreadable

            invalid_csv: 

            invalid_request: invalid email fallback selection

            invalid_request: invalid max email fallbacks

            invalid_roles: 

            missing_file: file field is required
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            unauthorized: not authenticated

            Missing, invalid, expired, or revoked bearer credential.

            Missing, invalid, expired, or revoked bearer credential.
        "402":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "insufficient_credits: not enough Funkel AI credits to start this search"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: company import could not be completed

            internal: failed to build Demo simulation
        "503":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unavailable: company import is unavailable"
      security:
        - bearerAuth: []
      summary: Preview company decision maker import
      tags:
        - Company Decision Maker Imports
      x-source: backend/internal/handlers/company_decision_maker_import.go:139
      x-source-registration: backend/cmd/api/main.go:1685
      x-handler: companyDecisionMakerImportHandler.Preview
  /api/company-decision-maker-imports/{jobID}/approval:
    post:
      description: Approve for this resource.
      operationId: post_api_company_decision_maker_imports_jobID_approval
      parameters:
        - in: path
          name: jobID
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                all_eligible:
                  type: boolean
                candidate_id:
                  type: string
                candidate_ids:
                  items:
                    type: string
                  type: array
                selections:
                  items:
                    properties:
                      candidate_id:
                        type: string
                      contact_route:
                        type: string
                    type: object
                  type: array
                target_list_id:
                  type: string
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/decisionmakers_ApprovalResponse"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid import ID

            invalid_request: invalid company import request

            too_many_companies: Select up to 250 companies for one search.

            invalid_body: invalid approval request
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            unauthorized: not authenticated

            Missing, invalid, expired, or revoked bearer credential.

            Missing, invalid, expired, or revoked bearer credential.
        "402":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "insufficient_credits: not enough Funkel AI credits to start this search"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: company import could not be completed"
      security:
        - bearerAuth: []
      summary: Approve
      tags:
        - Company Decision Maker Imports
      x-source: backend/internal/handlers/company_decision_maker_import.go:459
      x-source-registration: backend/cmd/api/main.go:1695
      x-handler: companyDecisionMakerImportHandler.Approve
  /api/company-decision-maker-imports/{jobID}/approved-members:
    get:
      description: Approved Members for this resource.
      operationId: get_api_company_decision_maker_imports_jobID_approved_members
      parameters:
        - in: path
          name: jobID
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/decisionmakers_ApprovedMembersResponse"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_id: invalid import ID"
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            unauthorized: not authenticated

            Missing, invalid, expired, or revoked bearer credential.

            Missing, invalid, expired, or revoked bearer credential.
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: company import not found"
      security:
        - bearerAuth: []
      summary: Approved Members
      tags:
        - Company Decision Maker Imports
      x-source: backend/internal/handlers/company_decision_maker_import.go:406
      x-source-registration: backend/cmd/api/main.go:1692
      x-handler: companyDecisionMakerImportHandler.ApprovedMembers
  /api/company-decision-maker-imports/{jobID}/batch-preview:
    post:
      description: |-
        creates a server-priced child preview from a reusable draft.
        The draft job ID is the route parameter so a selection cannot be detached
        from the uploaded source.
      operationId: post_api_company_decision_maker_imports_jobID_batch_preview
      parameters:
        - description: Idempotency key for this request.
          in: header
          name: Idempotency-Key
          schema:
            type: string
        - in: path
          name: jobID
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                email_fallback_enabled:
                  type: boolean
                max_email_fallbacks:
                  type: integer
                max_people_per_company:
                  type: integer
                roles:
                  items:
                    type: string
                  type: array
                search_channel:
                  type: string
                selected_row_ids:
                  items:
                    type: string
                  type: array
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/decisionmakers_PreviewResponse"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid import ID

            invalid_request: invalid company import request

            too_many_companies: Select up to 250 companies for one search.

            invalid_body: invalid company selection

            invalid_request: invalid selected company row
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            unauthorized: not authenticated

            Missing, invalid, expired, or revoked bearer credential.

            Missing, invalid, expired, or revoked bearer credential.
        "402":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "insufficient_credits: not enough Funkel AI credits to start this search"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: company import could not be completed"
      security:
        - bearerAuth: []
      summary: Batch Preview
      tags:
        - Company Decision Maker Imports
      x-source: backend/internal/handlers/company_decision_maker_import.go:273
      x-source-registration: backend/cmd/api/main.go:1686
      x-handler: companyDecisionMakerImportHandler.BatchPreview
  /api/company-decision-maker-imports/{jobID}/company-outcomes:
    get:
      description: Company Outcomes for this resource.
      operationId: get_api_company_decision_maker_imports_jobID_company_outcomes
      parameters:
        - in: path
          name: jobID
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/decisionmakers_CompanyOutcomesResponse"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_id: invalid import ID"
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            unauthorized: not authenticated

            Missing, invalid, expired, or revoked bearer credential.

            Missing, invalid, expired, or revoked bearer credential.
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: company import not found"
      security:
        - bearerAuth: []
      summary: Company Outcomes
      tags:
        - Company Decision Maker Imports
      x-source: backend/internal/handlers/company_decision_maker_import.go:424
      x-source-registration: backend/cmd/api/main.go:1693
      x-handler: companyDecisionMakerImportHandler.CompanyOutcomes
  /api/company-decision-maker-imports/{jobID}/confirm:
    post:
      description: Confirm company decision maker import for this resource.
      operationId: post_api_company_decision_maker_imports_jobID_confirm
      parameters:
        - in: path
          name: jobID
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                list_name:
                  type: string
                selection_fingerprint:
                  type: string
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/sqlc_CompanyDecisionMakerImportJob"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid import ID

            invalid_request: invalid company import request

            too_many_companies: Select up to 250 companies for one search.

            invalid_body: invalid review list name
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            unauthorized: not authenticated

            Missing, invalid, expired, or revoked bearer credential.

            Missing, invalid, expired, or revoked bearer credential.
        "402":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "insufficient_credits: not enough Funkel AI credits to start this search"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: company import could not be completed"
      security:
        - bearerAuth: []
      summary: Confirm company decision maker import
      tags:
        - Company Decision Maker Imports
      x-source: backend/internal/handlers/company_decision_maker_import.go:240
      x-source-registration: backend/cmd/api/main.go:1688
      x-handler: companyDecisionMakerImportHandler.Confirm
  /api/company-decision-maker-imports/{jobID}/metrics:
    get:
      description: |-
        returns bounded aggregate evidence from durable provider attempts,
        memberships, candidates, and credit settlements. It never returns provider
        request identifiers or customer contact data.
      operationId: get_api_company_decision_maker_imports_jobID_metrics
      parameters:
        - in: path
          name: jobID
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/decisionmakers_CompanyImportMetrics"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_id: invalid import ID"
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            unauthorized: not authenticated

            Missing, invalid, expired, or revoked bearer credential.

            Missing, invalid, expired, or revoked bearer credential.
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: company import not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: company import metrics could not be loaded"
        "503":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unavailable: company import metrics are unavailable"
      security:
        - bearerAuth: []
      summary: Metrics
      tags:
        - Company Decision Maker Imports
      x-source: backend/internal/handlers/company_decision_maker_import.go:350
      x-source-registration: backend/cmd/api/main.go:1690
      x-handler: companyDecisionMakerImportHandler.Metrics
  /api/company-decision-maker-imports/{jobID}/progress:
    get:
      description: Progress for this resource.
      operationId: get_api_company_decision_maker_imports_jobID_progress
      parameters:
        - in: path
          name: jobID
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/decisionmakers_ProgressResponse"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_id: invalid import ID"
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            unauthorized: not authenticated

            Missing, invalid, expired, or revoked bearer credential.

            Missing, invalid, expired, or revoked bearer credential.
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: company import not found"
      security:
        - bearerAuth: []
      summary: Progress
      tags:
        - Company Decision Maker Imports
      x-source: backend/internal/handlers/company_decision_maker_import.go:330
      x-source-registration: backend/cmd/api/main.go:1689
      x-handler: companyDecisionMakerImportHandler.Progress
  /api/company-decision-maker-imports/{jobID}/results:
    get:
      description: Results for this resource.
      operationId: get_api_company_decision_maker_imports_jobID_results
      parameters:
        - in: query
          name: offset
          schema:
            type: string
        - in: query
          name: limit
          schema:
            type: string
        - in: path
          name: jobID
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/handlers_companyImportResultsResponse"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_id: invalid import ID"
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            unauthorized: not authenticated

            Missing, invalid, expired, or revoked bearer credential.

            Missing, invalid, expired, or revoked bearer credential.
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: company import not found"
      security:
        - bearerAuth: []
      summary: Results
      tags:
        - Company Decision Maker Imports
      x-source: backend/internal/handlers/company_decision_maker_import.go:375
      x-source-registration: backend/cmd/api/main.go:1691
      x-handler: companyDecisionMakerImportHandler.Results
  /api/company-decision-maker-imports/{jobID}/retry:
    post:
      description: Retry company decision maker import for this resource.
      operationId: post_api_company_decision_maker_imports_jobID_retry
      parameters:
        - in: path
          name: jobID
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/decisionmakers_RetryResponse"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid import ID

            invalid_request: invalid company import request

            too_many_companies: Select up to 250 companies for one search.
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            unauthorized: not authenticated

            Missing, invalid, expired, or revoked bearer credential.

            Missing, invalid, expired, or revoked bearer credential.
        "402":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "insufficient_credits: not enough Funkel AI credits to start this search"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: company import could not be completed"
      security:
        - bearerAuth: []
      summary: Retry company decision maker import
      tags:
        - Company Decision Maker Imports
      x-source: backend/internal/handlers/company_decision_maker_import.go:442
      x-source-registration: backend/cmd/api/main.go:1694
      x-handler: companyDecisionMakerImportHandler.Retry
  /api/company-decision-maker-imports/{jobID}/rows:
    get:
      description: Rows for this resource.
      operationId: get_api_company_decision_maker_imports_jobID_rows
      parameters:
        - in: query
          name: offset
          schema:
            type: string
        - in: query
          name: limit
          schema:
            type: string
        - in: path
          name: jobID
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/decisionmakers_ImportRowsResponse"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_id: invalid import ID"
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            unauthorized: not authenticated

            Missing, invalid, expired, or revoked bearer credential.

            Missing, invalid, expired, or revoked bearer credential.
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: company import not found"
      security:
        - bearerAuth: []
      summary: Rows
      tags:
        - Company Decision Maker Imports
      x-source: backend/internal/handlers/company_decision_maker_import.go:308
      x-source-registration: backend/cmd/api/main.go:1687
      x-handler: companyDecisionMakerImportHandler.Rows
  /api/contacts:
    get:
      description: |-
        handles GET /api/contacts.
        Query params: status, search, app_id, campaign_id, list_id, pinned,
        fit_status, min_score, max_score, has_email, sort_by, sort_dir, limit, offset.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: get_api_contacts
      parameters:
        - in: query
          name: status
          schema:
            type: string
        - in: query
          name: search
          schema:
            type: string
        - in: query
          name: app_id
          schema:
            type: string
        - in: query
          name: campaign_id
          schema:
            type: string
        - in: query
          name: list_id
          schema:
            type: string
        - in: query
          name: pinned
          schema:
            type: string
        - in: query
          name: fit_status
          schema:
            type: string
        - in: query
          name: min_score
          schema:
            type: string
        - in: query
          name: max_score
          schema:
            type: string
        - in: query
          name: has_email
          schema:
            type: string
        - in: query
          name: sort_by
          schema:
            type: string
        - in: query
          name: sort_dir
          schema:
            type: string
        - in: query
          name: limit
          schema:
            type: string
        - in: query
          name: offset
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/handlers_contactListResponse"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_app_id: invalid app_id

            invalid_campaign_id: invalid campaign_id

            invalid_has_email: has_email must be true or false

            invalid_list_id: invalid list_id

            invalid_max_score: max_score must be an integer

            invalid_min_score: min_score must be an integer

            invalid_pinned: pinned must be true or false

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to count contacts

            internal: failed to fetch contacts
      security:
        - bearerAuth: []
      summary: List contacts
      tags:
        - Contacts
      x-source: backend/internal/handlers/contacts.go:75
      x-source-registration: backend/cmd/api/main.go:1785
      x-handler: contactsHandler.List
  /api/contacts/export:
    get:
      description: |-
        handles GET and POST /api/contacts/export.
        POST body: {"contact_ids": ["uuid", ...]} for selection-based export.
        GET query: ?status= for legacy "all matching filter" export.
        Both paths emit the same CSV column set.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: get_api_contacts_export
      parameters:
        - in: query
          name: status
          schema:
            type: string
        - in: query
          name: app_id
          schema:
            type: string
        - in: query
          name: campaign_id
          schema:
            type: string
        - in: query
          name: pinned
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                contact_ids:
                  items:
                    type: string
                  type: array
              type: object
              required:
                - contact_ids
        required: true
      responses:
        "200":
          description: CSV export of the selected contacts.
          content:
            text/csv:
              schema:
                type: string
                format: binary
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_app_id: invalid app_id

            invalid_body: invalid request body

            invalid_campaign_id: invalid campaign_id

            invalid_id: 

            invalid_pinned: pinned must be true or false

            invalid_user_id: invalid user ID

            no_ids: contact_ids must be non-empty

            too_many: 
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: failed to fetch contacts for export"
      security:
        - bearerAuth: []
      summary: Export contacts
      tags:
        - Contacts
      x-required-input-fields:
        - contact_ids
      x-source: backend/internal/handlers/contacts.go:549
      x-source-registration: backend/cmd/api/main.go:1786
      x-handler: contactsHandler.Export
    post:
      description: |-
        handles GET and POST /api/contacts/export.
        POST body: {"contact_ids": ["uuid", ...]} for selection-based export.
        GET query: ?status= for legacy "all matching filter" export.
        Both paths emit the same CSV column set.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: post_api_contacts_export
      parameters:
        - in: query
          name: status
          schema:
            type: string
        - in: query
          name: app_id
          schema:
            type: string
        - in: query
          name: campaign_id
          schema:
            type: string
        - in: query
          name: pinned
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                contact_ids:
                  items:
                    type: string
                  type: array
              type: object
              required:
                - contact_ids
        required: true
      responses:
        "200":
          description: CSV export of the selected contacts.
          content:
            text/csv:
              schema:
                type: string
                format: binary
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_app_id: invalid app_id

            invalid_body: invalid request body

            invalid_campaign_id: invalid campaign_id

            invalid_id: 

            invalid_pinned: pinned must be true or false

            invalid_user_id: invalid user ID

            no_ids: contact_ids must be non-empty

            too_many: 
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: failed to fetch contacts for export"
      security:
        - bearerAuth: []
      summary: Export contacts
      tags:
        - Contacts
      x-required-input-fields:
        - contact_ids
      x-source: backend/internal/handlers/contacts.go:549
      x-source-registration: backend/cmd/api/main.go:1787
      x-handler: contactsHandler.Export
  /api/contacts/{id}:
    get:
      description: |-
        handles GET /api/contacts/{id}.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: get_api_contacts_id
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/handlers_contactDetailResponse"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid contact ID

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_assigned: this app is not assigned to you"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: contact not found"
      security:
        - bearerAuth: []
      summary: Detail
      tags:
        - Contacts
      x-source: backend/internal/handlers/contacts.go:251
      x-source-registration: backend/cmd/api/main.go:1788
      x-handler: contactsHandler.Detail
  /api/contacts/{id}/fit:
    patch:
      description: |-
        handles PATCH /api/contacts/{id}/fit.
        Sets the user's assessment of lead quality: fit, partial, unfit.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: patch_api_contacts_id_fit
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                fit_status:
                  type: string
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  fit_status:
                    type: string
                type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_body: invalid request body

            invalid_id: invalid contact ID

            invalid_status: fit_status must be: fit, partial, unfit, or unreviewed

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: contact not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: failed to update fit status"
      security:
        - bearerAuth: []
      summary: Update Fit Status
      tags:
        - Contacts
      x-source: backend/internal/handlers/contacts.go:369
      x-source-registration: backend/cmd/api/main.go:1789
      x-handler: contactsHandler.UpdateFitStatus
  /api/contacts/{id}/pin:
    delete:
      description: |-
        handles DELETE /api/contacts/{id}/pin.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: delete_api_contacts_id_pin
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  pinned:
                    type: boolean
                type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid contact ID

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: contact not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: failed to unpin contact"
      security:
        - bearerAuth: []
      summary: Unpin
      tags:
        - Contacts
      x-source: backend/internal/handlers/contacts.go:493
      x-source-registration: backend/cmd/api/main.go:1791
      x-handler: contactsHandler.Unpin
    put:
      description: |-
        handles PUT /api/contacts/{id}/pin.
        Pins the deduped contact identity for the current user, so the person stays
        saved even if their latest campaign row changes.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: put_api_contacts_id_pin
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  pinned:
                    type: boolean
                  pinned_at:
                    format: date-time
                    type: string
                type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid contact ID

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: contact not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: failed to pin contact"
      security:
        - bearerAuth: []
      summary: Pin
      tags:
        - Contacts
      x-source: backend/internal/handlers/contacts.go:449
      x-source-registration: backend/cmd/api/main.go:1790
      x-handler: contactsHandler.Pin
  /api/dashboard/next-event:
    get:
      description: |-
        handles GET /api/dashboard/next-event.
        Returns the next upcoming scheduled send across all of the caller's
        campaigns, used by the dashboard AllClear card. 204 when nothing scheduled.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: get_api_dashboard_next_event
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/handlers_nextEventResponse"
          description: OK
        "204":
          description: No content.
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_user_id: invalid user ID"
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: failed to fetch next event"
      security:
        - bearerAuth: []
      summary: Next Event
      tags:
        - Dashboard
      x-source: backend/internal/handlers/dashboard.go:36
      x-source-registration: backend/cmd/api/main.go:1602
      x-handler: dashboardHandler.NextEvent
      parameters:
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
  /api/discovered-leads:
    get:
      description: |-
        handles GET /api/discovered-leads.
        Query params: status, app_id, campaign_id, search, limit, offset.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: get_api_discovered_leads
      parameters:
        - in: query
          name: status
          schema:
            type: string
        - in: query
          name: search
          schema:
            type: string
        - in: query
          name: app_id
          schema:
            type: string
        - in: query
          name: campaign_id
          schema:
            type: string
        - in: query
          name: limit
          schema:
            type: string
        - in: query
          name: offset
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/handlers_discoveredLeadListResponse"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_app_id: invalid app_id

            invalid_campaign_id: invalid campaign_id

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to count discovered leads

            internal: failed to fetch discovered leads
      security:
        - bearerAuth: []
      summary: List discovered leads
      tags:
        - Discovered Leads
      x-source: backend/internal/handlers/discovered_leads.go:73
      x-source-registration: backend/cmd/api/main.go:1805
      x-handler: discoveredLeadsHandler.List
  /api/discovered-leads/batch-approve:
    post:
      description: |-
        handles POST /api/discovered-leads/batch-approve.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: post_api_discovered_leads_batch_approve
      requestBody:
        content:
          application/json:
            schema:
              properties:
                campaign_id:
                  type: string
                ids:
                  items:
                    type: string
                  type: array
              type: object
              required:
                - campaign_id
                - ids
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                anyOf:
                  - $ref: "#/components/schemas/demosubmit_Response"
                  - properties:
                      approved:
                        type: integer
                    type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_body: invalid request body

            invalid_campaign_id: invalid campaign ID

            invalid_user_id: invalid user ID

            missing_campaign_id: campaign_id is required

            missing_ids: ids array is required
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            Missing, invalid, expired, or revoked bearer credential.

            unauthorized: not authenticated
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            not_found: campaign not found

            not_found: no discovered leads found
        "409":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "lead_excluded: "
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "simulation_error: failed to build Demo simulation"
        "503":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "workspace_policy_unavailable: workspace policy is unavailable"
      security:
        - bearerAuth: []
      summary: Batch Approve
      tags:
        - Discovered Leads
      x-required-input-fields:
        - campaign_id
        - ids
      x-source: backend/internal/handlers/discovered_leads.go:778
      x-source-registration: backend/cmd/api/main.go:1809
      x-handler: discoveredLeadsHandler.BatchApprove
      parameters:
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
  /api/discovered-leads/batch-reject:
    post:
      description: |-
        handles POST /api/discovered-leads/batch-reject. Flips status
        to "rejected" for each owned discovered lead in the request, skipping rows
        that don't belong to the caller. Returns the count of leads actually
        rejected.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: post_api_discovered_leads_batch_reject
      requestBody:
        content:
          application/json:
            schema:
              properties:
                ids:
                  items:
                    type: string
                  type: array
              type: object
              required:
                - ids
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                anyOf:
                  - $ref: "#/components/schemas/demosubmit_Response"
                  - properties:
                      rejected:
                        type: integer
                    type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_body: invalid request body

            invalid_user_id: invalid user ID

            missing_ids: ids array is required
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            Missing, invalid, expired, or revoked bearer credential.

            unauthorized: not authenticated
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: no discovered leads found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "simulation_error: failed to build Demo simulation"
        "503":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "workspace_policy_unavailable: workspace policy is unavailable"
      security:
        - bearerAuth: []
      summary: Batch Reject
      tags:
        - Discovered Leads
      x-required-input-fields:
        - ids
      x-source: backend/internal/handlers/discovered_leads.go:698
      x-source-registration: backend/cmd/api/main.go:1810
      x-handler: discoveredLeadsHandler.BatchReject
      parameters:
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
  /api/discovered-leads/counts:
    get:
      description: |-
        handles GET /api/discovered-leads/counts.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: get_api_discovered_leads_counts
      parameters:
        - in: query
          name: app_id
          schema:
            type: string
        - in: query
          name: campaign_id
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                additionalProperties:
                  type: integer
                type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_app_id: invalid app_id

            invalid_campaign_id: invalid campaign_id

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
      security:
        - bearerAuth: []
      summary: Status Counts
      tags:
        - Discovered Leads
      x-source: backend/internal/handlers/discovered_leads.go:314
      x-source-registration: backend/cmd/api/main.go:1806
      x-handler: discoveredLeadsHandler.StatusCounts
  /api/discovered-leads/{id}:
    get:
      description: |-
        handles one discovered lead for the Operator side panel.
        Both the user and selected product constrain the lookup.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: get_api_discovered_leads_id
      parameters:
        - in: query
          name: app_id
          schema:
            type: string
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/sqlc_ListDiscoveredLeadsByUserWithListRow"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_id: valid lead and app IDs are required"
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: lead not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: failed to load lead"
      security:
        - bearerAuth: []
      summary: Detail
      tags:
        - Discovered Leads
      x-source: backend/internal/handlers/discovered_leads.go:172
      x-source-registration: backend/cmd/api/main.go:1807
      x-handler: discoveredLeadsHandler.Detail
  /api/discovered-leads/{id}/approve:
    post:
      description: |-
        handles POST /api/discovered-leads/{id}/approve.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: post_api_discovered_leads_id_approve
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                campaign_id:
                  type: string
              type: object
              required:
                - campaign_id
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                anyOf:
                  - $ref: "#/components/schemas/demosubmit_Response"
                  - $ref: "#/components/schemas/sqlc_Lead"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            country_restricted: 

            invalid_body: invalid request body

            invalid_campaign_id: invalid campaign ID

            invalid_id: invalid discovered lead ID

            invalid_user_id: invalid user ID

            missing_campaign_id: campaign_id is required
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            Missing, invalid, expired, or revoked bearer credential.

            unauthorized: not authenticated
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            not_found: campaign not found

            not_found: discovered lead not found
        "409":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            lead_excluded: 

            no_send_route: 

            no_send_route: assign a connected LinkedIn sender to this campaign before approving leads
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            simulation_error: failed to build Demo simulation

            internal: 
        "503":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "workspace_policy_unavailable: workspace policy is unavailable"
      security:
        - bearerAuth: []
      summary: Approve
      tags:
        - Discovered Leads
      x-required-input-fields:
        - campaign_id
      x-source: backend/internal/handlers/discovered_leads.go:351
      x-source-registration: backend/cmd/api/main.go:1811
      x-handler: discoveredLeadsHandler.Approve
  /api/discovered-leads/{id}/refresh:
    post:
      description: |-
        re-fetches the lead's profile from Unipile and updates the stored
        fields. Used when the snapshot we have is stale (lead changed jobs, hadn't
        updated profile yet at discovery, etc).

        Uses the effective workspace identity and enforces resource ownership.
      operationId: post_api_discovered_leads_id_refresh
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  company:
                    type: string
                  first_name:
                    type: string
                  headline:
                    type: string
                  last_name:
                    type: string
                  unipile_emails:
                    items:
                      type: string
                    type: array
                type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid lead ID

            invalid_user_id: invalid user ID

            missing_provider_id: lead has no LinkedIn provider id to refresh

            no_linkedin_account: no connected LinkedIn account found
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: discovered lead not found"
        "502":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "refresh_failed: failed to fetch profile from LinkedIn"
      security:
        - bearerAuth: []
      summary: Refresh
      tags:
        - Discovered Leads
      x-source: backend/internal/handlers/discovered_leads.go:962
      x-source-registration: backend/cmd/api/main.go:1813
      x-handler: discoveredLeadsHandler.Refresh
  /api/discovered-leads/{id}/reject:
    post:
      description: |-
        handles POST /api/discovered-leads/{id}/reject.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: post_api_discovered_leads_id_reject
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/demosubmit_Response"
          description: OK
        "204":
          description: No content.
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid discovered lead ID

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            Missing, invalid, expired, or revoked bearer credential.

            unauthorized: not authenticated
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: discovered lead not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            simulation_error: failed to build Demo simulation

            internal: 
        "503":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "workspace_policy_unavailable: workspace policy is unavailable"
      security:
        - bearerAuth: []
      summary: Reject
      tags:
        - Discovered Leads
      x-source: backend/internal/handlers/discovered_leads.go:643
      x-source-registration: backend/cmd/api/main.go:1812
      x-handler: discoveredLeadsHandler.Reject
  /api/discovered-leads/{id}/sequence:
    get:
      description: |-
        handles GET /api/discovered-leads/{id}/sequence.
        Returns the campaign sequence for the discovered lead — live state if a
        matching active `lead` row exists, otherwise the campaign's step template.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: get_api_discovered_leads_id_sequence
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/handlers_discoveredLeadSequenceResponse"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid discovered lead ID

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: discovered lead not found"
      security:
        - bearerAuth: []
      summary: Sequence
      tags:
        - Discovered Leads
      x-source: backend/internal/handlers/discovered_leads.go:213
      x-source-registration: backend/cmd/api/main.go:1808
      x-handler: discoveredLeadsHandler.Sequence
  /api/enrichment/contacts/{id}:
    post:
      description: |-
        handles POST /api/enrichment/contacts/{id}.
        Looks up work email for an existing campaign lead and persists to leads.enriched_email.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: post_api_enrichment_contacts_id
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/handlers_EnrichmentResult"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid lead ID

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            Missing, invalid, expired, or revoked bearer credential.

            unauthorized: not authenticated
        "402":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "insufficient_credits: You're out of enrichment credits. Buy a pack in Settings → Billing."
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "feature_disabled: enrichment is not available"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            not_found: contact not found

            not_found: lead not found
        "429":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "quota_exceeded: daily enrichment limit reached, try again tomorrow"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            credit_decrement_failed: couldn't decrement credit

            internal: failed to save enriched email
        "502":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "enrichment_error: failed to find work email"
      security:
        - bearerAuth: []
      summary: Enrich Contact
      tags:
        - Enrichment
      x-source: backend/internal/handlers/enrichment.go:738
      x-source-registration: backend/cmd/api/main.go:1861
      x-handler: enrichmentHandler.EnrichContact
  /api/enrichment/discovered-leads/{id}:
    post:
      description: |-
        handles POST /api/enrichment/discovered-leads/{id}.
        Resolves a work email through the shared provider waterfall, then persists it.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: post_api_enrichment_discovered_leads_id
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/handlers_EnrichmentResult"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid lead ID

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            Missing, invalid, expired, or revoked bearer credential.

            unauthorized: not authenticated
        "402":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "insufficient_credits: You're out of enrichment credits. Buy a pack in Settings → Billing."
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "feature_disabled: enrichment is not available"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: discovered lead not found"
        "429":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "quota_exceeded: daily enrichment limit reached, try again tomorrow"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "credit_decrement_failed: couldn't decrement credit"
        "502":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "enrichment_error: failed to find work email"
      security:
        - bearerAuth: []
      summary: Enrich Discovered Lead
      tags:
        - Enrichment
      x-source: backend/internal/handlers/enrichment.go:245
      x-source-registration: backend/cmd/api/main.go:1858
      x-handler: enrichmentHandler.EnrichDiscoveredLead
  /api/enrichment/discovered-leads/{id}/resolve-linkedin:
    post:
      description: |-
        handles
        POST /api/enrichment/discovered-leads/{id}/resolve-linkedin. Delegates the
        entire claim/spend/provider-call/finalize sequence to
        leadidentity.ResolveLinkedInForLead (the same shared core the Hacker News
        collector's auto-resolve toggle calls) and translates its Outcome into an
        HTTP status the frontend's ResolveLinkedInButton renders directly.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: post_api_enrichment_discovered_leads_id_resolve_linkedin
      parameters:
        - in: query
          name: retry
          schema:
            type: string
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/handlers_ResolveLinkedInResult"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid lead ID

            invalid_source: linkedin resolution is only available for Hacker News and X leads

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            Missing, invalid, expired, or revoked bearer credential.

            unauthorized: not authenticated
        "402":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "insufficient_credits: You're out of enrichment credits. Buy a pack in Settings → Billing."
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "feature_disabled: enrichment is not available"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: discovered lead not found"
        "409":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "in_progress: linkedin resolution is already in progress for this lead"
        "429":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "quota_exceeded: daily LinkedIn resolution limit reached, try again tomorrow"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to resolve hacker news identity

            internal: failed to resolve linkedin profile

            internal: resolve linkedin is not configured

            internal: unrecognized resolution outcome
        "502":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "enrichment_error: failed to resolve linkedin profile"
      security:
        - bearerAuth: []
      summary: Resolve Discovered Lead Linked In
      tags:
        - Enrichment
      x-source: backend/internal/handlers/enrichment.go:513
      x-source-registration: backend/cmd/api/main.go:1859
      x-handler: enrichmentHandler.ResolveDiscoveredLeadLinkedIn
  /api/enrichment/list-members/{id}/resolve-linkedin:
    post:
      description: |-
        handles
        POST /api/enrichment/list-members/{id}/resolve-linkedin. lead_list_members'
        counterpart to ResolveDiscoveredLeadLinkedIn: CSV/Operator-imported,
        pre-campaign leads get the same "Resolve LinkedIn URL" capability, same
        4-credit spend, same shared daily quota, same atomic claim/refund
        guarantees. No source restriction (all list members are eligible) and no
        Hacker News identity preflight (not applicable to imported contacts).

        Uses the effective workspace identity and enforces resource ownership.
      operationId: post_api_enrichment_list_members_id_resolve_linkedin
      parameters:
        - in: query
          name: retry
          schema:
            type: string
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/handlers_ResolveLinkedInResult"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid list member ID

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            Missing, invalid, expired, or revoked bearer credential.

            unauthorized: not authenticated
        "402":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "insufficient_credits: You're out of enrichment credits. Buy a pack in Settings → Billing."
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "feature_disabled: enrichment is not available"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: list member not found"
        "409":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "in_progress: linkedin resolution is already in progress for this list member"
        "429":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "quota_exceeded: daily LinkedIn resolution limit reached, try again tomorrow"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to resolve linkedin profile

            internal: resolve linkedin is not configured

            internal: unrecognized resolution outcome
        "502":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "enrichment_error: failed to resolve linkedin profile"
      security:
        - bearerAuth: []
      summary: Resolve Lead List Member Linked In
      tags:
        - Enrichment
      x-source: backend/internal/handlers/enrichment.go:637
      x-source-registration: backend/cmd/api/main.go:1860
      x-handler: enrichmentHandler.ResolveLeadListMemberLinkedIn
  /api/features:
    get:
      description: |-
        handles GET /api/features.

        Returns the frontend availability map. Three keys exist today:

          - ninjapear_enrichment: global, comes from config.
          - insights: available to every user, including acting-as sessions.
          - hackernews_signals: global, gated on the global_feature_flags row for
            `hackernews_signals_v1`. Same value for every user, but still always
            false while acting-as -- same guard as insights, so a team member's
            own workspace state can never make this flag appear true while they
            are acting inside someone else's account.

        Global flag lookup errors degrade to false. The map remains best-effort UI
        hinting and must not break the dashboard when the database is unavailable.

        Workspace delegation restrictions apply; see the documented 403 errors.
      operationId: get_api_features
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  hackernews_signals:
                    type: boolean
                  insights:
                    type: boolean
                  ninjapear_enrichment:
                    type: boolean
                type: object
          description: OK
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: Missing, invalid, expired, or revoked bearer credential.
      security:
        - bearerAuth: []
      summary: Get Features
      tags:
        - Features
      x-source: backend/internal/handlers/enrichment.go:226
      x-source-registration: backend/cmd/api/main.go:1864
      x-handler: enrichmentHandler.GetFeatures
      parameters:
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
  /api/follow-ups:
    get:
      description: |-
        handles GET /api/follow-ups?limit=&offset=.
        Returns leads with status replied/hot ordered by latest message activity,
        plus a pending_count for the sidebar badge (replies still owed by the user).

        Uses the effective workspace identity and enforces resource ownership.
      operationId: get_api_follow_ups
      parameters:
        - in: query
          name: limit
          schema:
            type: string
        - in: query
          name: offset
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/handlers_followUpsListResponse"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_user_id: invalid user ID"
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to count pending follow-ups

            internal: failed to fetch follow-ups
      security:
        - bearerAuth: []
      summary: List follow ups
      tags:
        - Follow Ups
      x-source: backend/internal/handlers/follow_ups.go:91
      x-source-registration: backend/cmd/api/main.go:1792
      x-handler: followUpsHandler.List
  /api/follow-ups/leads/{leadId}/dismiss:
    post:
      description: |-
        handles POST /api/follow-ups/leads/{leadId}/dismiss.
        Suppresses a non-reminder-backed follow-up row (surfaced only because
        lead.status is replied/hot) until the lead's latest contact message
        changes. Reminder-backed rows are unaffected -- ListFollowUps/
        CountFollowUpsPending never apply this dismissal to the `OR
        ar.reminder_id IS NOT NULL` / `OR EXISTS (... lead_reminders ...)` branch.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: post_api_follow_ups_leads_leadId_dismiss
      parameters:
        - in: path
          name: leadId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                anyOf:
                  - $ref: "#/components/schemas/demosubmit_Response"
                  - $ref: "#/components/schemas/handlers_dismissFollowUpResponse"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_lead_id: invalid lead ID

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            Missing, invalid, expired, or revoked bearer credential.

            unauthorized: not authenticated
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: lead not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to verify lead

            simulation_error: failed to build Demo simulation

            internal: failed to dismiss follow-up

            internal: failed to resolve latest message
        "503":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "workspace_policy_unavailable: workspace policy is unavailable"
      security:
        - bearerAuth: []
      summary: Dismiss
      tags:
        - Follow Ups
      x-source: backend/internal/handlers/follow_ups.go:439
      x-source-registration: backend/cmd/api/main.go:1796
      x-handler: followUpsHandler.Dismiss
  /api/follow-ups/leads/{leadId}/notes:
    get:
      description: |-
        handles GET /api/follow-ups/leads/{leadId}/notes.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: get_api_follow_ups_leads_leadId_notes
      parameters:
        - in: query
          name: limit
          schema:
            type: string
        - in: query
          name: offset
          schema:
            type: string
        - in: path
          name: leadId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/handlers_leadNotesListResponse"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_lead_id: invalid lead ID

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            Missing, invalid, expired, or revoked bearer credential.

            unauthorized: not authenticated
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: lead not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to verify lead

            internal: failed to fetch notes
      security:
        - bearerAuth: []
      summary: List Notes
      tags:
        - Follow Ups
      x-source: backend/internal/handlers/follow_ups.go:161
      x-source-registration: backend/cmd/api/main.go:1794
      x-handler: followUpsHandler.ListNotes
    post:
      description: |-
        handles POST /api/follow-ups/leads/{leadId}/notes.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: post_api_follow_ups_leads_leadId_notes
      parameters:
        - description: Idempotency key for this request.
          in: header
          name: Idempotency-Key
          schema:
            type: string
        - in: path
          name: leadId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                body:
                  type: string
                reminder_at:
                  anyOf:
                    - type: string
                    - type: "null"
                reminder_body:
                  type: string
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/demosubmit_Response"
          description: OK
        "201":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/handlers_createLeadNoteResponse"
          description: Created
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_body: invalid request body

            invalid_lead_id: invalid lead ID

            invalid_reminder_at: reminder_at must be an RFC3339 timestamp

            invalid_user_id: invalid user ID

            missing_body: note body is required
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            Missing, invalid, expired, or revoked bearer credential.

            unauthorized: not authenticated
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "lead_not_found: lead not found"
        "409":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "note_request_invalid: note request is invalid or already used for another action"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to verify lead

            simulation_error: failed to build Demo simulation

            internal: failed to create note

            internal: failed to create reminder

            note_read_failed: note saved but could not be reloaded

            note_write_failed: could not save note and reminder

            reminder_read_failed: reminder saved but could not be reloaded
        "503":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "workspace_policy_unavailable: workspace policy is unavailable"
      security:
        - bearerAuth: []
      summary: Create Note
      tags:
        - Follow Ups
      x-source: backend/internal/handlers/follow_ups.go:219
      x-source-registration: backend/cmd/api/main.go:1795
      x-handler: followUpsHandler.CreateNote
  /api/follow-ups/pending-count:
    get:
      description: |-
        handles GET /api/follow-ups/pending-count for the sidebar badge.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: get_api_follow_ups_pending_count
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  count:
                    type: integer
                type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_user_id: invalid user ID"
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: failed to count pending follow-ups"
      security:
        - bearerAuth: []
      summary: Pending Count
      tags:
        - Follow Ups
      x-source: backend/internal/handlers/follow_ups.go:141
      x-source-registration: backend/cmd/api/main.go:1793
      x-handler: followUpsHandler.PendingCount
      parameters:
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
  /api/follow-ups/reminders/{id}/complete:
    post:
      description: |-
        handles POST /api/follow-ups/reminders/{id}/complete.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: post_api_follow_ups_reminders_id_complete
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                anyOf:
                  - $ref: "#/components/schemas/demosubmit_Response"
                  - properties:
                      reminder:
                        $ref: "#/components/schemas/handlers_leadReminderResponse"
                    type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_reminder_id: invalid reminder ID

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            Missing, invalid, expired, or revoked bearer credential.

            unauthorized: not authenticated
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: reminder not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            simulation_error: failed to build Demo simulation

            internal: failed to complete reminder

            internal: failed to load reminder
        "503":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "workspace_policy_unavailable: workspace policy is unavailable"
      security:
        - bearerAuth: []
      summary: Complete Reminder
      tags:
        - Follow Ups
      x-source: backend/internal/handlers/follow_ups.go:370
      x-source-registration: backend/cmd/api/main.go:1797
      x-handler: followUpsHandler.CompleteReminder
  /api/ideas:
    get:
      description: |-
        handles GET /api/ideas.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: get_api_ideas
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  building:
                    items:
                      $ref: "#/components/schemas/handlers_ideaResponse"
                    type: array
                  new:
                    items:
                      $ref: "#/components/schemas/handlers_ideaResponse"
                    type: array
                  shipped:
                    items:
                      $ref: "#/components/schemas/handlers_ideaResponse"
                    type: array
                  trending:
                    items:
                      $ref: "#/components/schemas/handlers_ideaResponse"
                    type: array
                type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_user_id: invalid user ID"
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            db_error: failed to check vote-count-reveal flag

            db_error: failed to list ideas

            db_error: failed to load vote-count-reveal threshold
      security:
        - bearerAuth: []
      summary: List ideas
      tags:
        - Ideas
      x-source: backend/internal/handlers/ideas.go:106
      x-source-registration: backend/cmd/api/main.go:1668
      x-handler: ideasHandler.List
      parameters:
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
    post:
      description: |-
        handles POST /api/ideas.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: post_api_ideas
      requestBody:
        content:
          application/json:
            schema:
              properties:
                text:
                  type: string
              type: object
        required: true
      responses:
        "201":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/handlers_ideaResponse"
          description: Created
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_body: invalid request body

            invalid_user_id: invalid user ID

            missing_text: text is required

            text_too_long: idea text must be 500 characters or fewer
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "429":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "rate_limited: You've pinned 3 ideas today. Come back tomorrow to add more."
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            db_error: failed to auto-vote idea

            db_error: failed to begin idea transaction

            db_error: failed to check rate limit

            db_error: failed to commit idea

            db_error: failed to create idea
      security:
        - bearerAuth: []
      summary: Create ideas
      tags:
        - Ideas
      x-source: backend/internal/handlers/ideas.go:219
      x-source-registration: backend/cmd/api/main.go:1669
      x-handler: ideasHandler.Create
      parameters:
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
  /api/ideas/{id}:
    delete:
      description: |-
        handles DELETE /api/ideas/{id}.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: delete_api_ideas_id
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  message:
                    type: string
                type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid idea ID

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "409":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "idea_locked: this idea can no longer be edited"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            db_error: failed to check idea lock state

            db_error: failed to delete idea
      security:
        - bearerAuth: []
      summary: Delete ideas
      tags:
        - Ideas
      x-source: backend/internal/handlers/ideas.go:404
      x-source-registration: backend/cmd/api/main.go:1671
      x-handler: ideasHandler.Delete
    put:
      description: |-
        handles PUT /api/ideas/{id}.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: put_api_ideas_id
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                text:
                  type: string
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/handlers_ideaResponse"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_body: invalid request body

            invalid_id: invalid idea ID

            invalid_user_id: invalid user ID

            missing_text: text is required

            text_too_long: idea text must be 500 characters or fewer
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: idea not found"
        "409":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "idea_locked: this idea can no longer be edited"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            db_error: failed to check idea lock state

            db_error: failed to update idea
      security:
        - bearerAuth: []
      summary: Update ideas
      tags:
        - Ideas
      x-source: backend/internal/handlers/ideas.go:332
      x-source-registration: backend/cmd/api/main.go:1670
      x-handler: ideasHandler.Update
  /api/ideas/{id}/vote:
    delete:
      description: |-
        handles DELETE /api/ideas/{id}/vote.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: delete_api_ideas_id_vote
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  voted:
                    type: boolean
                type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid idea ID

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            db_error: failed to begin vote transaction

            db_error: failed to check vote

            db_error: failed to commit vote change

            db_error: failed to count votes

            db_error: failed to lock idea votes

            db_error: failed to unvote
      security:
        - bearerAuth: []
      summary: Unvote
      tags:
        - Ideas
      x-source: backend/internal/handlers/ideas.go:472
      x-source-registration: backend/cmd/api/main.go:1673
      x-handler: ideasHandler.Unvote
    post:
      description: |-
        handles POST /api/ideas/{id}/vote.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: post_api_ideas_id_vote
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  voted:
                    type: boolean
                type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid idea ID

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "db_error: failed to vote"
      security:
        - bearerAuth: []
      summary: Vote
      tags:
        - Ideas
      x-source: backend/internal/handlers/ideas.go:443
      x-source-registration: backend/cmd/api/main.go:1672
      x-handler: ideasHandler.Vote
  /api/inbox/conversations:
    get:
      description: |-
        handles GET /api/inbox/conversations.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: get_api_inbox_conversations
      parameters:
        - in: query
          name: app_id
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: "#/components/schemas/handlers_conversationResponse"
                type: array
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_app_id: invalid app_id

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: failed to fetch conversations"
      security:
        - bearerAuth: []
      summary: List Conversations
      tags:
        - Inbox
      x-source: backend/internal/handlers/inbox.go:409
      x-source-registration: backend/cmd/api/main.go:1976
      x-handler: inboxHandler.ListConversations
  /api/inbox/conversations/sync:
    get:
      description: |-
        handles GET /api/inbox/conversations/sync.
        Fetches conversations live from Unipile and enriches with local data.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: get_api_inbox_conversations_sync
      parameters:
        - in: query
          name: app_id
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: "#/components/schemas/handlers_conversationResponse"
                type: array
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_app_id: invalid app_id

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            Missing, invalid, expired, or revoked bearer credential.

            unauthorized: not authenticated
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "demo_external_read_blocked: external reads are unavailable in the Demo workspace"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: failed to fetch platform accounts"
        "503":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "workspace_policy_unavailable: workspace policy is unavailable"
      security:
        - bearerAuth: []
      summary: Sync Conversations
      tags:
        - Inbox
      x-source: backend/internal/handlers/inbox.go:1425
      x-source-registration: backend/cmd/api/main.go:1977
      x-handler: inboxHandler.SyncConversations
  /api/inbox/conversations/{chatId}/messages/live:
    get:
      description: |-
        handles GET /api/inbox/conversations/{chatId}/messages/live.
        Fetches messages live from Unipile and merges with local ai_draft messages.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: get_api_inbox_conversations_chatId_messages_live
      parameters:
        - in: query
          name: cursor
          schema:
            type: string
        - in: path
          name: chatId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  messages:
                    items:
                      $ref: "#/components/schemas/handlers_messageResponse"
                    type: array
                  next_cursor:
                    type: string
                  self_avatar_url:
                    type: string
                type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_user_id: invalid user ID

            missing_chat_id: chat ID is required
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            Missing, invalid, expired, or revoked bearer credential.

            unauthorized: not authenticated
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "demo_external_read_blocked: external reads are unavailable in the Demo workspace"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: conversation not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to verify ownership

            internal: failed to fetch live messages
        "503":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "workspace_policy_unavailable: workspace policy is unavailable"
      security:
        - bearerAuth: []
      summary: Live Messages
      tags:
        - Inbox
      x-source: backend/internal/handlers/inbox.go:1784
      x-source-registration: backend/cmd/api/main.go:1980
      x-handler: inboxHandler.LiveMessages
  /api/inbox/conversations/{chatId}/messages/{messageId}/attachments/{attachmentId}:
    get:
      description: |-
        proxies attachment binaries from Unipile.
        Route: GET /api/inbox/conversations/{chatId}/messages/{messageId}/attachments/{attachmentId}
        LinkedIn CDN URLs are auth-scoped, so we fetch via Unipile and stream to the
        client. Authorization is verified by checking the user owns the chat's
        platform account.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: get_api_inbox_conversations_chatId_messages_messageId_attachments_attachmentId
      parameters:
        - in: path
          name: chatId
          required: true
          schema:
            type: string
        - in: path
          name: messageId
          required: true
          schema:
            type: string
        - in: path
          name: attachmentId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          description: Attachment bytes with the upstream Content-Type and Content-Disposition headers.
          content:
            application/octet-stream:
              schema:
                type: string
                format: binary
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_user_id: invalid user ID

            missing_params: chat, message, and attachment IDs are required
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            Missing, invalid, expired, or revoked bearer credential.

            unauthorized: not authenticated
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "demo_external_read_blocked: external reads are unavailable in the Demo workspace"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: conversation not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: failed to verify ownership"
        "502":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "attachment_fetch_failed: failed to fetch attachment"
        "503":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "workspace_policy_unavailable: workspace policy is unavailable"
      security:
        - bearerAuth: []
      summary: Get Message Attachment
      tags:
        - Inbox
      x-source: backend/internal/handlers/inbox.go:1992
      x-source-registration: backend/cmd/api/main.go:1981
      x-handler: inboxHandler.GetMessageAttachment
  /api/inbox/conversations/{chatId}/reply-live:
    post:
      description: |-
        handles POST /api/inbox/conversations/{chatId}/reply-live.
        Sends a reply via Unipile using the chat ID directly (works for all conversations).

        Uses the effective workspace identity and enforces resource ownership.
      operationId: post_api_inbox_conversations_chatId_reply_live
      parameters:
        - in: path
          name: chatId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                body:
                  type: string
              type: object
              required:
                - body
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  status:
                    type: string
                type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_body: invalid request body

            invalid_user_id: invalid user ID

            missing_body: reply body is required

            missing_chat_id: chat ID is required

            no_account: no connected LinkedIn account
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            Missing, invalid, expired, or revoked bearer credential.

            unauthorized: not authenticated
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: conversation not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to verify ownership

            send_error: failed to send reply
      security:
        - bearerAuth: []
      summary: Send Live Reply
      tags:
        - Inbox
      x-required-input-fields:
        - body
      x-source: backend/internal/handlers/inbox.go:1911
      x-source-registration: backend/cmd/api/main.go:1982
      x-handler: inboxHandler.SendLiveReply
  /api/inbox/conversations/{id}:
    get:
      description: |-
        handles GET /api/inbox/conversations/{id}.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: get_api_inbox_conversations_id
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/handlers_conversationDetailResponse"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid conversation ID

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: conversation not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: failed to fetch messages"
      security:
        - bearerAuth: []
      summary: Get Conversation
      tags:
        - Inbox
      x-source: backend/internal/handlers/inbox.go:491
      x-source-registration: backend/cmd/api/main.go:1979
      x-handler: inboxHandler.GetConversation
  /api/inbox/conversations/{id}/ai-reply:
    delete:
      description: |-
        handles DELETE /api/inbox/conversations/{id}/ai-reply.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: delete_api_inbox_conversations_id_ai_reply
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "204":
          description: No content.
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid conversation ID

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: conversation not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: failed to dismiss AI draft"
      security:
        - bearerAuth: []
      summary: Dismiss AIDraft
      tags:
        - Inbox
      x-source: backend/internal/handlers/inbox.go:1384
      x-source-registration: backend/cmd/api/main.go:1986
      x-handler: inboxHandler.DismissAIDraft
    post:
      description: |-
        handles POST /api/inbox/conversations/{id}/ai-reply.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: post_api_inbox_conversations_id_ai_reply
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                context:
                  type: string
                draft_intent:
                  type: string
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/handlers_messageResponse"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            context_too_long: reply context is too long

            invalid_body: invalid reply generation request

            invalid_draft_intent: invalid reply draft intent

            invalid_id: invalid conversation ID

            invalid_user_id: invalid user ID

            missing_booking_link: booking link is required for meeting drafts
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: conversation not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            ai_error: failed to generate AI reply

            internal: failed to fetch messages

            internal: failed to load user profile

            internal: failed to store AI reply

            specialist_audit_failed: failed to complete Reply Assistant audit

            specialist_audit_failed: failed to record Reply Assistant action audit

            specialist_audit_failed: failed to record Reply Assistant context audit

            specialist_audit_failed: failed to record Reply Assistant model audit

            specialist_audit_failed: failed to start Reply Assistant audit
        "503":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "reply_unavailable: reply service is unavailable"
      security:
        - bearerAuth: []
      summary: Generate Reply
      tags:
        - Inbox
      x-source: backend/internal/handlers/inbox.go:599
      x-source-registration: backend/cmd/api/main.go:1984
      x-handler: inboxHandler.GenerateReply
  /api/inbox/conversations/{id}/read:
    post:
      description: |-
        handles POST /api/inbox/conversations/{id}/read.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: post_api_inbox_conversations_id_read
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "204":
          description: No content.
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid conversation ID

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: failed to mark conversation as read"
      security:
        - bearerAuth: []
      summary: Mark Read
      tags:
        - Inbox
      x-source: backend/internal/handlers/inbox.go:566
      x-source-registration: backend/cmd/api/main.go:1983
      x-handler: inboxHandler.MarkRead
  /api/inbox/conversations/{id}/reply:
    post:
      description: |-
        handles POST /api/inbox/conversations/{id}/reply.
        Accepts either JSON body (text-only) or multipart/form-data (text + optional file attachment).

        Uses the effective workspace identity and enforces resource ownership.
      operationId: post_api_inbox_conversations_id_reply
      parameters:
        - description: Idempotency key for this request.
          in: header
          name: Idempotency-Key
          schema:
            type: string
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      requestBody:
        content:
          multipart/form-data:
            schema:
              properties:
                attachment:
                  format: binary
                  type: string
                body:
                  type: string
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/handlers_messageResponse"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            file_too_large: attachment must be 10MB or less

            invalid_body: failed to parse multipart form (max 10MB)

            invalid_body: invalid request body

            invalid_id: invalid conversation ID

            invalid_user_id: invalid user ID

            missing_body: reply body is required

            read_error: failed to read attachment
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: conversation not found"
        "409":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            reply_changed: reply changed or already has an attempt

            reply_delivery_uncertain: check the provider and thread before another send
        "422":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "reply_unavailable: this reply is unavailable; check the sender, latest message, body, and product"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "reply_state_unknown: reply sent; refresh the thread"
        "503":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "reply_unavailable: reply service is unavailable"
      security:
        - bearerAuth: []
      summary: Send Reply
      tags:
        - Inbox
      x-source: backend/internal/handlers/inbox.go:1023
      x-source-registration: backend/cmd/api/main.go:1985
      x-handler: inboxHandler.SendReply
  /api/inbox/unread-count:
    get:
      description: |-
        handles GET /api/inbox/unread-count.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: get_api_inbox_unread_count
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  count:
                    type: integer
                type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_user_id: invalid user ID"
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: failed to count unread conversations"
      security:
        - bearerAuth: []
      summary: Unread Count
      tags:
        - Inbox
      x-source: backend/internal/handlers/inbox.go:1965
      x-source-registration: backend/cmd/api/main.go:1978
      x-handler: inboxHandler.UnreadCount
      parameters:
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
  /api/insights:
    get:
      description: |-
        handles GET /api/insights?tab=open|snoozed|done|all.

        Default tab is `open`. Unknown tabs fall through to `open` rather than
        400; the frontend may send an experimental value during development and a
        safe default produces a less confusing UX than an error.
      operationId: get_api_insights
      parameters:
        - in: query
          name: tab
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: "#/components/schemas/handlers_insightDTO"
                type: array
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_user_id: invalid user ID"
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: failed to list insights"
      security:
        - bearerAuth: []
      summary: List Insights
      tags:
        - Insights
      x-source: backend/internal/handlers/insights.go:198
      x-source-registration: backend/cmd/api/main.go:1871
      x-handler: insightsHandler.ListInsights
  /api/insights/count:
    get:
      description: |-
        handles GET /api/insights/count.

        Returns the same `open` set as ListInsights(tab=open). Powers the sidebar
        badge so it stays in sync with the Open tab — no risk of a "5 in the
        badge but only 3 in the list" mismatch.
      operationId: get_api_insights_count
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  open:
                    type: integer
                type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_user_id: invalid user ID"
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: failed to count insights"
      security:
        - bearerAuth: []
      summary: Count Open Insights
      tags:
        - Insights
      x-source: backend/internal/handlers/insights.go:282
      x-source-registration: backend/cmd/api/main.go:1872
      x-handler: insightsHandler.CountOpenInsights
      parameters:
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
  /api/insights/{id}/apply:
    post:
      description: |-
        handles POST /api/insights/{id}/apply.

        Executes the proposed_changes_json payload that an auto-detector wrote
        into the insight row, then flips the insight's status to applied and
        records the change set + pre-change snapshot in insight_applications
        (Layer 4 undo will read the snapshot). The whole thing runs inside one
        transaction so a partial apply cannot leave the insight in a phantom
        applied state.

        Handles agent-scoped insights (disable_signals / adjust_score_floor) and
        campaign-scoped insights (activate_campaign, via applyCampaignActivation).
        Account/app-scoped insights have no mechanical writer yet and are
        rejected with 400.

        Access matches UpdateInsightStatus exactly: effective-workspace scope via
        GetInsightForUser (404 on cross-user).
      operationId: post_api_insights_id_apply
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/handlers_insightDTO"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_user_id: invalid user ID

            invalid_apply_payload: disable_signals must reference existing signal IDs

            invalid_score_floor: score_floor must be between 0 and 100

            invalid_transition: 

            scope_mismatch: insight scope does not match a known agent

            scope_mismatch: insight scope does not match a known campaign

            unsupported_apply_payload: this insight has no apply action

            unsupported_scope: only agent- or campaign-scoped insights can be applied

            : 

            invalid_transition: 

            scope_mismatch: insight scope does not match a known campaign

            unsupported_apply_payload: this insight has no apply action

            invalid_apply_payload: disable_signals must reference existing signal IDs

            invalid_id: invalid insight ID

            invalid_score_floor: score_floor must be between 0 and 100

            no_apply_payload: this insight has no apply action

            scope_mismatch: insight scope does not match a known agent

            unsupported_apply_payload: adding signals is not supported by one-click apply yet

            unsupported_scope: only agent- or campaign-scoped insights can be applied
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            unauthorized: not authenticated

            Missing, invalid, expired, or revoked bearer credential.

            Missing, invalid, expired, or revoked bearer credential.

            Missing, invalid, expired, or revoked bearer credential.
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "subscription_required: Start your subscription to use this feature. Visit Settings → Billing to continue."
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: insight not found"
        "409":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            insight_already_applied: this insight is no longer open

            insight_stale: this campaign is no longer in draft; the insight is out of date

            insight_already_applied: this insight is no longer open
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            simulation_error: failed to build Demo simulation

            internal: failed to validate signals

            internal: couldn't verify subscription state

            internal: failed to activate campaign

            internal: failed to begin transaction

            internal: failed to check activation prerequisites

            internal: failed to commit apply

            internal: failed to flip insight status

            internal: failed to load campaign

            internal: failed to marshal applied changes

            internal: failed to marshal snapshot

            internal: failed to write audit row

            internal: failed to begin transaction

            internal: failed to commit apply

            internal: failed to disable signal

            internal: failed to flip insight status

            internal: failed to load agent

            internal: failed to load insight

            internal: failed to marshal applied changes

            internal: failed to marshal snapshot

            internal: failed to snapshot signals

            internal: failed to update score floor

            internal: failed to write audit row
        "503":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "workspace_policy_unavailable: workspace policy is unavailable"
      security:
        - bearerAuth: []
      summary: Apply Insight
      tags:
        - Insights
      x-source: backend/internal/handlers/insights.go:446
      x-source-registration: backend/cmd/api/main.go:1874
      x-handler: insightsHandler.ApplyInsight
  /api/insights/{id}/status:
    patch:
      description: |-
        handles PATCH /api/insights/{id}/status.

        Allowed transitions in v1:

          - new      → dismissed   (user clicked "Dismiss")
          - new      → applied     (user clicked "I did this")
          - new      → new + snooze_until set ("Snooze 7d" or "Snooze 30d")
          - any      → archived    (admin-side only; the API surface accepts it
            for symmetry with future admin tooling, but
            the user-facing UI never emits archived)

        snooze_until in the past is rejected; the rule is "snooze must move the
        row out of the Open list", and a past timestamp would leave it visible.
      operationId: patch_api_insights_id_status
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                snooze_until:
                  anyOf:
                    - format: date-time
                      type: string
                    - type: "null"
                status:
                  type: string
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                anyOf:
                  - $ref: "#/components/schemas/demosubmit_Response"
                  - $ref: "#/components/schemas/handlers_insightDTO"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_user_id: invalid user ID

            invalid_body: invalid request body

            invalid_id: invalid insight ID

            invalid_snooze: snooze_until must be in the future

            invalid_status: status must be new, dismissed, applied, or archived

            invalid_transition: cannot reopen a dismissed or applied insight
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            unauthorized: not authenticated

            Missing, invalid, expired, or revoked bearer credential.

            Missing, invalid, expired, or revoked bearer credential.
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: insight not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            simulation_error: failed to build Demo simulation

            internal: failed to load insight

            internal: failed to update insight
        "503":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "workspace_policy_unavailable: workspace policy is unavailable"
      security:
        - bearerAuth: []
      summary: Update Insight Status
      tags:
        - Insights
      x-source: backend/internal/handlers/insights.go:319
      x-source-registration: backend/cmd/api/main.go:1873
      x-handler: insightsHandler.UpdateInsightStatus
  /api/integrations:
    get:
      description: |-
        returns all installed integrations for the user plus the available catalog.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: get_api_integrations
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  available:
                    items:
                      $ref: "#/components/schemas/handlers_CatalogEntry"
                    type: array
                  installed:
                    items:
                      $ref: "#/components/schemas/sqlc_Integration"
                    type: array
                type: object
          description: OK
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_user: Invalid user ID"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "db_error: Failed to fetch integrations"
      security:
        - bearerAuth: []
      summary: List integrations
      tags:
        - Integrations
      x-source: backend/internal/handlers/integrations.go:169
      x-source-registration: backend/cmd/api/main.go:1825
      x-handler: integrationsHandler.List
      parameters:
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
    post:
      description: |-
        creates a new integration from the catalog.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: post_api_integrations
      requestBody:
        content:
          application/json:
            schema:
              properties:
                config:
                  additionalProperties:
                    description: JSON value; the server permits arbitrary JSON at this field.
                  type: object
                events:
                  description: |-
                    Events is honored for type=webhook installs only — these become the
                    webhook_subscription's enabled events. Defaults to all supported events
                    when omitted so users get a sensible "subscribe to everything" baseline.
                  items:
                    type: string
                  type: array
                name:
                  type: string
                type:
                  type: string
              type: object
              required:
                - name
        required: true
      responses:
        "201":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/sqlc_Integration"
          description: Created
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            coming_soon: 

            invalid_event: 

            invalid_integration: Integration not found in catalog

            invalid_json: Invalid request body

            invalid_url: 

            missing_field: 

            missing_field: name is required

            use_clay_endpoint: Install Clay via POST /api/integrations/clay/install
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_user: Invalid user ID"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            db_error: Failed to install integration

            internal: 

            internal: Failed to serialize config
      security:
        - bearerAuth: []
      summary: Install
      tags:
        - Integrations
      x-required-input-fields:
        - name
      x-source: backend/internal/handlers/integrations.go:192
      x-source-registration: backend/cmd/api/main.go:1826
      x-handler: integrationsHandler.Install
      parameters:
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
  /api/integrations/clay/events:
    get:
      description: |-
        returns the caller's own Clay inbound event log, most recent
        first. Scoped by user_id in the SQL WHERE clause, matching every other
        per-user list query in this package.
        GET /api/integrations/clay/events

        Uses the effective workspace identity and enforces resource ownership.
      operationId: get_api_integrations_clay_events
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: "#/components/schemas/sqlc_ClayInboundEvent"
                type: array
          description: OK
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_user: Invalid user ID"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "db_error: Failed to fetch Clay events"
      security:
        - bearerAuth: []
      summary: List Events
      tags:
        - Integrations
      x-source: backend/internal/handlers/clay_install.go:121
      x-source-registration: backend/cmd/api/main.go:1833
      x-handler: clayHandler.ListEvents
      parameters:
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
  /api/integrations/clay/events/{eventID}/confirm:
    post:
      description: |-
        is the explicit human review-gate step (Terry's Task 4 checkpoint
        resolution, Plan 10-01, option-b): it is the only code path that calls
        decisionmakers.Service.Confirm for a Clay-sourced job, spending real
        credits. JWT-authenticated; GetClayInboundEventByID is scoped by user_id
        so confirming another user's event is impossible -- it 404s exactly like a
        nonexistent event.
        POST /api/integrations/clay/events/{eventID}/confirm

        Uses the effective workspace identity and enforces resource ownership.
      operationId: post_api_integrations_clay_events_eventID_confirm
      parameters:
        - in: path
          name: eventID
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                list_name:
                  type: string
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/sqlc_CompanyDecisionMakerImportJob"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_request: invalid company import request

            too_many_companies: Select up to 250 companies for one search.

            invalid_body: invalid review list name

            invalid_id: invalid event ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            Missing, invalid, expired, or revoked bearer credential.

            invalid_user: Invalid user ID
        "402":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "insufficient_credits: not enough Funkel AI credits to start this search"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: Clay event not found"
        "409":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "already_processed: this Clay event has already been processed"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: company import could not be completed"
      security:
        - bearerAuth: []
      summary: Confirm clay
      tags:
        - Integrations
      x-source: backend/internal/handlers/clay_inbound.go:211
      x-source-registration: backend/cmd/api/main.go:1834
      x-handler: clayHandler.Confirm
  /api/integrations/clay/install:
    post:
      description: |-
        mints a per-user Clay inbound connection: it verifies the caller
        owns the given app, generates a 32-byte bearer token, persists only its
        SHA-256 hash, and returns the raw token exactly once.
        POST /api/integrations/clay/install   body: {app_id}

        Uses the effective workspace identity and enforces resource ownership.
      operationId: post_api_integrations_clay_install
      requestBody:
        content:
          application/json:
            schema:
              properties:
                app_id:
                  type: string
              type: object
        required: true
      responses:
        "201":
          content:
            application/json:
              schema:
                properties:
                  inbound_url:
                    type: string
                  integration_id:
                    format: uuid
                    type:
                      - string
                      - "null"
                  token:
                    type: string
                type: object
          description: Created
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_app_id: Invalid app_id

            invalid_json: Invalid request body
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_user: Invalid user ID"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            db_error: Failed to install Clay connector

            internal: Failed to generate token

            internal: Failed to serialize config
      security:
        - bearerAuth: []
      summary: Install
      tags:
        - Integrations
      x-source: backend/internal/handlers/clay_install.go:44
      x-source-registration: backend/cmd/api/main.go:1832
      x-handler: clayHandler.Install
      parameters:
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
  /api/integrations/verify:
    post:
      description: |-
        is the "Send test" endpoint — it runs the provider's verify routine
        (typically a real round-trip that posts a test message) so the user gets
        instant confirmation before saving. Used by the install dialog and the
        manage dialog. POST body: {name, config}.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: post_api_integrations_verify
      requestBody:
        content:
          application/json:
            schema:
              properties:
                config:
                  additionalProperties:
                    description: JSON value; the server permits arbitrary JSON at this field.
                  type: object
                name:
                  type: string
              type: object
              required:
                - name
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                anyOf:
                  - properties:
                      error:
                        type: string
                      success:
                        type: boolean
                    type: object
                  - properties:
                      message:
                        type: string
                      success:
                        type: boolean
                    type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_json: Invalid request body

            missing_field: name is required
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_user: Invalid user ID"
      security:
        - bearerAuth: []
      summary: Verify
      tags:
        - Integrations
      x-required-input-fields:
        - name
      x-source: backend/internal/handlers/integrations.go:387
      x-source-registration: backend/cmd/api/main.go:1827
      x-handler: integrationsHandler.Verify
      parameters:
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
  /api/integrations/{id}:
    delete:
      description: |-
        removes an integration and its webhook subscriptions (cascades via FK).

        Uses the effective workspace identity and enforces resource ownership.
      operationId: delete_api_integrations_id
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "204":
          description: No content.
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_id: Invalid integration ID"
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_user: Invalid user ID"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "db_error: Failed to uninstall integration"
      security:
        - bearerAuth: []
      summary: Uninstall
      tags:
        - Integrations
      x-source: backend/internal/handlers/integrations.go:561
      x-source-registration: backend/cmd/api/main.go:1831
      x-handler: integrationsHandler.Uninstall
    get:
      description: |-
        returns an integration with its webhook subscriptions.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: get_api_integrations_id
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/handlers_integrationDetailResponse"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_id: Invalid integration ID"
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_user: Invalid user ID"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: Integration not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "db_error: Failed to fetch webhooks"
      security:
        - bearerAuth: []
      summary: Get integrations
      tags:
        - Integrations
      x-source: backend/internal/handlers/integrations.go:629
      x-source-registration: backend/cmd/api/main.go:1828
      x-handler: integrationsHandler.Get
    put:
      description: |-
        edits an installed integration's config. Secret-flagged fields are
        re-encrypted when the user supplies a new value; if a secret field is
        missing or empty in the request, the previously-stored value is preserved
        (so users don't have to re-paste API tokens to change a non-secret toggle).
        For webhook-routed integrations, the linked webhook_subscription URL is
        kept in sync.
        PUT /api/integrations/{id}   body: {config}

        Uses the effective workspace identity and enforces resource ownership.
      operationId: put_api_integrations_id
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                config:
                  additionalProperties:
                    description: JSON value; the server permits arbitrary JSON at this field.
                  type: object
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/sqlc_Integration"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: Invalid integration ID

            invalid_json: Invalid request body

            invalid_url: 

            unknown_integration: Integration not in catalog
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_user: Invalid user ID"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: Integration not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            db_error: Failed to update integration

            internal: 

            internal: Failed to serialize config
      security:
        - bearerAuth: []
      summary: Update integrations
      tags:
        - Integrations
      x-source: backend/internal/handlers/integrations.go:438
      x-source-registration: backend/cmd/api/main.go:1829
      x-handler: integrationsHandler.Update
  /api/integrations/{id}/test:
    post:
      description: |-
        re-runs the provider verify against an already-installed
        integration's stored config. Used by the Manage dialog's "Send test" button
        so secrets never leave the server.
        POST /api/integrations/{id}/test

        Uses the effective workspace identity and enforces resource ownership.
      operationId: post_api_integrations_id_test
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                anyOf:
                  - properties:
                      error:
                        type: string
                      success:
                        type: boolean
                    type: object
                  - properties:
                      message:
                        type: string
                      success:
                        type: boolean
                    type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_id: Invalid integration ID"
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_user: Invalid user ID"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: Integration not found"
      security:
        - bearerAuth: []
      summary: Verify By ID
      tags:
        - Integrations
      x-source: backend/internal/handlers/integrations.go:341
      x-source-registration: backend/cmd/api/main.go:1830
      x-handler: integrationsHandler.VerifyByID
  /api/leads/{id}:
    patch:
      description: |-
        handles PATCH /api/leads/{id}. Currently only supports moving a
        contact to a different list within the same app -- either a
        campaign-bound lead or a pre-campaign list member, whichever the ID
        resolves to -- and adjusts lead_count on the source + destination lists
        in the same transaction so the per-list counters stay in sync.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: patch_api_leads_id
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                list_id:
                  description: |-
                    ListID: when non-empty, moves the target contact to this list. The
                    contact may be a campaign-bound `leads` row or a pre-campaign
                    `lead_list_members` row -- whichever the ID resolves to. Required to
                    belong to the same app as the contact and to the same user. Other
                    fields (status, etc.) live on dedicated endpoints; this struct intentionally
                    stays narrow until we have a real reason to widen it.
                  type: string
              type: object
              required:
                - list_id
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                anyOf:
                  - $ref: "#/components/schemas/demosubmit_Response"
                  - properties:
                      id:
                        type: string
                      list_id:
                        type: string
                      prev_list_id:
                        anyOf:
                          - type: string
                          - type: "null"
                    type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            app_mismatch: destination list must be in the same app as the lead

            invalid_body: invalid request body

            invalid_id: invalid lead ID

            invalid_list_id: invalid list ID

            invalid_user_id: invalid user ID

            missing_list_id: list_id is required
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            Missing, invalid, expired, or revoked bearer credential.

            unauthorized: not authenticated
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_assigned: this app is not assigned to you"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            not_found: destination list not found

            not_found: lead not found
        "409":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "duplicate_in_destination: a contact with the same identity already exists in the destination list"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            simulation_error: failed to build Demo simulation

            internal: failed to begin transaction

            internal: failed to commit transaction

            internal: failed to decrement source list count

            internal: failed to increment destination list count
        "503":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "workspace_policy_unavailable: workspace policy is unavailable"
      security:
        - bearerAuth: []
      summary: Patch
      tags:
        - Leads
      x-required-input-fields:
        - list_id
      x-source: backend/internal/handlers/leads.go:46
      x-source-registration: backend/cmd/api/main.go:1698
      x-handler: leadsHandler.Patch
  /api/leads/{leadId}/mark-replied:
    post:
      description: |-
        handles POST /api/leads/{leadId}/mark-replied. Lead-scoped
        (not nested under a campaign route) so it works the same way from any
        surface that only has a lead ID -- the campaign contacts tab, the contact
        detail sliding panel, and the global contacts page all use it.

        Manual override for channels where a reply can be real but structurally
        invisible to automated sync -- e.g. an X/Twitter DM thread that flips to
        end-to-end encryption becomes permanently unreadable via X's API (quick
        task 260721-xdi). Marks the lead replied and cancels its remaining
        scheduled steps, same as an auto-detected reply would.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: post_api_leads_leadId_mark_replied
      parameters:
        - in: path
          name: leadId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  cancelled_count:
                    type: integer
                type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid lead ID

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            not_found: campaign not found

            not_found: lead not found
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to cancel pending actions

            internal: failed to count cancelled actions

            internal: failed to mark lead replied
      security:
        - bearerAuth: []
      summary: Mark Lead Replied
      tags:
        - Leads
      x-source: backend/internal/handlers/scheduled_actions.go:949
      x-source-registration: backend/cmd/api/main.go:1799
      x-handler: scheduledActionsHandler.MarkLeadReplied
  /api/leads/{leadId}/meetings:
    get:
      description: List For Lead for this resource.
      operationId: get_api_leads_leadId_meetings
      parameters:
        - in: path
          name: leadId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/handlers_leadMeetingsResponse"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_user_id: invalid user ID

            invalid_lead_id: invalid lead ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            unauthorized: not authenticated

            Missing, invalid, expired, or revoked bearer credential.
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: meeting not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to load meeting

            internal: failed to fetch meetings
      security:
        - bearerAuth: []
      summary: List For Lead
      tags:
        - Leads
      x-source: backend/internal/handlers/meetings.go:74
      x-source-registration: backend/cmd/api/main.go:1798
      x-handler: meetingsHandler.ListForLead
    post:
      description: Create For Lead for this resource.
      operationId: post_api_leads_leadId_meetings
      parameters:
        - in: path
          name: leadId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                scheduled_end_at:
                  anyOf:
                    - type: string
                    - type: "null"
                scheduled_start_at:
                  anyOf:
                    - type: string
                    - type: "null"
                status:
                  type: string
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  meeting:
                    $ref: "#/components/schemas/handlers_meetingResponse"
                type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_user_id: invalid user ID

            invalid_body: invalid request body

            invalid_schedule: 

            invalid_scheduled_end_at: scheduled_end_at must be an RFC3339 timestamp

            invalid_scheduled_start_at: scheduled_start_at must be an RFC3339 timestamp

            invalid_status: unsupported meeting status

            invalid_lead_id: invalid lead ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            unauthorized: not authenticated

            Missing, invalid, expired, or revoked bearer credential.

            Missing, invalid, expired, or revoked bearer credential.
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: meeting not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to load meeting

            internal: failed to save meeting
      security:
        - bearerAuth: []
      summary: Create For Lead
      tags:
        - Leads
      x-source: backend/internal/handlers/meetings.go:105
      x-source-registration: backend/cmd/api/main.go:1801
      x-handler: meetingsHandler.CreateForLead
  /api/leads/{leadId}/reenroll:
    post:
      description: |-
        handles POST /api/leads/{leadId}/reenroll. Lead-scoped, same
        shape as MarkLeadReplied, so it works from any surface that only has a
        lead ID (Action detail sheet, Contact detail panel).

        Manual re-enroll of a withdrawn lead. Honors LinkedIn's reinvite cool-off
        (schedules the resulting invite for cool_off_until if that's still in the
        future) but never touches metadata.auto_reenroll_count -- manual re-enroll
        is deliberately uncapped and never counts against, or is blocked by, the
        automatic path's retry cap (quick task 260915-rs1).

        Uses the effective workspace identity and enforces resource ownership.
      operationId: post_api_leads_leadId_reenroll
      parameters:
        - in: path
          name: leadId
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  status:
                    type: string
                type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid lead ID

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_assigned: this app is not assigned to you"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            not_found: campaign not found

            not_found: lead not found
        "409":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            no_send_route: assign a connected LinkedIn sender to this campaign before re-enrolling this lead

            not_withdrawn: lead is no longer withdrawn

            not_withdrawn: lead is not currently withdrawn
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: db pool not available

            internal: failed to re-enroll lead
      security:
        - bearerAuth: []
      summary: Reenroll Lead
      tags:
        - Leads
      x-source: backend/internal/handlers/scheduled_actions.go:1020
      x-source-registration: backend/cmd/api/main.go:1800
      x-handler: scheduledActionsHandler.ReenrollLead
  /api/lists:
    get:
      description: |-
        handles GET /api/lists (all lists across apps)

        Uses the effective workspace identity and enforces resource ownership.
      operationId: get_api_lists
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: "#/components/schemas/sqlc_LeadList"
                type: array
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_user_id: invalid user ID"
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "db_error: failed to list lead lists"
      security:
        - bearerAuth: []
      summary: List All
      tags:
        - Lead lists
      x-source: backend/internal/handlers/lead_lists.go:88
      x-source-registration: backend/cmd/api/main.go:1661
      x-handler: leadListsHandler.ListAll
      parameters:
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
  /api/lists/{id}:
    delete:
      description: |-
        handles DELETE /api/lists/{id} by archiving the list.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: delete_api_lists_id
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                anyOf:
                  - $ref: "#/components/schemas/demosubmit_Response"
                  - properties:
                      message:
                        type: string
                    type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid list ID

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            Missing, invalid, expired, or revoked bearer credential.

            unauthorized: not authenticated
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_assigned: this app is not assigned to you"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: list not found"
        "409":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "default_list_cannot_be_archived: the default list cannot be archived"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            simulation_error: failed to build Demo simulation

            archive_failed: failed to archive list

            db_error: failed to load list
        "503":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "workspace_policy_unavailable: workspace policy is unavailable"
      security:
        - bearerAuth: []
      summary: Delete lead lists
      tags:
        - Lead lists
      x-source: backend/internal/handlers/lead_lists.go:259
      x-source-registration: backend/cmd/api/main.go:1665
      x-handler: leadListsHandler.Delete
    get:
      description: |-
        handles GET /api/lists/{id} — list metadata + per-status counts.
        The counts power the lists table badges so the frontend doesn't need
        a second roundtrip per row.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: get_api_lists_id
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  list:
                    $ref: "#/components/schemas/sqlc_LeadList"
                  status_counts:
                    additionalProperties:
                      type: integer
                    type: object
                type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid list ID

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_assigned: this app is not assigned to you"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: list not found"
      security:
        - bearerAuth: []
      summary: Get lead lists
      tags:
        - Lead lists
      x-source: backend/internal/handlers/lead_lists.go:328
      x-source-registration: backend/cmd/api/main.go:1662
      x-handler: leadListsHandler.Get
    put:
      description: |-
        handles PUT /api/lists/{id}

        Uses the effective workspace identity and enforces resource ownership.
      operationId: put_api_lists_id
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                description:
                  type: string
                name:
                  type: string
              type: object
              required:
                - name
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                anyOf:
                  - $ref: "#/components/schemas/demosubmit_Response"
                  - $ref: "#/components/schemas/sqlc_LeadList"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_body: invalid request body

            invalid_id: invalid list ID

            invalid_user_id: invalid user ID

            missing_name: name is required
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            Missing, invalid, expired, or revoked bearer credential.

            unauthorized: not authenticated
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_assigned: this app is not assigned to you"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: list not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            simulation_error: failed to build Demo simulation

            db_error: failed to load list

            db_error: failed to update list
        "503":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "workspace_policy_unavailable: workspace policy is unavailable"
      security:
        - bearerAuth: []
      summary: Update lead lists
      tags:
        - Lead lists
      x-required-input-fields:
        - name
      x-source: backend/internal/handlers/lead_lists.go:189
      x-source-registration: backend/cmd/api/main.go:1664
      x-handler: leadListsHandler.Update
  /api/lists/{id}/leads:
    get:
      description: |-
        handles GET /api/lists/{id}/leads — paginated list members.
        Query params: limit (default 50, max 200), offset (default 0).

        Uses the effective workspace identity and enforces resource ownership.
      operationId: get_api_lists_id_leads
      parameters:
        - in: query
          name: limit
          schema:
            type: string
        - in: query
          name: offset
          schema:
            type: string
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: "#/components/schemas/sqlc_ListLeadsByListRow"
                type: array
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid list ID

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_assigned: this app is not assigned to you"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: list not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "db_error: failed to list leads"
      security:
        - bearerAuth: []
      summary: List Leads
      tags:
        - Lead lists
      x-source: backend/internal/handlers/lead_lists.go:372
      x-source-registration: backend/cmd/api/main.go:1663
      x-handler: leadListsHandler.ListLeads
  /api/mcp/approvals/approve:
    post:
      description: >-
        acquires one token and executes only its fingerprinted args.


        Uses the authenticated user's identity.


        Requires a logged-in session JWT with a session ID, in your own workspace. Personal access tokens and MCP OAuth
        tokens cannot approve actions.
      operationId: post_api_mcp_approvals_approve
      requestBody:
        content:
          application/json:
            schema:
              properties:
                app_id:
                  type: string
                token:
                  type: string
              type: object
              required:
                - app_id
                - token
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  result:
                    description: JSON value stored by the server; shape depends on this resource's configuration.
                  state:
                    type: string
                type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_approval: invalid account or product

            invalid_approval: token and app_id are required
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: Missing, invalid, expired, or revoked bearer credential.
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            session_required: sign in to your own workspace to approve

            demo_workspace_read_only: Demo workspace tools are read-only
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            product_not_found: product not found

            approval_not_found: approval not found
        "409":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "approval_invalid: approval is no longer pending"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            approval_failed: approval failed

            approval_state_unknown: action result could not be stored; do not retry
        "503":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            product_scope_unavailable: product scope is unavailable

            approval_unavailable: approval is unavailable

            workspace_policy_unavailable: workspace policy is unavailable
      security:
        - sessionJWT: []
      summary: Approve Action
      tags:
        - Mcp
      x-required-input-fields:
        - app_id
        - token
      x-source: backend/internal/handlers/mcp.go:925
      x-source-registration: backend/cmd/api/main.go:1973
      x-handler: mcpHandler.ApproveAction
  /api/mcp/approvals/inspect:
    post:
      description: >-
        returns the owned product name, a readable preview, and

        exact stored arguments for a separate human review. The browser presentation

        never changes the fingerprinted arguments used by ApproveAction.


        Requires a logged-in session JWT with a session ID, in your own workspace. Personal access tokens and MCP OAuth
        tokens cannot approve actions.
      operationId: post_api_mcp_approvals_inspect
      requestBody:
        content:
          application/json:
            schema:
              properties:
                app_id:
                  type: string
                token:
                  type: string
              type: object
              required:
                - app_id
                - token
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                allOf:
                  - $ref: "#/components/schemas/aiagent_ConfirmationView"
                  - properties:
                      preview:
                        type: string
                      product_name:
                        type: string
                    required:
                      - product_name
                      - preview
                    type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_approval: invalid account or product

            invalid_approval: token and app_id are required
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: Missing, invalid, expired, or revoked bearer credential.
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "session_required: sign in to your own workspace to approve"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            approval_not_found: approval not found

            product_not_found: product not found
        "503":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            approval_unavailable: approval is unavailable

            product_scope_unavailable: product scope is unavailable
      security:
        - sessionJWT: []
      summary: Inspect Approval
      tags:
        - Mcp
      x-required-input-fields:
        - app_id
        - token
      x-source: backend/internal/handlers/mcp.go:878
      x-source-registration: backend/cmd/api/main.go:1972
      x-handler: mcpHandler.InspectApproval
  /api/me/active-agents:
    get:
      description: |-
        handles GET /api/me/active-agents. Returns the set of agents
        owned by the current user that have an in-flight discovery or enrichment
        job in River right now. Polled every few seconds by the frontend toast.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: get_api_me_active_agents
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: "#/components/schemas/handlers_runningAgentResponse"
                type: array
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_user_id: invalid user ID"
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: failed to load active agents"
      security:
        - bearerAuth: []
      summary: Active Agents
      tags:
        - Me
      x-source: backend/internal/handlers/agents.go:1643
      x-source-registration: backend/cmd/api/main.go:1647
      x-handler: agentsHandler.ActiveAgents
      parameters:
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
  /api/me/active-offer:
    get:
      description: |-
        handles GET /api/me/active-offer. Returns the redeemable
        private offer targeting the logged-in user's email so the paywall can swap
        its full-price subscribe CTA for a "resume your discounted offer" button
        when the user abandoned Stripe checkout after starting an offer flow.
        Returns an empty body when there is no active offer (frontend reads token).

        Uses the authenticated user (never X-Acting-As) — offers are keyed to the
        real signed-in email, not the workspace they happen to be acting in.

        Uses the authenticated user's identity.
      operationId: get_api_me_active_offer
      responses:
        "200":
          content:
            application/json:
              schema:
                anyOf:
                  - additionalProperties:
                      type: string
                    type: object
                  - properties:
                      public_title:
                        type: string
                      token:
                        type: string
                    type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_user_id: invalid user ID"
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to load user

            internal: failed to look up offer
      security:
        - bearerAuth: []
      summary: Get Active Offer For Me
      tags:
        - Me
      x-source: backend/internal/handlers/offers.go:436
      x-source-registration: backend/cmd/api/main.go:1919
      x-handler: offersHandler.GetActiveOfferForMe
      parameters:
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
  /api/me/announcements:
    get:
      description: handles GET /api/me/announcements?surface=dashboard_home.
      operationId: get_api_me_announcements
      parameters:
        - in: query
          name: surface
          schema:
            type: string
        - in: query
          name: limit
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: "#/components/schemas/handlers_announcementDTO"
                type: array
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_user_id: invalid user ID

            invalid_surface: invalid announcement surface
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "db_error: failed to fetch announcements"
      security:
        - bearerAuth: []
      summary: List announcements
      tags:
        - Me
      x-source: backend/internal/handlers/announcements.go:77
      x-source-registration: backend/cmd/api/main.go:1654
      x-handler: announcementsHandler.List
  /api/me/announcements/{id}/cta-click:
    post:
      description: handles POST /api/me/announcements/{id}/cta-click.
      operationId: post_api_me_announcements_id_cta_click
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "204":
          description: No content.
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_user_id: invalid user ID

            invalid_id: invalid announcement ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: announcement not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "db_error: failed to track announcement CTA"
      security:
        - bearerAuth: []
      summary: CTAClick
      tags:
        - Me
      x-source: backend/internal/handlers/announcements.go:142
      x-source-registration: backend/cmd/api/main.go:1656
      x-handler: announcementsHandler.CTAClick
  /api/me/announcements/{id}/dismiss:
    post:
      description: handles POST /api/me/announcements/{id}/dismiss.
      operationId: post_api_me_announcements_id_dismiss
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "204":
          description: No content.
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_user_id: invalid user ID

            invalid_id: invalid announcement ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: announcement not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "db_error: failed to dismiss announcement"
      security:
        - bearerAuth: []
      summary: Dismiss
      tags:
        - Me
      x-source: backend/internal/handlers/announcements.go:117
      x-source-registration: backend/cmd/api/main.go:1655
      x-handler: announcementsHandler.Dismiss
  /api/me/entitlement:
    get:
      description: |-
        handles GET /api/me/entitlement.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: get_api_me_entitlement
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/handlers_entitlementResponse"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_user_id: invalid user ID"
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "user_not_found: user not found"
      security:
        - bearerAuth: []
      summary: Get entitlement
      tags:
        - Account usage
      x-source: backend/internal/handlers/entitlement.go:55
      x-source-registration: backend/cmd/api/main.go:1920
      x-handler: entitlementHandler.Get
      parameters:
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
  /api/me/notifications:
    get:
      description: handles GET /api/me/notifications?limit=30.
      operationId: get_api_me_notifications
      parameters:
        - in: query
          name: limit
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: "#/components/schemas/handlers_notificationDTO"
                type: array
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_user_id: invalid user ID"
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "db_error: Failed to fetch notifications"
      security:
        - bearerAuth: []
      summary: List notifications
      tags:
        - Me
      x-source: backend/internal/handlers/notifications.go:57
      x-source-registration: backend/cmd/api/main.go:1650
      x-handler: notificationsHandler.List
  /api/me/notifications/read-all:
    post:
      description: handles POST /api/me/notifications/read-all.
      operationId: post_api_me_notifications_read_all
      responses:
        "204":
          description: No content.
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_user_id: invalid user ID"
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "db_error: Failed to mark all read"
      security:
        - bearerAuth: []
      summary: Mark All Read
      tags:
        - Me
      x-source: backend/internal/handlers/notifications.go:119
      x-source-registration: backend/cmd/api/main.go:1653
      x-handler: notificationsHandler.MarkAllRead
      parameters:
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
  /api/me/notifications/unread-count:
    get:
      description: handles GET /api/me/notifications/unread-count.
      operationId: get_api_me_notifications_unread_count
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  count:
                    type: integer
                type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_user_id: invalid user ID"
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "db_error: Failed to fetch unread count"
      security:
        - bearerAuth: []
      summary: Unread Count
      tags:
        - Me
      x-source: backend/internal/handlers/notifications.go:87
      x-source-registration: backend/cmd/api/main.go:1651
      x-handler: notificationsHandler.UnreadCount
      parameters:
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
  /api/me/notifications/{id}/read:
    post:
      description: handles POST /api/me/notifications/{id}/read.
      operationId: post_api_me_notifications_id_read
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "204":
          description: No content.
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_user_id: invalid user ID

            invalid_id: invalid notification ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "db_error: Failed to mark read"
      security:
        - bearerAuth: []
      summary: Mark Read
      tags:
        - Me
      x-source: backend/internal/handlers/notifications.go:101
      x-source-registration: backend/cmd/api/main.go:1652
      x-handler: notificationsHandler.MarkRead
  /api/me/recent-signal-runs:
    get:
      description: |-
        handles GET /api/me/recent-signal-runs?since=<unix_ms>.
        Powers the global "agent activity" toast — frontend polls this and toasts
        each new run it hasn't seen. Defaults to last 5 minutes when since is missing.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: get_api_me_recent_signal_runs
      parameters:
        - in: query
          name: since
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  properties:
                    agent_id:
                      type: string
                    agent_name:
                      type: string
                    label:
                      type: string
                    last_run_at_ms:
                      type: integer
                    signal_category:
                      type: string
                    signal_id:
                      type: string
                    signal_key:
                      type: string
                  required:
                    - signal_id
                    - agent_id
                    - agent_name
                    - signal_key
                    - signal_category
                    - label
                    - last_run_at_ms
                  type: object
                type: array
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_user_id: invalid user ID"
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "db_error: Failed to fetch recent runs"
      security:
        - bearerAuth: []
      summary: Recent Signal Runs
      tags:
        - Me
      x-source: backend/internal/handlers/agents.go:1162
      x-source-registration: backend/cmd/api/main.go:1646
      x-handler: agentsHandler.RecentSignalRuns
  /api/me/workspaces:
    get:
      description: |-
        handles GET /api/me/workspaces. Returns the memberships the
        logged-in user has — the frontend uses this to render a workspace switcher.
        Always uses the AUTH user id (never the acting-as id) so a member sees their
        own list of workspaces, not the owner's.

        Uses the authenticated user's identity.
      operationId: get_api_me_workspaces
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/handlers_workspacesResponse"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_user_id: invalid user ID"
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: failed to list workspaces"
      security:
        - bearerAuth: []
      summary: List Workspaces
      tags:
        - Me
      x-source: backend/internal/handlers/teams.go:1315
      x-source-registration: backend/cmd/api/main.go:1515
      x-handler: teamsHandler.ListWorkspaces
  /api/me/workspaces/demo/join:
    post:
      description: |-
        handles POST /api/me/workspaces/demo/join. The client
        selects no owner, role, products, or capabilities. The server resolves and
        locks the singleton Demo owner before it repairs one fixed membership.

        Uses the authenticated user's identity.
      operationId: post_api_me_workspaces_demo_join
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/handlers_workspaceSummary"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_user_id: invalid user ID"
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "409":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "demo_workspace_owner: Demo workspace owner cannot join"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to begin transaction

            internal: failed to commit Demo membership
        "503":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "demo_workspace_unavailable: Demo workspace is unavailable"
      security:
        - bearerAuth: []
      summary: Join Demo Workspace
      tags:
        - Me
      x-source: backend/internal/handlers/teams.go:182
      x-source-registration: backend/cmd/api/main.go:1516
      x-handler: teamsHandler.JoinDemoWorkspace
  /api/me/workspaces/{member_id}:
    delete:
      description: |-
        handles DELETE /api/me/workspaces/{member_id}.

        This is deliberately separate from RemoveMember: a member may remove only
        their own membership, and the authenticated user id must come from the JWT
        even when X-Acting-As changes the effective workspace owner. Membership
        deletion and its canonical "removed" audit event are one transaction.

        Uses the authenticated user's identity.
      operationId: delete_api_me_workspaces_member_id
      parameters:
        - in: path
          name: member_id
          required: true
          schema:
            type: string
      responses:
        "204":
          description: No content.
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid member ID

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: workspace membership not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to begin transaction

            internal: failed to commit transaction

            internal: failed to leave workspace

            internal: failed to lock workspace membership

            internal: failed to write audit
      security:
        - bearerAuth: []
      summary: Leave Workspace
      tags:
        - Me
      x-source: backend/internal/handlers/teams.go:801
      x-source-registration: backend/cmd/api/main.go:1517
      x-handler: teamsHandler.LeaveWorkspace
  /api/meetings/{id}:
    patch:
      description: Update meetings for this resource.
      operationId: patch_api_meetings_id
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                scheduled_end_at:
                  anyOf:
                    - type: string
                    - type: "null"
                scheduled_start_at:
                  anyOf:
                    - type: string
                    - type: "null"
                status:
                  type: string
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  meeting:
                    $ref: "#/components/schemas/handlers_meetingResponse"
                type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_user_id: invalid user ID

            invalid_body: invalid request body

            invalid_schedule: 

            invalid_scheduled_end_at: scheduled_end_at must be an RFC3339 timestamp

            invalid_scheduled_start_at: scheduled_start_at must be an RFC3339 timestamp

            invalid_status: unsupported meeting status

            invalid_meeting_id: invalid meeting ID

            invalid_schedule: 
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            unauthorized: not authenticated

            Missing, invalid, expired, or revoked bearer credential.

            Missing, invalid, expired, or revoked bearer credential.

            Missing, invalid, expired, or revoked bearer credential.
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: meeting not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: failed to load meeting"
      security:
        - bearerAuth: []
      summary: Update meetings
      tags:
        - Meetings
      x-source: backend/internal/handlers/meetings.go:152
      x-source-registration: backend/cmd/api/main.go:1802
      x-handler: meetingsHandler.Update
  /api/onboarding/progress:
    get:
      description: |-
        returns booleans for each onboarding milestone for the current user.

        Uses the authenticated user's identity.
      operationId: get_api_onboarding_progress
      parameters:
        - in: query
          name: scope
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/handlers_OnboardingProgress"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_user_id: invalid user ID"
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: failed to load progress"
      security:
        - bearerAuth: []
      summary: Get Progress
      tags:
        - Onboarding
      x-source: backend/internal/handlers/onboarding.go:32
      x-source-registration: backend/cmd/api/main.go:1877
      x-handler: onboardingHandler.GetProgress
  /api/operator/memory:
    get:
      description: >-
        List operator memory for this resource.


        Rate limit: 20 requests per minute, shared with routes in this middleware group, keyed by authenticated user, or
        IP when unauthenticated.
      operationId: get_api_operator_memory
      parameters:
        - in: query
          name: app_id
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: "#/components/schemas/sqlc_AgentMemory"
                type: array
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_app: a valid app_id is required

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "429":
          description: Rate limit exceeded. Wait before retrying.
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "operator_memory_failed: could not load Operator memory"
      security:
        - bearerAuth: []
      summary: List operator memory
      tags:
        - Operator
      x-source: backend/internal/handlers/operator_memory.go:40
      x-source-registration: backend/cmd/api/main.go:1965
      x-handler: operatorMemoryHandler.List
    put:
      description: >-
        Upsert for this resource.


        Rate limit: 20 requests per minute, shared with routes in this middleware group, keyed by authenticated user, or
        IP when unauthenticated.
      operationId: put_api_operator_memory
      requestBody:
        content:
          application/json:
            schema:
              properties:
                app_id:
                  type: string
                key:
                  type: string
                reason:
                  type: string
                source:
                  type: string
                value:
                  description: JSON value stored by the server; shape depends on this resource's configuration.
              type: object
              required:
                - key
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/sqlc_AgentMemory"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_app: a valid app_id is required

            invalid_user_id: invalid user ID

            invalid_memory: app_id, key, and JSON value are required

            invalid_memory: memory key, value, or reason is invalid

            invalid_source: memory source is invalid
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "app_not_found: product not found"
        "429":
          description: Rate limit exceeded. Wait before retrying.
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "operator_memory_failed: could not save Operator memory"
      security:
        - bearerAuth: []
      summary: Upsert
      tags:
        - Operator
      x-required-input-fields:
        - key
      x-source: backend/internal/handlers/operator_memory.go:52
      x-source-registration: backend/cmd/api/main.go:1966
      x-handler: operatorMemoryHandler.Upsert
      parameters:
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
  /api/operator/memory/{memoryID}:
    delete:
      description: >-
        Delete operator memory for this resource.


        Rate limit: 20 requests per minute, shared with routes in this middleware group, keyed by authenticated user, or
        IP when unauthenticated.
      operationId: delete_api_operator_memory_memoryID
      parameters:
        - in: query
          name: app_id
          schema:
            type: string
        - in: path
          name: memoryID
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "204":
          description: No content.
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_app: a valid app_id is required

            invalid_user_id: invalid user ID

            invalid_memory: invalid memory id
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "429":
          description: Rate limit exceeded. Wait before retrying.
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "operator_memory_failed: could not clear Operator memory"
      security:
        - bearerAuth: []
      summary: Delete operator memory
      tags:
        - Operator
      x-source: backend/internal/handlers/operator_memory.go:84
      x-source-registration: backend/cmd/api/main.go:1967
      x-handler: operatorMemoryHandler.Delete
  /api/operator/notification-preferences:
    put:
      description: >-
        is the sole opt-in path for external Operator

        email. It is intentionally an explicit PUT; absence of a row means off.


        Rate limit: 20 requests per minute, shared with routes in this middleware group, keyed by authenticated user, or
        IP when unauthenticated.
      operationId: put_api_operator_notification_preferences
      requestBody:
        content:
          application/json:
            schema:
              properties:
                app_id:
                  type: string
                category:
                  type: string
                enabled:
                  type: boolean
                frequency:
                  type: string
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/sqlc_OperatorNotificationPreference"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_app: a valid app_id is required

            invalid_user_id: invalid user ID

            invalid_notification_preference: valid app_id, category, and frequency are required
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "429":
          description: Rate limit exceeded. Wait before retrying.
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "operator_notification_preference_failed: could not save notification preference"
      security:
        - bearerAuth: []
      summary: Set Notification Preference
      tags:
        - Operator
      x-source: backend/internal/handlers/operator_memory.go:103
      x-source-registration: backend/cmd/api/main.go:1968
      x-handler: operatorMemoryHandler.SetNotificationPreference
      parameters:
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
  /api/operator/outcomes:
    get:
      description: >-
        Uses the effective workspace identity and enforces resource ownership.


        Rate limit: 20 requests per minute, shared with routes in this middleware group, keyed by authenticated user, or
        IP when unauthenticated.
      operationId: get_api_operator_outcomes
      parameters:
        - in: query
          name: app_id
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  evidence:
                    description: JSON value stored by the server; shape depends on this resource's configuration.
                  outcomes:
                    items:
                      $ref: "#/components/schemas/operatoroutcomes_Outcome"
                    type: array
                type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_app: a valid app_id is required

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "app_not_found: product not found"
        "429":
          description: Rate limit exceeded. Wait before retrying.
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "operator_outcomes_failed: could not load live outcomes"
      security:
        - bearerAuth: []
      summary: Get operator outcomes
      tags:
        - Operator
      x-source: backend/internal/handlers/operator_outcomes.go:22
      x-source-registration: backend/cmd/api/main.go:1964
      x-handler: operatorOutcomesHandler.Get
  /api/operator/tasks:
    get:
      description: List operator tasks for this resource.
      operationId: get_api_operator_tasks
      parameters:
        - in: query
          name: app_id
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: "#/components/schemas/handlers_operatorTaskView"
                type: array
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_app: app_id is required

            invalid_user: invalid user id
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "operator_tasks_failed: could not load tasks"
      security:
        - bearerAuth: []
      summary: List operator tasks
      tags:
        - Operator
      x-source: backend/internal/handlers/operator_tasks.go:89
      x-source-registration: backend/cmd/api/main.go:1958
      x-handler: operatorTasksHandler.List
  /api/operator/tasks/{taskID}:
    get:
      description: Get operator tasks for this resource.
      operationId: get_api_operator_tasks_taskID
      parameters:
        - in: query
          name: app_id
          schema:
            type: string
        - in: path
          name: taskID
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  actions:
                    items:
                      $ref: "#/components/schemas/handlers_operatorActionView"
                    type: array
                  steps:
                    items:
                      $ref: "#/components/schemas/handlers_operatorStepView"
                    type: array
                  task:
                    $ref: "#/components/schemas/handlers_operatorTaskView"
                type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_app: app_id is required

            invalid_user: invalid user id

            invalid_task: invalid task id
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "task_not_found: task not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            operator_tasks_failed: could not load actions

            operator_tasks_failed: could not load task
      security:
        - bearerAuth: []
      summary: Get operator tasks
      tags:
        - Operator
      x-source: backend/internal/handlers/operator_tasks.go:105
      x-source-registration: backend/cmd/api/main.go:1959
      x-handler: operatorTasksHandler.Get
  /api/operator/tasks/{taskID}/actions:
    get:
      description: Actions for this resource.
      operationId: get_api_operator_tasks_taskID_actions
      parameters:
        - in: query
          name: app_id
          schema:
            type: string
        - in: path
          name: taskID
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  actions:
                    items:
                      $ref: "#/components/schemas/handlers_operatorActionView"
                    type: array
                  steps:
                    items:
                      $ref: "#/components/schemas/handlers_operatorStepView"
                    type: array
                  task:
                    $ref: "#/components/schemas/handlers_operatorTaskView"
                type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_app: app_id is required

            invalid_user: invalid user id

            invalid_task: invalid task id
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "task_not_found: task not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            operator_tasks_failed: could not load actions

            operator_tasks_failed: could not load task
      security:
        - bearerAuth: []
      summary: Actions
      tags:
        - Operator
      x-source: backend/internal/handlers/operator_tasks.go:140
      x-source-registration: backend/cmd/api/main.go:1960
      x-handler: operatorTasksHandler.Actions
  /api/operator/tasks/{taskID}/events:
    get:
      description: Events for this resource.
      operationId: get_api_operator_tasks_taskID_events
      parameters:
        - in: query
          name: app_id
          schema:
            type: string
        - in: query
          name: after_sequence
          schema:
            type: string
        - in: path
          name: taskID
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            text/event-stream:
              schema:
                type: string
          description: Server-sent event stream.
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_app: app_id is required

            invalid_user: invalid user id

            invalid_sequence: after_sequence must be non-negative

            invalid_task: invalid task id
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "task_not_found: task not found"
      security:
        - bearerAuth: []
      summary: Events
      tags:
        - Operator
      x-source: backend/internal/handlers/operator_tasks.go:267
      x-source-registration: backend/cmd/api/main.go:1961
      x-handler: operatorTasksHandler.Events
  /api/operator/tasks/{taskID}/retry:
    post:
      description: Uses the effective workspace identity and enforces resource ownership.
      operationId: post_api_operator_tasks_taskID_retry
      parameters:
        - in: path
          name: taskID
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                app_id:
                  type: string
                expected_version:
                  type: integer
                step_id:
                  type: string
              type: object
              required:
                - app_id
                - step_id
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  step_id:
                    type: string
                  task:
                    $ref: "#/components/schemas/handlers_operatorTaskView"
                type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_app: app_id is required

            invalid_user: invalid user id

            invalid_body: app_id, expected_version, and step_id are required

            invalid_step: invalid step id

            invalid_task: invalid task id
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            step_not_found: step not found

            task_not_found: task not found
        "409":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            retry_not_queued: task could not be retried safely; refresh and try again

            step_not_retryable: only a recoverable failed step can be retried
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "operator_tasks_failed: task retried but could not be reloaded"
        "503":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "retry_unavailable: task retry is not configured"
      security:
        - bearerAuth: []
      summary: Retry operator tasks
      tags:
        - Operator
      x-required-input-fields:
        - app_id
        - capability_id
        - idempotency_key
        - input_json
        - step_id
      x-source: backend/internal/handlers/operator_tasks.go:209
      x-source-registration: backend/cmd/api/main.go:1971
      x-handler: operatorTasksHandler.Retry
  /api/operator/tasks/{taskID}/{operation}:
    post:
      description: Uses the effective workspace identity and enforces resource ownership.
      operationId: post_api_operator_tasks_taskID_operation_pause_resume_cancel
      parameters:
        - in: path
          name: taskID
          required: true
          schema:
            type: string
        - in: path
          name: operation
          required: true
          schema:
            pattern: ^(pause|resume|cancel)$
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                app_id:
                  type: string
                expected_version:
                  type: integer
                step_id:
                  type: string
              type: object
              required:
                - app_id
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  task:
                    $ref: "#/components/schemas/handlers_operatorTaskView"
                type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_app: app_id is required

            invalid_user: invalid user id

            invalid_body: app_id and expected_version are required

            invalid_task: invalid task id
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            not_found: unknown operation

            task_not_found: task not found
        "409":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_task_state: task cannot perform that operation

            resume_not_queued: task could not be resumed safely; refresh and try again

            stale_version: task changed; refresh and try again
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            operator_tasks_failed: task changed but could not be reloaded

            operator_tasks_failed: task resumed but could not be reloaded
        "503":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            resume_unavailable: task resume is not configured

            task_control_unavailable: task control is not configured
      security:
        - bearerAuth: []
      summary: Control
      tags:
        - Operator
      x-required-input-fields:
        - app_id
      x-source: backend/internal/handlers/operator_tasks.go:142
      x-source-registration: backend/cmd/api/main.go:1970
      x-handler: operatorTasksHandler.Control
  /api/platform-accounts:
    get:
      description: |-
        returns all platform accounts for the authenticated user.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: get_api_platform_accounts
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: "#/components/schemas/handlers_publicPlatformAccount"
                type: array
          description: OK
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_user: Invalid user ID"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "db_error: Failed to fetch platform accounts"
      security:
        - bearerAuth: []
      summary: List platform accounts
      tags:
        - Platform Accounts
      x-source: backend/internal/handlers/platform_accounts.go:97
      x-source-registration: backend/cmd/api/main.go:1749
      x-handler: platformAccountsHandler.List
      parameters:
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
    post:
      description: |-
        adds a new platform account with pending status (no real OAuth in Phase 2).

        Uses the effective workspace identity and enforces resource ownership.
      operationId: post_api_platform_accounts
      requestBody:
        content:
          application/json:
            schema:
              properties:
                display_name:
                  type: string
                platform:
                  type: string
              type: object
              required:
                - display_name
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  error:
                    properties:
                      code:
                        type: string
                      message:
                        type: string
                      platform:
                        type: string
                      upgrade_required:
                        type: boolean
                    type: object
                type: object
          description: OK
        "201":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/handlers_publicPlatformAccount"
          description: Created
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_json: Invalid request body

            invalid_platform: Platform must be one of: linkedin, x, twitter, reddit, email

            missing_field: display_name is required
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_user: Invalid user ID"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "db_error: Failed to create platform account"
      security:
        - bearerAuth: []
      summary: Create platform accounts
      tags:
        - Platform Accounts
      x-required-input-fields:
        - display_name
      x-source: backend/internal/handlers/platform_accounts.go:122
      x-source-registration: backend/cmd/api/main.go:1750
      x-handler: platformAccountsHandler.Create
      parameters:
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
  /api/platform-accounts/{id}:
    delete:
      description: |-
        disconnects a platform account while preserving local send history.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: delete_api_platform_accounts_id
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/handlers_deletePlatformAccountResponse"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_id: Invalid account ID"
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_user: Invalid user ID"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: Platform account not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            db_error: Failed to disconnect platform account

            db_error: Failed to hide disconnected platform account
        "502":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "provider_disconnect_failed: Failed to disconnect the provider account. Try again."
      security:
        - bearerAuth: []
      summary: Delete platform accounts
      tags:
        - Platform Accounts
      x-source: backend/internal/handlers/platform_accounts.go:211
      x-source-registration: backend/cmd/api/main.go:1751
      x-handler: platformAccountsHandler.Delete
  /api/platform-accounts/{id}/connect:
    post:
      description: |-
        initiates the Unipile hosted auth flow for a pending platform account.
        Returns a JSON object with the hosted auth URL that the frontend redirects to.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: post_api_platform_accounts_id_connect
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  url:
                    type: string
                type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            account_deleted: This account was deleted. Add a new account instead.

            already_connected: Account is already connected

            invalid_id: Invalid account ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            Missing, invalid, expired, or revoked bearer credential.

            invalid_user: Invalid user ID
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "subscription_required: Start your subscription to use this feature. Visit Settings → Billing to continue."
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: Platform account not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: couldn't verify subscription state

            encryption_not_configured: integration encryption is not configured

            internal: failed to create oauth state

            internal: failed to create oauth verifier

            internal: Failed to generate auth nonce

            internal: Failed to store auth nonce

            unipile_error: Failed to get auth link
        "503":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "x_oauth_not_configured: X account connection is not configured yet"
      security:
        - bearerAuth: []
      summary: Connect
      tags:
        - Platform Accounts
      x-source: backend/internal/handlers/platform_accounts.go:401
      x-source-registration: backend/cmd/api/main.go:1752
      x-handler: platformAccountsHandler.Connect
  /api/platform-accounts/{id}/email-signature:
    patch:
      description: |-
        handles PATCH /api/platform-accounts/{id}/email-signature.
        The signature lives on the email sender account so campaign sends and Inbox
        replies use the same saved footer.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: patch_api_platform_accounts_id_email_signature
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                signature:
                  anyOf:
                    - type: string
                    - type: "null"
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  email_signature:
                    type: string
                type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_body: invalid request body

            invalid_id: invalid account ID

            invalid_platform: signatures can only be saved on email accounts

            invalid_user_id: invalid user ID

            missing_signature: signature is required

            signature_too_long: signature must be 5000 characters or fewer
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: account not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: failed to update email signature"
      security:
        - bearerAuth: []
      summary: Update Email Signature
      tags:
        - Platform Accounts
      x-source: backend/internal/handlers/platform_accounts.go:748
      x-source-registration: backend/cmd/api/main.go:1756
      x-handler: platformAccountsHandler.UpdateEmailSignature
  /api/platform-accounts/{id}/enrich:
    post:
      description: |-
        handles POST /api/platform-accounts/{id}/enrich.
        Re-fetches profile data from Unipile and updates metadata (e.g. profile picture).

        Uses the effective workspace identity and enforces resource ownership.
      operationId: post_api_platform_accounts_id_enrich
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  status:
                    type: string
                type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid account ID

            invalid_user_id: invalid user ID

            missing_ids: account not fully connected
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: account not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "enrich_failed: failed to fetch profile from LinkedIn"
      security:
        - bearerAuth: []
      summary: Enrich
      tags:
        - Platform Accounts
      x-source: backend/internal/handlers/platform_accounts.go:822
      x-source-registration: backend/cmd/api/main.go:1757
      x-handler: platformAccountsHandler.Enrich
  /api/platform-accounts/{id}/invite-withdrawal:
    patch:
      description: |-
        handles PATCH /api/platform-accounts/{id}/invite-withdrawal.
        Configures auto-withdrawal of stale pending LinkedIn invitations, and its
        sibling setting: whether a withdrawn lead is automatically re-invited once
        its cool-off period passes (ParseResendAfterCooloffSetting,
        lead_reenrollment.go). Both settings describe one lifecycle (withdraw after
        N days -> resend after cool-off) and live on the account for the same
        reason withdraw_after_days does: LinkedIn's re-invite rules are account
        health, not campaign-scoped. Reads optional `enabled`, `days`, and
        `resend_after_cooloff` from the body. Days are clamped server-side to [3, 30].

        Uses the effective workspace identity and enforces resource ownership.
      operationId: patch_api_platform_accounts_id_invite_withdrawal
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                days:
                  anyOf:
                    - type: integer
                    - type: "null"
                enabled:
                  anyOf:
                    - type: boolean
                    - type: "null"
                resend_after_cooloff:
                  anyOf:
                    - type: boolean
                    - type: "null"
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  resend_after_cooloff:
                    description: JSON value; the server permits arbitrary JSON at this field.
                  withdraw_after_days:
                    description: JSON value; the server permits arbitrary JSON at this field.
                  withdraw_pending_invites:
                    description: JSON value; the server permits arbitrary JSON at this field.
                type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_body: invalid request body

            invalid_id: invalid account ID

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: account not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: failed to update setting"
      security:
        - bearerAuth: []
      summary: Update Invite Withdrawal
      tags:
        - Platform Accounts
      x-source: backend/internal/handlers/platform_accounts.go:669
      x-source-registration: backend/cmd/api/main.go:1755
      x-handler: platformAccountsHandler.UpdateInviteWithdrawal
  /api/platform-accounts/{id}/limits:
    patch:
      description: |-
        handles PATCH /api/platform-accounts/{id}/limits.
        Updates daily invite and message limits stored in the account metadata.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: patch_api_platform_accounts_id_limits
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                weekly_invite_limit:
                  anyOf:
                    - type: integer
                    - type: "null"
                weekly_message_limit:
                  anyOf:
                    - type: integer
                    - type: "null"
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  weekly_invite_limit:
                    description: JSON value; the server permits arbitrary JSON at this field.
                  weekly_message_limit:
                    description: JSON value; the server permits arbitrary JSON at this field.
                type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_body: invalid request body

            invalid_id: invalid account ID

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: account not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: failed to update limits"
      security:
        - bearerAuth: []
      summary: Update Limits
      tags:
        - Platform Accounts
      x-source: backend/internal/handlers/platform_accounts.go:518
      x-source-registration: backend/cmd/api/main.go:1753
      x-handler: platformAccountsHandler.UpdateLimits
  /api/platform-accounts/{id}/setup-status:
    get:
      description: |-
        reports whether a connected sender is already wired into
        campaigns. The settings page uses this to avoid showing setup CTAs after an
        X account is assigned to a campaign whose Lead Finder is listening on X.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: get_api_platform_accounts_id_setup_status
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/handlers_platformAccountSetupStatusResponse"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_id: Invalid account ID"
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_user: Invalid user ID"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: Platform account not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "db_error: Failed to fetch platform account setup"
      security:
        - bearerAuth: []
      summary: Setup Status
      tags:
        - Platform Accounts
      x-source: backend/internal/handlers/platform_accounts.go:286
      x-source-registration: backend/cmd/api/main.go:1759
      x-handler: platformAccountsHandler.SetupStatus
  /api/platform-accounts/{id}/usage:
    get:
      description: |-
        handles GET /api/platform-accounts/{id}/usage.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: get_api_platform_accounts_id_usage
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/handlers_usageResponse"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_id: Invalid account ID"
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_user: Invalid user ID"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: Platform account not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            db_error: Failed to fetch X usage

            db_error: Failed to fetch history

            db_error: Failed to fetch usage
      security:
        - bearerAuth: []
      summary: Usage
      tags:
        - Platform Accounts
      x-source: backend/internal/handlers/platform_accounts.go:903
      x-source-registration: backend/cmd/api/main.go:1758
      x-handler: platformAccountsHandler.Usage
  /api/platform-accounts/{id}/warmup-skip:
    patch:
      description: |-
        handles PATCH /api/platform-accounts/{id}/warmup-skip.
        User-attestable opt-out of the 14-day warmup ramp. Used when the LinkedIn
        account has been actively used outside this tool and is already trusted by
        LinkedIn. Risky if used on a fresh account — the UI must warn before calling.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: patch_api_platform_accounts_id_warmup_skip
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                skip:
                  type: boolean
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                properties:
                  warmup_skip:
                    type: boolean
                type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_body: invalid request body

            invalid_id: invalid account ID

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: account not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: failed to update warmup setting"
      security:
        - bearerAuth: []
      summary: Update Warmup Skip
      tags:
        - Platform Accounts
      x-source: backend/internal/handlers/platform_accounts.go:602
      x-source-registration: backend/cmd/api/main.go:1754
      x-handler: platformAccountsHandler.UpdateWarmupSkip
  /api/scheduled-actions/{id}:
    get:
      description: |-
        handles GET /api/scheduled-actions/{id}.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: get_api_scheduled_actions_id
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/handlers_ActionDetailResponse"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid action ID

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            not_found: action not found

            not_found: campaign not found
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to fetch lead

            internal: failed to load message preparation
      security:
        - bearerAuth: []
      summary: Get Action
      tags:
        - Scheduled Actions
      x-source: backend/internal/handlers/scheduled_actions.go:1130
      x-source-registration: backend/cmd/api/main.go:1770
      x-handler: scheduledActionsHandler.GetAction
  /api/scheduled-actions/{id}/approve:
    post:
      description: |-
        handles POST /api/scheduled-actions/{id}/approve.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: post_api_scheduled_actions_id_approve
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "204":
          description: No content.
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid action ID

            invalid_transition: can only approve pending actions

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            not_found: action not found

            not_found: campaign not found
        "409":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "lead_excluded: "
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to approve action

            internal: failed to check latest exclusions
      security:
        - bearerAuth: []
      summary: Approve Action
      tags:
        - Scheduled Actions
      x-source: backend/internal/handlers/scheduled_actions.go:299
      x-source-registration: backend/cmd/api/main.go:1771
      x-handler: scheduledActionsHandler.ApproveAction
  /api/scheduled-actions/{id}/skip:
    post:
      description: |-
        handles POST /api/scheduled-actions/{id}/skip.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: post_api_scheduled_actions_id_skip
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "204":
          description: No content.
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid action ID

            invalid_transition: can only skip pending or approved actions

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            not_found: action not found

            not_found: campaign not found
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: failed to skip action"
      security:
        - bearerAuth: []
      summary: Skip Action
      tags:
        - Scheduled Actions
      x-source: backend/internal/handlers/scheduled_actions.go:365
      x-source-registration: backend/cmd/api/main.go:1772
      x-handler: scheduledActionsHandler.SkipAction
  /api/table-views:
    get:
      description: handles GET /api/table-views?app_id=...&table_key=...
      operationId: get_api_table_views
      parameters:
        - in: query
          name: app_id
          schema:
            type: string
        - in: query
          name: table_key
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: "#/components/schemas/sqlc_TableView"
                type: array
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_app: a valid app_id is required

            invalid_user_id: invalid user ID

            invalid_table_key: table_key is required
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "table_views_failed: could not load saved views"
      security:
        - bearerAuth: []
      summary: List table views
      tags:
        - Table Views
      x-source: backend/internal/handlers/table_views.go:39
      x-source-registration: backend/cmd/api/main.go:1782
      x-handler: tableViewsHandler.List
    post:
      description: |-
        handles POST /api/table-views. Creating/renaming a view with the
        same (app_id, table_key, name) as an existing one updates it in place.
      operationId: post_api_table_views
      requestBody:
        content:
          application/json:
            schema:
              properties:
                app_id:
                  type: string
                config:
                  description: JSON value stored by the server; shape depends on this resource's configuration.
                is_default:
                  type: boolean
                name:
                  type: string
                table_key:
                  type: string
              type: object
              required:
                - name
                - table_key
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/sqlc_TableView"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_app: a valid app_id is required

            invalid_user_id: invalid user ID

            invalid_table_view: app_id, table_key, name, and a valid JSON config are required
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "table_views_failed: could not save view"
      security:
        - bearerAuth: []
      summary: Upsert
      tags:
        - Table Views
      x-required-input-fields:
        - name
        - table_key
      x-source: backend/internal/handlers/table_views.go:59
      x-source-registration: backend/cmd/api/main.go:1783
      x-handler: tableViewsHandler.Upsert
      parameters:
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
  /api/table-views/{id}:
    delete:
      description: |-
        handles DELETE /api/table-views/{id}.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: delete_api_table_views_id
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "204":
          description: No content.
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_user_id: invalid user ID

            invalid_view_id: invalid view id
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "table_views_failed: could not delete view"
      security:
        - bearerAuth: []
      summary: Delete table views
      tags:
        - Table Views
      x-source: backend/internal/handlers/table_views.go:115
      x-source-registration: backend/cmd/api/main.go:1784
      x-handler: tableViewsHandler.Delete
  /api/team/invitations:
    get:
      description: |-
        handles GET /api/team/invitations.

        Read-gate: requires "manage_team". Owners always pass (own workspace);
        admins-with-manage-team pass under acting-as; plain members do not.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: get_api_team_invitations
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: "#/components/schemas/handlers_inviteResponse"
                type: array
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_user_id: invalid user ID"
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: Missing, invalid, expired, or revoked bearer credential.
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "manage_team_required: you need the Manage Team capability to view invitations"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: failed to list invitations"
      security:
        - bearerAuth: []
      summary: List Pending Invitations
      tags:
        - Team
      x-source: backend/internal/handlers/teams.go:432
      x-source-registration: backend/cmd/api/main.go:1913
      x-handler: teamsHandler.ListPendingInvitations
      parameters:
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
    post:
      description: |-
        handles POST /api/team/invitations.

        Uniform write gate (see SPEC Section 7 of the team-admin phase):
         1. require capability "manage_team"
         5. if incoming role=admin or any capability bool=true → require "promote_admins"

        (gates 2 and 4 don't apply — no existing member to self-edit or admin-target).
        Mutation + audit insert in one transaction; email send after commit so a
        Resend hiccup can't roll back the invitation.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: post_api_team_invitations
      requestBody:
        content:
          application/json:
            schema:
              properties:
                app_ids:
                  items:
                    type: string
                  type: array
                can_manage_billing:
                  type: boolean
                can_manage_team:
                  type: boolean
                can_promote_admins:
                  type: boolean
                email:
                  type: string
                role:
                  type: string
              type: object
        required: true
      responses:
        "201":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/handlers_inviteResponse"
          description: Created
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            caps_require_admin: capabilities can only be granted to admins

            invalid_body: invalid request body

            invalid_email: invalid email address

            invalid_role: role must be 'member' or 'admin'

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            Missing, invalid, expired, or revoked bearer credential.

            unauthorized: not authenticated
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            manage_team_required: you need the Manage Team capability to invite members

            promote_admins_required: you need the Promote Admins capability to invite admins
        "409":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invitation_exists: a pending invitation already exists for this email"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to begin transaction

            internal: failed to commit transaction

            internal: failed to create invitation

            internal: failed to generate invitation token

            internal: failed to write audit
      security:
        - bearerAuth: []
      summary: Invite
      tags:
        - Team
      x-source: backend/internal/handlers/teams.go:278
      x-source-registration: backend/cmd/api/main.go:1912
      x-handler: teamsHandler.Invite
      parameters:
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
  /api/team/invitations/by-token/{token}:
    get:
      description: |-
        handles GET /api/team/invitations/by-token/{token}. No
        auth required — the recipient may not have an account yet. Surfaces just
        enough info for the accept page to render.
      operationId: get_api_team_invitations_by_token_token
      parameters:
        - in: path
          name: token
          required: true
          schema:
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/handlers_publicInvitationResponse"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "missing_token: token is required"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: invitation not found"
      security: []
      summary: Get Public Invitation
      tags:
        - Team
      x-source: backend/internal/handlers/teams.go:1096
      x-source-registration: backend/cmd/api/main.go:1433
      x-handler: teamsHandler.GetPublicInvitation
  /api/team/invitations/by-token/{token}/accept:
    post:
      description: |-
        handles POST /api/team/invitations/by-token/{token}/accept.
        Requires auth — the JWT email must match the invitation email. The whole
        flow (lock invitation, create team_members row, mark invitation accepted,
        write audit) runs in one transaction. The invitation is NEVER deleted —
        only `accepted_at` is set — because team_member_audit.target_invitation_id
        is ON DELETE SET NULL and we want to preserve the trail.

        Uses the authenticated user's identity.
      operationId: post_api_team_invitations_by_token_token_accept
      parameters:
        - in: path
          name: token
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/handlers_acceptInvitationResponse"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_user_id: invalid user ID

            missing_token: token is required

            self_invite: you cannot accept your own invitation
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "email_mismatch: this invitation was sent to a different email address"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: invitation not found"
        "409":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            already_accepted: this invitation has already been accepted

            already_member: you are already a member of this workspace
        "410":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            expired: this invitation has expired

            no_longer_valid: this invitation is no longer valid

            revoked: this invitation has been revoked
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to begin transaction

            internal: failed to commit transaction

            internal: failed to create membership

            internal: failed to load user

            internal: failed to lock invitation

            internal: failed to mark invitation accepted

            internal: failed to write audit
      security:
        - bearerAuth: []
      summary: Accept Invitation
      tags:
        - Team
      x-source: backend/internal/handlers/teams.go:1137
      x-source-registration: backend/cmd/api/main.go:1918
      x-handler: teamsHandler.AcceptInvitation
  /api/team/invitations/{id}:
    delete:
      description: |-
        handles DELETE /api/team/invitations/{id}.

        Uniform write gate:
         1. require capability "manage_team"
         5. if locked invitation has role='admin' or any cap=true → require "promote_admins"

        (gate 2 doesn't apply — invitation targets an email, not a team_members row).
        Mutation + audit insert in one transaction.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: delete_api_team_invitations_id
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "204":
          description: No content.
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid invitation ID

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: Missing, invalid, expired, or revoked bearer credential.
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            manage_team_required: you need the Manage Team capability to revoke invitations

            promote_admins_required: you need the Promote Admins capability to revoke admin invitations
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: invitation not found or no longer pending"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to begin transaction

            internal: failed to commit transaction

            internal: failed to lock invitation

            internal: failed to revoke invitation

            internal: failed to write audit
      security:
        - bearerAuth: []
      summary: Revoke Invitation
      tags:
        - Team
      x-source: backend/internal/handlers/teams.go:465
      x-source-registration: backend/cmd/api/main.go:1914
      x-handler: teamsHandler.RevokeInvitation
  /api/team/members:
    get:
      description: |-
        handles GET /api/team/members.

        Read-gate: requires "manage_team". Same shape as ListPendingInvitations.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: get_api_team_members
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: "#/components/schemas/handlers_memberResponse"
                type: array
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_user_id: invalid user ID"
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: Missing, invalid, expired, or revoked bearer credential.
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "manage_team_required: you need the Manage Team capability to view members"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: failed to list members"
      security:
        - bearerAuth: []
      summary: List Members
      tags:
        - Team
      x-source: backend/internal/handlers/teams.go:554
      x-source-registration: backend/cmd/api/main.go:1915
      x-handler: teamsHandler.ListMembers
      parameters:
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
  /api/team/members/audit:
    get:
      description: |-
        handles GET /api/team/members/audit.

        Owner-only by design: even an admin with full caps can't view the audit
        trail. This guarantees the principal can always see what their admins did,
        even if every admin account has been compromised.

        Uses the effective workspace identity and enforces resource ownership.

        Workspace delegation restrictions apply; see the documented 403 errors.
      operationId: get_api_team_members_audit
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: "#/components/schemas/handlers_auditEntryResponse"
                type: array
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_user_id: invalid user ID"
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: Missing, invalid, expired, or revoked bearer credential.
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "owner_only: only the workspace owner can view the audit log"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: failed to load audit"
      security:
        - bearerAuth: []
      summary: List Member Audit
      tags:
        - Team
      x-source: backend/internal/handlers/teams.go:1036
      x-source-registration: backend/cmd/api/main.go:1922
      x-handler: teamsHandler.ListMemberAudit
      parameters:
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
  /api/team/members/{id}:
    delete:
      description: |-
        handles DELETE /api/team/members/{id}.

        Uniform write gate:
         1. require "manage_team"
         2. forbid self-edit
         5. if locked target has role='admin' or any cap=true → require "promote_admins"

        Mutation + audit insert in one transaction.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: delete_api_team_members_id
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "204":
          description: No content.
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_id: invalid member ID

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: Missing, invalid, expired, or revoked bearer credential.
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            cannot_edit_self: you cannot remove yourself

            manage_team_required: you need the Manage Team capability to remove members

            promote_admins_required: you need the Promote Admins capability to remove admins
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: member not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to begin transaction

            internal: failed to commit transaction

            internal: failed to lock member

            internal: failed to remove member

            internal: failed to write audit
      security:
        - bearerAuth: []
      summary: Remove Member
      tags:
        - Team
      x-source: backend/internal/handlers/teams.go:706
      x-source-registration: backend/cmd/api/main.go:1917
      x-handler: teamsHandler.RemoveMember
    put:
      description: |-
        handles PUT /api/team/members/{id}.

        Uniform write gate:
         1. require "manage_team"
         2. forbid self-edit

        Admin targets are refused with 400 (admins have all apps by role; demote to
        re-scope). Mutation + audit insert in one transaction.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: put_api_team_members_id
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                app_ids:
                  items:
                    type: string
                  type: array
              type: object
        required: true
      responses:
        "204":
          description: No content.
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            cannot_scope_admin_apps: admins have access to all apps. demote to member first to scope app_ids

            invalid_body: invalid request body

            invalid_id: invalid member ID

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: Missing, invalid, expired, or revoked bearer credential.
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            cannot_edit_self: you cannot edit your own membership

            manage_team_required: you need the Manage Team capability to edit members
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: member not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to begin transaction

            internal: failed to commit transaction

            internal: failed to lock member

            internal: failed to update member

            internal: failed to write audit
      security:
        - bearerAuth: []
      summary: Update Member Apps
      tags:
        - Team
      x-source: backend/internal/handlers/teams.go:599
      x-source-registration: backend/cmd/api/main.go:1916
      x-handler: teamsHandler.UpdateMemberApps
  /api/team/members/{id}/role:
    patch:
      description: >-
        handles PATCH /api/team/members/{id}/role.


        Uniform write gate:
         1. require "manage_team"
         2. forbid self-edit
         4. lock target row (any role change OR any cap change OR locked row already admin → also require "promote_admins")

        Atomic CTE returns before+after JSON for the audit diff. Up to two audit

        rows written in the same transaction (role_changed + capabilities_changed).


        Uses the effective workspace identity and enforces resource ownership.
      operationId: patch_api_team_members_id_role
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                can_manage_billing:
                  type: boolean
                can_manage_team:
                  type: boolean
                can_promote_admins:
                  type: boolean
                role:
                  type: string
              type: object
        required: true
      responses:
        "204":
          description: No content.
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            caps_require_admin: capabilities can only be granted to admins

            invalid_body: invalid request body

            invalid_id: invalid member ID

            invalid_role: role must be 'member' or 'admin'

            invalid_user_id: invalid user ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: Missing, invalid, expired, or revoked bearer credential.
        "403":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            cannot_edit_self: you cannot edit your own role or capabilities

            manage_team_required: you need the Manage Team capability to update member roles

            promote_admins_required: you need the Promote Admins capability to change admin roles or capabilities
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: member not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to begin transaction

            internal: failed to commit transaction

            internal: failed to update member

            internal: failed to write caps audit

            internal: failed to write role audit
      security:
        - bearerAuth: []
      summary: Update Member Role
      tags:
        - Team
      x-source: backend/internal/handlers/teams.go:887
      x-source-registration: backend/cmd/api/main.go:1921
      x-handler: teamsHandler.UpdateMemberRole
  /api/user/limits:
    get:
      description: |-
        handles GET /api/user/limits — returns the user's plan limits and current usage.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: get_api_user_limits
      responses:
        "200":
          content:
            application/json:
              schema:
                anyOf:
                  - properties:
                      lead_source_credits_extra:
                        type: integer
                      lead_source_credits_monthly:
                        type: integer
                      max_agents_per_app:
                        type: integer
                      max_agents_total:
                        type: integer
                      max_apps:
                        type: integer
                      max_campaigns_per_app:
                        type: integer
                      max_x_accounts:
                        type: integer
                      unlimited:
                        type: boolean
                    type: object
                  - properties:
                      current_apps:
                        type: integer
                      lead_source_credits_extra:
                        type: integer
                      lead_source_credits_monthly:
                        type: integer
                      max_agents_per_app:
                        type: integer
                      max_agents_total:
                        type: integer
                      max_apps:
                        type: integer
                      max_campaigns_per_app:
                        type: integer
                      max_x_accounts:
                        type: integer
                      unlimited:
                        type: boolean
                    type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_user_id: invalid user ID"
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
      security:
        - bearerAuth: []
      summary: Limits
      tags:
        - Account usage
      x-source: backend/internal/handlers/apps.go:1029
      x-source-registration: backend/cmd/api/main.go:1607
      x-handler: appsHandler.Limits
      parameters:
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
  /api/users/me:
    get:
      description: |-
        returns the logged-in user's profile (Account tab pre-fill source).

        Uses the authenticated user's identity.
      operationId: get_api_users_me
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/handlers_userProfileResponse"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_user_id: invalid user id"
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: user not found"
      security:
        - bearerAuth: []
      summary: Get Me
      tags:
        - Users
      x-source: backend/internal/handlers/users.go:88
      x-source-registration: backend/cmd/api/main.go:1927
      x-handler: usersHandler.GetMe
      parameters:
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
    patch:
      description: |-
        persists Account-tab edits.

        Uses the authenticated user's identity.
      operationId: patch_api_users_me
      requestBody:
        content:
          application/json:
            schema:
              properties:
                booking_link:
                  type: string
                digest_emails_enabled:
                  type: boolean
                first_name:
                  type: string
                language:
                  type: string
                last_name:
                  type: string
                timezone:
                  type: string
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/handlers_userProfileResponse"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_body: invalid request body

            invalid_booking_link: booking link must be a valid http or https URL

            invalid_language: unsupported language

            invalid_timezone: invalid IANA timezone

            invalid_user_id: invalid user id

            missing_name: first and last name are required

            name_too_long: names must be 100 characters or fewer
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to read updated profile

            internal: failed to update profile
      security:
        - bearerAuth: []
      summary: Update Me
      tags:
        - Users
      x-source: backend/internal/handlers/users.go:119
      x-source-registration: backend/cmd/api/main.go:1928
      x-handler: usersHandler.UpdateMe
      parameters:
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
  /api/users/me/api-tokens:
    get:
      description: |-
        serves GET /api/users/me/api-tokens. Returns active (un-revoked)
        tokens for the logged-in user, newest first.
      operationId: get_api_users_me_api_tokens
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: "#/components/schemas/handlers_apiTokenListItem"
                type: array
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_user_id: invalid user id"
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: couldn't load tokens"
      security:
        - bearerAuth: []
      summary: List api tokens
      tags:
        - API tokens
      x-source: backend/internal/handlers/api_tokens.go:127
      x-source-registration: backend/cmd/api/main.go:1934
      x-handler: apiTokensHandler.List
      parameters:
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
    post:
      description: >-
        serves POST /api/users/me/api-tokens. Validates the name and

        expiry choice, enforces the per-user cap, generates a fresh token, and

        returns the plaintext exactly once.


        You can have at most 20 active tokens. The response returns the plaintext token once. Store it securely;
        subsequent list requests never return it.
      operationId: post_api_users_me_api_tokens
      requestBody:
        content:
          application/json:
            schema:
              properties:
                expires_in_days:
                  type: integer
                  enum:
                    - 0
                    - 7
                    - 30
                    - 60
                    - 90
                    - 180
                    - 365
                  default: 0
                  description: Zero means no expiry.
                name:
                  type: string
                  minLength: 1
                  maxLength: 64
                  description: Non-empty name after trimming whitespace.
              type: object
              required:
                - name
        required: true
      responses:
        "201":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/handlers_createAPITokenResponse"
          description: Created
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_user_id: invalid user id

            invalid_body: invalid request body

            invalid_expiry: expires_in_days must be one of 0, 7, 30, 60, 90, 180, 365

            missing_name: name is required

            name_too_long: name must be 64 characters or fewer

            token_limit_reached: you have reached the maximum number of active tokens — revoke an existing one first
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: couldn't check token quota

            internal: couldn't generate token

            internal: couldn't save token
      security:
        - bearerAuth: []
      summary: Create api tokens
      tags:
        - API tokens
      x-source: backend/internal/handlers/api_tokens.go:158
      x-source-registration: backend/cmd/api/main.go:1935
      x-handler: apiTokensHandler.Create
      parameters:
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
  /api/users/me/api-tokens/{id}:
    delete:
      description: |-
        serves DELETE /api/users/me/api-tokens/{id}. Soft-revokes the token
        (sets revoked_at = NOW()) so it stays in the audit trail. The middleware
        filters revoked rows out at lookup time, so any in-flight request using
        this token after revocation will 401 on its next call.
      operationId: delete_api_users_me_api_tokens_id
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "204":
          description: No content.
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_user_id: invalid user id

            invalid_id: invalid token id
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: couldn't revoke token"
      security:
        - bearerAuth: []
      summary: Revoke
      tags:
        - API tokens
      x-source: backend/internal/handlers/api_tokens.go:246
      x-source-registration: backend/cmd/api/main.go:1936
      x-handler: apiTokensHandler.Revoke
  /api/users/me/change-password:
    post:
      description: |-
        verifies the current password then writes a new hash.
        Returns 400 for OAuth-only accounts (no password to change), 401 for a
        wrong current password, 204 on success.

        Uses the authenticated user's identity.
      operationId: post_api_users_me_change_password
      requestBody:
        content:
          application/json:
            schema:
              properties:
                current_password:
                  type: string
                new_password:
                  type: string
              type: object
              required:
                - current_password
                - new_password
        required: true
      responses:
        "204":
          description: No content.
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_body: invalid request body

            invalid_user_id: invalid user id

            missing_fields: current and new password are required

            no_password: password change is not available for accounts signed in via Google or LinkedIn

            weak_password: password must be at least 8 characters
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            unauthorized: not authenticated

            wrong_password: current password is incorrect
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: user not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: failed to process password

            internal: failed to update password
      security:
        - bearerAuth: []
      summary: Change Password
      tags:
        - Users
      x-required-input-fields:
        - current_password
        - new_password
      x-source: backend/internal/handlers/users.go:195
      x-source-registration: backend/cmd/api/main.go:1929
      x-handler: usersHandler.ChangePassword
      parameters:
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
  /api/users/me/sessions:
    get:
      description: List user sessions for this resource.
      operationId: get_api_users_me_sessions
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: "#/components/schemas/handlers_userSessionListItem"
                type: array
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_user_id: invalid user id"
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "unauthorized: not authenticated"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: couldn't load sessions"
      security:
        - bearerAuth: []
      summary: List user sessions
      tags:
        - Users
      x-source: backend/internal/handlers/user_sessions.go:45
      x-source-registration: backend/cmd/api/main.go:1937
      x-handler: userSessionsHandler.List
      parameters:
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
  /api/users/me/sessions/{id}:
    delete:
      description: Revoke for this resource.
      operationId: delete_api_users_me_sessions_id
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "204":
          description: No content.
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_user_id: invalid user id

            invalid_id: invalid session id
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            unauthorized: not authenticated

            Missing, invalid, expired, or revoked bearer credential.
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: couldn't revoke session"
      security:
        - bearerAuth: []
      summary: Revoke
      tags:
        - Users
      x-source: backend/internal/handlers/user_sessions.go:76
      x-source-registration: backend/cmd/api/main.go:1938
      x-handler: userSessionsHandler.Revoke
  /api/webhooks/outbound:
    get:
      description: |-
        returns all webhook subscriptions for the user with masked secrets.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: get_api_webhooks_outbound
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: "#/components/schemas/handlers_webhookSubscriptionSafe"
                type: array
          description: OK
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_user: Invalid user ID"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "db_error: Failed to fetch webhooks"
      security:
        - bearerAuth: []
      summary: List webhook outbound
      tags:
        - Outbound webhooks
      x-source: backend/internal/handlers/webhooks_outbound.go:62
      x-source-registration: backend/cmd/api/main.go:1849
      x-handler: webhookOutboundHandler.List
      parameters:
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
    post:
      description: |-
        adds a new webhook subscription with auto-generated HMAC secret.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: post_api_webhooks_outbound
      requestBody:
        content:
          application/json:
            schema:
              properties:
                events:
                  items:
                    type: string
                  type: array
                integration_id:
                  type: string
                url:
                  type: string
              type: object
              required:
                - events
                - url
        required: true
      responses:
        "201":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/sqlc_WebhookSubscription"
          description: Created
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_event: 

            invalid_id: Invalid integration ID

            invalid_json: Invalid request body

            invalid_url: Webhook URL must be a public HTTPS URL

            missing_field: events is required (at least one event)

            missing_field: url is required
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_user: Invalid user ID"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: Integration not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            db_error: Failed to create webhook

            internal: Failed to generate webhook secret
      security:
        - bearerAuth: []
      summary: Create webhook outbound
      tags:
        - Outbound webhooks
      x-required-input-fields:
        - events
        - url
      x-source: backend/internal/handlers/webhooks_outbound.go:85
      x-source-registration: backend/cmd/api/main.go:1850
      x-handler: webhookOutboundHandler.Create
      parameters:
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
  /api/webhooks/outbound/sample-payload:
    get:
      description: |-
        returns a representative payload for the requested event so
        the frontend can render a live preview block in the create/edit form.
        GET /api/webhooks/outbound/sample-payload?event=<event>
      operationId: get_api_webhooks_outbound_sample_payload
      parameters:
        - in: query
          name: event
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                additionalProperties:
                  description: Arbitrary JSON value.
                type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_event: 

            missing_param: event query param is required
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: Missing, invalid, expired, or revoked bearer credential.
      security:
        - bearerAuth: []
      summary: Sample Payload
      tags:
        - Outbound webhooks
      x-source: backend/internal/handlers/webhooks_outbound.go:351
      x-source-registration: backend/cmd/api/main.go:1854
      x-handler: webhookOutboundHandler.SamplePayload
  /api/webhooks/outbound/test-url:
    post:
      description: |-
        sends a signed test payload to an unsaved URL so the user can verify
        connectivity before creating the subscription. The signing secret is generated
        fresh for this single request and returned in the response so the user can
        validate signature handling on their side.
        POST /api/webhooks/outbound/test-url   body: {url, event}

        Uses the effective workspace identity and enforces resource ownership.
      operationId: post_api_webhooks_outbound_test_url
      requestBody:
        content:
          application/json:
            schema:
              properties:
                event:
                  type: string
                url:
                  type: string
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                anyOf:
                  - properties:
                      error:
                        type: string
                      secret:
                        type: string
                      status_code:
                        type: integer
                      success:
                        type: boolean
                    type: object
                  - properties:
                      response_body:
                        type: string
                      secret:
                        type: string
                      status_code:
                        type: integer
                      success:
                        type: boolean
                    type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_event: 

            invalid_json: Invalid request body

            invalid_url: Webhook URL must be a public HTTPS URL
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_user: Invalid user ID"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            internal: Failed to build test request

            internal: Failed to generate test secret
      security:
        - bearerAuth: []
      summary: Test URL
      tags:
        - Outbound webhooks
      x-source: backend/internal/handlers/webhooks_outbound.go:370
      x-source-registration: backend/cmd/api/main.go:1855
      x-handler: webhookOutboundHandler.TestURL
      parameters:
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
  /api/webhooks/outbound/{id}:
    delete:
      description: |-
        removes a webhook subscription.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: delete_api_webhooks_outbound_id
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "204":
          description: No content.
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_id: Invalid webhook ID"
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_user: Invalid user ID"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "db_error: Failed to delete webhook"
      security:
        - bearerAuth: []
      summary: Delete webhook outbound
      tags:
        - Outbound webhooks
      x-source: backend/internal/handlers/webhooks_outbound.go:242
      x-source-registration: backend/cmd/api/main.go:1852
      x-handler: webhookOutboundHandler.Delete
    put:
      description: |-
        modifies a webhook subscription (URL, events, active status). Secret cannot be changed.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: put_api_webhooks_outbound_id
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      requestBody:
        content:
          application/json:
            schema:
              properties:
                active:
                  anyOf:
                    - type: boolean
                    - type: "null"
                events:
                  items:
                    type: string
                  type: array
                url:
                  anyOf:
                    - type: string
                    - type: "null"
              type: object
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/handlers_webhookSubscriptionSafe"
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_event: 

            invalid_id: Invalid webhook ID

            invalid_json: Invalid request body

            invalid_url: Webhook URL must be a public HTTPS URL
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_user: Invalid user ID"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: Webhook not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "db_error: Failed to update webhook"
      security:
        - bearerAuth: []
      summary: Update webhook outbound
      tags:
        - Outbound webhooks
      x-source: backend/internal/handlers/webhooks_outbound.go:168
      x-source-registration: backend/cmd/api/main.go:1851
      x-handler: webhookOutboundHandler.Update
  /api/webhooks/outbound/{id}/test:
    post:
      description: |-
        sends a test payload to a webhook URL with HMAC signature.

        Uses the effective workspace identity and enforces resource ownership.
      operationId: post_api_webhooks_outbound_id_test
      parameters:
        - in: query
          name: event
          schema:
            type: string
        - in: path
          name: id
          required: true
          schema:
            type: string
        - name: X-Acting-As
          in: header
          schema:
            type: string
            format: uuid
          description: Optional workspace-owner user ID. Requires current team membership and access to the requested product.
            Identity and token operations still use the authenticated user.
      responses:
        "200":
          content:
            application/json:
              schema:
                anyOf:
                  - properties:
                      error:
                        type: string
                      status_code:
                        type: integer
                      success:
                        type: boolean
                    type: object
                  - properties:
                      response_body:
                        type: string
                      status_code:
                        type: integer
                      success:
                        type: boolean
                    type: object
          description: OK
        "400":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: |-
            invalid_event: 

            invalid_id: Invalid webhook ID
        "401":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "invalid_user: Invalid user ID"
        "404":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "not_found: Webhook not found"
        "500":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
          description: "internal: Failed to build test request"
      security:
        - bearerAuth: []
      summary: Test
      tags:
        - Outbound webhooks
      x-source: backend/internal/handlers/webhooks_outbound.go:268
      x-source-registration: backend/cmd/api/main.go:1853
      x-handler: webhookOutboundHandler.Test
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: Personal access token (funkel_pat_ prefix) or valid session JWT. Use a placeholder in examples; never
        publish a live credential.
    sessionJWT:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: Logged-in browser session JWT with a valid session ID. Personal access tokens cannot call this operation.
    refreshCookie:
      type: apiKey
      in: cookie
      name: refresh_token
      description: The refresh cookie issued during login or signup. This operation rotates session tokens.
  schemas:
    Error:
      type: object
      properties:
        error:
          type: string
        code:
          type: string
        details:
          type: string
      required:
        - error
    ai_AppSuggestion:
      properties:
        description:
          type: string
        icp:
          $ref: "#/components/schemas/ai_ICPSuggestion"
        logo_url:
          type: string
        name:
          type: string
        product_brain:
          $ref: "#/components/schemas/ai_ProductBrainSuggestion"
        website_url:
          type: string
      required:
        - name
        - description
        - logo_url
        - website_url
        - icp
        - product_brain
      type: object
    ai_ICPSuggestion:
      properties:
        additional_criteria:
          type: string
        company_size:
          type: string
        company_sizes:
          items:
            type: string
          type: array
        company_types:
          items:
            type: string
          type: array
        description:
          type: string
        exclude_service_providers:
          type: boolean
        excluded_companies:
          items:
            type: string
          type: array
        excluded_keywords:
          items:
            type: string
          type: array
        industries:
          items:
            type: string
          type: array
        job_titles:
          items:
            type: string
          type: array
        lead_matching_mode:
          type: string
        mandatory_keywords:
          items:
            type: string
          type: array
        regions:
          items:
            type: string
          type: array
      required:
        - description
        - job_titles
        - company_size
        - company_sizes
        - company_types
        - regions
        - industries
        - excluded_companies
        - excluded_keywords
        - mandatory_keywords
        - additional_criteria
        - lead_matching_mode
        - exclude_service_providers
      type: object
    ai_MessageAgentRationale:
      properties:
        angle:
          type: string
        buyer_pain:
          type: string
        guardrail:
          type: string
        signal:
          type: string
      required:
        - angle
        - buyer_pain
        - signal
        - guardrail
      type: object
    ai_ProductBrainSuggestion:
      properties:
        buyer_pain:
          type: string
        competitors:
          type: string
        differentiators:
          type: string
        guardrails:
          type: string
        learnings:
          type: string
        objections:
          type: string
        positioning:
          type: string
        proof_points:
          type: string
        voice_guidelines:
          type: string
      required:
        - positioning
        - buyer_pain
        - differentiators
        - competitors
        - proof_points
        - voice_guidelines
        - objections
        - guardrails
        - learnings
      type: object
    aiagent_ConfirmationView:
      properties:
        args:
          additionalProperties:
            description: Arbitrary JSON value.
          type: object
        expires_at:
          format: date-time
          type: string
        state:
          type: string
        tool_name:
          type: string
      required:
        - tool_name
        - args
        - state
        - expires_at
      type: object
    billing_BetaRewardEvent:
      type: string
    billing_BetaRewardEventDef:
      properties:
        credits:
          type: integer
        key:
          $ref: "#/components/schemas/billing_BetaRewardEvent"
        label:
          type: string
      required:
        - key
        - credits
        - label
      type: object
    billing_BetaRewardEventStat:
      allOf:
        - $ref: "#/components/schemas/billing_BetaRewardEventDef"
        - properties:
            earned:
              type: boolean
            granted_at:
              type: string
          required:
            - earned
          type: object
    billing_BetaRewardStatus:
      properties:
        eligible:
          type: boolean
        events:
          items:
            $ref: "#/components/schemas/billing_BetaRewardEventStat"
          type: array
        max_credits:
          type: integer
        total_earned:
          type: integer
      required:
        - eligible
        - total_earned
        - max_credits
        - events
      type: object
    billing_CreditCatalogDTO:
      properties:
        operations:
          items:
            $ref: "#/components/schemas/billing_OperationPricingDTO"
          type: array
        packs:
          items:
            $ref: "#/components/schemas/billing_CreditPackDTO"
          type: array
      required:
        - packs
        - operations
      type: object
    billing_CreditPackDTO:
      properties:
        credits:
          type: integer
        discount_label:
          type: string
        discount_percent:
          type: integer
        label:
          type: string
        name:
          $ref: "#/components/schemas/billing_PackName"
        price_cents:
          type: integer
        price_label:
          type: string
        value_label:
          type: string
      required:
        - name
        - label
        - credits
        - price_cents
        - price_label
        - value_label
      type: object
    billing_OperationKey:
      type: string
    billing_OperationPricingDTO:
      properties:
        amounts:
          items:
            $ref: "#/components/schemas/billing_PricingAmount"
          type: array
        base_credits:
          type: integer
        description:
          type: string
        key:
          $ref: "#/components/schemas/billing_OperationKey"
        label:
          type: string
        miss_credits:
          type: integer
        per_result_credits:
          type: integer
        success_credits:
          type: integer
      required:
        - key
        - label
        - description
        - amounts
      type: object
    billing_PackName:
      type: string
    billing_PricingAmount:
      properties:
        credits:
          type: integer
        included_in_estimate:
          type: boolean
        included_in_maximum:
          type: boolean
        label:
          type: string
        settlement:
          $ref: "#/components/schemas/billing_UsageStatus"
      required:
        - label
        - credits
        - settlement
        - included_in_estimate
        - included_in_maximum
      type: object
    billing_UsageLine:
      properties:
        credits:
          type: integer
        label:
          type: string
        operation_key:
          $ref: "#/components/schemas/billing_OperationKey"
        quantity:
          type: integer
        status:
          $ref: "#/components/schemas/billing_UsageStatus"
      required:
        - operation_key
        - label
        - credits
        - status
      type: object
    billing_UsageStatus:
      type: string
    billing_WalletSummary:
      properties:
        admin_granted_available:
          type: integer
        appsumo_available:
          type: integer
        legacy_available:
          type: integer
        nearest_expiry:
          anyOf:
            - format: date-time
              type: string
            - type: "null"
        purchased_available:
          type: integer
        subscription_available:
          type: integer
        total_available:
          type: integer
      required:
        - total_available
        - legacy_available
        - subscription_available
        - appsumo_available
        - purchased_available
        - admin_granted_available
      type: object
    decisionmakers_ApprovalOutcome:
      properties:
        candidate_id:
          type: string
        contact_route:
          type: string
        list_id:
          type: string
        member_id:
          type: string
        reason:
          type: string
        status:
          type: string
      required:
        - candidate_id
        - status
      type: object
    decisionmakers_ApprovalResponse:
      properties:
        approved:
          type: integer
        email_fallback_list_id:
          type: string
        failed:
          type: integer
        linkedin_list_id:
          type: string
        outcomes:
          items:
            $ref: "#/components/schemas/decisionmakers_ApprovalOutcome"
          type: array
        route_counts:
          additionalProperties:
            type: integer
          type: object
        skipped:
          type: integer
      required:
        - outcomes
        - approved
        - skipped
        - failed
      type: object
    decisionmakers_ApprovedMemberView:
      properties:
        campaign_ids:
          items:
            type: string
          type: array
        candidate_id:
          type: string
        company_domain:
          type: string
        company_name:
          type: string
        contact_route:
          type: string
        first_name:
          type: string
        last_name:
          type: string
        list_id:
          type: string
        member_id:
          type: string
        requested_role:
          type: string
      required:
        - candidate_id
        - member_id
        - list_id
        - contact_route
        - first_name
        - last_name
        - company_name
        - company_domain
        - requested_role
        - campaign_ids
      type: object
    decisionmakers_ApprovedMembersResponse:
      properties:
        items:
          items:
            $ref: "#/components/schemas/decisionmakers_ApprovedMemberView"
          type: array
        limit:
          type: integer
        offset:
          type: integer
        total:
          type: integer
      required:
        - items
        - total
        - offset
        - limit
      type: object
    decisionmakers_ChannelEconomics:
      properties:
        maximum_combined_cost_usd:
          type: number
        provider_cash_cost_usd:
          type: number
        provider_cost_headroom_usd:
          type: number
        provider_cost_within_allowance:
          type: boolean
        settled_credits:
          type: integer
      required:
        - settled_credits
        - provider_cash_cost_usd
        - maximum_combined_cost_usd
        - provider_cost_headroom_usd
        - provider_cost_within_allowance
      type: object
    decisionmakers_CompanyImportMetrics:
      properties:
        actionable_people:
          type: integer
        cache_coverage:
          type: integer
        cache_economics:
          $ref: "#/components/schemas/decisionmakers_ChannelEconomics"
        cost_per_actionable_person_usd:
          type: number
        cost_per_executed_company_usd:
          type: number
        discovery_economics:
          $ref: "#/components/schemas/decisionmakers_ChannelEconomics"
        duplicate_count:
          type: integer
        email_fallback_attempts:
          type: integer
        email_fallback_outcomes:
          additionalProperties:
            type: integer
          type: object
        executed_companies:
          type: integer
        margin_formula:
          type: string
        margin_thresholds:
          $ref: "#/components/schemas/decisionmakers_MarginThresholds"
        outcomes:
          additionalProperties:
            type: integer
          type: object
        overall_economics:
          $ref: "#/components/schemas/decisionmakers_ChannelEconomics"
        prospeo_incremental_coverage:
          type: integer
        provider_attempts:
          type: integer
        provider_cash_cost_usd:
          type: number
        provider_no_match_count:
          type: integer
        provider_pages:
          type: integer
        providers:
          additionalProperties:
            $ref: "#/components/schemas/decisionmakers_ProviderMetricSummary"
          type: object
        quickenrich_coverage:
          type: integer
        request_id_presence:
          additionalProperties:
            type: boolean
          type: object
        retry_attempts:
          type: integer
        returned_candidates:
          type: integer
        settled_credits:
          type: integer
        source:
          type: string
        unique_companies:
          type: integer
        unknown_outcomes:
          type: integer
        valid_candidates:
          type: integer
        valid_linkedin:
          type: integer
        validation_rejected:
          type: integer
        verified_work_email:
          type: integer
        work_email_economics:
          $ref: "#/components/schemas/decisionmakers_ChannelEconomics"
      required:
        - source
        - unique_companies
        - executed_companies
        - provider_attempts
        - provider_pages
        - returned_candidates
        - valid_candidates
        - provider_no_match_count
        - outcomes
        - retry_attempts
        - unknown_outcomes
        - email_fallback_attempts
        - email_fallback_outcomes
        - cache_coverage
        - quickenrich_coverage
        - prospeo_incremental_coverage
        - valid_linkedin
        - verified_work_email
        - actionable_people
        - duplicate_count
        - validation_rejected
        - provider_cash_cost_usd
        - cost_per_executed_company_usd
        - cost_per_actionable_person_usd
        - settled_credits
        - overall_economics
        - discovery_economics
        - work_email_economics
        - cache_economics
        - margin_thresholds
        - margin_formula
        - request_id_presence
        - providers
      type: object
    decisionmakers_CompanyOutcomeView:
      properties:
        candidate_count:
          type: integer
        canonical_company_id:
          type: string
        company_domain:
          type: string
        company_name:
          type: string
        failure_code:
          type: string
        requested_roles:
          items:
            type: string
          type: array
        retry_eligible:
          type: boolean
        source_row_numbers:
          items:
            type: integer
          type: array
        status:
          type: string
      required:
        - canonical_company_id
        - company_name
        - company_domain
        - source_row_numbers
        - requested_roles
        - status
        - retry_eligible
        - candidate_count
      type: object
    decisionmakers_CompanyOutcomesResponse:
      properties:
        items:
          items:
            $ref: "#/components/schemas/decisionmakers_CompanyOutcomeView"
          type: array
        limit:
          type: integer
        offset:
          type: integer
        total:
          type: integer
      required:
        - items
        - total
        - offset
        - limit
      type: object
    decisionmakers_ImportRowView:
      properties:
        company_domain:
          type: string
        company_linkedin_url:
          type: string
        company_name:
          type: string
        id:
          type: string
        row_number:
          type: integer
      required:
        - id
        - row_number
        - company_name
      type: object
    decisionmakers_ImportRowsResponse:
      properties:
        items:
          items:
            $ref: "#/components/schemas/decisionmakers_ImportRowView"
          type: array
        limit:
          type: integer
        offset:
          type: integer
        total:
          type: integer
      required:
        - items
        - total
        - offset
        - limit
      type: object
    decisionmakers_MarginThresholds:
      properties:
        one_credit_cache_usd:
          type: number
        three_credit_success_usd:
          type: number
        two_credit_no_match_usd:
          type: number
      required:
        - three_credit_success_usd
        - two_credit_no_match_usd
        - one_credit_cache_usd
      type: object
    decisionmakers_PreviewDuplicate:
      properties:
        duplicate_of_row:
          type: integer
        reason:
          type: string
        row:
          type: integer
      required:
        - row
        - reason
        - duplicate_of_row
      type: object
    decisionmakers_PreviewFailure:
      properties:
        reason:
          type: string
        row:
          type: integer
      required:
        - row
        - reason
      type: object
    decisionmakers_PreviewResponse:
      properties:
        affordable_row_ids:
          items:
            type: string
          type: array
        cache_operation_cost:
          type: integer
        duplicate_rows:
          type: integer
        duplicates:
          items:
            $ref: "#/components/schemas/decisionmakers_PreviewDuplicate"
          type: array
        eligible_cache_hits:
          type: integer
        email_fallback_enabled:
          type: boolean
        estimated_credits:
          type: integer
        failures:
          items:
            $ref: "#/components/schemas/decisionmakers_PreviewFailure"
          type: array
        is_draft:
          type: boolean
        job_id:
          type: string
        last_verified_at:
          anyOf:
            - format: date-time
              type: string
            - type: "null"
        max_email_fallbacks:
          type: integer
        max_people_per_company:
          type: integer
        maximum_credits:
          type: integer
        roles:
          items:
            type: string
          type: array
        rows:
          type: integer
        search_channel:
          type: string
        selected_rows:
          type: integer
        selection_fingerprint:
          type: string
        source_job_id:
          type: string
        unique_companies:
          type: integer
        usage:
          $ref: "#/components/schemas/decisionmakers_UsageView"
      required:
        - search_channel
        - job_id
        - rows
        - unique_companies
        - duplicate_rows
        - duplicates
        - roles
        - max_people_per_company
        - estimated_credits
        - maximum_credits
        - eligible_cache_hits
        - cache_operation_cost
        - usage
        - is_draft
        - affordable_row_ids
        - selection_fingerprint
        - email_fallback_enabled
        - max_email_fallbacks
      type: object
    decisionmakers_ProgressResponse:
      properties:
        cache_operation_cost:
          type: integer
        completed_work_items:
          type: integer
        eligible_approval_count:
          type: integer
        eligible_cache_hits:
          type: integer
        failure_code:
          type: string
        job_id:
          type: string
        last_verified_at:
          anyOf:
            - format: date-time
              type: string
            - type: "null"
        queued_work_items:
          type: integer
        retryable_failure_count:
          type: integer
        review_list_id:
          type: string
        running_work_items:
          type: integer
        search_channel:
          type: string
        status:
          type: string
        total_rows:
          type: integer
        total_work_items:
          type: integer
        usage:
          $ref: "#/components/schemas/decisionmakers_UsageView"
      required:
        - search_channel
        - job_id
        - status
        - total_rows
        - total_work_items
        - completed_work_items
        - queued_work_items
        - running_work_items
        - retryable_failure_count
        - eligible_approval_count
        - eligible_cache_hits
        - cache_operation_cost
        - usage
      type: object
    decisionmakers_ProviderMetricSummary:
      properties:
        attempts:
          type: integer
        companies_covered:
          type: integer
        effective_verified_email_cost_usd:
          type: number
        no_match_count:
          type: integer
        outcomes:
          additionalProperties:
            type: integer
          type: object
        pages:
          type: integer
        provider_cash_cost_usd:
          type: number
        request_id_present:
          type: boolean
        retry_attempts:
          type: integer
        returned_candidates:
          type: integer
        unknown_outcomes:
          type: integer
        valid_candidates:
          type: integer
        verified_work_email_outcomes:
          type: integer
        work_email_attempts:
          type: integer
        work_email_cash_cost_usd:
          type: number
      required:
        - attempts
        - pages
        - companies_covered
        - returned_candidates
        - valid_candidates
        - no_match_count
        - outcomes
        - retry_attempts
        - unknown_outcomes
        - work_email_attempts
        - verified_work_email_outcomes
        - request_id_present
        - provider_cash_cost_usd
        - work_email_cash_cost_usd
        - effective_verified_email_cost_usd
      type: object
    decisionmakers_ResultView:
      properties:
        cache_hit:
          type: boolean
        company_domain:
          type: string
        company_linkedin_url:
          type: string
        company_name:
          type: string
        contact_route:
          type: string
        email:
          type: string
        email_approved:
          type: boolean
        email_outcome:
          type: string
        email_ready:
          type: boolean
        email_status:
          type: string
        failure_code:
          type: string
        first_name:
          type: string
        freshness_state:
          type: string
        full_name:
          type: string
        headline:
          type: string
        id:
          type: string
        identity_verified:
          type: boolean
        last_name:
          type: string
        last_verified_at:
          anyOf:
            - format: date-time
              type: string
            - type: "null"
        linkedin_approved:
          type: boolean
        linkedin_outcome:
          type: string
        linkedin_ready:
          type: boolean
        linkedin_url:
          type: string
        match_status:
          type: string
        not_outreach_ready:
          type: boolean
        profile_picture_url:
          type: string
        provider_person_id:
          type: string
        requested_role:
          type: string
        returned_title:
          type: string
        search_channel:
          type: string
      required:
        - search_channel
        - linkedin_ready
        - email_ready
        - linkedin_approved
        - email_approved
        - linkedin_outcome
        - email_outcome
        - id
        - company_name
        - requested_role
        - email_status
        - match_status
        - freshness_state
        - cache_hit
        - identity_verified
        - not_outreach_ready
        - contact_route
      type: object
    decisionmakers_RetryResponse:
      properties:
        billing_cycle:
          type: integer
        requeued:
          type: integer
        reserved_credits:
          type: integer
      required:
        - requeued
      type: object
    decisionmakers_UsageView:
      properties:
        actual_credits:
          type: integer
        estimated_credits:
          type: integer
        lines:
          items:
            $ref: "#/components/schemas/billing_UsageLine"
          type: array
        maximum_credits:
          type: integer
        refunded_credits:
          type: integer
      required:
        - lines
      type: object
    demosubmit_Envelope:
      properties:
        credits_spent:
          type: integer
        details:
          $ref: "#/components/schemas/demosubmit_simulationDetails"
        external_calls:
          type: boolean
        message:
          type: string
        operation:
          $ref: "#/components/schemas/demosubmit_Operation"
        saved:
          type: boolean
        simulated:
          type: boolean
        work_enqueued:
          type: boolean
      required:
        - simulated
        - operation
        - saved
        - work_enqueued
        - external_calls
        - credits_spent
        - message
        - details
      type: object
    demosubmit_Operation:
      type: string
    demosubmit_Response:
      properties:
        demo_simulation:
          $ref: "#/components/schemas/demosubmit_Envelope"
      required:
        - demo_simulation
      type: object
    demosubmit_simulationDetails:
      description: JSON value; the server permits arbitrary JSON at this field.
    engine_PreviewCounts:
      properties:
        cheap:
          type: integer
        dedup:
          type: integer
        search:
          type: integer
      required:
        - search
        - dedup
        - cheap
      type: object
    engine_PreviewProfile:
      properties:
        first_name:
          type: string
        headline:
          type: string
        last_name:
          type: string
        location:
          type: string
        profile_picture_url:
          type: string
        profile_url:
          type: string
        provider_id:
          type: string
      required:
        - provider_id
        - first_name
        - last_name
        - headline
        - location
        - profile_url
        - profile_picture_url
      type: object
    engine_PreviewResult:
      properties:
        counts:
          $ref: "#/components/schemas/engine_PreviewCounts"
        profiles:
          items:
            $ref: "#/components/schemas/engine_PreviewProfile"
          type: array
        signal_key:
          description: |-
            SignalKey echoes the signal that was previewed so the UI can label the
            result correctly when no signal_id was specified by the caller.
          type: string
      required:
        - profiles
        - counts
        - signal_key
      type: object
    handlers_ActionDetailResponse:
      properties:
        action:
          $ref: "#/components/schemas/handlers_ActionWithReview"
        lead:
          $ref: "#/components/schemas/sqlc_Lead"
      required:
        - action
        - lead
      type: object
    handlers_ActionWithLead:
      allOf:
        - $ref: "#/components/schemas/sqlc_ListScheduledActionsWithLeadByCampaignRow"
        - properties:
            has_review_draft:
              type: boolean
            overdue_reason:
              type: string
            review_status:
              type: string
          required:
            - has_review_draft
          type: object
    handlers_ActionWithReview:
      allOf:
        - $ref: "#/components/schemas/sqlc_ScheduledAction"
        - properties:
            has_review_draft:
              type: boolean
            review_status:
              type: string
          required:
            - has_review_draft
          type: object
    handlers_AgentMessageView:
      properties:
        cache_read_tokens:
          type:
            - integer
            - "null"
        cache_write_tokens:
          type:
            - integer
            - "null"
        content_jsonb:
          description: JSON value stored by the server; shape depends on this resource's configuration.
        created_at:
          format: date-time
          type:
            - string
            - "null"
        id:
          format: uuid
          type:
            - string
            - "null"
        input_tokens:
          type:
            - integer
            - "null"
        model:
          type: string
        output_tokens:
          type:
            - integer
            - "null"
        provider:
          description: |-
            Provider/model are observability-only (support and debugging);
            the frontend never renders them in assistant bubbles or tool
            cards. Never expose a stored opaque provider-scoped state blob
            here — the projection function drops it.
          type: string
        role:
          type: string
        thread_id:
          format: uuid
          type:
            - string
            - "null"
        tool_calls_jsonb:
          description: JSON value stored by the server; shape depends on this resource's configuration.
        tool_results_jsonb:
          description: JSON value stored by the server; shape depends on this resource's configuration.
      required:
        - id
        - thread_id
        - role
        - content_jsonb
        - tool_calls_jsonb
        - tool_results_jsonb
        - input_tokens
        - output_tokens
        - cache_read_tokens
        - cache_write_tokens
        - created_at
        - provider
        - model
      type: object
    handlers_CatalogEntry:
      properties:
        category:
          type: string
        coming_soon:
          type: boolean
        config_schema:
          items:
            $ref: "#/components/schemas/handlers_CatalogField"
          type: array
        description:
          type: string
        help_url:
          type: string
        logo_url:
          type: string
        name:
          type: string
        type:
          type: string
      required:
        - name
        - type
        - category
        - description
        - config_schema
      type: object
    handlers_CatalogField:
      properties:
        help_text:
          type: string
        help_url:
          type: string
        key:
          type: string
        label:
          type: string
        placeholder:
          type: string
        required:
          type: boolean
        secret:
          type: boolean
        type:
          type: string
      required:
        - key
        - label
        - type
      type: object
    handlers_DraftCampaignLead:
      properties:
        company:
          type: string
        first_name:
          type: string
        headline:
          type: string
        id:
          format: uuid
          type:
            - string
            - "null"
        last_name:
          type: string
        profile_picture_url:
          type: string
        profile_url:
          type: string
        provider_id:
          type: string
        status:
          type: string
      required:
        - id
        - first_name
        - last_name
        - company
        - headline
        - profile_url
        - profile_picture_url
        - provider_id
        - status
      type: object
    handlers_EnrichmentResult:
      properties:
        provider:
          type: string
        status:
          type: string
        work_email:
          type: string
      required:
        - work_email
        - status
      type: object
    handlers_GroupedActions:
      properties:
        awaiting_acceptance:
          items:
            $ref: "#/components/schemas/handlers_ActionWithLead"
          type: array
        draft_leads:
          items:
            $ref: "#/components/schemas/handlers_DraftCampaignLead"
          type: array
        later:
          items:
            $ref: "#/components/schemas/handlers_ActionWithLead"
          type: array
        overdue:
          items:
            $ref: "#/components/schemas/handlers_ActionWithLead"
          type: array
        this_week:
          items:
            $ref: "#/components/schemas/handlers_ActionWithLead"
          type: array
        today:
          items:
            $ref: "#/components/schemas/handlers_ActionWithLead"
          type: array
        tomorrow:
          items:
            $ref: "#/components/schemas/handlers_ActionWithLead"
          type: array
      required:
        - awaiting_acceptance
        - overdue
        - today
        - tomorrow
        - this_week
        - later
        - draft_leads
      type: object
    handlers_OnboardingProgress:
      properties:
        has_account:
          type: boolean
        has_active_agent:
          type: boolean
        has_agent:
          type: boolean
        has_any_campaign:
          type: boolean
        has_app:
          type: boolean
        has_campaign:
          type: boolean
      required:
        - has_app
        - has_account
        - has_active_agent
        - has_agent
        - has_campaign
        - has_any_campaign
      type: object
    handlers_ResolveLinkedInResult:
      properties:
        profile_url:
          type: string
        route_status:
          description: |-
            RouteStatus is engine.RouteResolvedLinkedInIdentity's outcome, set
            only on the "success" branch: "enrolled", "already_enrolled",
            "already_active_elsewhere", "no_linkedin_account", or
            "no_active_campaign".
          type: string
        status:
          type: string
      required:
        - status
      type: object
    handlers_acceptInvitationResponse:
      properties:
        membership:
          $ref: "#/components/schemas/handlers_workspaceSummary"
        owner_user_id:
          type: string
        status:
          type: string
      required:
        - status
        - owner_user_id
        - membership
      type: object
    handlers_agentDiscoveryFunnelResponse:
      properties:
        campaign_enrollment_count:
          type: integer
        enabled_linkedin_signal_count:
          type: integer
        exhausted_linkedin_signals:
          items:
            $ref: "#/components/schemas/handlers_exhaustedLinkedInSignal"
          type: array
        linked_campaign_count:
          type: integer
        list_count:
          type: integer
        list_id:
          type: string
        list_name:
          type: string
        pending:
          type: integer
        qualified:
          type: integer
        rejected:
          type: integer
        reviewed:
          type: integer
        window_days:
          type: integer
      required:
        - window_days
        - reviewed
        - rejected
        - qualified
        - pending
        - list_count
        - linked_campaign_count
        - campaign_enrollment_count
        - enabled_linkedin_signal_count
        - exhausted_linkedin_signals
      type: object
    handlers_agentPassportResponse:
      properties:
        capabilities:
          items:
            type: string
          type: array
        status:
          type: string
      required:
        - status
      type: object
    handlers_agentRejectedProfileItem:
      properties:
        first_name:
          type: string
        headline:
          type: string
        id:
          type: string
        last_name:
          type: string
        location:
          type: string
        profile_picture_url:
          type: string
        profile_url:
          type: string
        promotion_message:
          type: string
        promotion_status:
          type: string
        rejected_at_ms:
          type: integer
        rejection_reason:
          type: string
        score:
          anyOf:
            - type: integer
            - type: "null"
        score_floor:
          anyOf:
            - type: integer
            - type: "null"
      required:
        - id
        - first_name
        - last_name
        - headline
        - location
        - profile_url
        - profile_picture_url
        - rejection_reason
        - rejected_at_ms
      type: object
    handlers_agentRejectedProfilesResponse:
      properties:
        count:
          type: integer
        rows:
          items:
            $ref: "#/components/schemas/handlers_agentRejectedProfileItem"
          type: array
      required:
        - count
        - rows
      type: object
    handlers_agentResponse:
      allOf:
        - $ref: "#/components/schemas/sqlc_Agent"
        - properties:
            agent_passport:
              anyOf:
                - $ref: "#/components/schemas/handlers_agentPassportResponse"
                - type: "null"
          type: object
    handlers_agentSourceResponse:
      properties:
        auto_resolve_linkedin:
          description: |-
            AutoResolveLinkedin is hackernews-only (Part 4); always false for
            other sources. Deliberately NOT omitempty: a missing key on decode
            would leave a reused destination struct's prior boolean value
            untouched (both in Go and in a naive client-side merge), so this
            field is always explicitly serialized.
          type: boolean
        daily_read_budget:
          type: integer
        enabled:
          type: boolean
        queries:
          items:
            type: string
          type: array
        read_units_today:
          type: integer
        rules:
          items:
            $ref: "#/components/schemas/handlers_agentSourceRuleResponse"
          type: array
        source:
          type: string
      required:
        - source
        - enabled
        - queries
        - read_units_today
        - auto_resolve_linkedin
      type: object
    handlers_agentSourceRuleResponse:
      properties:
        api_errors_today:
          type: integer
        candidates_rejected_ai_error_today:
          type: integer
        candidates_rejected_score_today:
          type: integer
        candidates_scored_today:
          type: integer
        duplicates_today:
          type: integer
        id:
          type: string
        last_checked_at:
          anyOf:
            - format: date-time
              type: string
            - type: "null"
        last_error:
          type: string
        last_lead_created_at:
          anyOf:
            - format: date-time
              type: string
            - type: "null"
        last_matched_at:
          anyOf:
            - format: date-time
              type: string
            - type: "null"
        last_streamed_at:
          anyOf:
            - format: date-time
              type: string
            - type: "null"
        leads_created_today:
          type: integer
        query:
          type: string
        search_post_reads_today:
          type: integer
        status:
          type: string
        status_reason:
          type: string
        stream_post_reads_today:
          type: integer
        stream_recent_count:
          type: integer
        stream_recent_count_checked_at:
          anyOf:
            - format: date-time
              type: string
            - type: "null"
        stream_sync_error:
          type: string
        stream_sync_status:
          type: string
        stream_synced_at:
          anyOf:
            - format: date-time
              type: string
            - type: "null"
        user_reads_today:
          type: integer
      required:
        - id
        - query
        - status
        - stream_post_reads_today
        - search_post_reads_today
        - user_reads_today
        - candidates_scored_today
        - candidates_rejected_score_today
        - candidates_rejected_ai_error_today
        - leads_created_today
        - duplicates_today
        - api_errors_today
      type: object
    handlers_analyticsOverviewResponse:
      properties:
        accepted_count:
          type: integer
        accepted_rate:
          type: number
        hot_count:
          type: integer
        invited_count:
          type: integer
        low_fit_active_count:
          type: integer
        opted_out_count:
          type: integer
        replied_count:
          type: integer
        reply_rate:
          type: number
        total_leads:
          type: integer
        unfit_count:
          type: integer
      required:
        - invited_count
        - accepted_count
        - replied_count
        - hot_count
        - total_leads
        - accepted_rate
        - reply_rate
        - opted_out_count
        - unfit_count
        - low_fit_active_count
      type: object
    handlers_announcementDTO:
      properties:
        body:
          type: string
        created_at:
          type: string
        cta_href:
          type: string
        cta_label:
          type: string
        ends_at:
          type: string
        id:
          type: string
        priority:
          type: integer
        slug:
          type: string
        starts_at:
          type: string
        status:
          type: string
        surface:
          type: string
        targeting:
          additionalProperties:
            description: Arbitrary JSON value.
          type: object
        title:
          type: string
        updated_at:
          type: string
        variant:
          type: string
      required:
        - id
        - slug
        - surface
        - priority
        - title
        - body
        - cta_label
        - cta_href
        - variant
      type: object
    handlers_apiTokenListItem:
      properties:
        created_at:
          type: string
        expires_at:
          anyOf:
            - type: string
            - type: "null"
        id:
          type: string
        last_used_at:
          anyOf:
            - type: string
            - type: "null"
        name:
          type: string
        prefix_preview:
          type: string
      required:
        - id
        - name
        - prefix_preview
        - last_used_at
        - expires_at
        - created_at
      type: object
    handlers_appResponse:
      allOf:
        - $ref: "#/components/schemas/sqlc_App"
        - properties:
            lead_finder_passport:
              anyOf:
                - $ref: "#/components/schemas/handlers_agentPassportResponse"
                - type: "null"
            specialist_passports:
              additionalProperties:
                anyOf:
                  - $ref: "#/components/schemas/handlers_agentPassportResponse"
                  - type: "null"
              type: object
          type: object
    handlers_attachmentResponse:
      properties:
        file_name:
          type: string
        file_size:
          type: integer
        id:
          type: string
        mimetype:
          type: string
        type:
          type: string
        url:
          type: string
      required:
        - id
        - type
        - url
        - file_name
        - mimetype
        - file_size
      type: object
    handlers_auditEntryResponse:
      properties:
        action:
          type: string
        actor_email:
          type: string
        actor_first_name:
          type: string
        actor_last_name:
          type: string
        actor_user_id:
          type: string
        after_state:
          description: JSON value stored by the server; shape depends on this resource's configuration.
        before_state:
          description: JSON value stored by the server; shape depends on this resource's configuration.
        created_at:
          type: string
        id:
          type: string
        target_email:
          type: string
        target_first_name:
          type: string
        target_invitation_id:
          type: string
        target_last_name:
          type: string
        target_member_user_id:
          type: string
      required:
        - id
        - action
        - created_at
      type: object
    handlers_authResponse:
      properties:
        access_token:
          type: string
        checkout_url:
          description: |-
            Phase 16: SPEC Req 4 + 14 — for non-beta password signup, the frontend
            reads checkout_url to redirect to Stripe. This plan returns "" as a
            placeholder; plan 16-05 wires it to billing.LockCohortAndCreateCheckout.
            For beta users (beta_grandfather=true) checkout_url stays empty forever
            (frontend redirects to /dashboard).
          type: string
        user:
          $ref: "#/components/schemas/handlers_userInfo"
      required:
        - access_token
        - user
        - checkout_url
      type: object
    handlers_batchPromoteRejectedProfileOutcome:
      properties:
        id:
          type: string
        message:
          type: string
        status:
          type: string
      required:
        - id
        - status
      type: object
    handlers_batchPromoteRejectedProfilesResponse:
      properties:
        outcomes:
          items:
            $ref: "#/components/schemas/handlers_batchPromoteRejectedProfileOutcome"
          type: array
        queued:
          type: integer
      required:
        - queued
        - outcomes
      type: object
    handlers_bookingProviderAccountDTO:
      properties:
        account_email:
          type: string
        account_name:
          type: string
        connection_type:
          type: string
        created_at:
          type: string
        id:
          type: string
        last_error_code:
          type: string
        last_error_message:
          type: string
        last_verified_at:
          anyOf:
            - type: string
            - type: "null"
        last_webhook_seen_at:
          anyOf:
            - type: string
            - type: "null"
        provider:
          type: string
        status:
          type: string
        webhook_status:
          type: string
      required:
        - id
        - provider
        - connection_type
        - status
        - account_name
        - account_email
        - webhook_status
        - created_at
      type: object
    handlers_bookingProviderBackfillResult:
      properties:
        continuation:
          type: integer
        continuation_enqueued:
          type: boolean
        next_cursor:
          type: string
        pages:
          type: integer
        processed:
          type: integer
        truncated:
          type: boolean
      required:
        - processed
        - pages
        - truncated
        - continuation
        - continuation_enqueued
      type: object
    handlers_bookingProviderCard:
      properties:
        configured:
          type: boolean
        disabled_reason:
          type: string
        label:
          type: string
        provider:
          type: string
        supports_api_key:
          type: boolean
        supports_oauth:
          type: boolean
      required:
        - provider
        - label
        - configured
        - supports_oauth
        - supports_api_key
      type: object
    handlers_bookingProviderOAuthStartResponse:
      properties:
        auth_url:
          type: string
      required:
        - auth_url
      type: object
    handlers_bookingProvidersResponse:
      properties:
        accounts:
          items:
            $ref: "#/components/schemas/handlers_bookingProviderAccountDTO"
          type: array
        calendar_sync_enabled:
          type: boolean
        entitled:
          type: boolean
        entitlement_source:
          type: string
        providers:
          items:
            $ref: "#/components/schemas/handlers_bookingProviderCard"
          type: array
      required:
        - calendar_sync_enabled
        - entitled
        - entitlement_source
        - providers
        - accounts
      type: object
    handlers_campaignCreationPreflightResponse:
      properties:
        required_channels:
          items:
            type: string
          type: array
      required:
        - required_channels
      type: object
    handlers_campaignEnrollmentSummary:
      properties:
        enrolled:
          type: integer
        planned_actions:
          type: integer
        skipped:
          type: integer
        skipped_reasons:
          additionalProperties:
            type: integer
          type: object
      required:
        - enrolled
        - skipped
        - planned_actions
        - skipped_reasons
      type: object
    handlers_campaignPlatformStats:
      properties:
        accepted_count:
          type: integer
        contacted_count:
          type: integer
        followed_count:
          type: integer
        invited_count:
          type: integer
        platform:
          type: string
        replied_count:
          type: integer
        sent_count:
          type: integer
        total_leads:
          type: integer
      required:
        - platform
        - total_leads
        - contacted_count
        - sent_count
        - invited_count
        - followed_count
        - accepted_count
        - replied_count
      type: object
    handlers_campaignStatsResponse:
      properties:
        accepted_count:
          type: integer
        accepted_rate:
          type: number
        by_platform:
          items:
            $ref: "#/components/schemas/handlers_campaignPlatformStats"
          type: array
        contacted_count:
          type: integer
        invited_count:
          type: integer
        next_launch_at:
          anyOf:
            - format: date-time
              type: string
            - type: "null"
        planned_actions:
          type: integer
        replied_count:
          type: integer
        reply_rate:
          type: number
        review_leads:
          type: integer
        sent_count:
          type: integer
        total_leads:
          type: integer
      required:
        - total_leads
        - review_leads
        - contacted_count
        - sent_count
        - invited_count
        - accepted_count
        - replied_count
        - accepted_rate
        - reply_rate
        - planned_actions
        - next_launch_at
      type: object
    handlers_campaignWithAssignments:
      allOf:
        - $ref: "#/components/schemas/sqlc_Campaign"
        - properties:
            account_assignments:
              additionalProperties:
                anyOf:
                  - type: string
                  - type: "null"
              type: object
            channel_readiness:
              anyOf:
                - $ref: "#/components/schemas/policies_CampaignChannelReadiness"
                - type: "null"
            last_import_list_id:
              anyOf:
                - type: string
                - type: "null"
              description: |-
                LastImportListID is the most recently used list for this campaign,
                derived from leads.list_id (quick task 260819-qra). Nil when the
                campaign has no list-based leads yet.
          required:
            - account_assignments
          type: object
    handlers_campaignWithStats:
      allOf:
        - $ref: "#/components/schemas/sqlc_Campaign"
        - properties:
            account_assignments:
              additionalProperties:
                anyOf:
                  - type: string
                  - type: "null"
              type: object
            stats:
              anyOf:
                - $ref: "#/components/schemas/handlers_campaignStatsResponse"
                - type: "null"
          required:
            - stats
            - account_assignments
          type: object
    handlers_companyImportResultsResponse:
      properties:
        email_fallback:
          items:
            $ref: "#/components/schemas/decisionmakers_ResultView"
          type: array
        items:
          items:
            $ref: "#/components/schemas/decisionmakers_ResultView"
          type: array
        limit:
          type: integer
        linkedin_ready:
          items:
            $ref: "#/components/schemas/decisionmakers_ResultView"
          type: array
        offset:
          type: integer
        unresolved:
          items:
            $ref: "#/components/schemas/decisionmakers_ResultView"
          type: array
      required:
        - items
        - linkedin_ready
        - email_fallback
        - unresolved
        - offset
        - limit
      type: object
    handlers_contactDetailResponse:
      properties:
        contact:
          $ref: "#/components/schemas/sqlc_GetContactByIDRow"
        discovered_lead_id:
          type: string
        discovered_lead_source:
          type: string
        enrichment:
          description: JSON value stored by the server; shape depends on this resource's configuration.
        import_notes:
          type: string
        linkedin_profile_url:
          type: string
        linkedin_provider_id:
          type: string
        sequence:
          items:
            $ref: "#/components/schemas/handlers_sequenceStep"
          type: array
        signal_metadata:
          description: JSON value stored by the server; shape depends on this resource's configuration.
        signals:
          items:
            type: string
          type: array
      required:
        - contact
        - signals
        - enrichment
        - signal_metadata
        - sequence
      type: object
    handlers_contactListResponse:
      properties:
        contacts:
          items:
            $ref: "#/components/schemas/sqlc_ListGlobalContactsRow"
          type: array
        limit:
          type: integer
        offset:
          type: integer
        total:
          type: integer
      required:
        - contacts
        - total
        - limit
        - offset
      type: object
    handlers_conversationDetailResponse:
      properties:
        id:
          format: uuid
          type:
            - string
            - "null"
        last_message_at:
          format: date-time
          type:
            - string
            - "null"
        last_message_preview:
          type: string
        lead:
          anyOf:
            - $ref: "#/components/schemas/handlers_leadContext"
            - type: "null"
        messages:
          items:
            $ref: "#/components/schemas/handlers_messageResponse"
          type: array
        participant_headline:
          type: string
        participant_name:
          type: string
        platform:
          type: string
        platform_account_id:
          format: uuid
          type:
            - string
            - "null"
        unread_count:
          type: integer
      required:
        - id
        - platform_account_id
        - participant_name
        - participant_headline
        - platform
        - last_message_preview
        - last_message_at
        - unread_count
        - lead
        - messages
      type: object
    handlers_conversationResponse:
      properties:
        app_id:
          format: uuid
          type:
            - string
            - "null"
        id:
          format: uuid
          type:
            - string
            - "null"
        last_message_at:
          format: date-time
          type:
            - string
            - "null"
        last_message_preview:
          type: string
        lead:
          anyOf:
            - $ref: "#/components/schemas/handlers_leadContext"
            - type: "null"
        participant_headline:
          type: string
        participant_name:
          type: string
        platform:
          type: string
        platform_account_id:
          format: uuid
          type:
            - string
            - "null"
        profile_picture_url:
          type: string
        provider_chat_id:
          type: string
        unread_count:
          type: integer
      required:
        - id
        - platform_account_id
        - provider_chat_id
        - participant_name
        - participant_headline
        - profile_picture_url
        - platform
        - last_message_preview
        - last_message_at
        - unread_count
        - lead
        - app_id
      type: object
    handlers_countryRestrictionImpactResponse:
      properties:
        blocked_count:
          type: integer
        cancelled_count:
          type: integer
        planned_count:
          type: integer
      required:
        - planned_count
        - blocked_count
      type: object
    handlers_createAPITokenResponse:
      properties:
        created_at:
          type: string
        expires_at:
          anyOf:
            - type: string
            - type: "null"
        id:
          type: string
        name:
          type: string
        prefix_preview:
          type: string
        token:
          type: string
      required:
        - id
        - token
        - name
        - prefix_preview
        - expires_at
        - created_at
      type: object
    handlers_createCampaignResponse:
      properties:
        agent_id:
          format: uuid
          type:
            - string
            - "null"
        app_id:
          format: uuid
          type:
            - string
            - "null"
        created_at:
          format: date-time
          type:
            - string
            - "null"
        enrollment_summary:
          $ref: "#/components/schemas/handlers_campaignEnrollmentSummary"
        id:
          format: uuid
          type:
            - string
            - "null"
        list_id:
          format: uuid
          type:
            - string
            - "null"
        name:
          type: string
        settings:
          description: JSON value stored by the server; shape depends on this resource's configuration.
        status:
          type: string
        updated_at:
          format: date-time
          type:
            - string
            - "null"
        user_id:
          format: uuid
          type:
            - string
            - "null"
      required:
        - id
        - app_id
        - user_id
        - name
        - status
        - settings
        - created_at
        - updated_at
        - agent_id
        - list_id
        - enrollment_summary
      type: object
    handlers_createLeadNoteResponse:
      properties:
        note:
          $ref: "#/components/schemas/handlers_leadNoteResponse"
        reminder:
          anyOf:
            - $ref: "#/components/schemas/handlers_leadReminderResponse"
            - type: "null"
      required:
        - note
      type: object
    handlers_creditCatalogResponse:
      allOf:
        - $ref: "#/components/schemas/billing_CreditCatalogDTO"
        - properties:
            can_purchase:
              type: boolean
            credit_breakdown:
              anyOf:
                - $ref: "#/components/schemas/billing_WalletSummary"
                - type: "null"
            credits:
              type: integer
            has_stripe_customer:
              type: boolean
            requires_billing_admin:
              type: boolean
          required:
            - credits
            - can_purchase
            - requires_billing_admin
            - has_stripe_customer
          type: object
    handlers_csvImportFailure:
      properties:
        reason:
          type: string
        row:
          type: integer
      required:
        - row
        - reason
      type: object
    handlers_csvImportResponse:
      properties:
        created:
          type: integer
        enrolled:
          type: integer
        failed:
          type: integer
        failures:
          items:
            $ref: "#/components/schemas/handlers_csvImportFailure"
          type: array
        list_id:
          type: string
        skipped_duplicate:
          type: integer
        skipped_incompatible:
          type: integer
        skipped_reasons:
          additionalProperties:
            type: integer
          type: object
      required:
        - list_id
        - created
        - skipped_duplicate
        - failed
        - failures
        - enrolled
        - skipped_incompatible
      type: object
    handlers_currentPlanResponse:
      properties:
        beta_ends_at:
          type: integer
        beta_grandfather:
          type: boolean
        cadence:
          type: string
        cancel_at_period_end:
          type: boolean
        credits:
          type: integer
        current_period_end:
          type: integer
        current_price_id:
          type: string
        dm_sync_credits_extra:
          type: integer
        dm_sync_credits_monthly:
          type: integer
        dunning_state:
          type: string
        has_stripe_subscription:
          type: boolean
        included_credits:
          type: integer
        lead_source_credits_extra:
          type: integer
        lead_source_credits_monthly:
          type: integer
        max_agents_per_app:
          type: integer
        max_agents_total:
          type: integer
        max_apps:
          type: integer
        max_linkedin_accounts:
          description: |-
            Phase 16: capacity (from user_limits) + current usage (live counts).
            Frontend renders "X of Y used" widgets from these fields.
          type: integer
        max_sender_accounts:
          type: integer
        max_x_accounts:
          type: integer
        next_billing_date:
          type: integer
        next_charge_amount_cents:
          description: |-
            NextChargeAmountCents is this subscription's own recurring price total
            (sum of item unit_amount * quantity), NOT "the most recent paid
            invoice" -- the invoices list mixes in every add-on's separate Stripe
            subscription, so picking "latest paid invoice" there would surface an
            add-on's charge instead of the plan's. Scoped to THIS subscription only.
          type: integer
        payment_method_brand:
          type: string
        payment_method_exp_month:
          type: integer
        payment_method_exp_year:
          type: integer
        payment_method_last4:
          type: string
        payment_method_type:
          description: |-
            Phase 16: default payment method on the Stripe customer (resolved via
            invoice_settings.default_payment_method). All five are optional; when
            PaymentMethodType is empty the UI renders "No payment method on file".
            PaymentMethodType is the Stripe PM type string ("card", "link",
            "us_bank_account", "sepa_debit", ...). Card-specific fields are only
            populated when Type == "card".
          type: string
        plan_name:
          type: string
        status:
          type: string
        trial_ends_at:
          type: integer
        used_agents:
          type: integer
        used_apps:
          type: integer
        used_linkedin_accounts:
          type: integer
        used_sender_accounts:
          type: integer
        used_x_accounts:
          type: integer
      required:
        - plan_name
        - cadence
        - current_price_id
        - has_stripe_subscription
        - cancel_at_period_end
        - dunning_state
        - credits
        - included_credits
        - beta_grandfather
        - status
        - max_linkedin_accounts
        - max_sender_accounts
        - max_agents_total
        - max_apps
        - max_x_accounts
        - lead_source_credits_monthly
        - lead_source_credits_extra
        - dm_sync_credits_monthly
        - dm_sync_credits_extra
        - used_linkedin_accounts
        - used_sender_accounts
        - used_x_accounts
        - used_agents
        - used_apps
      type: object
    handlers_dailyActivityRow:
      properties:
        action_count:
          type: integer
        action_type:
          type: string
        date:
          type: string
      required:
        - date
        - action_type
        - action_count
      type: object
    handlers_dailyInviteRow:
      properties:
        day:
          type: string
        invites:
          type: integer
      required:
        - day
        - invites
      type: object
    handlers_dailyOverviewRow:
      properties:
        actions_completed:
          type: integer
        app_id:
          type: string
        app_name:
          type: string
        date:
          type: string
        leads_touched:
          type: integer
      required:
        - date
        - app_id
        - app_name
        - actions_completed
        - leads_touched
      type: object
    handlers_deletePlatformAccountResponse:
      properties:
        paused_campaigns:
          type: integer
        status:
          type: string
      required:
        - status
        - paused_campaigns
      type: object
    handlers_discoveredLeadListResponse:
      properties:
        counts:
          additionalProperties:
            type: integer
          type: object
        leads:
          items:
            $ref: "#/components/schemas/sqlc_ListDiscoveredLeadsByUserWithListRow"
          type: array
        total:
          type: integer
      required:
        - leads
        - counts
        - total
      type: object
    handlers_discoveredLeadSequenceResponse:
      properties:
        campaign_id:
          type: string
        campaign_name:
          type: string
        mode:
          type: string
        sequence:
          items:
            $ref: "#/components/schemas/handlers_sequenceStep"
          type: array
      required:
        - mode
        - sequence
      type: object
    handlers_dismissFollowUpResponse:
      properties:
        dismissed_at:
          format: date-time
          type: string
        lead_id:
          type: string
      required:
        - lead_id
        - dismissed_at
      type: object
    handlers_enrollListContactsResponse:
      properties:
        added:
          type: integer
        failed:
          type: integer
        member_ids:
          items:
            type: string
          type: array
        operation_id:
          type: string
        outcomes:
          items:
            $ref: "#/components/schemas/handlers_enrollmentOutcome"
          type: array
        processed_count:
          type: integer
        receipt_id:
          type: string
        resume_after:
          anyOf:
            - format: date-time
              type: string
            - type: "null"
        resume_available:
          type: boolean
        skipped:
          type: integer
        skipped_reasons:
          additionalProperties:
            type: integer
          type: object
        status:
          type: string
        total_requested:
          type: integer
      required:
        - status
        - added
        - skipped
        - failed
        - processed_count
        - total_requested
        - outcomes
        - skipped_reasons
      type: object
    handlers_enrollmentOutcome:
      properties:
        display_name:
          type: string
        lead_id:
          type: string
        member_id:
          type: string
        reason:
          type: string
        source_company_domain:
          type: string
        source_company_name:
          type: string
        source_csv_row_number:
          anyOf:
            - type: integer
            - type: "null"
        source_kind:
          type: string
        status:
          type: string
      required:
        - member_id
        - display_name
        - status
      type: object
    handlers_entitlementResponse:
      properties:
        beta_ends_at:
          description: "BetaEndsAt: Unix seconds when the beta free window ends (0 = lifetime)."
          type: integer
        beta_grandfather:
          type: boolean
        cadence:
          type: string
        credits:
          type: integer
        current_price_id:
          type: string
        dunning_state:
          type: string
        has_stripe_subscription:
          type: boolean
        included_credits:
          type: integer
        plan_name:
          type: string
        status:
          description: |-
            Status: "none" | "trialing" | "active" | "past_due" | "beta" | "appsumo".
            Expired beta (beta_ends_at in the past, no other access) reports "none"
            so the frontend shows the subscribe prompt.
            Mirrors currentPlanResponse.Status but is not payment-info-sensitive.
          type: string
        trial_ends_at:
          type: integer
      required:
        - plan_name
        - cadence
        - current_price_id
        - has_stripe_subscription
        - dunning_state
        - credits
        - included_credits
        - beta_grandfather
        - status
      type: object
    handlers_exhaustedLinkedInSignal:
      properties:
        consecutive_empty_runs:
          type: integer
        id:
          type: string
        label:
          type: string
      required:
        - id
        - label
        - consecutive_empty_runs
      type: object
    handlers_followUpsListResponse:
      properties:
        follow_ups:
          items:
            $ref: "#/components/schemas/sqlc_ListFollowUpsRow"
          type: array
        limit:
          type: integer
        offset:
          type: integer
        pending_count:
          type: integer
      required:
        - follow_ups
        - pending_count
        - limit
        - offset
      type: object
    handlers_funnelAnalysisResponse:
      properties:
        ai_insight:
          type: string
        funnel:
          $ref: "#/components/schemas/handlers_analyticsOverviewResponse"
        steps:
          items:
            $ref: "#/components/schemas/handlers_stepStatsRow"
          type: array
      required:
        - funnel
        - steps
        - ai_insight
      type: object
    handlers_generatedStepResult:
      properties:
        error:
          type: string
        message_template:
          type: string
        rationale:
          $ref: "#/components/schemas/ai_MessageAgentRationale"
        step_id:
          type: string
        step_order:
          type: integer
        subject_template:
          type: string
      required:
        - step_id
        - step_order
        - message_template
      type: object
    handlers_ideaResponse:
      properties:
        author_name:
          type: string
        created_at:
          type: string
        id:
          type: string
        is_own:
          type: boolean
        locked:
          type: boolean
        shipped_at:
          type: string
        shipped_link:
          type: string
        shipped_note:
          type: string
        status:
          type: string
        text:
          type: string
        vote_count:
          anyOf:
            - type: integer
            - type: "null"
        voted:
          type: boolean
      required:
        - id
        - text
        - author_name
        - is_own
        - status
        - voted
        - locked
        - created_at
        - vote_count
      type: object
    handlers_insightDTO:
      properties:
        body_md:
          type: string
        category:
          type: string
        created_at:
          format: date-time
          type: string
        id:
          type: string
        proposed_changes:
          anyOf:
            - $ref: "#/components/schemas/insights_ProposedChanges"
            - type: "null"
        scope:
          $ref: "#/components/schemas/handlers_insightScope"
        severity:
          type: string
        snooze_until:
          anyOf:
            - format: date-time
              type: string
            - type: "null"
        status:
          type: string
        suggested_action:
          type: string
        title:
          type: string
      required:
        - id
        - scope
        - category
        - severity
        - title
        - body_md
        - status
        - snooze_until
        - created_at
      type: object
    handlers_insightScope:
      properties:
        id:
          type: string
        type:
          type: string
      required:
        - type
      type: object
    handlers_integrationDetailResponse:
      properties:
        integration:
          $ref: "#/components/schemas/sqlc_Integration"
        webhooks:
          items:
            $ref: "#/components/schemas/handlers_webhookSubscriptionSafe"
          type: array
      required:
        - integration
        - webhooks
      type: object
    handlers_inviteResponse:
      properties:
        accept_url:
          description: |-
            AcceptURL is the full /team/accept?token=… link the owner can copy and
            hand over manually if the email never arrives (Resend hiccup, recipient
            spam-filter, DNS issue, etc.). Surfaced for every non-accepted,
            non-revoked invite — even after the expiry date, so the owner can spot
            why a copied link stopped working.
          type: string
        app_ids:
          items:
            type: string
          type: array
        can_manage_billing:
          type: boolean
        can_manage_team:
          type: boolean
        can_promote_admins:
          type: boolean
        created_at:
          type: string
        email:
          type: string
        expires_at:
          type: string
        id:
          type: string
        role:
          type: string
      required:
        - id
        - email
        - app_ids
        - role
        - can_manage_team
        - can_manage_billing
        - can_promote_admins
        - expires_at
        - created_at
        - accept_url
      type: object
    handlers_leadContext:
      properties:
        company:
          type: string
        first_name:
          type: string
        headline:
          type: string
        id:
          format: uuid
          type:
            - string
            - "null"
        last_name:
          type: string
        profile_picture_url:
          type: string
      required:
        - id
        - first_name
        - last_name
        - company
        - headline
        - profile_picture_url
      type: object
    handlers_leadListWithReviewState:
      allOf:
        - $ref: "#/components/schemas/sqlc_LeadList"
        - properties:
            pending_review_count:
              anyOf:
                - type: integer
                - type: "null"
            review_job_status:
              type: string
          type: object
    handlers_leadMeetingsResponse:
      properties:
        current_meeting:
          anyOf:
            - $ref: "#/components/schemas/handlers_meetingResponse"
            - type: "null"
        meetings:
          items:
            $ref: "#/components/schemas/handlers_meetingResponse"
          type: array
      required:
        - current_meeting
        - meetings
      type: object
    handlers_leadNoteResponse:
      properties:
        body:
          type: string
        created_at:
          format: date-time
          type: string
        id:
          type: string
        lead_id:
          type: string
        reminder:
          anyOf:
            - $ref: "#/components/schemas/handlers_leadReminderResponse"
            - type: "null"
        updated_at:
          format: date-time
          type: string
      required:
        - id
        - lead_id
        - body
        - created_at
        - updated_at
      type: object
    handlers_leadNotesListResponse:
      properties:
        limit:
          type: integer
        notes:
          items:
            $ref: "#/components/schemas/handlers_leadNoteResponse"
          type: array
        offset:
          type: integer
      required:
        - notes
        - limit
        - offset
      type: object
    handlers_leadReminderResponse:
      properties:
        body:
          type: string
        completed_at:
          anyOf:
            - format: date-time
              type: string
            - type: "null"
        confidence:
          type: integer
        conversation_id:
          anyOf:
            - type: string
            - type: "null"
        created_at:
          format: date-time
          type: string
        due_at:
          format: date-time
          type: string
        evidence:
          type: string
        id:
          type: string
        lead_id:
          type: string
        note_id:
          anyOf:
            - type: string
            - type: "null"
        reason:
          type: string
        source:
          type: string
        status:
          type: string
        suggested_message:
          type: string
        updated_at:
          format: date-time
          type: string
      required:
        - id
        - lead_id
        - note_id
        - body
        - due_at
        - status
        - completed_at
        - created_at
        - updated_at
        - source
        - reason
        - evidence
        - suggested_message
        - confidence
      type: object
    handlers_meetingResponse:
      properties:
        app_id:
          anyOf:
            - type: string
            - type: "null"
        booking_url:
          type: string
        campaign_id:
          anyOf:
            - type: string
            - type: "null"
        conversation_id:
          anyOf:
            - type: string
            - type: "null"
        created_at:
          format: date-time
          type: string
        external_booking_id:
          type: string
        external_event_id:
          anyOf:
            - type: string
            - type: "null"
        id:
          type: string
        invitee_email:
          anyOf:
            - type: string
            - type: "null"
        invitee_name:
          anyOf:
            - type: string
            - type: "null"
        lead_id:
          anyOf:
            - type: string
            - type: "null"
        link_click_count:
          type: integer
        link_clicked_at:
          anyOf:
            - format: date-time
              type: string
            - type: "null"
        provider:
          type: string
        provider_event_occurred_at:
          anyOf:
            - format: date-time
              type: string
            - type: "null"
        provider_event_type:
          anyOf:
            - type: string
            - type: "null"
        raw_payload_expires_at:
          anyOf:
            - format: date-time
              type: string
            - type: "null"
        scheduled_end_at:
          anyOf:
            - format: date-time
              type: string
            - type: "null"
        scheduled_start_at:
          anyOf:
            - format: date-time
              type: string
            - type: "null"
        source:
          type: string
        source_message_id:
          anyOf:
            - type: string
            - type: "null"
        status:
          type: string
        superseded_at:
          anyOf:
            - format: date-time
              type: string
            - type: "null"
        updated_at:
          format: date-time
          type: string
        user_id:
          type: string
      required:
        - id
        - user_id
        - app_id
        - campaign_id
        - lead_id
        - conversation_id
        - source_message_id
        - source
        - provider
        - status
        - booking_url
        - provider_event_type
        - external_event_id
        - external_booking_id
        - invitee_email
        - invitee_name
        - scheduled_start_at
        - scheduled_end_at
        - provider_event_occurred_at
        - link_clicked_at
        - link_click_count
        - superseded_at
        - raw_payload_expires_at
        - created_at
        - updated_at
      type: object
    handlers_memberResponse:
      properties:
        app_ids:
          items:
            type: string
          type: array
        can_manage_billing:
          type: boolean
        can_manage_team:
          type: boolean
        can_promote_admins:
          type: boolean
        email:
          type: string
        first_name:
          type: string
        id:
          type: string
        joined_at:
          type: string
        last_name:
          type: string
        role:
          type: string
        user_id:
          type: string
      required:
        - id
        - user_id
        - email
        - first_name
        - last_name
        - role
        - app_ids
        - can_manage_team
        - can_manage_billing
        - can_promote_admins
        - joined_at
      type: object
    handlers_messageResponse:
      properties:
        attachments:
          items:
            $ref: "#/components/schemas/handlers_attachmentResponse"
          type: array
        body:
          type: string
        created_at:
          format: date-time
          type:
            - string
            - "null"
        id:
          description: |-
            ID is either the local UUID (formatted) for messages persisted in
            our DB, or the Unipile provider message ID for messages fetched live
            from the platform. Either way the frontend treats it as an opaque
            string for React keys / dedup — it must never be null/empty.
          type: string
        is_ai_generated:
          type: boolean
        rationale:
          anyOf:
            - $ref: "#/components/schemas/ai_MessageAgentRationale"
            - type: "null"
        sender_name:
          type: string
        sender_type:
          type: string
        subject:
          type: string
      required:
        - id
        - sender_type
        - sender_name
        - body
        - is_ai_generated
        - created_at
      type: object
    handlers_nextEventResponse:
      properties:
        action_type:
          type: string
        app_id:
          type: string
        app_name:
          type: string
        scheduled_at:
          format: date-time
          type: string
      required:
        - scheduled_at
        - action_type
        - app_id
        - app_name
      type: object
    handlers_notificationDTO:
      properties:
        body:
          type: string
        created_at_ms:
          type: integer
        id:
          type: string
        payload:
          additionalProperties:
            description: Arbitrary JSON value.
          type: object
        read_at_ms:
          anyOf:
            - type: integer
            - type: "null"
        title:
          type: string
        type:
          type: string
      required:
        - id
        - type
        - title
        - body
        - payload
        - created_at_ms
      type: object
    handlers_operatorActionView:
      properties:
        cost_cents:
          type: integer
        deep_link:
          type: string
        error:
          type: string
        id:
          type: string
        result:
          type: string
        reversibility:
          type: string
        risk:
          type: string
        status:
          type: string
        step_id:
          type: string
        target_id:
          type: string
        target_type:
          type: string
      required:
        - id
        - step_id
        - status
        - target_type
        - target_id
        - cost_cents
        - risk
        - reversibility
      type: object
    handlers_operatorCapabilityView:
      properties:
        availability_note:
          type: string
        cost:
          type: string
        domain:
          type: string
        execution_class:
          type: string
        id:
          type: string
        status:
          type: string
        summary:
          type: string
        title:
          type: string
      required:
        - id
        - title
        - summary
        - domain
        - execution_class
        - cost
        - availability_note
        - status
      type: object
    handlers_operatorStepView:
      properties:
        error:
          type: string
        id:
          type: string
        idempotent:
          type: boolean
        result:
          type: string
        sequence:
          type: integer
        status:
          type: string
        summary:
          type: string
      required:
        - id
        - sequence
        - status
        - summary
        - idempotent
      type: object
    handlers_operatorTaskView:
      properties:
        app_id:
          type: string
        id:
          type: string
        max_cost_cents:
          type: integer
        status:
          type: string
        title:
          type: string
        version:
          type: integer
      required:
        - id
        - app_id
        - title
        - status
        - version
        - max_cost_cents
      type: object
    handlers_peopleCSVItem:
      properties:
        company:
          type: string
        created_member_id:
          type: string
        email:
          type: string
        first_name:
          type: string
        headline:
          type: string
        last_name:
          type: string
        linkedin_url:
          type: string
        reason:
          type: string
        row:
          type: integer
        status:
          type: string
      required:
        - row
        - first_name
        - last_name
        - company
        - headline
        - linkedin_url
        - email
        - status
        - reason
      type: object
    handlers_peopleCSVResponse:
      properties:
        created:
          type: integer
        duplicate:
          type: integer
        failed:
          type: integer
        import_id:
          type: string
        invalid:
          type: integer
        items:
          items:
            $ref: "#/components/schemas/handlers_peopleCSVItem"
          type: array
        limit:
          type: integer
        list_id:
          type: string
        offset:
          type: integer
        selection_fingerprint:
          type: string
        skipped_duplicate:
          type: integer
        status:
          type: string
        total:
          type: integer
        valid:
          type: integer
      required:
        - import_id
        - selection_fingerprint
        - status
        - items
        - offset
        - limit
        - total
        - valid
        - duplicate
        - invalid
        - created
        - skipped_duplicate
        - failed
      type: object
    handlers_platformAccountSetupCampaignResponse:
      properties:
        app_id:
          format: uuid
          type:
            - string
            - "null"
        campaign_id:
          format: uuid
          type:
            - string
            - "null"
        campaign_name:
          type: string
        campaign_status:
          type: string
        has_x_listening:
          type: boolean
        platform:
          type: string
      required:
        - campaign_id
        - app_id
        - campaign_name
        - campaign_status
        - platform
        - has_x_listening
      type: object
    handlers_platformAccountSetupStatusResponse:
      properties:
        active_assigned_campaigns:
          type: integer
        active_listening_assigned_campaigns:
          type: integer
        assigned_campaigns:
          type: integer
        campaigns:
          items:
            $ref: "#/components/schemas/handlers_platformAccountSetupCampaignResponse"
          type: array
        listening_assigned_campaigns:
          type: integer
        ready:
          type: boolean
      required:
        - assigned_campaigns
        - active_assigned_campaigns
        - listening_assigned_campaigns
        - active_listening_assigned_campaigns
        - ready
        - campaigns
      type: object
    handlers_promoteRejectedProfileStatusResponse:
      properties:
        message:
          type: string
        status:
          type: string
      required:
        - status
      type: object
    handlers_publicInvitationResponse:
      properties:
        app_count:
          type: integer
        email:
          type: string
        owner_email:
          type: string
        owner_first_name:
          type: string
        owner_last_name:
          type: string
        owner_user_id:
          type: string
        state:
          type: string
      required:
        - owner_user_id
        - email
        - owner_email
        - owner_first_name
        - owner_last_name
        - app_count
        - state
      type: object
    handlers_publicPlatformAccount:
      properties:
        created_at:
          format: date-time
          type:
            - string
            - "null"
        display_name:
          type: string
        id:
          format: uuid
          type:
            - string
            - "null"
        metadata:
          description: JSON value stored by the server; shape depends on this resource's configuration.
        platform:
          type: string
        status:
          type: string
        updated_at:
          format: date-time
          type:
            - string
            - "null"
        user_id:
          format: uuid
          type:
            - string
            - "null"
      required:
        - id
        - user_id
        - platform
        - display_name
        - status
        - metadata
        - created_at
        - updated_at
      type: object
    handlers_rejectionClass:
      properties:
        class:
          type: string
        code:
          type: string
        count:
          type: integer
        latest_at_ms:
          type: integer
        sample_reasoning:
          type: string
      required:
        - class
        - code
        - count
        - sample_reasoning
        - latest_at_ms
      type: object
    handlers_reschedulePlannedResponse:
      properties:
        planned_count:
          type: integer
        rescheduled_count:
          type: integer
      required:
        - planned_count
        - rescheduled_count
      type: object
    handlers_reviewGenerationResult:
      properties:
        body:
          type: string
        error:
          type: string
        id:
          type: string
        status:
          type: string
        subject:
          type: string
        version_id:
          type: string
      required:
        - id
        - status
      type: object
    handlers_runningAgentResponse:
      properties:
        agent_id:
          type: string
        agent_name:
          type: string
        phase:
          type: string
        started_at:
          type: string
      required:
        - agent_id
        - agent_name
        - started_at
        - phase
      type: object
    handlers_sequenceStep:
      properties:
        action_type:
          type: string
        executed_at:
          format: date-time
          type:
            - string
            - "null"
        message_content:
          type: string
        scheduled_at:
          format: date-time
          type:
            - string
            - "null"
        status:
          type: string
        step_deleted_at:
          format: date-time
          type:
            - string
            - "null"
        step_order:
          type: integer
        step_type:
          type: string
      required:
        - step_type
        - step_order
        - action_type
        - status
        - message_content
        - scheduled_at
        - executed_at
        - step_deleted_at
      type: object
    handlers_signalStatsRow:
      properties:
        acceptance_rate:
          type: number
        actions_completed:
          type: integer
        approval_rate:
          type: number
        avg_match_score:
          type: number
        leads_accepted:
          type: integer
        leads_approved:
          type: integer
        leads_found:
          type: integer
        leads_in_campaign:
          type: integer
        leads_replied:
          type: integer
        reply_rate:
          type: number
        signal:
          type: string
      required:
        - signal
        - leads_found
        - leads_approved
        - approval_rate
        - leads_in_campaign
        - leads_accepted
        - acceptance_rate
        - leads_replied
        - reply_rate
        - actions_completed
        - avg_match_score
      type: object
    handlers_specialistActivityDTO:
      properties:
        event_type:
          type: string
        id:
          type: string
        learning_suggestions:
          items:
            type: string
          type: array
        occurred_at:
          format: date-time
          type: string
        specialist_key:
          type: string
        status:
          type: string
        subject:
          type: string
        subject_href:
          type: string
        summary:
          type: string
        title:
          type: string
        tone:
          type: string
      required:
        - id
        - specialist_key
        - event_type
        - title
        - summary
        - subject
        - subject_href
        - status
        - tone
        - occurred_at
      type: object
    handlers_specialistActivityResponse:
      properties:
        activities:
          items:
            $ref: "#/components/schemas/handlers_specialistActivityDTO"
          type: array
        derived_at:
          format: date-time
          type: string
      required:
        - derived_at
        - activities
      type: object
    handlers_specialistRunComputedDTO:
      properties:
        duration_ms:
          type: integer
        step_count:
          type: integer
      type: object
    handlers_specialistRunDTO:
      properties:
        completed_at:
          anyOf:
            - format: date-time
              type: string
            - type: "null"
        computed:
          $ref: "#/components/schemas/handlers_specialistRunComputedDTO"
        computeid_capability:
          type: string
        computeid_gate_outcome:
          type: string
        computeid_passport_id:
          type: string
        error_code:
          type: string
        error_message:
          type: string
        id:
          type: string
        input_tokens:
          type: integer
        metadata:
          description: JSON value stored by the server; shape depends on this resource's configuration.
        objective:
          type: string
        output_tokens:
          type: integer
        skill_slug:
          type: string
        skill_version:
          type: string
        specialist_key:
          type: string
        started_at:
          format: date-time
          type: string
        status:
          type: string
        trigger_source:
          type: string
      required:
        - id
        - specialist_key
        - trigger_source
        - objective
        - skill_slug
        - skill_version
        - status
        - input_tokens
        - output_tokens
        - metadata
        - started_at
        - computed
      type: object
    handlers_specialistRunDetailResponse:
      properties:
        run:
          $ref: "#/components/schemas/handlers_specialistRunDTO"
        steps:
          items:
            $ref: "#/components/schemas/handlers_specialistRunStepDTO"
          type: array
      required:
        - run
        - steps
      type: object
    handlers_specialistRunStepDTO:
      properties:
        created_at:
          format: date-time
          type: string
        error_code:
          type: string
        id:
          type: string
        index:
          type: integer
        input:
          description: JSON value stored by the server; shape depends on this resource's configuration.
        kind:
          type: string
        metadata:
          description: JSON value stored by the server; shape depends on this resource's configuration.
        output:
          description: JSON value stored by the server; shape depends on this resource's configuration.
        run_id:
          type: string
        status:
          type: string
        summary:
          type: string
        tool_name:
          type: string
      required:
        - id
        - run_id
        - index
        - kind
        - status
        - summary
        - input
        - output
        - metadata
        - created_at
      type: object
    handlers_specialistRunsResponse:
      properties:
        runs:
          items:
            $ref: "#/components/schemas/handlers_specialistRunDTO"
          type: array
      required:
        - runs
      type: object
    handlers_stepStatsRow:
      properties:
        approved_actions:
          type: integer
        awaiting_approval_actions:
          type: integer
        cancelled_actions:
          type: integer
        completed_actions:
          type: integer
        configured_delay_hours:
          type: integer
        executing_actions:
          type: integer
        failed_actions:
          type: integer
        hot_replied:
          type: integer
        leads_replied:
          type: integer
        pending_actions:
          type: integer
        reply_rate:
          type: number
        skipped_actions:
          type: integer
        step_order:
          type: integer
        step_type:
          type: string
        total_actions:
          type: integer
        waiting_connection_actions:
          type: integer
      required:
        - step_order
        - step_type
        - total_actions
        - completed_actions
        - pending_actions
        - approved_actions
        - executing_actions
        - failed_actions
        - skipped_actions
        - leads_replied
        - hot_replied
        - reply_rate
        - configured_delay_hours
        - cancelled_actions
        - waiting_connection_actions
        - awaiting_approval_actions
      type: object
    handlers_unmatchedBookingEventResponse:
      properties:
        account_label:
          type: string
        created_at:
          format: date-time
          type: string
        event_type:
          type: string
        external_booking_id:
          type: string
        id:
          type: string
        invitee_email:
          type: string
        invitee_name:
          type: string
        match_reason:
          type: string
        provider:
          type: string
        provider_label:
          type: string
        scheduled_end_at:
          anyOf:
            - format: date-time
              type: string
            - type: "null"
        scheduled_start_at:
          anyOf:
            - format: date-time
              type: string
            - type: "null"
      required:
        - id
        - provider
        - provider_label
        - event_type
        - invitee_email
        - invitee_name
        - scheduled_start_at
        - scheduled_end_at
        - match_reason
        - account_label
        - created_at
        - external_booking_id
      type: object
    handlers_unmatchedBookingEventsResponse:
      properties:
        events:
          items:
            $ref: "#/components/schemas/handlers_unmatchedBookingEventResponse"
          type: array
      required:
        - events
      type: object
    handlers_updateCampaignResponse:
      allOf:
        - $ref: "#/components/schemas/sqlc_Campaign"
        - properties:
            enrolled_count:
              anyOf:
                - type: integer
                - type: "null"
            skipped_count:
              anyOf:
                - type: integer
                - type: "null"
            skipped_reasons:
              additionalProperties:
                type: integer
              type: object
          type: object
    handlers_usageResponse:
      properties:
        invite_daily_history:
          items:
            $ref: "#/components/schemas/handlers_dailyInviteRow"
          type: array
        invite_hard_cap:
          type: integer
        invite_limit:
          description: |-
            Effective per-account caps — tier-derived defaults + admin overrides
            applied. Surfaced so the UI doesn't duplicate the (tier-dependent)
            default numbers and drift from backend enforcement.
          type: integer
        invites_used:
          type: integer
        message_hard_cap:
          type: integer
        message_limit:
          type: integer
        messages_used:
          type: integer
        pending_invites:
          type: integer
        window_days:
          type: integer
        x:
          anyOf:
            - $ref: "#/components/schemas/handlers_xUsage"
            - type: "null"
          description: |-
            X accounts only (Phase 3, FR-14): today's follow/DM sends vs the
            warmup-adjusted per-day caps. Nil for non-X platforms.
      required:
        - invites_used
        - messages_used
        - pending_invites
        - window_days
        - invite_daily_history
        - invite_limit
        - message_limit
        - invite_hard_cap
        - message_hard_cap
      type: object
    handlers_userInfo:
      properties:
        beta_grandfather:
          type: boolean
        email:
          type: string
        first_name:
          type: string
        id:
          type: string
        last_name:
          type: string
      required:
        - id
        - email
        - beta_grandfather
      type: object
    handlers_userProfileResponse:
      properties:
        auth_provider:
          type: string
        booking_link:
          type: string
        digest_emails_enabled:
          type: boolean
        email:
          type: string
        first_name:
          type: string
        has_password:
          type: boolean
        id:
          type: string
        language:
          type: string
        last_name:
          type: string
        timezone:
          type: string
      required:
        - id
        - email
        - first_name
        - last_name
        - language
        - timezone
        - digest_emails_enabled
        - booking_link
        - auth_provider
        - has_password
      type: object
    handlers_userSessionListItem:
      properties:
        created_at:
          type: string
        expires_at:
          type: string
        id:
          type: string
        ip_address:
          type: string
        is_current:
          type: boolean
        last_seen_at:
          type: string
        user_agent:
          type: string
      required:
        - id
        - user_agent
        - ip_address
        - last_seen_at
        - expires_at
        - created_at
        - is_current
      type: object
    handlers_webhookSubscriptionSafe:
      properties:
        active:
          type: boolean
        created_at:
          format: date-time
          type:
            - string
            - "null"
        events:
          items:
            type: string
          type: array
        id:
          format: uuid
          type:
            - string
            - "null"
        integration_id:
          format: uuid
          type:
            - string
            - "null"
        secret:
          type: string
        updated_at:
          format: date-time
          type:
            - string
            - "null"
        url:
          type: string
        user_id:
          format: uuid
          type:
            - string
            - "null"
      required:
        - id
        - user_id
        - integration_id
        - url
        - secret
        - events
        - active
        - created_at
        - updated_at
      type: object
    handlers_workspaceSummary:
      properties:
        app_ids:
          items:
            type: string
          type: array
        can_manage_billing:
          type: boolean
        can_manage_team:
          type: boolean
        can_promote_admins:
          type: boolean
        is_demo:
          type: boolean
        member_id:
          type: string
        owner_email:
          type: string
        owner_first_name:
          type: string
        owner_last_name:
          type: string
        owner_user_id:
          type: string
        role:
          type: string
      required:
        - member_id
        - owner_user_id
        - owner_email
        - owner_first_name
        - owner_last_name
        - role
        - app_ids
        - can_manage_team
        - can_manage_billing
        - can_promote_admins
        - is_demo
      type: object
    handlers_workspacesResponse:
      properties:
        memberships:
          items:
            $ref: "#/components/schemas/handlers_workspaceSummary"
          type: array
      required:
        - memberships
      type: object
    handlers_xUsage:
      properties:
        base_dm_cap:
          type: integer
        base_follow_cap:
          type: integer
        dm_daily_cap:
          type: integer
        dms_today:
          type: integer
        follow_daily_cap:
          type: integer
        follows_today:
          type: integer
        fully_warmed:
          type: boolean
        warmup_day:
          type: integer
      required:
        - dms_today
        - follows_today
        - dm_daily_cap
        - follow_daily_cap
        - warmup_day
        - fully_warmed
        - base_dm_cap
        - base_follow_cap
      type: object
    insights_ProposedAddSignal:
      properties:
        category:
          type: string
        key:
          type: string
        label:
          type: string
      required:
        - category
        - key
        - label
      type: object
    insights_ProposedChanges:
      properties:
        activate_campaign:
          description: |-
            ActivateCampaign, when true, marks a campaign-scoped insight as
            mechanically applicable: Apply transitions the campaign draft/paused
            -> active through the exact same policy gates as the manual Launch
            button (subscription check, ValidateCampaignTransition,
            RequireCampaignActivationReady). Only the stuck_draft_campaign
            detector emits this today.
          type: boolean
        add_signals:
          items:
            $ref: "#/components/schemas/insights_ProposedAddSignal"
          type: array
        adjust_score_floor:
          anyOf:
            - type: integer
            - type: "null"
        disable_signals:
          items:
            $ref: "#/components/schemas/insights_ProposedSignalRef"
          type: array
      type: object
    insights_ProposedSignalRef:
      properties:
        id:
          type: string
        key:
          type: string
        label:
          type: string
        platform:
          type: string
      required:
        - id
        - platform
        - key
        - label
      type: object
    operatoroutcomes_Outcome:
      properties:
        count_scope:
          type: string
        evidence:
          additionalProperties:
            type: integer
          type: object
        execution_shape:
          type: string
        id:
          type: string
        link:
          type: string
        scope:
          type: string
        source_id_limit:
          type: integer
        source_ids:
          items:
            type: string
          type: array
        title:
          type: string
        why_now:
          type: string
      required:
        - id
        - title
        - why_now
        - scope
        - execution_shape
        - evidence
        - source_ids
        - source_id_limit
        - count_scope
        - link
      type: object
    policies_CampaignChannelReadiness:
      properties:
        ready_channels:
          items:
            type: string
          type: array
        step_channels:
          items:
            type: string
          type: array
        usable_channels:
          items:
            type: string
          type: array
        warnings:
          items:
            $ref: "#/components/schemas/policies_ChannelWarning"
          type: array
      required:
        - step_channels
        - usable_channels
        - ready_channels
        - warnings
      type: object
    policies_ChannelWarning:
      properties:
        channel:
          type: string
        message:
          type: string
      required:
        - channel
        - message
      type: object
    sqlc_Agent:
      properties:
        app_id:
          format: uuid
          type:
            - string
            - "null"
        b2b:
          type: boolean
        config:
          description: JSON value stored by the server; shape depends on this resource's configuration.
        created_at:
          format: date-time
          type:
            - string
            - "null"
        icp_description:
          type:
            - string
            - "null"
        icp_filters:
          description: JSON value stored by the server; shape depends on this resource's configuration.
        id:
          format: uuid
          type:
            - string
            - "null"
        last_lead_digest_at:
          format: date-time
          type:
            - string
            - "null"
        last_run_at:
          format: date-time
          type:
            - string
            - "null"
        leads_found:
          type: integer
        list_id:
          format: uuid
          type:
            - string
            - "null"
        max_signals:
          type: integer
        name:
          type: string
        next_run_at:
          format: date-time
          type:
            - string
            - "null"
        require_current_employer:
          type: boolean
        score_floor:
          type: integer
        status:
          type: string
        type:
          type: string
        updated_at:
          format: date-time
          type:
            - string
            - "null"
        user_id:
          format: uuid
          type:
            - string
            - "null"
      required:
        - id
        - app_id
        - user_id
        - name
        - status
        - type
        - config
        - leads_found
        - max_signals
        - created_at
        - updated_at
        - list_id
        - last_run_at
        - next_run_at
        - icp_description
        - icp_filters
        - score_floor
        - last_lead_digest_at
        - b2b
        - require_current_employer
      type: object
    sqlc_AgentMemory:
      properties:
        app_id:
          format: uuid
          type:
            - string
            - "null"
        created_at:
          format: date-time
          type:
            - string
            - "null"
        id:
          format: uuid
          type:
            - string
            - "null"
        memory_key:
          type: string
        reason:
          type: string
        source:
          type: string
        updated_at:
          format: date-time
          type:
            - string
            - "null"
        user_id:
          format: uuid
          type:
            - string
            - "null"
        value_json:
          description: JSON value stored by the server; shape depends on this resource's configuration.
        version:
          type: integer
      required:
        - id
        - user_id
        - app_id
        - memory_key
        - value_json
        - source
        - reason
        - version
        - created_at
        - updated_at
      type: object
    sqlc_AgentSignal:
      properties:
        agent_id:
          format: uuid
          type:
            - string
            - "null"
        config:
          description: JSON value stored by the server; shape depends on this resource's configuration.
        created_at:
          format: date-time
          type:
            - string
            - "null"
        description:
          type:
            - string
            - "null"
        discovery_state:
          description: JSON value stored by the server; shape depends on this resource's configuration.
        enabled:
          type: boolean
        estimated_matches:
          type:
            - integer
            - "null"
        id:
          format: uuid
          type:
            - string
            - "null"
        label:
          type: string
        last_run_at:
          format: date-time
          type:
            - string
            - "null"
        manual_launch_cooldown_until:
          format: date-time
          type:
            - string
            - "null"
        next_run_at:
          format: date-time
          type:
            - string
            - "null"
        platform:
          type: string
        run_interval_hours:
          type: integer
        signal_category:
          type: string
        signal_key:
          type: string
        updated_at:
          format: date-time
          type:
            - string
            - "null"
      required:
        - id
        - agent_id
        - platform
        - signal_category
        - signal_key
        - label
        - description
        - enabled
        - config
        - estimated_matches
        - created_at
        - updated_at
        - last_run_at
        - next_run_at
        - run_interval_hours
        - manual_launch_cooldown_until
        - discovery_state
      type: object
    sqlc_AgentThread:
      properties:
        app_id:
          format: uuid
          type:
            - string
            - "null"
        created_at:
          format: date-time
          type:
            - string
            - "null"
        id:
          format: uuid
          type:
            - string
            - "null"
        title:
          type: string
        updated_at:
          format: date-time
          type:
            - string
            - "null"
        user_id:
          format: uuid
          type:
            - string
            - "null"
      required:
        - id
        - user_id
        - app_id
        - title
        - created_at
        - updated_at
      type: object
    sqlc_App:
      properties:
        created_at:
          format: date-time
          type:
            - string
            - "null"
        description:
          type:
            - string
            - "null"
        icp_description:
          type:
            - string
            - "null"
        icp_filters:
          description: JSON value stored by the server; shape depends on this resource's configuration.
        id:
          format: uuid
          type:
            - string
            - "null"
        language:
          type: string
        logo_url:
          type:
            - string
            - "null"
        name:
          type: string
        product_brain:
          description: JSON value stored by the server; shape depends on this resource's configuration.
        status:
          type: string
        updated_at:
          format: date-time
          type:
            - string
            - "null"
        url:
          type:
            - string
            - "null"
        user_id:
          format: uuid
          type:
            - string
            - "null"
      required:
        - id
        - user_id
        - name
        - url
        - description
        - icp_description
        - icp_filters
        - status
        - created_at
        - updated_at
        - logo_url
        - product_brain
        - language
      type: object
    sqlc_Campaign:
      properties:
        agent_id:
          format: uuid
          type:
            - string
            - "null"
        app_id:
          format: uuid
          type:
            - string
            - "null"
        created_at:
          format: date-time
          type:
            - string
            - "null"
        creation_idempotency_key:
          description: Caller retry key. Clone-from-list requests use one canonical UUID; compatible non-clone requests may leave
            it null.
          type:
            - string
            - "null"
        creation_request_fingerprint:
          description: "Lowercase SHA-256 of canonical create intent: app path ID, name, agent, list, clone flag, account, sorted
            assignments, normalized mode, and ordered normalized workflow steps."
          type:
            - string
            - "null"
        id:
          format: uuid
          type:
            - string
            - "null"
        list_id:
          format: uuid
          type:
            - string
            - "null"
        name:
          type: string
        settings:
          description: JSON value stored by the server; shape depends on this resource's configuration.
        status:
          type: string
        status_changed_at:
          format: date-time
          type:
            - string
            - "null"
        updated_at:
          format: date-time
          type:
            - string
            - "null"
        user_id:
          format: uuid
          type:
            - string
            - "null"
      required:
        - id
        - app_id
        - user_id
        - name
        - status
        - settings
        - created_at
        - updated_at
        - agent_id
        - list_id
        - creation_idempotency_key
        - creation_request_fingerprint
        - status_changed_at
      type: object
    sqlc_CampaignSignal:
      properties:
        campaign_id:
          format: uuid
          type:
            - string
            - "null"
        description:
          type:
            - string
            - "null"
        enabled:
          type: boolean
        estimated_matches:
          type:
            - integer
            - "null"
        id:
          format: uuid
          type:
            - string
            - "null"
        label:
          type: string
        platform:
          type: string
        signal_key:
          type: string
      required:
        - id
        - campaign_id
        - platform
        - signal_key
        - label
        - description
        - enabled
        - estimated_matches
      type: object
    sqlc_ClayInboundEvent:
      properties:
        clay_connection_id:
          format: uuid
          type:
            - string
            - "null"
        company_name:
          type: string
        error_message:
          type: string
        id:
          format: uuid
          type:
            - string
            - "null"
        job_id:
          format: uuid
          type:
            - string
            - "null"
        raw_payload:
          description: JSON value stored by the server; shape depends on this resource's configuration.
        received_at:
          format: date-time
          type:
            - string
            - "null"
        status:
          type: string
        user_id:
          format: uuid
          type:
            - string
            - "null"
      required:
        - id
        - user_id
        - clay_connection_id
        - received_at
        - status
        - company_name
        - job_id
        - error_message
        - raw_payload
      type: object
    sqlc_CompanyDecisionMakerImportJob:
      properties:
        app_id:
          format: uuid
          type:
            - string
            - "null"
        approval_metadata:
          description: JSON value stored by the server; shape depends on this resource's configuration.
        billing_cycle:
          type: integer
        completed_at:
          format: date-time
          type:
            - string
            - "null"
        completed_row_count:
          type: integer
        content_sha256:
          type: string
        created_at:
          format: date-time
          type:
            - string
            - "null"
        email_fallback_enabled:
          type: boolean
        email_output_list_id:
          format: uuid
          type:
            - string
            - "null"
        estimated_credits:
          type: integer
        failure_code:
          type: string
        file_name:
          type: string
        file_size_bytes:
          type: integer
        id:
          format: uuid
          type:
            - string
            - "null"
        idempotency_key:
          type: string
        linkedin_output_list_id:
          format: uuid
          type:
            - string
            - "null"
        max_email_fallbacks:
          type: integer
        max_people_per_company:
          type: integer
        provider_usage:
          description: JSON value stored by the server; shape depends on this resource's configuration.
        refunded_at:
          format: date-time
          type:
            - string
            - "null"
        refunded_credits:
          type: integer
        requested_roles:
          description: JSON value stored by the server; shape depends on this resource's configuration.
        reserved_at:
          format: date-time
          type:
            - string
            - "null"
        reserved_credits:
          type: integer
        review_list_id:
          format: uuid
          type:
            - string
            - "null"
        row_count:
          type: integer
        search_channel:
          type: string
        search_model:
          type: string
        selection_fingerprint:
          type: string
        settled_at:
          format: date-time
          type:
            - string
            - "null"
        settled_credits:
          type: integer
        source_draft_job_id:
          format: uuid
          type:
            - string
            - "null"
        status:
          type: string
        storage_key:
          type: string
        updated_at:
          format: date-time
          type:
            - string
            - "null"
        user_id:
          format: uuid
          type:
            - string
            - "null"
        workspace_id:
          format: uuid
          type:
            - string
            - "null"
      required:
        - id
        - user_id
        - workspace_id
        - idempotency_key
        - file_name
        - storage_key
        - content_sha256
        - file_size_bytes
        - requested_roles
        - max_people_per_company
        - status
        - row_count
        - completed_row_count
        - estimated_credits
        - reserved_credits
        - settled_credits
        - refunded_credits
        - provider_usage
        - approval_metadata
        - failure_code
        - reserved_at
        - settled_at
        - refunded_at
        - completed_at
        - created_at
        - updated_at
        - source_draft_job_id
        - app_id
        - review_list_id
        - billing_cycle
        - search_model
        - email_fallback_enabled
        - max_email_fallbacks
        - selection_fingerprint
        - linkedin_output_list_id
        - email_output_list_id
        - search_channel
      type: object
    sqlc_CountSignalFirstLeadOutcomesRow:
      properties:
        contacted:
          type: integer
        replied:
          type: integer
        source:
          type: string
        total:
          type: integer
      required:
        - source
        - total
        - replied
        - contacted
      type: object
    sqlc_CountSourceRouteOutcomesRow:
      properties:
        count:
          type: integer
        route_action:
          type: string
        source:
          type: string
        status:
          type: string
      required:
        - source
        - route_action
        - status
        - count
      type: object
    sqlc_GetContactByIDRow:
      properties:
        app_id:
          format: uuid
          type:
            - string
            - "null"
        app_name:
          type: string
        campaign_id:
          format: uuid
          type:
            - string
            - "null"
        campaign_name:
          type: string
        company:
          type: string
        created_at:
          format: date-time
          type:
            - string
            - "null"
        dedup_fingerprint:
          type: string
        derived_status:
          type: string
        enriched_email:
          type:
            - string
            - "null"
        first_name:
          type: string
        fit_status:
          type: string
        headline:
          type: string
        id:
          format: uuid
          type:
            - string
            - "null"
        last_name:
          type: string
        list_id:
          format: uuid
          type:
            - string
            - "null"
        list_name:
          type: string
        match_reasoning:
          type:
            - string
            - "null"
        match_score:
          type:
            - integer
            - "null"
        matched_signals:
          items:
            type: string
          type: array
        metadata:
          description: JSON value stored by the server; shape depends on this resource's configuration.
        pinned_at:
          format: date-time
          type:
            - string
            - "null"
        platform:
          type: string
        platform_account_id:
          format: uuid
          type:
            - string
            - "null"
        profile_picture_url:
          type: string
        profile_url:
          type: string
        provider_id:
          type: string
        status:
          type: string
        updated_at:
          format: date-time
          type:
            - string
            - "null"
      required:
        - id
        - campaign_id
        - platform_account_id
        - provider_id
        - first_name
        - last_name
        - company
        - headline
        - profile_url
        - profile_picture_url
        - status
        - metadata
        - dedup_fingerprint
        - created_at
        - updated_at
        - enriched_email
        - fit_status
        - match_score
        - match_reasoning
        - pinned_at
        - derived_status
        - campaign_name
        - app_id
        - app_name
        - platform
        - matched_signals
        - list_id
        - list_name
      type: object
    sqlc_Integration:
      properties:
        config:
          description: JSON value stored by the server; shape depends on this resource's configuration.
        created_at:
          format: date-time
          type:
            - string
            - "null"
        id:
          format: uuid
          type:
            - string
            - "null"
        name:
          type: string
        status:
          type: string
        type:
          type: string
        updated_at:
          format: date-time
          type:
            - string
            - "null"
        user_id:
          format: uuid
          type:
            - string
            - "null"
      required:
        - id
        - user_id
        - name
        - type
        - status
        - config
        - created_at
        - updated_at
      type: object
    sqlc_Lead:
      properties:
        already_connected:
          type: boolean
        campaign_id:
          format: uuid
          type:
            - string
            - "null"
        company:
          type: string
        connection_accepted_at:
          format: date-time
          type:
            - string
            - "null"
        contact_id:
          format: uuid
          type:
            - string
            - "null"
        created_at:
          format: date-time
          type:
            - string
            - "null"
        current_enrollment_id:
          format: uuid
          type:
            - string
            - "null"
        dedup_fingerprint:
          type: string
        enriched_email:
          type:
            - string
            - "null"
        first_name:
          type: string
        fit_status:
          type: string
        headline:
          type: string
        id:
          format: uuid
          type:
            - string
            - "null"
        last_name:
          type: string
        list_id:
          format: uuid
          type:
            - string
            - "null"
        match_reasoning:
          type:
            - string
            - "null"
        match_score:
          type:
            - integer
            - "null"
        metadata:
          description: JSON value stored by the server; shape depends on this resource's configuration.
        mirrored_avatar_key:
          type: string
        platform_account_id:
          format: uuid
          type:
            - string
            - "null"
        profile_picture_url:
          type: string
        profile_url:
          type: string
        provider_id:
          type: string
        provider_internal_id:
          type: string
        replied_at:
          format: date-time
          type:
            - string
            - "null"
        replied_at_step_id:
          format: uuid
          type:
            - string
            - "null"
        route_channel:
          type: string
        source_signal_id:
          format: uuid
          type:
            - string
            - "null"
        status:
          type: string
        updated_at:
          format: date-time
          type:
            - string
            - "null"
      required:
        - id
        - campaign_id
        - platform_account_id
        - provider_id
        - first_name
        - last_name
        - company
        - headline
        - profile_url
        - status
        - metadata
        - created_at
        - updated_at
        - dedup_fingerprint
        - profile_picture_url
        - provider_internal_id
        - connection_accepted_at
        - enriched_email
        - fit_status
        - match_score
        - match_reasoning
        - replied_at
        - replied_at_step_id
        - already_connected
        - list_id
        - mirrored_avatar_key
        - contact_id
        - source_signal_id
        - route_channel
        - current_enrollment_id
      type: object
    sqlc_LeadList:
      properties:
        app_id:
          format: uuid
          type:
            - string
            - "null"
        archived_at:
          format: date-time
          type:
            - string
            - "null"
        contact_route:
          type: string
        created_at:
          format: date-time
          type:
            - string
            - "null"
        description:
          type:
            - string
            - "null"
        id:
          format: uuid
          type:
            - string
            - "null"
        is_default:
          type: boolean
        is_review:
          type: boolean
        lead_count:
          type: integer
        name:
          type: string
        review_job_id:
          format: uuid
          type:
            - string
            - "null"
        updated_at:
          format: date-time
          type:
            - string
            - "null"
        user_id:
          format: uuid
          type:
            - string
            - "null"
      required:
        - id
        - app_id
        - user_id
        - name
        - description
        - is_default
        - lead_count
        - created_at
        - updated_at
        - is_review
        - review_job_id
        - contact_route
        - archived_at
      type: object
    sqlc_ListDiscoveredLeadsByUserWithListRow:
      properties:
        agent_id:
          format: uuid
          type:
            - string
            - "null"
        agent_name:
          type: string
        campaign_id:
          format: uuid
          type:
            - string
            - "null"
        campaign_lead_id:
          format: uuid
          type:
            - string
            - "null"
        company:
          type: string
        contact_id:
          format: uuid
          type:
            - string
            - "null"
        created_at:
          format: date-time
          type:
            - string
            - "null"
        enrichment:
          description: JSON value stored by the server; shape depends on this resource's configuration.
        first_name:
          type: string
        headline:
          type: string
        id:
          format: uuid
          type:
            - string
            - "null"
        last_name:
          type: string
        linkedin_provider_id:
          type: string
        list_id:
          format: uuid
          type:
            - string
            - "null"
        list_name:
          type: string
        match_score:
          type: integer
        matched_signals:
          items:
            type: string
          type: array
        metadata:
          description: JSON value stored by the server; shape depends on this resource's configuration.
        mirrored_avatar_key:
          type: string
        platform:
          type: string
        profile_picture_url:
          type: string
        profile_url:
          type: string
        provider_id:
          type: string
        provider_internal_id:
          type: string
        reviewed_at:
          format: date-time
          type:
            - string
            - "null"
        route_action:
          type: string
        route_channel:
          type: string
        route_reason:
          type: string
        source:
          type: string
        source_signal_id:
          format: uuid
          type:
            - string
            - "null"
        status:
          type: string
        updated_at:
          format: date-time
          type:
            - string
            - "null"
        user_id:
          format: uuid
          type:
            - string
            - "null"
      required:
        - id
        - agent_id
        - campaign_id
        - user_id
        - provider_id
        - platform
        - first_name
        - last_name
        - company
        - headline
        - profile_url
        - matched_signals
        - match_score
        - status
        - enrichment
        - metadata
        - reviewed_at
        - created_at
        - updated_at
        - list_id
        - profile_picture_url
        - provider_internal_id
        - mirrored_avatar_key
        - source
        - contact_id
        - source_signal_id
        - route_channel
        - route_action
        - route_reason
        - linkedin_provider_id
        - campaign_lead_id
        - list_name
        - agent_name
      type: object
    sqlc_ListFollowUpsRow:
      properties:
        app_id:
          format: uuid
          type:
            - string
            - "null"
        app_name:
          type: string
        campaign_id:
          format: uuid
          type:
            - string
            - "null"
        campaign_name:
          type: string
        conversation_id:
          format: uuid
          type:
            - string
            - "null"
        current_meeting_scheduled_start_at:
          format: date-time
          type:
            - string
            - "null"
        current_meeting_status:
          type: string
        first_name:
          type: string
        has_user_responded:
          type: boolean
        headline:
          type: string
        last_message_at:
          format: date-time
          type:
            - string
            - "null"
        last_message_preview:
          type: string
        last_name:
          type: string
        latest_note_at:
          format: date-time
          type:
            - string
            - "null"
        latest_note_body:
          type: string
        lead_id:
          format: uuid
          type:
            - string
            - "null"
        profile_picture_url:
          type: string
        profile_url:
          type: string
        provider_chat_id:
          type:
            - string
            - "null"
        reminder_body:
          type: string
        reminder_confidence:
          type: integer
        reminder_conversation_id:
          format: uuid
          type:
            - string
            - "null"
        reminder_due:
          type: boolean
        reminder_due_at:
          format: date-time
          type:
            - string
            - "null"
        reminder_evidence:
          type: string
        reminder_id:
          format: uuid
          type:
            - string
            - "null"
        reminder_reason:
          type: string
        reminder_source:
          type: string
        reminder_suggested_message:
          type: string
        replied_at:
          format: date-time
          type:
            - string
            - "null"
        status:
          type: string
        unread_count:
          type:
            - integer
            - "null"
      required:
        - lead_id
        - first_name
        - last_name
        - profile_picture_url
        - headline
        - profile_url
        - status
        - replied_at
        - campaign_name
        - campaign_id
        - app_id
        - app_name
        - conversation_id
        - provider_chat_id
        - unread_count
        - last_message_preview
        - last_message_at
        - latest_note_body
        - latest_note_at
        - reminder_id
        - reminder_body
        - reminder_due_at
        - reminder_source
        - reminder_reason
        - reminder_evidence
        - reminder_suggested_message
        - reminder_confidence
        - reminder_conversation_id
        - reminder_due
        - current_meeting_status
        - current_meeting_scheduled_start_at
        - has_user_responded
      type: object
    sqlc_ListGlobalContactsRow:
      properties:
        agent_name:
          type: string
        app_id:
          format: uuid
          type:
            - string
            - "null"
        app_name:
          type: string
        campaign_id:
          format: uuid
          type:
            - string
            - "null"
        campaign_name:
          type: string
        company:
          type: string
        created_at:
          format: date-time
          type:
            - string
            - "null"
        dedup_fingerprint:
          type: string
        derived_status:
          type: string
        enriched_email:
          type:
            - string
            - "null"
        first_name:
          type: string
        fit_status:
          type: string
        headline:
          type: string
        id:
          format: uuid
          type:
            - string
            - "null"
        last_name:
          type: string
        list_name:
          type:
            - string
            - "null"
        match_score:
          type:
            - integer
            - "null"
        matched_signals:
          description: JSON value; the server permits arbitrary JSON at this field.
        metadata:
          description: JSON value stored by the server; shape depends on this resource's configuration.
        pinned_at:
          format: date-time
          type:
            - string
            - "null"
        platform:
          type: string
        platform_account_id:
          format: uuid
          type:
            - string
            - "null"
        profile_picture_url:
          type: string
        profile_url:
          type: string
        provider_id:
          type: string
        status:
          type: string
        updated_at:
          format: date-time
          type:
            - string
            - "null"
      required:
        - id
        - campaign_id
        - platform_account_id
        - provider_id
        - first_name
        - last_name
        - company
        - headline
        - profile_url
        - profile_picture_url
        - status
        - metadata
        - dedup_fingerprint
        - created_at
        - updated_at
        - enriched_email
        - fit_status
        - match_score
        - pinned_at
        - derived_status
        - campaign_name
        - app_id
        - app_name
        - platform
        - matched_signals
        - list_name
        - agent_name
      type: object
    sqlc_ListLeadsByListRow:
      properties:
        already_connected:
          type: boolean
        campaign_id:
          format: uuid
          type:
            - string
            - "null"
        campaign_name:
          type: string
        campaign_status:
          type: string
        company:
          type: string
        connection_accepted_at:
          format: date-time
          type:
            - string
            - "null"
        contact_id:
          format: uuid
          type:
            - string
            - "null"
        created_at:
          format: date-time
          type:
            - string
            - "null"
        current_enrollment_id:
          format: uuid
          type:
            - string
            - "null"
        dedup_fingerprint:
          type: string
        enriched_email:
          type:
            - string
            - "null"
        first_name:
          type: string
        fit_status:
          type: string
        headline:
          type: string
        id:
          format: uuid
          type:
            - string
            - "null"
        last_name:
          type: string
        list_id:
          format: uuid
          type:
            - string
            - "null"
        match_reasoning:
          type:
            - string
            - "null"
        match_score:
          type:
            - integer
            - "null"
        metadata:
          description: JSON value stored by the server; shape depends on this resource's configuration.
        mirrored_avatar_key:
          type: string
        platform_account_id:
          format: uuid
          type:
            - string
            - "null"
        profile_picture_url:
          type: string
        profile_url:
          type: string
        provider_id:
          type: string
        provider_internal_id:
          type: string
        replied_at:
          format: date-time
          type:
            - string
            - "null"
        replied_at_step_id:
          format: uuid
          type:
            - string
            - "null"
        route_channel:
          type: string
        source_signal_id:
          format: uuid
          type:
            - string
            - "null"
        status:
          type: string
        updated_at:
          format: date-time
          type:
            - string
            - "null"
      required:
        - id
        - campaign_id
        - platform_account_id
        - provider_id
        - first_name
        - last_name
        - company
        - headline
        - profile_url
        - status
        - metadata
        - created_at
        - updated_at
        - dedup_fingerprint
        - profile_picture_url
        - provider_internal_id
        - connection_accepted_at
        - enriched_email
        - fit_status
        - match_score
        - match_reasoning
        - replied_at
        - replied_at_step_id
        - already_connected
        - list_id
        - mirrored_avatar_key
        - contact_id
        - source_signal_id
        - route_channel
        - current_enrollment_id
        - campaign_name
        - campaign_status
      type: object
    sqlc_ListNextLaunchesRow:
      properties:
        agent_id:
          format: uuid
          type:
            - string
            - "null"
        config:
          description: JSON value stored by the server; shape depends on this resource's configuration.
        created_at:
          format: date-time
          type:
            - string
            - "null"
        description:
          type:
            - string
            - "null"
        discovery_state:
          description: JSON value stored by the server; shape depends on this resource's configuration.
        enabled:
          type: boolean
        estimated_matches:
          type:
            - integer
            - "null"
        id:
          format: uuid
          type:
            - string
            - "null"
        label:
          type: string
        last_run_at:
          format: date-time
          type:
            - string
            - "null"
        leads_found:
          type: integer
        manual_launch_cooldown_until:
          format: date-time
          type:
            - string
            - "null"
        next_run_at:
          format: date-time
          type:
            - string
            - "null"
        platform:
          type: string
        run_interval_hours:
          type: integer
        signal_category:
          type: string
        signal_key:
          type: string
        updated_at:
          format: date-time
          type:
            - string
            - "null"
      required:
        - id
        - agent_id
        - platform
        - signal_category
        - signal_key
        - label
        - description
        - enabled
        - config
        - estimated_matches
        - created_at
        - updated_at
        - last_run_at
        - next_run_at
        - run_interval_hours
        - manual_launch_cooldown_until
        - discovery_state
        - leads_found
      type: object
    sqlc_ListScheduledActionsWithLeadByCampaignRow:
      properties:
        action_type:
          type: string
        attachment_content_type:
          type:
            - string
            - "null"
        attachment_filename:
          type:
            - string
            - "null"
        attachment_s3_key:
          type:
            - string
            - "null"
        campaign_id:
          format: uuid
          type:
            - string
            - "null"
        created_at:
          format: date-time
          type:
            - string
            - "null"
        delivery_mode:
          type: string
        enrollment_id:
          format: uuid
          type:
            - string
            - "null"
        error_message:
          type:
            - string
            - "null"
        executed_at:
          format: date-time
          type:
            - string
            - "null"
        id:
          format: uuid
          type:
            - string
            - "null"
        invite_action_id:
          format: uuid
          type:
            - string
            - "null"
        lead_company:
          type: string
        lead_first_name:
          type: string
        lead_headline:
          type: string
        lead_id:
          format: uuid
          type:
            - string
            - "null"
        lead_last_name:
          type: string
        lead_profile_picture_url:
          type: string
        lead_profile_url:
          type: string
        lead_provider_id:
          type: string
        lead_status:
          type: string
        message_content:
          type:
            - string
            - "null"
        message_mode:
          type: string
        metadata:
          description: JSON value stored by the server; shape depends on this resource's configuration.
        platform_account_id:
          format: uuid
          type:
            - string
            - "null"
        requires_connection:
          type: boolean
        retry_count:
          type: integer
        scheduled_at:
          format: date-time
          type:
            - string
            - "null"
        sent_body:
          type: string
        sent_subject:
          type: string
        status:
          type: string
        subject_content:
          type: string
        updated_at:
          format: date-time
          type:
            - string
            - "null"
        workflow_step_id:
          format: uuid
          type:
            - string
            - "null"
      required:
        - id
        - campaign_id
        - workflow_step_id
        - platform_account_id
        - lead_id
        - status
        - action_type
        - message_content
        - scheduled_at
        - executed_at
        - error_message
        - retry_count
        - metadata
        - created_at
        - updated_at
        - message_mode
        - attachment_s3_key
        - attachment_filename
        - attachment_content_type
        - delivery_mode
        - subject_content
        - sent_body
        - sent_subject
        - requires_connection
        - invite_action_id
        - enrollment_id
        - lead_status
        - lead_first_name
        - lead_last_name
        - lead_company
        - lead_provider_id
        - lead_headline
        - lead_profile_url
        - lead_profile_picture_url
      type: object
    sqlc_OperatorNotificationPreference:
      properties:
        app_id:
          format: uuid
          type:
            - string
            - "null"
        category:
          type: string
        enabled:
          type: boolean
        frequency:
          type: string
        updated_at:
          format: date-time
          type:
            - string
            - "null"
        user_id:
          format: uuid
          type:
            - string
            - "null"
        version:
          type: integer
      required:
        - user_id
        - app_id
        - category
        - frequency
        - enabled
        - version
        - updated_at
      type: object
    sqlc_ScheduledAction:
      properties:
        action_type:
          type: string
        attachment_content_type:
          type:
            - string
            - "null"
        attachment_filename:
          type:
            - string
            - "null"
        attachment_s3_key:
          type:
            - string
            - "null"
        campaign_id:
          format: uuid
          type:
            - string
            - "null"
        created_at:
          format: date-time
          type:
            - string
            - "null"
        delivery_mode:
          type: string
        enrollment_id:
          format: uuid
          type:
            - string
            - "null"
        error_message:
          type:
            - string
            - "null"
        executed_at:
          format: date-time
          type:
            - string
            - "null"
        id:
          format: uuid
          type:
            - string
            - "null"
        invite_action_id:
          format: uuid
          type:
            - string
            - "null"
        lead_id:
          format: uuid
          type:
            - string
            - "null"
        message_content:
          type:
            - string
            - "null"
        message_mode:
          type: string
        metadata:
          description: JSON value stored by the server; shape depends on this resource's configuration.
        platform_account_id:
          format: uuid
          type:
            - string
            - "null"
        requires_connection:
          type: boolean
        retry_count:
          type: integer
        scheduled_at:
          format: date-time
          type:
            - string
            - "null"
        sent_body:
          type: string
        sent_subject:
          type: string
        status:
          type: string
        subject_content:
          type: string
        updated_at:
          format: date-time
          type:
            - string
            - "null"
        workflow_step_id:
          format: uuid
          type:
            - string
            - "null"
      required:
        - id
        - campaign_id
        - workflow_step_id
        - platform_account_id
        - lead_id
        - status
        - action_type
        - message_content
        - scheduled_at
        - executed_at
        - error_message
        - retry_count
        - metadata
        - created_at
        - updated_at
        - message_mode
        - attachment_s3_key
        - attachment_filename
        - attachment_content_type
        - delivery_mode
        - subject_content
        - sent_body
        - sent_subject
        - requires_connection
        - invite_action_id
        - enrollment_id
      type: object
    sqlc_TableView:
      properties:
        app_id:
          format: uuid
          type:
            - string
            - "null"
        config:
          description: JSON value stored by the server; shape depends on this resource's configuration.
        created_at:
          format: date-time
          type:
            - string
            - "null"
        id:
          format: uuid
          type:
            - string
            - "null"
        is_default:
          type: boolean
        name:
          type: string
        table_key:
          type: string
        updated_at:
          format: date-time
          type:
            - string
            - "null"
        user_id:
          format: uuid
          type:
            - string
            - "null"
        version:
          type: integer
      required:
        - id
        - user_id
        - app_id
        - table_key
        - name
        - is_default
        - config
        - version
        - created_at
        - updated_at
      type: object
    sqlc_WebhookSubscription:
      properties:
        active:
          type: boolean
        created_at:
          format: date-time
          type:
            - string
            - "null"
        events:
          items:
            type: string
          type: array
        id:
          format: uuid
          type:
            - string
            - "null"
        integration_id:
          format: uuid
          type:
            - string
            - "null"
        secret:
          type: string
        updated_at:
          format: date-time
          type:
            - string
            - "null"
        url:
          type: string
        user_id:
          format: uuid
          type:
            - string
            - "null"
      required:
        - id
        - user_id
        - integration_id
        - url
        - secret
        - events
        - active
        - created_at
        - updated_at
      type: object
    sqlc_WorkflowStep:
      properties:
        attachment_content_type:
          type:
            - string
            - "null"
        attachment_filename:
          type:
            - string
            - "null"
        attachment_s3_key:
          type:
            - string
            - "null"
        campaign_id:
          format: uuid
          type:
            - string
            - "null"
        created_at:
          format: date-time
          type:
            - string
            - "null"
        delay_unit:
          type: string
        delay_value:
          type: integer
        deleted_at:
          format: date-time
          type:
            - string
            - "null"
        deleted_by:
          format: uuid
          type:
            - string
            - "null"
        id:
          format: uuid
          type:
            - string
            - "null"
        is_final:
          type: boolean
        message_mode:
          type: string
        message_template:
          type:
            - string
            - "null"
        step_order:
          type: integer
        step_type:
          type: string
        subject_template:
          type: string
        updated_at:
          format: date-time
          type:
            - string
            - "null"
      required:
        - id
        - campaign_id
        - step_order
        - step_type
        - message_template
        - delay_value
        - delay_unit
        - created_at
        - updated_at
        - message_mode
        - attachment_s3_key
        - attachment_filename
        - attachment_content_type
        - is_final
        - subject_template
        - deleted_at
        - deleted_by
      type: object
