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

# Build a TFU Live agent

> Create a draft, discover business capabilities, edit the reviewed version and prepare it for use through the tenant-scoped TFU Live API.

TFU Live is one Agent containing **Brain** (shared business knowledge and behaviour)
and **Voice** (spoken delivery). Create it through the dedicated
`/api/tfu-live-agents` module. Ordinary agents keep their existing creation body and
purpose types. Runtime is not a fourth outreach/inbound/appointment purpose.

Use the returned `tfu_live:<profileId>` public id on the TFU Live routes.
The server verifies the stored tenant-owned identity; an id prefix grants no
access. Runtime conversion and mixed ordinary/TFU authored bodies are
refused.

These changes are local development code, not a production publication.

Agency owners can also create agents from the dashboard, using **Build with AI**
or **Configure myself**. The same sub-account ownership and resource checks apply
to manual edits and typed or spoken co-authoring. `project_user` access does not
permit creation. Provider administration remains restricted to staff.

## Prerequisites

* An agency API key with `agents:read` to inspect an agent or `agents:write` to
  create, edit and prepare it. Write scope also covers reads.
* A connected sub-account belonging to that key's agency. A narrower credential
  location binding still applies. Supplying another `locationId` does not grant access.
* An authorised test environment and synthetic records. A local server can still
  use a shared development database and external providers.

Use `Authorization: Bearer YOUR_API_KEY`, as described in
[Authentication](/authentication). The examples use placeholder IDs, not working
customer records. No staff credential or internal service key is required.

## Step 1: Create an empty draft

Send this body to [Create agent](/api-reference/tfu-live/create-tfu-live-agent) with
`?locationId=YOUR_LOCATION_ID`:

```json theme={"dark"}
{
  "id": "example_agent",
  "draft": true
}
```

Choose a stable `id` using letters, digits, underscores or hyphens, up to 100
characters. Keep it if you lose the response: read that ID before retrying.
Retry creation with the same `id` and original body to finish an interrupted
campaign attachment. The retry keeps an existing campaign and its settings.
Campaign and private cadence writes commit together, so a failed attachment
cannot leave a partial campaign or duplicate cadence. A saved provider draft
remains available for the same-id retry; creation is not reported as complete
until the campaign is confirmed. `TFU_LIVE_CAMPAIGN_PENDING` reports that recoverable partial creation. TFU Live does not use the ordinary agent's `Idempotency-Key` replay contract.
An existing conflicting ID returns an error rather than creating another agent.

The response is `201` with `data.agent` and `data.campaign`. New agents, including
empty drafts, automatically receive a linked paused campaign and a private Power
Dialer cadence, and start with two workflows, **Schedule a callback** and **Stop
contact**, unless the body already brings a workflow that schedules an AI call
or puts the lead on DND. `data.campaign.id` is the campaign identifier; `profileId` is its
internal TFU Live profile binding. Caller numbers and trigger tags start empty.
Choose them in **Settings** and the **Brain** trigger before going live.
Read the actual `data.agent` object for `id`, `ifVersion`, `brain`, `voice` and
`setup`. A draft has `provisioningPolicy: "explicit"`. Creating and editing this
draft does not provision provider agents or activate a campaign.

Alternatively, supply `id`, `name`, `brain` and `voice` together to create a
complete agent. Complete creation starts setup immediately. A successful create
still does not mean setup has finished.

