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

# agent.json Reference

`agent.json` is the agent. `create` writes it, `add`, `remove`, `set` and `unset` edit it, `validate` checks it, and `deploy` generates the deployable function from it. Every problem `validate` reports names the field and the fix.

```jsonc theme={null}
{
  "name": "support-agent",                       // [A-Za-z0-9-], 1–64, starts with a letter, no trailing dash; the function's name
  "kind": "configuration",                       // or "code"; inferred from instructions / entry when absent
  "model": "meta-llama/Llama-3.3-70B-Instruct",  // required; a Telnyx inference model id. create writes this one without --model; it is hosted and needs no key
  "instructions": "instructions.md",             // configuration-based; a code-based agent has "entry": "src/agent.ts" instead
  "channels": [{ "type": "messaging_profile", "id": "<id>" }],   // the by-artifact form (see below)
  "phone": ["+15550001234"],                     // numbers in the account; deploy sets up calls and texts
  "email": true,                                 // or { "address": "support@acme.example" }; deploy creates the inbox and the domain webhook
  "tools": [                                     // what the model can call; add tool writes each entry
    { "type": "web_search", "research": true, "domains": ["acme.example"], "freshness": "week" },
    { "type": "mcp", "name": "deepwiki", "url": "https://mcp.deepwiki.com/mcp", "secret": "DEEPWIKI_TOKEN", "allow": ["read_wiki_structure"], "tools": [ /* listed by add tool mcp */ ] },
    { "type": "agent_gateway", "id": "<instance id>" }   // declared; not called in the beta phase
  ],
  "memory": { "type": "agent_memory", "namespace": "default", "profile": "{{conversation}}" },
  "limits": { "steps": 8 },                      // the harness step cap per run, positive integer
  "approvals": { "fetch_page": "ask" },          // ask | allow | deny, keyed by tool name as the model sees it
  "voice": {                                     // the call; an example, not the defaults, which the Voice table below has
    "greeting": "Hi, this is Acme support. How can I help?",
    "eager": true,
    "bargeIn": true,
    "keypressInterrupts": false,
    "harness": false,
    "filler": { "afterMs": 700, "phrases": ["One moment.", "Let me check that for you.", "Sure, just a second."], "maxPerCall": 0 },   // or false
    "stt": { "eotThreshold": 0.7, "eagerEotThreshold": 0.5, "eotTimeoutMs": 1500 },
    "tts": { "voice": "Telnyx.Ultra.00967b2f-88a6-4a31-8153-110a92134b9f" }
  },
  "web": true,                                   // or { "title": "Acme Support", "greeting": "Hi!" }; a chat page at <mount>/web and /web/embed.js
  "secrets": ["OPENAI_API_KEY", "TELNYX_PUBLIC_KEY"],   // names only; values live on the platform
  "deployed": { "functionId": "…", "functionName": "…", "mountUrl": "…" }   // written by deploy; do not edit
}
```

## Identity and Model

