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

# Register a business for caller ID

> Registers one business and its numbers with Twilio in one call. It runs the same checks as the dashboard form: the business must be yours, each number must be held by it, and every detail must pass.

No one is signed in to tick "I'm authorised", so you name the person who confirmed the details in `confirmedBy`. They are kept with the submission as its consent record. The business's own representative goes in `details` and is sent to Twilio.

Leave out `numbers` to register every number the summary marks `supported` for this business. Numbers held in two Twilio accounts need one call per account, each naming its numbers.

Sending the same `submissionId` again returns the registration it created, so a retry after a timeout never registers twice. The answer is `202`: the registration is queued, and Twilio reviews it over the following days. Read the summary to follow it.

One EIN is registered once per Twilio account. When another of your businesses with the same EIN is already registered there, such as a second location of one company, the call answers `409` `identity_conflict`. Send it again with `linkExisting: true` to add this business's numbers to that registration. They then use its details and caller name, and `linked` is `true` in the answer.

Required API key scope: `phone_numbers:write`.



## OpenAPI

````yaml /openapi.yaml post /api/phone-trust/businesses/{locationId}/registration
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: Caller ID
    description: >-
      Register the businesses behind your calling numbers with Twilio, so
      carriers know who is calling and label the calls less often as spam. Read
      where every number stands, then register one business per call, naming the
      person who confirmed its details.
  - 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/phone-trust/businesses/{locationId}/registration:
    post:
      tags:
        - Caller ID
      summary: Register a business for caller ID
      description: >-
        Registers one business and its numbers with Twilio in one call. It runs
        the same checks as the dashboard form: the business must be yours, each
        number must be held by it, and every detail must pass.


        No one is signed in to tick "I'm authorised", so you name the person who
        confirmed the details in `confirmedBy`. They are kept with the
        submission as its consent record. The business's own representative goes
        in `details` and is sent to Twilio.


        Leave out `numbers` to register every number the summary marks
        `supported` for this business. Numbers held in two Twilio accounts need
        one call per account, each naming its numbers.


        Sending the same `submissionId` again returns the registration it
        created, so a retry after a timeout never registers twice. The answer is
        `202`: the registration is queued, and Twilio reviews it over the
        following days. Read the summary to follow it.


        One EIN is registered once per Twilio account. When another of your
        businesses with the same EIN is already registered there, such as a
        second location of one company, the call answers `409`
        `identity_conflict`. Send it again with `linkExisting: true` to add this
        business's numbers to that registration. They then use its details and
        caller name, and `linked` is `true` in the answer.


        Required API key scope: `phone_numbers:write`.
      operationId: register-caller-id-business
      parameters:
        - name: locationId
          in: path
          required: true
          schema:
            type: string
            minLength: 1
          description: The business (sub-account) to register.
          example: Cc6UY6Z5DiCwMgnwF0av
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CallerIdRegisterRequest'
            examples:
              default:
                value:
                  submissionId: 9b1d2f6e-4c3a-4e8b-a1d0-7c5e2f9a3b14
                  authorized: true
                  termsVersion: caller-trust-2026-10-10
                  confirmedBy:
                    name: Pat Partner
                    email: pat@agency.example
                    title: Operations lead
                  futureNumbers: true
                  details:
                    legalName: Forge Fitness LLC
                    businessType: Limited Liability Corporation
                    industry: CONSUMER
                    identifierType: EIN
                    identifier: '123456789'
                    website: https://forge.example
                    street: 1 Main St
                    city: Newtown
                    region: PA
                    postalCode: '18940'
                    country: US
                    operatingRegion: USA_AND_CANADA
                    callerName: FORGE PT
                    firstName: Jane
                    lastName: Doe
                    email: jane@forge.example
                    phone: '+12155550100'
                    businessTitle: Owner
                    jobPosition: CEO
                    useCase: Lead Nurturing
                    employeeCount: '8'
                    dailyCalls: '40'
      responses:
        '202':
          description: >-
            Queued for Twilio, or the registration a repeated `submissionId`
            already created.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CallerIdRegisterResponse'
              examples:
                default:
                  value:
                    success: true
                    registration:
                      id: 3f2b8c1e-7d4a-4f8e-9b6a-2c1d0e5f7a9b
                      locationId: Cc6UY6Z5DiCwMgnwF0av
                      ownerType: business
                      businessName: Forge Fitness LLC
                      state: queued
                      version: 4
                      profile: null
                      products:
                        shakenStir:
                          status: pending
                          checkedAt: null
                          reason: null
                        cnam:
                          status: pending
                          checkedAt: null
                          reason: null
                        voiceIntegrity:
                          status: pending
                          checkedAt: null
                          reason: null
                      futureNumbersEnabled: true
                      numbers:
                        - '+16093551813'
                      submittedAt: '2026-10-30T12:00:00.000Z'
                      nextCheckAt: '2026-10-30T12:00:00.000Z'
                      problem: null
                    excluded: []
        '401':
          description: No valid credential.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CallerIdErrorResponse'
              examples:
                unauthorized:
                  value:
                    success: false
                    error: Unauthorized
                    message: Please sign in to continue.
        '403':
          description: The key lacks `phone_numbers:write`, or the business is not yours.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CallerIdErrorResponse'
              examples:
                scope:
                  value:
                    success: false
                    error: InsufficientScope
                    message: >-
                      This access key does not allow this action. Please ask
                      your account admin to change its permissions.
                    requiredScope: phone_numbers:write
                forbidden:
                  value:
                    success: false
                    error: forbidden
                    message: >-
                      You don't have access to this business. Please ask your
                      agency if you need it.
        '409':
          description: >-
            Something changed underneath the call: a number moved, or is already
            in another registration. Or another of your businesses with the same
            EIN is registered in this Twilio account, and `linkExisting` was not
            `true`. Then `references` names it: `registrationId`, `locationId`,
            `businessName`, and `linkable` (whether it can be joined now).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CallerIdErrorResponse'
              examples:
                selectionChanged:
                  value:
                    success: false
                    error: selection_changed
                    message: >-
                      The eligible numbers changed. Refresh and review your
                      selection again.
                assignmentConflict:
                  value:
                    success: false
                    error: assignment_conflict
                    message: >-
                      A number is already linked to another business
                      registration. Your agency needs to review its ownership.
                identityConflict:
                  value:
                    success: false
                    error: identity_conflict
                    message: >-
                      This business already has a registration in this account.
                      Your agency can link the correct business.
        '422':
          description: >-
            A detail is missing or wrong (`errors` names each field), consent is
            missing, or the business has no number it can register.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CallerIdErrorResponse'
              examples:
                invalidDetails:
                  value:
                    success: false
                    error: invalid_details
                    message: Check the highlighted details before continuing.
                    errors:
                      - field: identifier
                        code: FieldInvalid
                        message: Please check this and try again.
                    references:
                      fields:
                        - field: identifier
                          code: FieldInvalid
                consentRequired:
                  value:
                    success: false
                    error: consent_required
                    message: >-
                      Confirm your authority and review the current registration
                      terms.
                noEligibleNumbers:
                  value:
                    success: false
                    error: no_eligible_numbers
                    message: No eligible numbers were selected for this business.
        '429':
          description: More than 30 registration writes in a minute.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CallerIdErrorResponse'
              examples:
                rateLimited:
                  value:
                    success: false
                    error: rate_limited
                    message: Please wait a moment before trying again.
        '503':
          description: >-
            Caller ID is not available for your account, or Twilio could not be
            reached.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CallerIdErrorResponse'
              examples:
                unavailable:
                  value:
                    success: false
                    error: registration_unavailable
                    message: >-
                      Caller registration is not available yet. Please contact
                      your agency.
      security:
        - ApiKeyAuth: []
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |-
            curl --request POST \
              --url https://api.teamfollowup.ai/api/phone-trust/businesses/{locationId}/registration \
              --header 'Authorization: Bearer YOUR_API_KEY' \
              --header 'Content-Type: application/json' \
              --data '{
              "submissionId": "9b1d2f6e-4c3a-4e8b-a1d0-7c5e2f9a3b14",
              "authorized": true,
              "termsVersion": "caller-trust-2026-10-10",
              "confirmedBy": {
                "name": "Pat Partner",
                "email": "pat@agency.example",
                "title": "Operations lead"
              },
              "futureNumbers": true,
              "details": {
                "legalName": "Forge Fitness LLC",
                "businessType": "Limited Liability Corporation",
                "industry": "CONSUMER",
                "identifierType": "EIN",
                "identifier": "123456789",
                "website": "https://forge.example",
                "street": "1 Main St",
                "city": "Newtown",
                "region": "PA",
                "postalCode": "18940",
                "country": "US",
                "operatingRegion": "USA_AND_CANADA",
                "callerName": "FORGE PT",
                "firstName": "Jane",
                "lastName": "Doe",
                "email": "jane@forge.example",
                "phone": "+12155550100",
                "businessTitle": "Owner",
                "jobPosition": "CEO",
                "useCase": "Lead Nurturing",
                "employeeCount": "8",
                "dailyCalls": "40"
              }
            }'