Or start from a Brain template, a ready-made agent: send `id`, `brainTemplateId`
and the `values` it needs (a calendar, who takes transfers) to create an explicit
draft that already has its workflows and a starting prompt. List them with
[List TFU Live Brain templates](/api-reference/tfu-live/list-tfu-live-brain-templates);
the [workflows contract](/tfu-live/workflows-contract#brain-templates) describes
each and what it needs.

## Step 2: Discover only the capabilities you need

Read [Discover TFU Live capabilities](/api-reference/tfu-live/get-tfu-live-catalog)
at `/api/tfu-live-agents/tfu_live:example_agent/brain/catalog?locationId=YOUR_LOCATION_ID`.

An agent's abilities are **workflows**: each has a name (the AI's tool name),
a description (when the AI should run it) and steps built from nodes. The
catalog's `data.workflows` lists every node with its settings (in words and as
JSON Schema), outputs, timing and the arguments it takes, the templates to start
from (Live transfer, Book appointment, Cancel appointment, Human assistance, Stop
contact, Check tag, Add tag, Schedule a callback, Check service area) and which
of their settings must be filled before saving, the path rules and the limits.
`data.subAccount` says which CRM the sub-account uses: on the native CRM
(`nativeCrm: true`) there are no tags, so tag nodes are refused. The catalog is
not an agent's saved configuration; read the agent separately for its current
state. Create a draft before requesting its catalog. The
[workflows contract](/tfu-live/workflows-contract) describes every shape.

`data.capabilities` is the short index of the tool groups that stay tools. Add
`group=outreach` to retrieve that group's tool schema, configuration and business
questions. `supported` reports the connected runtime's support, not business readiness.

| Group | Business purpose |
| - | - |
| `outreach` | Send the lead a text during the call. |
| `conversation_history` | Retrieve relevant earlier conversations. |
| `skills` | Retrieve business playbooks when relevant. |

The catalog also lists `voices` and background `rooms`. Do not invent names or
infer what a voice sounds like from its name.

Resolve calendars, tags, custom fields, pipelines, reps and teams with
[Look up TFU Live resources](/api-reference/tfu-live/look-up-tfu-live-resources)
(`GET /api/tfu-live-agents/{id}/resources/{kind}`). It reads the same lookups a
save verifies against, with this module's scope and sub-account checks. An
unavailable lookup (`status: "unavailable"`) is unknown, not an empty list.

Calendars come from the sub-account's CRM. A native sub-account lists its own
native calendars (`source: "native"`): they work in roleplay, but a real call
cannot book on them yet, so going live needs GoHighLevel. A GoHighLevel
sub-account lists its GoHighLevel calendars only (`source: "gohighlevel"`), and a
save there refuses a native calendar. `sources` reports each source on its own: a
source that could not be read is `unavailable` while the others are still listed.

Read `data.agent.authoring.editable` before editing an existing agent. A false
value with `legacy_voice_contract`, `unrepresented_capability` or
`outdated_format` in `authoring.reasons` means the API cannot represent that agent
losslessly; `outdated_format` is an agent saved before the workflow model. Edits and preparation return `409` until an explicit
migration is completed. Reading it does not change existing behaviour.

## Step 3: Translate the business request into a reviewed edit

For “cover postal code 10001, explain the boundary elsewhere, and speak calmly”,
send the complete authored object below to
[Update agent](/api-reference/tfu-live/update-tfu-live-agent) with
`?locationId=YOUR_LOCATION_ID`:

```json theme={"dark"}
{
  "ifVersion": "REPLACE_WITH_LOADED_VERSION",
  "name": "Consultation desk",
  "brain": {
    "prompt": "Help people arrange a consultation. Confirm that their postal code is covered before offering an appointment.",
    "capabilities": [
      "outreach"
    ],
    "capabilityConfig": {
      "workflows": {
        "wf_area2k9x": {
          "id": "wf_area2k9x",
          "name": "Check service area",
          "description": "Use when the caller wants to know whether we serve their area.",
          "steps": [
            {
              "id": "zip",
              "type": "collect_information",
              "config": {
                "items": [
                  {
                    "id": "zip_code",
                    "label": "Zip code",
                    "question": "What is the zip code of the address?",
                    "type": "text",
                    "field": "contact.postalCode"
                  }
                ]
              }
            },
            {
              "id": "coverage",
              "type": "check_zip_code",
              "config": {
                "field": "contact.postalCode",
                "codes": [
                  "10001"
                ]
              },
              "outputs": {
                "in_area": [
                  {
                    "id": "covered",
                    "type": "steer_agent",
                    "config": {
                      "guidance": "Offer a consultation."
                    }
                  }
                ],
                "out_of_area": [
                  {
                    "id": "outside",
                    "type": "steer_agent",
                    "config": {
                      "guidance": "Explain that this area is not covered."
                    }
                  }
                ]
              }
            }
          ]
        }
      }
    }
  },
  "voice": {
    "voiceId": "marin",
    "prompt": "Speak at a calm pace and pronounce addresses clearly."
  }
}
```

