> ## 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.

# Update project campaign

> Update a campaign embedded in a project from the project-management surface. Customer role: `agency_admin`.

Required API key scope: `projects:write`.



## OpenAPI

````yaml /openapi.yaml put /api/v2/projects/{id}/campaigns/{campaignId}
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/{id}/campaigns/{campaignId}:
    put:
      tags:
        - Projects
      summary: Update project campaign
      description: >-
        Update a campaign embedded in a project from the project-management
        surface. Customer role: `agency_admin`.


        Required API key scope: `projects:write`.
      operationId: put-v2-projects-by-id-campaigns-by-campaignId
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            minLength: 1
          description: Project id.
          example: 64f2a8c8a2b3c4d5e6f7a8b9
        - name: campaignId
          in: path
          required: true
          schema:
            type: string
            minLength: 1
          description: Campaign id.
          example: campaign_speed_to_lead_123
      requestBody:
        required: true
        description: Project campaign update payload.
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ProjectsCampaignUpdateRequest'
            examples:
              default:
                value:
                  name: Speed to Lead
                  activeTags:
                    - new_lead
                  inactiveTags:
                    - do_not_call
                  dialing: s2l_pd
                  agentType: appointment_booking
                  agentId: agent_2b7c9f4a
                  fromNumbers:
                    - '+14155551234'
                  active: false
                  config:
                    cadenceId: cadence_123
                    calendarId: calendar_primary
                    transferNumber: '+14155550100'
                  callingWindow:
                    days:
                      - 1
                      - 2
                      - 3
                      - 4
                      - 5
                    start: '09:00'
                    end: '17:00'
                    tz: America/New_York
                  multipleTimezoneCheck: true
                  dropOutsideWindow: false
      responses:
        '200':
          description: Updated project with campaign changes.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectsProjectResponse'
              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.
        '404':
          description: Resource not found or not visible to the caller.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectsErrorResponse'
              examples:
                default:
                  value:
                    success: false
                    message: Project not found.
        '409':
          description: The requested change conflicts with the current account state.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectsErrorResponse'
              examples:
                default:
                  value:
                    success: false
                    code: SubAccountCapReached
                    message: You have reached your project limit.
        '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 PUT \
              --url https://api.teamfollowup.ai/api/v2/projects/{id}/campaigns/{campaignId} \
              --header 'Authorization: Bearer YOUR_API_KEY' \
              --header 'Content-Type: application/json' \
              --data '{
              "name": "Speed to Lead",
              "activeTags": [
                "new_lead"
              ],
              "inactiveTags": [
                "do_not_call"
              ],
              "dialing": "s2l_pd",
              "agentType": "appointment_booking",
              "agentId": "agent_2b7c9f4a",
              "fromNumbers": [
                "+14155551234"
              ],
              "active": false,
              "config": {
                "cadenceId": "cadence_123",
                "calendarId": "calendar_primary",
                "transferNumber": "+14155550100"
              },
              "callingWindow": {
                "days": [
                  1,
                  2,
                  3,
                  4,
                  5
                ],
                "start": "09:00",
                "end": "17:00",
                "tz": "America/New_York"
              },
              "multipleTimezoneCheck": true,
              "dropOutsideWindow": false
            }'
components:
  schemas:
    ProjectsCampaignUpdateRequest:
      type: object
      additionalProperties: {}
      properties:
        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
          enum:
            - s2l
            - s2l_pd
            - pd
          example: s2l_pd
        agentType:
          type: string
          enum:
            - appointment_confirmation
            - appointment_booking
            - live_transfer
            - live_transfer_and_tag
            - live_transfer_and_booking
            - add_tag
            - inbound_receptionist
          description: >-
            Legacy V2 template the campaign runs. Must be one of the seven
            published template ids; this module does not accept the three
            campaign types.
          example: appointment_booking
        agentId:
          type: string
          example: agent_2b7c9f4a
        fromNumbers:
          type: array
          items:
            type: string
          example:
            - '+14155551234'
        active:
          type: boolean
          example: false
        config:
          $ref: '#/components/schemas/ProjectsCampaignConfig'
        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
    ProjectsProjectResponse:
      type: object
      additionalProperties: false
      required:
        - success
        - data
      properties:
        success:
          type: boolean
        data:
          $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.
    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.
    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
    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.
    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
  securitySchemes:
    ApiKeyAuth:
      type: http
      scheme: bearer
      bearerFormat: API key
      description: 'Send your API key as `Authorization: Bearer YOUR_API_KEY`.'

````