components:
  schemas:
    CallerIdRegisterRequest:
      type: object
      required:
        - submissionId
        - authorized
        - termsVersion
        - confirmedBy
        - details
      properties:
        submissionId:
          type: string
          pattern: ^[a-f0-9-]{36}$
          description: >-
            A lowercase UUID you make once per registration and reuse on every
            retry.
        authorized:
          type: boolean
          description: >-
            Must be `true`: the person in `confirmedBy` is authorised to
            register this business.
        termsVersion:
          type: string
          description: The summary's `termsVersion`.
        confirmedBy:
          type: object
          required:
            - name
            - email
            - title
          description: >-
            The person at your agency who confirmed these details. Kept with the
            submission, never sent to Twilio.
          properties:
            name:
              type: string
              maxLength: 100
            email:
              type: string
              maxLength: 254
              format: email
            title:
              type: string
              maxLength: 100
        details:
          type: object
          additionalProperties:
            type: string
          description: >-
            The business and its authorised representative, keyed by the
            summary's `fields`. `businessIdentity` defaults to
            `direct_customer`.
        numbers:
          type: array
          items:
            type: string
            pattern: ^\+[1-9]\d{7,14}$
          description: E.164 numbers to register. Leave out for every ready number.
        futureNumbers:
          type: boolean
          default: true
          description: Also register this business's new numbers automatically.
        linkExisting:
          type: boolean
          default: false
          description: >-
            When another of your businesses with the same EIN is already
            registered in the same Twilio account, add this business's numbers
            to that registration. They use its details and caller name.
    CallerIdRegisterResponse:
      type: object
      additionalProperties: {}
      required:
        - success
        - registration
      properties:
        success:
          type: boolean
        registration:
          $ref: '#/components/schemas/CallerIdRegistration'
        linked:
          type: boolean
          description: >-
            `true` when this business joined the registration of another
            business with the same EIN.
        excluded:
          type: array
          description: >-
            Numbers left out, with why: `reasonCode` and the sentence in
            `reason`.
          items:
            type: object
            additionalProperties: {}
            properties:
              number:
                type: string
              reasonCode:
                type: string
              reason:
                type: string
    CallerIdErrorResponse:
      type: object
      description: >-
        The error envelope every endpoint answers with. Branch on `error`, show
        `message` to people. Some modules add fields of their own, listed on
        that module's error schema.
      required:
        - success
        - error
        - message
      properties:
        success:
          type: boolean
          example: false
        error:
          type: string
          description: >-
            A stable code for what went wrong. Branch on this: the same failure
            always answers with the same code.
          example: NotFound
        message:
          type: string
          description: >-
            One plain sentence for a person: what happened and what to do next.
            Show it, never parse it: the wording can change, and browser users
            get it in their own language. API keys and OAuth apps always get
            English.
          example: We couldn't find that. It may have been moved or deleted.
        errors:
          type: array
          description: >-
            Sent when specific fields were refused: one entry for each field to
            fix.
          items:
            type: object
            additionalProperties: false
            required:
              - field
              - code
              - message
            properties:
              field:
                type: string
                description: Where the value sits in the request, as a dotted path.
                example: name
              code:
                type: string
                description: A stable code for what is wrong with the value.
                example: FieldRequired
              message:
                type: string
                description: One plain sentence for a person.
                example: Please fill this in.
        details:
          type: object
          additionalProperties: {}
          description: >-
            Data your code may need to act on the failure, such as ids or
            limits. Never text for people.
        ref:
          type: string
          description: >-
            Sent only when the failure was on our side. Quote it to support so
            they can find the request. It matches the `x-request-id` response
            header.
          example: 3f2b8c1e-7d4a-4f8e-9b6a-2c1d0e5f7a9b
        requiredScope:
          type: string
          description: The scope the key needs, on `InsufficientScope`.
        references:
          type: object
          additionalProperties: {}
          properties:
            fields:
              type: array
              items:
                type: object
                additionalProperties: {}
                properties:
                  field:
                    type: string
                  code:
                    type: string
    CallerIdRegistration:
      type: object
      additionalProperties: {}
      description: One business registration as it stands.
      properties:
        id:
          type: string
        locationId:
          type:
            - string
            - 'null'
        ownerType:
          type: string
          description: '`business` for a registration made here.'
        businessName:
          type: string
        state:
          type: string
          description: >-
            Where the registration stands: `draft`, `queued`, `under_review`,
            `complete`, `partial` or `needs_attention`.
        version:
          type: integer
        profile:
          type:
            - object
            - 'null'
          additionalProperties: {}
        products:
          type: object
          additionalProperties: {}
          description: >-
            Each Twilio product (`shakenStir`, `cnam`, `voiceIntegrity`) with
            its own `status`, `checkedAt` and `reason`.
        futureNumbersEnabled:
          type: boolean
        numbers:
          type: array
          items:
            type: string
        linkedLocations:
          type: array
          items:
            type: string
          description: >-
            Other businesses (location IDs) with the same EIN whose numbers this
            registration covers.
        clientNames:
          type: object
          additionalProperties:
            type: string
          description: >-
            In Get caller ID status: the name of each business this registration
            serves (its own and the linked ones), by location ID. A client's own
            key gets only its own name.
        submittedAt:
          type:
            - string
            - 'null'
        nextCheckAt:
          type:
            - string
            - 'null'
        problem:
          type:
            - object
            - 'null'
          additionalProperties: {}
          description: 'Why it needs attention: `code`, `message` and `fields`.'
  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.