| Field | Meaning |
| - | - |
| `name` | The function's and the project's name. Letters, digits and dashes, 1 to 64 characters, starting with a letter and not ending with a dash. |
| `kind` | `configuration` (the CLI generates the runtime code from this file) or `code` (the user's own class under `entry`). Inferred when absent. |
| `model` | A Telnyx inference model id. `telnyx-acp models` lists the ones the account can use, hosted models first. An `openai/*`, `anthropic/*`, `google/*` or `groq/*` model needs the user's own key in the secret named after the provider (`OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, `GOOGLE_API_KEY`, `GROQ_API_KEY`), and in `.env` for `dev`. `set model <id>` changes it; on a terminal, `set model` alone opens the account's list. |
| `instructions` | The file with the system prompt, inlined at deploy. On a call, keep it short and spoken. |
| `entry` | Code-based agents only: the TypeScript file exporting the agent class. |

## Phone, Email, Web and Channels

| Field | Meaning |
| - | - |
| `phone` | E.164 numbers in the account the agent answers on. `deploy` looks each up before shipping (a missing or inactive number stops the `deploy`, named), then puts it on a Call Control application for calls and a messaging profile for texts, creating them named `<agent> (telnyx-acp)` when the number is bare. `add phone` / `remove phone` edit the list. |
| `email` | `true`, or `{ "address": "support@acme.example" }`. `true` is an inbox named after the agent on the account's shared inbound subdomain, Telnyx's own domain with no DNS to set up; `deploy` prints the address. An address names an inbox on a domain of the account's, which must be verified with inbound enabled. `deploy` finds or creates the inbox and subscribes its domain to `email.received`. `add email [address]`, `set email.address <address>` and `remove email` edit it. |
| `web` | `true`, or `{ "title": "Acme Support", "greeting": "Hi!" }`. The function serves a chat page at `<mount>/web` and an embed script at `<mount>/web/embed.js`, and `dev` serves the same page. Without `web`, `/web` answers 404. The page is public once deployed: anyone with the URL can talk to the agent, so the wizard leaves it off unless asked. `title` is the page's title and the launcher's label, the agent's name with dashes and underscores as spaces by default; `greeting` is the first line a visitor sees, `Hi! Ask me anything.` by default. Each is at most 200 characters. When the agent has a number, the page shows the first one as a `tel:` link; under `dev`, with a number or a `voice` block, the page also has a mic button, which the deployed page does not offer yet. `add web [--title <title>]`, `set web.title`, `set web.greeting` and `remove web` edit it. |
| `channels` | The by-artifact form: `call_control_application`, `messaging_profile`, `texml_application` or `email_domain` by id. `deploy` points the artifact's inbound webhook at the agent and never touches numbers. A TeXML application is wired like the others (its `voice_url`), but the agent answers its webhooks with 501 until the handler lands. |

> [!WARNING]
>
> **Beta.** `texml_application` channels are not handled yet. See [Beta Limitations](/docs/agents/voice#beta-limitations).

[Voice](/docs/agents/voice) and [Messaging](/docs/agents/messaging) cover what happens at `deploy` and at runtime.

## Voice

Every knob is optional; unset, a call runs with the settings below. `set voice.<knob> <value>` changes one, and `unset voice.<knob>` restores the default.

| Field | Default | Meaning |
| - | - | - |
| `greeting` | "Hi, this is \<name>. How can I help?", with the name's dashes and underscores as spaces | What the caller hears first. |
| `eager` | `true` | Start the reply on the STT model's first guess of the end of the caller's turn, before it is confirmed; the words are confirmed or the turn is discarded. Latency first. |
| `bargeIn` | `true` | The caller talking over the agent stops it once two words are heard; `true` is `{ "minWords": 2 }`. `{ "minWords": 0 }` stops on any sound, `false` never stops. |
| `keypressInterrupts` | `false` | A keypad press stops the agent. |
| `harness` | `false` | Off: a turn goes straight to the model with the conversation in memory, and the exchange is written to the message log behind the call. On: the harness runs per turn, with the tools, at the cost of a second or more before the first word; a turn on a call is not a durable run, and a tool marked `ask` is left out. |
| `filler` | on | What a call says when the model is slow to start. `false` turns it off. The phrase is spoken, never written to the message log or to memory. |
| `filler.afterMs` | `700` | Milliseconds after the caller is confirmed done before a phrase is said, `100` to `5000`. |
| `filler.phrases` | "One moment.", "Let me check that for you.", "Sure, just a second." | One to ten short sentences, said in turn. |
| `filler.maxPerCall` | `0` | How many turns in one call may be filled; `0` for all of them. |
| `stt.eotThreshold` | the platform's | The STT model's confidence to confirm the end of a turn. Sent only when set. |
| `stt.eagerEotThreshold` | `0.5` | The confidence at which an eager turn starts. |
| `stt.eotTimeoutMs` | the platform's | Silence after which the turn ends regardless. Sent only when set. |
| `tts.voice` | `Telnyx.Ultra.00967b2f-…` | The Telnyx voice id. `tts.voiceSettings` passes provider settings through. |

## Memory

| Field | Meaning |
| - | - |
| `memory.type` | `agent_memory`. |
| `memory.namespace` | The Agent Memory namespace. `default` exists in every organization; others are created with the [namespaces API](/docs/agent-memory/isolation). |
| `memory.profile` | The profile template. `{{conversation}}` is replaced by the conversation's key: the caller's number on a call or a text, the sender's address on an email, and the conversation's own name otherwise (`alice` for `invoke --conversation alice`, the `session-…` id of a `chat`, the `web-…` id of a browser visitor), unless `rpc … receive` gave the conversation a `from`, which then stays its key. A fixed string makes every conversation share one profile. |

`set memory '{ … }'` writes the block, `set memory.namespace <name>` changes one field of it, and `unset memory` removes it. Without the block nothing is read or written. [Memory](/docs/agents/memory) covers the behaviour.

## Limits, Approvals, Tools

| Field | Meaning |
| - | - |
| `limits.steps` | How many model steps one run may take; the default is 8. `set limits.steps <n>` changes it. |
| `approvals` | Per tool name as the model sees it (`fetch_page`, `deepwiki__read_wiki_structure`): `ask` pauses the run until `telnyx-acp rpc … approve <id>` or `deny <id>` decides it; `allow`, the default for a tool not named, runs without asking; `deny` hides the tool from the model. On a call a tool marked `ask` is left out, since a call cannot wait for an approval. In the beta phase `ask` works under `dev` only: a deployed run with an `ask` tool pauses but cannot be decided yet, and `deploy` warns. `set approvals.<tool> ask` writes an entry, and `unset approvals.<tool>` removes it. `telnyx-acp tools` lists the names. |
| `tools` | The entries below, one per `add tool`, or the path of a JSON file holding them (`"tools": "tools.json"`: relative, inside the project, ending in `.json`); `telnyx-acp tools --file tools.json` moves the list out and `--inline` back, and every verb reads and writes it where it lives. The managed loop builds the Web Search and MCP tools from them and offers them to the model beside any tool set a host supplies; a gateway entry is declared and validated only. `remove tool <key>` removes one by its key: the type for `web_search`, the name for `mcp`, the id for `agent_gateway`. A key listed twice fails `validate`. |

### Tool Entries

| Entry | Fields |
| - | - |
| `{ "type": "web_search" }` | `contents` (default `true`; `false` leaves out `fetch_page`), `research` (default `false`; `true` adds `research`), `domains` (up to 50 bare hostnames, lowercased), `freshness` (`day`, `week`, `month`, `year`). |
| `{ "type": "mcp" }` | `name` (lowercase letters, digits, underscores, up to 32, starting with a letter; the tools' prefix), `url` (the Streamable HTTP endpoint, http or https), `secret` (a secret name, sent as a bearer token), `header` (the header the secret goes in instead), `allow` (the server's tool names to keep), `tools` (the server's list: `{ name, description?, inputSchema }` each, as `add tool mcp` wrote it; `tools --refresh` rewrites it). A `secret` the project does not declare is a `validate` problem with the `add secret` fix. |
| `{ "type": "agent_gateway" }` | `id`, the instance id. |

A tool call whose execution fails answers the model with text (`{ "error": "<tool> failed: …" }`) and the run goes on; arguments that fail the tool's schema still fail the run, and an MCP server's own error result is passed through as its text. Each call writes one `{"toolCall":"<tool>", …its arguments, "ms":…,"ok":…}` line to the logs, with `error` when `ok` is false.

## Secrets

`secrets` lists names only. `telnyx-acp add secret <NAME>` prompts for the value (hidden; stdin when piped), stores it on the platform and binds it; the value never touches `agent.json` or a command line. Some secrets have a meaning of their own:

* `TELNYX_PUBLIC_KEY`: the account's webhook public key. A project with a phone number, an email inbox or a channel needs it; the deployed webhook handlers verify every signature with it and answer 401 without it. `deploy` asks for it once on a terminal.
* `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, `GOOGLE_API_KEY`, `GROQ_API_KEY`: sent as the caller's key for an `openai/*`, `anthropic/*`, `google/*` or `groq/*` model.

## What Deploy Writes

`deployed` is the CLI's record and is not edited by hand. It holds:

* `functionId`, `functionName` (what the platform knows the function as; the verbs address it by this, so renaming the agent does not lose it), `mountUrl` and `actorType`
* `revision`, the active revision's id, and `revisions`, a note per revision this CLI shipped, keyed by id: the project's git short SHA, or `manual` outside a git repository
* `phones[]`, per number the artifacts, whether `deploy` created them, and where the number was before
* `email`, the inbox, its address and domain, the webhook, and whether `deploy` created each
* `channels[]`, each artifact's previous webhook, so `delete` can restore it