Replace `ifVersion` with the value from your last read. This is a complete
TFU Live authoring replacement, unlike the ordinary agent's partial `config`
patch. To change one workflow without sending the rest, use
[Replace TFU Live workflow](/api-reference/tfu-live/replace-tfu-live-workflow)
and its neighbours, which run the same checks. Preserve unrelated prompt text, capability configuration and existing
workflows. The response returns the next `ifVersion`. A workflow id is `wf_`
followed by 8 lowercase letters or digits; keep an existing workflow's id when you
change it, so its tool keeps its history.

Brain text may remain incomplete while an explicit draft is being built. Voice
instructions must remain non-empty. Fields outside this authoring contract,
including model selection, runtime changes and campaign activation, are refused.

For appointments, build a workflow from the Book appointment template. Find
slots offers times from one calendar, or from ordered routing rules over collected
answers: the first matching rule's calendar is used, an unknown answer is asked
for, and there is no default calendar, so add a last rule with `is_set` for a
catch-all. When one choice item decides, every one of its answers must choose a
calendar. Every calendar must be active in this sub-account. The calendar's CRM
sub-account supplies its timezone at runtime; do not ask for an appointment
timezone. Collect information gathers the answers during the call, before
availability and booking; post-call extraction is too late for this decision.

## Step 4: Prepare the exact saved version

Explicit drafts stay unprovisioned until you send this body to
[Prepare TFU Live agent](/api-reference/tfu-live/prepare-tfu-live-agent) at
`/api/tfu-live-agents/tfu_live:example_agent/prepare?locationId=YOUR_LOCATION_ID`:

```json theme={"dark"}
{
  "ifVersion": "REPLACE_WITH_LOADED_VERSION"
}
```

Preparation accepts only `ifVersion`, not authored fields. Preparation validates the saved
configuration and starts setup for that exact version. Read the agent again to
observe progress. Existing agents without the explicit draft policy start setup
when edited.

A `202` response means the request was accepted. `setupStarted` reports whether
setup was requested; `setupError` reports an edit saved without confirmed setup.
`setup.status` can be `draft`, `queued`, `provisioning`, `ready`,
`provisioning_blocked` or `provisioning_uncertain`. Inspect `failureCode` on a
blocked or uncertain revision before retrying. `setup.revision` and
`setup.verifiedAt` describe verified setup, not campaign activation.

An edit can be pending while the prior verified version remains in service.
Always use the newly returned `ifVersion` and the current `setup.status`; an old
verification timestamp does not make a pending edit ready.

To go back to an earlier save, send `ifVersion` and that save's `revision`
number from the agent's `changeHistory` to
`POST /api/tfu-live-agents/{id}/restore`. The authored text of that revision
comes back as a new revision, through the same save and setup as an edit.
History is never rewritten, and the last fifty saves can be restored.

## Boundaries

* Creating, saving and preparing an agent do not create or activate a campaign.
  Campaign permissions and activation checks remain separate. Campaign settings stay shared; current customer TFU campaign mutation adapters remain incomplete. Ordinary outcome prompts and workflow graphs are intentionally not applicable, not aliases for Brain text.
