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

> ## Agent Instructions
> Use the public API base URL: https://api.teamfollowup.ai/api.
> Authenticate public API requests with Authorization: Bearer YOUR_API_KEY.
> Use product-owned terms: agents, calls, campaigns, contacts, phone numbers, billing, and configuration.

# List projects

> List projects visible to the authenticated agency. Agency admins are scoped to their own agency even when query filters are supplied. Customer role: `agency_admin`.

Required API key scope: `projects:read`.



## OpenAPI

````yaml /openapi.yaml get /api/v2/projects
openapi: 3.1.0
info:
  title: TFUAI Server API
  version: 1.0.0
  description: >-
    API reference for the TFUAI SaaS server.


    All published endpoints are mounted under `/api`. Responses use a standard
    envelope: `{ "success": true, ... }` on success and `{ "success": false,
    "error": "..." }` on failure.


    Product API modules are being launched module by module. This spec currently
    exposes only contracted public modules.


    Authenticate public API requests with an API key in the `Authorization`
    header: `Authorization: Bearer YOUR_API_KEY`.
servers:
  - url: https://api.teamfollowup.ai
    description: Production API origin. Public API paths are under /api.
  - url: https://devapi.teamfollowup.ai
    description: Development API origin. Public API paths are under /api.
security: []
tags:
  - name: Agents
    description: Manage voice agents, campaigns, workflows, and outcomes.
  - name: Analytics
    description: >-
      Minimal, non-billing performance summary: volume, conversion, and pickup
      metrics.
  - name: Calls
    description: Review calls, test runs, and execution history.
  - name: Campaign Workflows
    description: >-
      What runs after a call lands: for each outcome (booked, opted out, asked
      for a callback…), an ordered chain of actions — tag the contact, move a
      pipeline stage, send an SMS, book an appointment. Stored as a graph:
      `nodes` are the actions, `edges` say what follows what, and
      `dispositionEntries` maps each outcome to the node its chain starts at.


      **Pick the narrowest endpoint that does the job.**


      | To | Call | |

      | --- | --- | --- |

      | See what fires per outcome | `GET .../workflow/digest` | Reads as plain
      text, no graph walking |

      | Add steps to an outcome | `POST .../outcomes/{outcome}/nodes` | Server
      derives node ids, edge ids, handles, layout |

      | Change a message, tag or setting | `PATCH .../workflow` | Merges;
      carries no graph, so it cannot damage one |

      | Remove one step | `DELETE .../nodes/{nodeId}` | Also re-links the chain
      around it |

      | Author or replace the whole graph | `PUT .../workflow` | Replaces
      everything you send |


      `PUT` is the only one that can re-wire, position nodes, build an `if`,
      save an unconfigured draft, or copy a whole workflow — and the only one
      that can overwrite a change someone else made after you read it. Use it
      when you are genuinely authoring the graph, and send `expectedVersion`
      when you do. For everything else the narrower endpoints are both easier
      and safer.
  - name: Contacts
    description: Contact list, filters, contact call history, and do-not-call actions.
  - name: GHL
    description: CRM variables and contact helpers used by agent and campaign setup flows.
  - name: Phone Numbers
    description: >-
      Caller ID pool, number search, assignment, movement, and release
      endpoints.
  - name: Power Dialer
    description: >-
      Power Dialer queue, lead cadence, callback, parked lead, and campaign
      schedule endpoints.
  - name: Projects
    description: >-
      Project management endpoints: list, update, disconnect, configure project
      campaigns, and validate setup.
  - name: Skills and Tools
    description: Manage reusable agent behaviors and in-call tool descriptions.
  - name: Voices
    description: Voice catalogue endpoints for choosing the voice used by an agent.
