Command-line tool (CLI)
The telenow command works with your Telenow account from a terminal: agents, calls and transcripts, text chat, prompts, campaigns, phone numbers, knowledge bases, sub-accounts and more.
Read a call's transcript or find out why it went wrong, chat with an agent by text, edit an agent's prompt in local files and push it back safely, place a test call, run a campaign or buy a number. It is built for scripts, CI and AI coding assistants as much as for people: output is JSON, errors have fixed exit codes, and anything that dials, spends, deletes or cannot be undone asks first. The same command also builds apps for the dashboard (App commands).
Install
npm install -g telenow
telenow --help
Or run it without installing: npx telenow <command>.
- Node.js 20 or newer.
- The account commands on this page need
telenow0.7.0 or newer.telenow --helplists them under Account commands. telenow <command> --helpshows a command's own options.
Sign in
telenow login # signs in through the dashboard, in your browser
telenow whoami # the org, key name and key role in use
telenow login signs you in through the dashboard:
- It prints a code and a link, and opens the link in your browser.
- On that page, check that the code matches your terminal, then pick the organization, the key's role (at most your own) and its name, and approve.
- The CLI receives a new organization API key made for this computer, named
telenow CLI — <computer> — <date>, and saves it. The key is listed with your other keys, and revoked the same way.
- A key made this way expires (after 90 days by default).
loginprints when. After that, a command answers exit2withlogin_expiredand the date, and you runtelenow loginagain. - Over SSH, in a container or without a display, it prints the link instead of opening it (
--no-browserasks for that), so you can approve on any device. Ctrl-C stops waiting. - To use a key you already have (create one in the dashboard), run
telenow login --key: it asks for the key without showing it. A server without browser sign-in also asks for a key, and says why.
Every command acts as that key: its organization and its role. login checks a key with the server first and saves it only if the server accepts it. Saved logins live in ~/.config/telenow/config.json (under $XDG_CONFIG_HOME when that is set), readable only by you. The key is never printed, not even by whoami.
A viewer or member key can read agents but not change them. Changes need an owner, admin or developer key (Agents API).
Scripts and CI
There is no flag that takes the key on the command line, so it never lands in shell history. Without a terminal, use one of these:
# CI: set TELENOW_API_KEY from your CI's secret store. Nothing is saved.
telenow agents list
# A script that saves a login: pipe the key in.
printf '%s' "$KEY" | telenow login --key-stdin
TELENOW_API_KEY overrides any saved login.
More than one org, or another server
--profile <name>keeps one login per organization:telenow login --profile acme, then--profile acmeon any command.telenow profiles listshows the saved logins, andtelenow profiles use <name>makes one the current login.--base-url <url>points at another server (the default ishttps://api.telenow.ai). It must behttps, except onlocalhost. A login saved with--base-urlremembers its server.telenow logoutforgets a saved login on this computer.--revokefirst revokes its key on the server; without it the key stays valid (revoke it in the dashboard later if you no longer need it).
Environment variables
| Variable | What it does |
|---|---|
TELENOW_API_KEY | The key to use, instead of a saved login. It never takes a saved login's server: for a server other than https://api.telenow.ai, set TELENOW_BASE_URL or --base-url too. |
TELENOW_BASE_URL | The server, like --base-url. |
TELENOW_PROFILE | The saved login to use, like --profile. |
TELENOW_CONFIG | Where the config file is. |
TELENOW_TIMEOUT_MS | How long one request may take, in milliseconds (default 30000). |
The CLI reads no configuration from the current folder, so a cloned repository cannot point your saved key at another server.
Output
-
JSON on stdout. Most lists show a short view of each row;
--fullshows whole rows. Secrets in an agent always come back masked. -
--fields a,b.cprints only these fields, picked from the whole row; a dot reaches into an object. Pass it in scripts and from AI assistants to keep the output small:telenow calls list --fields id,status,duration_sec -
--output tableis for people.--output rawprints the server's whole answer, with no projection. -
Transcripts and diffs print as plain text.
-
Notes go to stderr as JSON lines, and
--quiethides them. A list with more pages says so there, for example{"more":{"total":…,"nextOffset":…}}; add--offset <n>or--all.
Errors and exit codes
An error is one JSON line on stderr:
{"error":{"message":"not signed in (no profile 'default'): run `telenow login`, or set TELENOW_API_KEY"}}
When the server answered, the error also carries its status and code, and any details the server sent (for example currentVersion on a conflict). Branch on the exit code and code, not on the message text.
| Exit | Meaning |
|---|---|
0 | OK |
1 | Bad command or input, or a check that refused it (for example needs_yes or invalid_prompt). Any other 4xx from the server is 1 too. |
2 | Not signed in, or the key's role may not do this (401 / 403) |
3 | Not found (404) |
4 | Conflict (409): someone changed it since you read it — or the dashboard holds an unpublished draft |
5 | Rate limited (429) — see Rate limits & quotas |
6 | Server error (5xx) |
7 | Network: the server could not be reached, or did not answer in time |
Reads are retried twice on 429, 502, 503, 504 and network errors before the CLI gives up. Changes are never retried on their own; see Safety.
Edit an agent's prompt in files
Keep an agent's live prompt (and, for a flow agent, its flow) in local files, change it with any editor, and push it back only if nobody else changed the agent meanwhile.
telenow prompts pull <agent-id> # writes .telenow/agents/<agent-id>/
# edit .telenow/agents/<agent-id>/prompt.md (and flow.json)
telenow prompts diff <agent-id> # what your files change on the server
telenow prompts push <agent-id> --dry-run # check it with the server, save nothing
telenow prompts push <agent-id>
pull writes these files under .telenow/agents/<agent-id>/ (--dir <path> for another folder):
| File | Holds |
|---|---|
prompt.md | The live system prompt (for a flow agent, its shared prompt) |
flow.json | The live flow graph — flow agents only |
agent.json | Which agent, server and version you pulled, and a hash of each file |
Nothing else is written: no provider settings, keys or tokens. When the .telenow folder is created it gets a .gitignore of *, so git ignores it; delete that file to commit your prompts. pull never overwrites local edits that were not pushed: it stops with exit 1, and pull --force throws them away.
prompts diff prints a unified diff; --format json gives the changed fields.
What push checks
- Someone changed the agent since your pull? Push stops with exit
4,agent_version_conflict, naming your version and the server's. Runprompts diffto see the difference andprompts pull --forceto start again from the server's copy.push --forceoverwrites the other change; use it only when you mean to. - Nothing changed? It says so and stops.
- The dashboard holds an unpublished draft of this agent? Push stops with exit
4,dashboard_draft. Publish or discard the draft in the dashboard first.--ignore-draftpushes anyway, but the dashboard's next Publish writes the draft over your push. - The server checks the changed prompt and flow — the same checks as Check a prompt before you save and Validate before you save. Errors stop the push with exit
1,invalid_prompt, and nothing is saved. Warnings print on stderr. - It saves with
PUT /api/agents/{id}andIf-Match: "<the version you pulled>", sending only what changed. If someone saves in the moment between, the server refuses with409instead of overwriting their change. - It records the new version, so your next push builds on this one.
--dry-run stops after step 4 and prints the request it would send. Files pulled from one server are never pushed to another: diff and push stop with exit 1 and name both servers.
Other agent settings
telenow agents update <agent-id> --file patch.json
- The file holds only the fields to change, in camelCase (
systemPrompt,ttsVoice). Bothagents createandagents updatewarn about a snake_case key, which the server ignores. Every field: Agent field reference. - The server replaces
metadatawhole, soupdatemerges yourmetadatakeys over the agent's current ones, under a version check.--replace-metadatasends yours as the wholemetadata. --expected-version <n>refuses if the agent is no longer at that version.- Provider and model ids are checked against the platform's catalog before
agents createoragents updatesends anything. This coversllmProvider,llmModel,sttProvider,ttsProvider, and the model insttConfig/ttsConfig. The server would save a misspelt id, and it would fail only when a call starts. Instead the CLI stops with exit1andnot_in_catalog, naming the closest ids.telenow models listshows them all (--kind llm|stt|tts|…,--provider,--fullfor prices and limits).--skip-catalog-checksends it anyway. telenow flows validate --file flow.jsonchecks a flow graph with the server, as calls will, and saves nothing.
Commands
| Group | Commands | API reference |
|---|---|---|
| Agents | agents list, get, create, update, delete, versions, diff, restore, stats | Agents, Agent versioning |
| Prompts | prompts pull, diff, push | above |
| Flows | flows validate | Flow agents API |
| Models | models list | Catalog |
| Calls | calls list, get, transcript, cost, insights, trace, recording, live, export, export-columns; call place | Sessions & calls, Recordings |
| Chat | chat <agent-id>, chat send, messages, end | Chat API |
| Campaigns | campaigns list, get, create, add-targets, start, pause, cancel, calls, results, analytics | Campaigns |
| Knowledge bases | kb list, get, create, add-doc, docs, attach, attached, detach, delete | Knowledge bases |
| Voices | voices list, clones | Getting a provider's voices |
| Numbers | numbers list, get, search, providers, requirements, buy, release, assign, unassign, default-outbound, inbound-action, renewal-risk, reclaimable, reclaim | Phone numbers |
| Do-not-call | dnc list, add, import, remove | Do-Not-Call (DNC) |
| Webhooks | webhooks list, create, test, deliveries, replay, disable | Webhook management, events |
| Analytics | analytics | Call analytics |
| Sub-accounts | sub-accounts list, get, usage, create, update, suspend, resume, rotate-key, close | Sub-accounts |
| Your login | login, logout, whoami, profiles list, profiles use | Authentication |
Agents
agents list— filter with--search <text>,--status draft|published(draft= has unpublished dashboard changes),--kind flow|singleand--active true|false. Page with--limit,--offsetor--all.agents get <agent-id>— one agent, secrets masked.agents create --file agent.json— camelCase fields;--file -reads stdin.agents delete <agent-id>— asks first.agents versions <agent-id>lists saved versions, newest first;agents diff <agent-id> <version>shows what one version changed;agents restore <agent-id> <version>makes it live again, saved as a new version.agents stats <agent-id>— call totals and average latencies.
Calls
telenow calls list --agent <agent-id> --from 2026-11-01 --fields id,status,duration_sec
telenow calls transcript <call-id>
calls list— newest first. Filter with--agent,--status(for exampleactive,ended),--from/--to(YYYY-MM-DD) and--number(either side of the call; comma-separated for several).--sort newest|oldest|longest|shortest.- The days in
--from/--toare read in your computer's time zone, or the one you pass as--tz Europe/London. The server would otherwise use its own. calls get <call-id>— one call with its transcript.calls transcript <call-id>— one line per turn,<role>: <text>; when a call was handed between agents, the speaking agent's name follows the role. It is the cheapest way to read a call.--format jsongives the turns as the server sends them.calls live— how many calls are going on now, per agent (--agentfor one).
Find out why a call went wrong
telenow calls transcript <call-id>
telenow calls cost <call-id>
telenow calls insights <call-id> --generate
telenow calls trace <call-id> --out trace.json
telenow calls recording <call-id> --out call.wav
calls cost— what the call was charged, line by line (money as decimal strings). It issettled: falsefor a few minutes after the call ends. The CLI tells that apart from a call still going and from an unknown call (exit3).calls insights— the summary, sentiment, topics and word counts. They exist only once made:--generatemakes them now from the transcript. Telenow pays for that, not your organization; it needs anowner,adminordeveloperkey.calls trace— the call's debug timeline, when the agent's debug mode recorded one. It needs anowneroradminkey. It prints a summary: whether the call was traced, how many events, whether the trace hit its limit (the end of the call is then missing), and why there is none. A trace can be several MB, so--out <file>saves all of it.calls recording— lists the call's recordings;--out <file>saves one with your key. A transferred call has two (the part after the transfer is markedtransfer_bridge), and you pick one with--recording <id>. The real type (audio/wav,audio/mpeg) is in the output, whatever the file name.calls export --out calls.csv— the calls as CSV. It takes the same filters ascalls list, plus--channel,--disposition,--campaignand--call-mode.--columns a,bpicks the columns, andcalls export-columnslists them. With no dates it covers the last 30 days. The server stops at a row limit, and the output says so (truncated, with the rows kept and the total).
Chat with an agent
telenow chat <agent-id> # a conversation in your terminal
telenow chat send <agent-id> --message "Where is my order?" # one turn, for scripts
- A chat runs the agent's tools for real: its HTTP tools, installed apps and dataset lookups. The CLI names them first.
- An agent whose follow-up calls are on for chat can phone people from a chat, so the CLI asks first (
--yeswithout a terminal). - The session is always ended for you.
chat <agent-id>ends it on/end, Ctrl-D or Ctrl-C.chat sendends a new session right after its reply, unless you pass--keep. An open session keeps counting until it has been idle long enough to end itself. - To hold a conversation from a script, start with
--keep, send each next message with--session <id>, add--endto the last one (or runtelenow chat end <id>), and read it back withchat messages <id> --format text. - When a reply offers options, type a number in the terminal, or pass
--option <id>tochat send. - The CLI chats as an id made from your login, never your name.
--identifier <id>chats as one of your customers; use the same one on every message of that session. --var name=valuesets variables on the first message only. A session the server has ended answers exit1withSESSION_EXPIRED: start a new one without--session.
API: Chat API.
Place a call
telenow call place --agent <agent-id> --to +919876543210 --dry-run
telenow call place --agent <agent-id> --to +919876543210
A real call that costs money. The CLI names the number, the agent and the caller ID, then asks first.
- Numbers are written
+country code, then the number. --from <+number>or--from-number-id <id>picks the caller ID from your organization's numbers; by default the server picks.--var name=valuesets a call variable (repeat for more);--first-response <text>sets the agent's opening line for this call.--machine-detection <mode>—hangup/drop, oronfor voicemail detection.--queuelets the server dial when calling is allowed, with calling hours checked — prefer it for customers. With--queue,--retries <n>sets the attempts in all.
API: Place an outbound call and Queue calls instead of dialing now.
Campaigns
telenow campaigns create --name "November renewals" --agent <agent-id> \
--window 09:30-18:00 --timezone Asia/Kolkata
telenow campaigns add-targets <campaign-id> --file people.csv
telenow campaigns start <campaign-id>
telenow campaigns get <campaign-id>
telenow campaigns results <campaign-id> --all
campaigns createalways makes a draft; nothing dials untilstart. Set it with--name,--agent,--concurrency,--window HH:MM-HH:MM,--timezone(IANA, defaultUTC),--from/--from-number-idand--max-attempts, or from--file(JSON, camelCase; flags override it).campaigns add-targets --filetakes a.csvwith aphone_numbercolumn, or a.jsonlist of{phoneNumber, id?, variables?}. The file is checked before anything is sent. JSON goes in batches, and rows are deduplicated byidwithin--source(defaultcli): give each person anid, and a re-run adds nobody twice. Adding people to a running or completed campaign gets them called, so it asks first.start,pause(startresumes it) andcancel(for good; people not yet called are skipped) each read the campaign back and report its real status.startandcancelask first.campaigns getalso says why a campaign is not dialling, if it is not.campaigns callslists each person and where they are (--status pending|dialing|completed|failed|skipped).campaigns resultsgives the outcome per person (--outcome,--since,--cursor,--all).campaigns analyticsgives totals, failure reasons and best hours.
Knowledge bases
telenow kb create --name "Pricing FAQ"
telenow kb add-doc <kb-id> --file pricing.pdf
telenow kb docs <kb-id>
telenow kb attach <kb-id> --agent <agent-id>
kb add-doctakes one of--file(PDF, DOCX, TXT, MD, CSV, JSON or XLSX),--url(the server fetches the page) or--text-file(added as it is; needs--title). It prints a short view;--fullincludes the document's text.kb docsshows whether each document is ready; wait forembeddedbefore relying on it.kb attach <kb-id> --agent <agent-id> --doc <document-id>(repeat--docfor more) lets the agent answer from those documents only.--wholewidens it back to the whole base. Without--doc, a new base is attached whole. One already limited to some documents is refused rather than silently left as it was. Each run reads the scope back and prints it.kb attached --agent <agent-id>lists the bases an agent answers from, withdocument_ids(nullmeans the whole base).kb detach <kb-id> --agent <agent-id>stops an agent using it; the knowledge base stays.kb deleteasks first, and agents using it lose it.
Voices
telenow voices list --provider elevenlabs --language hi --gender female
voices list --provider <provider>lists a provider's voices, to pick an agent'sttsVoice. Narrow it with--model,--language(hi,hi-INorHindi),--genderand--limit.voices clones --provider <provider>lists your organization's cloned voices.
Phone numbers
telenow numbers search --country US --pattern 415 --match starts
telenow numbers buy +14155550123 --country US --pattern 415 --match starts
telenow numbers assign <number-id> --agent <agent-id>
numbers listlists your organization's numbers (--include-releasedadds released ones);numbers get <number-id>shows one with its price.numbers search --country <CC>asks a carrier what it has for sale, and buys nothing. Narrow it with--provider,--pattern,--match starts|contains|ends,--typeand--region.numbers providerslists the carriers open to you.numbers buy <+number>spends money: the setup fee and the first month now, then every month until you release it.- It runs the search again, with the same options as the search that showed the number, and buys only if that exact number is still offered.
- The price comes from the carrier, never from anything you type.
- It shows the price in the carrier's currency, with USD beside it, then asks first.
- A retry with the same
--idempotency-keybuys once. - In a regulated country, pass an accepted compliance application with
--compliance <id>;numbers requirements --country <CC> --type <t> --end-user <type>lists the documents a carrier needs. Sending those documents stays in the dashboard.
numbers release <number-id>cannot be undone: the carrier takes the number back and nothing is refunded. A number on your own carrier account stays yours. It asks first.numbers assign <number-id> --agent <agent-id>makes an agent answer the number.- Assigning also takes the number from any agent that had it, and clears its default-outbound flag and its inbound action, so the CLI asks first when any of that would happen.
- It sends nothing when the number is already that agent's.
numbers unassignasks first.
numbers default-outbound <number-id> [--off]makes the number its agent's caller ID for outbound calls.numbers inbound-action <number-id> --action <a>(or--clear) sets what a number with no agent does with a call.numbers renewal-riskshows renewals ahead and whether your balance covers them.numbers reclaimablelists numbers lost for non-payment that can still be bought back, andnumbers reclaim <number-id>buys one back at today's price, asking first.
Do-not-call, webhooks and analytics
dnc add <number> --reason <text>— never call this number; queued calls to it are dropped.dnc import --fileadds many (one number per line, or CSVnumber,reason).dnc list --search <text>.dnc remove <entry-id>allows calls again, so it asks first.webhooks create --url https://… --events call.ended,call.analyzedsends your events to a URL. Event names are checked against the server's own list (Webhook events); the default is all of them.--agentlimits it to one agent;--include-recordingand--include-transcriptadd those to call events. The signing secret is shown once — store it then (verify signatures).webhooks test <webhook-id> --event <type>sends one sample event now.webhooks deliveries <webhook-id>lists what was sent and how it went (--fulladds payloads and response bodies).webhooks disableasks first.webhooks replay <webhook-id> <delivery-id>sends a past event to your endpoint again. It carries the original data, and your endpoint may act on it twice. So it asks first, naming the event, when it happened and your endpoint's host, but never the data. It needs anowneroradminkey, and it shares an hourly budget withwebhooks test.analytics— call numbers per day and per agent, for the last 30 days by default.--from/--to(YYYY-MM-DD, UTC) and--agent.
Sub-accounts
telenow sub-accounts create --name "Acme Clinic" --monthly-limit 50 \
--key-role admin --save-profile acme
telenow whoami --profile acme
Every sub-account command needs an owner or admin key, reads included.
- A sub-account spends from your wallet. So
createneeds a spend limit (--monthly-limitand/or--total-limit, in USD) or--no-limitsaid out loud, and a--key-role(admin,developerorviewer).--max-concurrentcaps its calls.--external-id <your-id>makes a retried create safe: a second one with the same id is refused, not made twice. - Its key's secret is shown only once, so you say where it goes before anything is made:
--save-profile <name>: a login profile, used with--profile <name>. It never becomes your current login.--key-file <path>: a new file, readable only by you.--show-key: prints it, for a secret store fed from a pipe.- Nothing else prints the secret,
--output rawincluded. Create and rotate send no idempotency key, so the server never keeps the secret for a retry.
sub-accounts list(--status,--external-id),getandusage(--from/--toas instants, for example2026-11-01T00:00:00Z) show its limits, calls and spend.updatechanges its name, your id for it, or its limits (noneremoves a limit).suspendends its live calls at once, so it asks first, saying how many;resumeundoes it.rotate-keymakes a new key, and by default every key it has now stops working, so it asks first.--keep-existingkeeps them.closecannot be undone: all its keys are revoked and its campaigns paused. It asks first, and is refused while the sub-account still holds phone numbers.
Safety
Asks first. The CLI asks before anything that dials, spends, deletes or cannot be undone:
- calls and campaigns:
call place,campaigns start,campaigns cancel, andcampaigns add-targetsto a running or completed campaign; - a chat with an agent that can phone people;
- numbers:
numbers buy,releaseandreclaim, andnumbers assign/unassignwhen they move a number; dnc remove,webhooks disableandwebhooks replay;- sub-accounts:
sub-accounts suspend,rotate-keyandclose; - every delete.
At a terminal, the CLI says exactly what will happen (who is called, from which number, by which agent; what it costs; what is deleted) and goes ahead only when you type yes. Without a terminal (a script, CI, an AI assistant) it refuses with exit 1 and needs_yes, unless you pass --yes:
{"error":{"message":"Remove do-not-call entry abc: its number can be called again. — add --yes to go ahead (there is no terminal to ask)","code":"needs_yes"}}
--dry-run, on every command that changes something on the server, prints the request it would send — method, path, headers and body, never the API key — and changes nothing. It may still read from the server, for example to find your organization.
Safe retries. Every create sends an Idempotency-Key (Idempotency). After a timeout, run the same command again with the same --idempotency-key <key>: the server does it once and returns the first answer, instead of a second call or a second campaign.
telenow call place --agent <agent-id> --to +919876543210 --idempotency-key renewal-0042 --yes
When the server answers a retry from its first answer, a note on stderr says nothing was done again. That answer is the first one as it was, so a field such as deduplicated still reads false.
There are two exceptions:
- File uploads (
kb add-doc --file, CSV targets anddnc import) go without the key, because the server refuses a keyed body over 5 MiB and documents may be larger.--idempotency-keywith--fileis refused. A re-run is still safe where it matters: a campaign skips numbers already pending, and the do-not-call import skips numbers already listed. sub-accounts createandrotate-keysend no key, so that the server never keeps a new key's secret for a retry. Use--external-idon a create instead.
Files you ask for (--out) are readable only by you, and an existing file is never replaced without --force. A download that stalls or ends early leaves nothing behind; a slow one that keeps moving is never cut off.
For AI coding assistants
telenow agent-instructions >> AGENTS.md
telenow agent-instructions prints how an AI coding assistant should use the CLI, to paste into AGENTS.md or CLAUDE.md; --path prints where the skill file is instead, for tools that load skill files. It tells the assistant to:
- run
telenow whoamifirst, and ask you to runtelenow login— never to ask for a key in the chat or put one on a command line; - pass
--fields, and read calls withcalls transcript; - change prompts with
prompts pull,diffandpush, and stop on a conflict or a dashboard draft instead of forcing it; - end every chat session it starts, and ask you before chatting with an agent that can phone people;
- ask you for a sub-account's spend limit and key role, and never use
--show-keyunless you ask, since the secret would land in the conversation; - run
--dry-runfirst, and add--yesonly after you have confirmed exactly who is called, what is bought, or what is started or deleted.
Shell completion
source <(telenow completion bash) # in ~/.bashrc
source <(telenow completion zsh) # in ~/.zshrc
telenow completion fish > ~/.config/fish/completions/telenow.fish
The script is generated from the installed version's own commands, app commands included.
App commands
The same package builds installable apps for the dashboard. Those commands are covered in the Build Apps docs:
| Command | What it does | Docs |
|---|---|---|
telenow app init <name> | Scaffold a new app | Build your first app |
telenow dev [--port N] | Live preview with hot reload | Run it locally |
telenow validate | Check telenow.app.json | Manifest reference |
telenow build | Validate, bundle and zip the app | Packaging & distribution |
Related
- Authentication — API keys and roles
- Overview & conventions — the REST API the CLI calls
- Rate limits & quotas
- MCP overview — your account from ChatGPT, Claude or Grok