* TFU Live create/get/update own complete authored revisions. Use `GET /api/tfu-live-agents?locationId=...` for its list and `GET /api/tfu-live-agents/{id}/operations` for current runtime support. Clone, delete and split-test are not implemented for TFU.
* The agent's abilities are `brain.capabilityConfig.workflows`, part of the complete authored aggregate. Each workflow is a tool for the AI: its name gives the tool name, its description is when to run it, and renaming it renames the tool. There are no special workflows; every node can be used in any workflow. Collect information is the only way the AI supplies inputs: its items become the tool's arguments. A node with outputs ends its list: the steps after it go on its outputs. Tags are never chosen by the AI: Check tag, Add tag and Remove tag each hold one tag. A Transfer node has the outputs Connected, Nobody available and Not connected; a call makes one transfer attempt, a fallback transfer goes only on Nobody available, and after Connected only actions and Conditions may follow. Put on DND ends the lead's journey, so nothing follows it. The catalog's `workflows` contract lists the node formats and limits. Saving validates the workflows with the runtime validator but executes nothing, and refuses an invalid workflow with `422`, naming the workflow and step. Custom fields, pipelines and calendars a new or changed step names must exist in this sub-account. HTTPS webhooks also pass outbound URL policy. A webhook sends the call's context (`workflow`, `sentAt`, `ids`, `contact`, `call`) plus its `fields`, each from fixed text, a contact field, a call detail or a collected answer. Authentication headers must be secret: send `{"secret":{"value":"…"}}` once, and the value is stored encrypted and read back only as `{"secret":{"id":"…"}}`, which you send back unchanged to keep it. The formats workflows replaced (`brain.hooks`, `followUps`, `outcomeSteering`, `customTools`, `appointments`, per-tool settings such as `transfer_call` or `check_area_code`, and workflows keyed by tool id) are refused until the agent's migration has run.
* A Notify teammate node sending to Slack uses symbolic `main` or `alerts` channels, resolved by the runtime from the call tenant. `authoring.bindingWarnings` distinguishes an unverified mapping from a ready integration.
* Transfer destinations and team-channel mappings require existing authorised
  configuration. A Transfer node names its destination; it does not create one.
* Split/master authoring and provider-specific tuning are not supported by this
  contract. Voice delivery instructions do not implement numeric provider controls.
* The internal conversational builder retrieves focused authoring guidance and
  capability details through its own authenticated tools. Customer backends can
  use this guide and the discovery requests above; this API does not expose the
  internal builder conversation, voice session or arbitrary tool executor.

## Shared campaign controls, separate authoring

Caller-number ownership, calling windows, timezones and concurrency use shared
campaign services and checks. Follow-up timing uses the campaign-bound cadence.
TFU Live still has its own verified profile and call lifecycle.

Workflows run when the AI calls their tool, during the conversation. They are
not the ordinary campaign outcome workflow graph. Some action implementations are
shared, but this API does not accept that graph's schema or claim its endpoint
compatibility.

Campaign association requires the TFU Live profile and the campaign binding to
be visible to the runtime that validates them. A local setup with separate SaaS
and voice databases does not prove this association works end to end. This
change does not switch databases or migrate records.

## Handle errors

| Status | What your backend should do |
| - | - |
| `400` | Supply the required query location and valid query values. |
| `403` | Check the key's role and `agents:read` or `agents:write` scope. |
| `404` | Treat the agent or sub-account as unavailable to this credential. Do not probe other tenants. |
| `409` | Re-read the agent and reconcile the requested change against its current `ifVersion`. |
| `422` | Correct the authored configuration, resource binding or unsupported field. |
| `502`, `503` | Setup/provider verification is unavailable. Read current state before retrying a mutation. |

TFU Live errors use `{ "success": false, "error": "Explanation", "code": "…" }`,
and a workflow refusal also names `workflowId`, `stepId` and `field`. Branch on
`code`, never on the words: the codes are listed in the
[workflows contract](/tfu-live/workflows-contract#errors). Missing-scope
responses also identify `requiredScope`.

## In the API

* [Create agent](/api-reference/tfu-live/create-tfu-live-agent)
* [Get agent](/api-reference/tfu-live/get-tfu-live-agent)
* [Update agent](/api-reference/tfu-live/update-tfu-live-agent)
* [Agent field glossary](/glossary#agents)

## Related

* [The workflows contract](/tfu-live/workflows-contract)
* [The guidance the AI builder retrieves](/tfu-live/builder-guidance)
* [What an agent is](/agents/overview)
* [Authentication](/authentication)
* [Go live](/campaigns/going-live)