paths:
  /api/v2/projects:
    get:
      tags:
        - Projects
      summary: List projects
      description: >-
        List projects visible to the authenticated agency. Agency admins are
        scoped to their own agency even when query filters are supplied.
        Customer role: `agency_admin`.


        Required API key scope: `projects:read`.
      operationId: get-v2-projects
      parameters:
        - name: agencyName
          in: query
          required: false
          schema:
            type: string
          description: >-
            Filter by agency name. Agency admins are always pinned to their own
            agency.
          example: Acme Agency
        - name: active
          in: query
          required: false
          schema:
            type: string
            enum:
              - 'true'
              - 'false'
          description: Filter by whether the project accepts calls.
          example: 'true'
      responses:
        '200':
          description: Visible projects.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectsListResponse'
              examples:
                default:
                  value:
                    success: true
                    data:
                      - id: 64f2a8c8a2b3c4d5e6f7a8b9
                        crm: ghl
                        locationId: loc_9f7a123
                        companyId: company_123
                        timezone: America/New_York
                        agencyName: Acme Agency
                        agencyId: partner_123
                        active: true
                        status: live
                        installed: true
                        callbacksHandledByHuman: false
                        multipleTimezoneCheck: true
                        callingWindowOverride: null
                        knowledgeBase: Clinic hours are Monday through Friday, 9 AM to 5 PM.
                        businessName: Acme Clinic
                        businessNameOverridden: false
                        campaigns:
                          - id: campaign_speed_to_lead_123
                            name: Speed to Lead
                            activeTags:
                              - new_lead
                            inactiveTags:
                              - do_not_call
                            dialing: s2l_pd
                            agentType: appointment_booking
                            agentId: agent_2b7c9f4a
                            fromNumbers:
                              - '+14155551234'
                            active: false
                            callingWindow:
                              days:
                                - 1
                                - 2
                                - 3
                                - 4
                                - 5
                              start: '09:00'
                              end: '17:00'
                              tz: America/New_York
                            multipleTimezoneCheck: true
                            dropOutsideWindow: false
                            config:
                              cadenceId: cadence_123
                              calendarId: calendar_primary
                              transferNumber: '+14155550100'
                            createdAt: '2026-07-08T16:20:00.000Z'
                            updatedAt: '2026-07-08T16:30:00.000Z'
                        createdAt: '2026-07-08T16:10:00.000Z'
                        updatedAt: '2026-07-08T16:30:00.000Z'
                        ghl:
                          id: loc_9f7a123
                          name: Acme Clinic
                          timezone: America/New_York
                          country: US
                          phone: '+14155550000'
                          address: 100 Market St
                          city: San Francisco
                          state: CA
                          postalCode: '94105'
                          email: frontdesk@example.test
                          website: https://example.test
        '400':
          description: Validation failed or the request is malformed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectsErrorResponse'
              examples:
                default:
                  value:
                    success: false
                    error: VALIDATION
                    message: businessName is required.
        '401':
          description: Missing or invalid authentication.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectsErrorResponse'
              examples:
                default:
                  value:
                    success: false
                    error: Unauthorized
                    message: Authentication required.
        '403':
          description: Authenticated but not permitted to access the requested resource.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectsErrorResponse'
              examples:
                default:
                  value:
                    success: false
                    error: Forbidden
                    message: You do not have permission to access this resource.
        '429':
          description: Rate limit exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectsErrorResponse'
              examples:
                default:
                  value:
                    success: false
                    error: TooManyRequests
                    message: Too many requests, please try again later.
        '500':
          description: Unexpected server error.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectsErrorResponse'
              examples:
                default:
                  value:
                    success: false
                    error: InternalError
                    message: Unexpected server error.
      security:
        - ApiKeyAuth: []
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |-
            curl --request GET \
              --url https://api.teamfollowup.ai/api/v2/projects \
              --header 'Authorization: Bearer YOUR_API_KEY'
