> ## 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, campaigns, projects (GoHighLevel sub-accounts), contacts, calls, outcomes, skills, cadences, phone numbers.
> Scope requests by locationId rather than projectName.
> Read the guide linked from each API reference group before recommending an endpoint.

# Connect a number to a TFU Live agent

> Make a number the agency already has (an attachable one) this rep’s number, for the calls it makes and the calls it answers, on the sub-account’s TFU Live calling trunk. Free: nothing is bought or charged. Its calls keep going where they went until the rep can answer them. Connecting a number that is already the rep’s changes nothing (unchanged). One number is set up for a rep at a time. Machine credentials use the returned tfu_live: agent id.

Required scope: `agents:write`.

Required API key scope: `agents:write`.



## OpenAPI

````yaml /openapi.yaml post /api/tfu-live-agents/{id}/numbers/attach
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": "<code>", "message": "..." }` on failure. Branch on the `error`
    code; `message` is for people and its wording can change.


    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.
security: []
tags:
  - name: Agents
    description: >-
      Build the conversation: identity and settings, script, lead context and
      split testing.
  - name: Analytics
    description: >-
      Understand call volume, pickup, booking and transfer metrics within the
      selected reporting scope.
  - name: Billing
    description: >-
      Balances, spend, purchases and sub-account rebilling. Reference and guides
      only; the builder chatbot has no billing playbook.
  - name: Calls
    description: >-
      Review completed calls, recordings, transcripts and campaign run history;
      distinguish observation from placing a new call.
  - name: Campaigns
    description: >-
      Configure what starts calls, who is eligible, calling windows, caller IDs,
      activation and the actions that follow each outcome.


      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?view=summary` | 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: >-
      Find contacts and their calls, manage do-not-call status and identify
      internal contacts excluded from reporting.
  - name: In-call Capabilities
    description: >-
      Configure actions and instructions the agent uses during a live call:
      booking, transfers, lookups and custom skills.
  - name: Master agent
    description: >-
      Run a shared agent and campaign across linked projects; manage
      inheritance, local bindings, synchronization and rollout readiness.
  - name: Native Calendar
    description: >-
      The diary of a sub-account with no CRM: its bookable calendars, the free
      slots they offer, and the appointments on them. Availability is computed
      per request against live bookings and is never cached, because a cached
      slot is one somebody else has already taken.
  - name: Native Contacts
    description: >-
      The people a sub-account with no CRM calls, and the fields it keeps on
      them. This is the system of record for those contacts: there is nowhere
      else they exist. A sub-account with a CRM keeps its contacts there and
      every operation here answers 400 for it.
  - name: Phone Numbers
    description: >-
      Find and manage caller IDs, assign numbers to campaigns and understand
      connected Twilio numbers.
  - name: Projects
    description: >-
      Understand project ownership, inspect configuration and validate the local
      resources used by a campaign.
  - name: TFU Live
    description: >-
      Configure instructions, conversation style, workflows, Chat and call
      handling, then test and go live.
  - name: Voices
    description: >-
      Browse the voice catalogue and select a voice; configure speech and
      listening behavior in Agents.
paths:
  /api/tfu-live-agents/{id}/numbers/attach:
    post:
      tags:
        - TFU Live
      summary: Connect a number to a TFU Live agent
      description: >-
        Make a number the agency already has (an attachable one) this rep’s
        number, for the calls it makes and the calls it answers, on the
        sub-account’s TFU Live calling trunk. Free: nothing is bought or
        charged. Its calls keep going where they went until the rep can answer
        them. Connecting a number that is already the rep’s changes nothing
        (unchanged). One number is set up for a rep at a time. Machine
        credentials use the returned tfu_live: agent id.


        Required scope: `agents:write`.


        Required API key scope: `agents:write`.
      operationId: post-tfu-live-agents-by-id-numbers-attach
      parameters:
        - name: locationId
          in: query
          required: true
          schema:
            type: string
          description: >-
            Sub-account belonging to the authenticated agency and any narrower
            credential location binding.
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: TFU Live agent id. Never use an ordinary provider agent id.
        - name: campaignId
          in: query
          schema:
            type: string
            pattern: ^[a-zA-Z0-9_-]{1,100}$
          description: >-
            When more than one campaign uses this rep, the one whose calling
            line to use.
      requestBody:
        description: '{ number }: one of the attachable numbers.'
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TfuLiveNumberAttachRequest'
            examples:
              default:
                value:
                  number: '+14155550199'
      responses:
        '200':
          description: Successful response.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TfuLiveNumberAttachResponse'
              example:
                success: true
                number: '+14155550199'
                line:
                  number: '+14155550199'
                  status: ready
                  managedBy: self
                  inbound: true
                  numbers:
                    - '+14155550199'
                inbound: true
                unchanged: false
        '400':
          description: >-
            Refused: { success: false, error, code, message }. Nothing was
            bought or charged.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TfuLiveNumberError'
              example:
                success: false
                error: invalid_request
                code: invalid_request
                message: Please check the details and try again.
        '401':
          description: >-
            Refused: { success: false, error, code, message }. Nothing was
            bought or charged.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TfuLiveNumberError'
              example:
                success: false
                error: Unauthorized
                message: Please sign in to continue.
        '403':
          description: >-
            Refused: { success: false, error, code, message }. Nothing was
            bought or charged.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TfuLiveNumberError'
              example:
                success: false
                error: InsufficientScope
                message: >-
                  This access key does not allow this action. Please ask your
                  account admin to change its permissions.
                requiredScope: agents:write
        '404':
          description: >-
            Refused: { success: false, error, code, message }. Nothing was
            bought or charged.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TfuLiveNumberError'
              example:
                success: false
                error: not_found
                code: not_found
                message: We couldn't find that. It may have been moved or deleted.
        '409':
          description: >-
            Refused: { success: false, error, code, message }. Nothing was
            bought or charged.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TfuLiveNumberError'
              example:
                success: false
                error: quote_expired
                code: quote_expired
                message: >-
                  No purchase was made. Please ask for a fresh price before
                  confirming.
        '422':
          description: >-
            Refused: { success: false, error, code, message }. Nothing was
            bought or charged.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TfuLiveNumberError'
              example:
                success: false
                error: invalid_request
                code: invalid_request
                message: Please check the details and try again.
        '429':
          description: >-
            Refused: { success: false, error, code, message }. Nothing was
            bought or charged.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TfuLiveNumberError'
              example:
                success: false
                error: TooManyRequests
                message: That's a lot at once. Please wait a moment and try again.
        '503':
          description: >-
            Refused: { success: false, error, code, message }. Nothing was
            bought or charged.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TfuLiveNumberError'
              example:
                success: false
                error: line_unverified
                code: line_unverified
                message: >-
                  We couldn't finish this right now. Please try again in a
                  moment.
      security:
        - ApiKeyAuth: []
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |-
            curl --request POST \
              --url https://api.teamfollowup.ai/api/tfu-live-agents/{id}/numbers/attach \
              --header 'Authorization: Bearer YOUR_API_KEY' \
              --header 'Content-Type: application/json' \
              --data '{
              "number": "+14155550199"
            }'
components:
  schemas:
    TfuLiveNumberAttachRequest:
      type: object
      required:
        - number
      additionalProperties: false
      properties:
        number:
          type: string
          pattern: ^\+[1-9]\d{7,14}$
          description: One of the attachable numbers.
          example: '+14155550123'
    TfuLiveNumberAttachResponse:
      type: object
      required:
        - success
        - number
        - line
        - inbound
        - unchanged
      additionalProperties: false
      properties:
        success:
          const: true
        number:
          type: string
        line:
          type:
            - object
            - 'null'
          additionalProperties: false
          required:
            - number
            - status
            - managedBy
            - inbound
            - numbers
          description: The rep’s calling line as it is now.
          properties:
            number:
              type:
                - string
                - 'null'
              description: >-
                The rep’s number: the one it calls from and answers, else the
                first caller ID on its line. Null when it has none yet.
            status:
              type: string
              enum:
                - ready
                - not_set_up
              description: 'ready: the line can place calls.'
            managedBy:
              type:
                - string
                - 'null'
              enum:
                - self
                - staff
                - null
              description: 'Who set up the line: self (from TFU Live) or staff (our team).'
            inbound:
              type: boolean
              description: Calls to number are answered by the rep.
            numbers:
              type: array
              items:
                type: string
              description: Every caller ID of the rep on its line.
        inbound:
          type: boolean
        unchanged:
          type: boolean
          description: 'The number was already the rep’s: nothing changed.'
    TfuLiveNumberError:
      type: object
      required:
        - success
        - error
      additionalProperties: {}
      properties:
        success:
          const: false
        error:
          type: string
          description: >-
            What happened and what to do, in words. Nothing was bought or
            charged unless it says so.
        code:
          type: string
          description: >-
            The refusal, kept exactly as the purchase engine named it. Act on
            it: card_required (add a usable billing card; the quote stays good),
            search_required (search again, then quote), quote_expired (quote
            again), number_unavailable (choose another), purchase_in_progress (a
            number is already being set up for this rep: read Get TFU Live agent
            numbers), crm_required (connect GoHighLevel first), line_unavailable
            or number_setup_unavailable (our team must look at the line),
            line_unverified (retry in a moment), inbound_unconfigured,
            not_allowed, not_found, invalid_request; for a number the agency
            already has, also number_not_in_account, number_on_another_trunk and
            number_in_use. Codes the calling line names start TFU_LIVE_LINE_.
        message:
          type: string
          description: >-
            The same words as error. A status-wide gate (authentication, scope,
            rate limit) puts its own name in error and its words here.
  securitySchemes:
    ApiKeyAuth:
      type: http
      scheme: bearer
      bearerFormat: API key
      description: 'Send your API key as `Authorization: Bearer YOUR_API_KEY`.'

````

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