components:
  schemas:
    ProjectsListResponse:
      type: object
      additionalProperties: false
      required:
        - success
        - data
      properties:
        success:
          type: boolean
        data:
          type: array
          items:
            $ref: '#/components/schemas/ProjectsProject'
    ProjectsErrorResponse:
      type: object
      additionalProperties: {}
      required:
        - success
      properties:
        success:
          type: boolean
        error:
          type: string
          example: VALIDATION
        code:
          type: string
          example: SubAccountCapReached
        message:
          type: string
          example: Project not found.
    ProjectsProject:
      type: object
      additionalProperties: {}
      required:
        - id
        - crm
        - locationId
        - active
        - status
        - installed
        - campaigns
        - createdAt
        - updatedAt
      properties:
        id:
          type: string
          example: 64f2a8c8a2b3c4d5e6f7a8b9
        crm:
          type: string
          enum:
            - ghl
            - kommo
          description: >-
            The CRM backing the project. Determines which connection details are
            present.
          example: ghl
        locationId:
          type: string
          example: loc_9f7a123
        companyId:
          type: string
          example: company_123
        timezone:
          type:
            - string
            - 'null'
          example: America/New_York
        agencyName:
          type:
            - string
            - 'null'
          example: Acme Agency
        agencyId:
          type:
            - string
            - 'null'
          example: partner_123
        active:
          type: boolean
          description: >-
            Whether the project accepts calls. A parked project occupies no plan
            slot.
          example: true
        status:
          type: string
          enum:
            - live
            - stopped
            - disconnected
          description: >-
            The project's operating state: `live` is connected and dialling,
            `stopped` is connected but parked (`active: false`), `disconnected`
            means the CRM connection is not usable.
          example: live
        installed:
          type: boolean
          description: >-
            Whether the CRM integration is set up. A project can be installed
            yet `disconnected` when its credential has stopped working — that
            needs a reconnect, not a reinstall.
          example: true
        callbacksHandledByHuman:
          type: boolean
          example: false
        multipleTimezoneCheck:
          type: boolean
          example: true
        callingWindowOverride:
          oneOf:
            - $ref: '#/components/schemas/ProjectsCallingWindow'
            - type: array
              items:
                $ref: '#/components/schemas/ProjectsCallingWindow'
            - type: 'null'
        knowledgeBase:
          type: string
          example: Clinic hours are Monday through Friday, 9 AM to 5 PM.
        businessName:
          type:
            - string
            - 'null'
          example: Acme Clinic
        businessNameOverridden:
          type: boolean
          example: false
        campaigns:
          type: array
          items:
            $ref: '#/components/schemas/ProjectsCampaign'
        uninstalledAt:
          type: string
          format: date-time
          example: '2026-07-08T16:30:00.000Z'
        createdAt:
          type: string
          format: date-time
          example: '2026-07-08T16:10:00.000Z'
        updatedAt:
          type: string
          format: date-time
          example: '2026-07-08T16:30:00.000Z'
        kommo:
          allOf:
            - $ref: '#/components/schemas/ProjectsKommoAccount'
          description: Present only when `crm` is `kommo`.
        ghl:
          oneOf:
            - $ref: '#/components/schemas/ProjectsConnectedLocation'
            - type: 'null'
          description: >-
            Live location data, present only when `crm` is `ghl`. `null` means
            the connection could not be used — prefer `status` over interpreting
            this yourself.
    ProjectsCallingWindow:
      type: object
      additionalProperties: false
      required:
        - days
        - start
        - end
        - tz
      properties:
        days:
          type: array
          minItems: 1
          items:
            type: integer
            minimum: 1
            maximum: 7
          example:
            - 1
            - 2
            - 3
            - 4
            - 5
        start:
          type: string
          pattern: ^\d{1,2}:\d{2}$
          example: '09:00'
        end:
          type: string
          pattern: ^\d{1,2}:\d{2}$
          example: '17:00'
        tz:
          type: string
          example: America/New_York
    ProjectsCampaign:
      type: object
      additionalProperties: {}
      required:
        - id
        - name
        - activeTags
        - inactiveTags
        - dialing
        - agentId
        - fromNumbers
        - active
        - config
        - createdAt
        - updatedAt
      properties:
        id:
          type: string
          example: campaign_speed_to_lead_123
        name:
          type: string
          example: Speed to Lead
        activeTags:
          type: array
          items:
            type: string
          example:
            - new_lead
        inactiveTags:
          type: array
          items:
            type: string
          example:
            - do_not_call
        dialing:
          type:
            - string
            - 'null'
          enum:
            - s2l
            - s2l_pd
            - pd
            - reactivation
            - null
          description: >-
            How the campaign dials. `reactivation` is retired and reads only:
            campaigns created before it was withdrawn still report it and still
            dial, but it can no longer be set on a create or update. Null when
            the campaign carries no dispatch mode (e.g. an inbound
            receptionist).
          example: s2l_pd
        agentType:
          type:
            - string
            - 'null'
          enum:
            - appointment_confirmation
            - appointment_booking
            - live_transfer
            - live_transfer_and_tag
            - live_transfer_and_booking
            - add_tag
            - inbound_receptionist
            - null
          description: >-
            Legacy V2 template the campaign was built against. This module
            speaks the seven template ids, not the three campaign types the
            Agents module publishes under the same field name. It names the
            clone source, not what the agent can do. Null when the campaign
            carries no recognised template.
          example: appointment_booking
        agentId:
          type: string
          example: agent_2b7c9f4a
        fromNumbers:
          type: array
          items:
            type: string
          example:
            - '+14155551234'
        active:
          type: boolean
          description: Whether the campaign is enrolling and dialling.
          example: false
        callingWindow:
          oneOf:
            - $ref: '#/components/schemas/ProjectsCallingWindow'
            - type: array
              items:
                $ref: '#/components/schemas/ProjectsCallingWindow'
            - type: 'null'
        multipleTimezoneCheck:
          type: boolean
          example: true
        dropOutsideWindow:
          type: boolean
          example: false
        config:
          $ref: '#/components/schemas/ProjectsCampaignConfig'
        createdAt:
          type: string
          format: date-time
          example: '2026-07-08T16:20:00.000Z'
        updatedAt:
          type: string
          format: date-time
          example: '2026-07-08T16:30:00.000Z'
    ProjectsKommoAccount:
      type: object
      additionalProperties: false
      required:
        - accountId
      properties:
        accountId:
          type: string
          example: '31415926'
        accountName:
          type:
            - string
            - 'null'
          example: Acme Clinic
        accountTimezone:
          type:
            - string
            - 'null'
          example: America/New_York
        subdomain:
          type: string
          example: acmeclinic
    ProjectsConnectedLocation:
      type: object
      additionalProperties: {}
      properties:
        id:
          type: string
          example: loc_9f7a123
        name:
          type:
            - string
            - 'null'
          example: Acme Clinic
        timezone:
          type:
            - string
            - 'null'
          example: America/New_York
        country:
          type:
            - string
            - 'null'
          example: US
        phone:
          type:
            - string
            - 'null'
          example: '+14155550000'
        address:
          type:
            - string
            - 'null'
          example: 100 Market St
        city:
          type:
            - string
            - 'null'
          example: San Francisco
        state:
          type:
            - string
            - 'null'
          example: CA
        postalCode:
          type:
            - string
            - 'null'
          example: '94105'
        email:
          type:
            - string
            - 'null'
          example: frontdesk@example.test
        website:
          type:
            - string
            - 'null'
          example: https://example.test
    ProjectsCampaignConfig:
      type: object
      additionalProperties: {}
      properties:
        cadenceId:
          type: string
          example: cadence_123
        calendarId:
          type: string
          example: calendar_primary
        transferNumber:
          type: string
          example: '+14155550100'
        advancedRouting:
          type: boolean
          example: false
        transferRoutes:
          type: array
          items:
            type: object
            additionalProperties: {}
            required:
              - id
              - number
              - condition
            properties:
              id:
                type: string
                example: route_sales
              number:
                type: string
                example: '+14155550100'
              condition:
                type: string
                example: Use for pricing questions.
  securitySchemes:
    ApiKeyAuth:
      type: http
      scheme: bearer
      bearerFormat: API key
      description: 'Send your API key as `Authorization: Bearer YOUR_API_KEY`.'

````