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 telenow 0.7.0 or newer. telenow --help lists them under Account commands.
  • telenow <command> --help shows 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:

  1. It prints a code and a link, and opens the link in your browser.
  2. 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.
  3. 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). login prints when. After that, a command answers exit 2 with login_expired and the date, and you run telenow login again.
  • Over SSH, in a container or without a display, it prints the link instead of opening it (--no-browser asks 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 acme on any command. telenow profiles list shows the saved logins, and telenow profiles use <name> makes one the current login.
  • --base-url <url> points at another server (the default is https://api.telenow.ai). It must be https, except on localhost. A login saved with --base-url remembers its server.
  • telenow logout forgets a saved login on this computer. --revoke first 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

VariableWhat it does
TELENOW_API_KEYThe 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_URLThe server, like --base-url.
TELENOW_PROFILEThe saved login to use, like --profile.
TELENOW_CONFIGWhere the config file is.
TELENOW_TIMEOUT_MSHow 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; --full shows whole rows. Secrets in an agent always come back masked.

  • --fields a,b.c prints 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 table is for people. --output raw prints the server's whole answer, with no projection.

  • Transcripts and diffs print as plain text.

  • Notes go to stderr as JSON lines, and --quiet hides 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.

ExitMeaning
0OK
1Bad 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.
2Not signed in, or the key's role may not do this (401 / 403)
3Not found (404)
4Conflict (409): someone changed it since you read it — or the dashboard holds an unpublished draft
5Rate limited (429) — see Rate limits & quotas
6Server error (5xx)
7Network: 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):

FileHolds
prompt.mdThe live system prompt (for a flow agent, its shared prompt)
flow.jsonThe live flow graph — flow agents only
agent.jsonWhich 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

  1. Someone changed the agent since your pull? Push stops with exit 4, agent_version_conflict, naming your version and the server's. Run prompts diff to see the difference and prompts pull --force to start again from the server's copy. push --force overwrites the other change; use it only when you mean to.
  2. Nothing changed? It says so and stops.
  3. 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-draft pushes anyway, but the dashboard's next Publish writes the draft over your push.
  4. 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.
  5. It saves with PUT /api/agents/{id} and If-Match: "<the version you pulled>", sending only what changed. If someone saves in the moment between, the server refuses with 409 instead of overwriting their change.
  6. 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). Both agents create and agents update warn about a snake_case key, which the server ignores. Every field: Agent field reference.
  • The server replaces metadata whole, so update merges your metadata keys over the agent's current ones, under a version check. --replace-metadata sends yours as the whole metadata.
  • --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 create or agents update sends anything. This covers llmProvider, llmModel, sttProvider, ttsProvider, and the model in sttConfig / ttsConfig. The server would save a misspelt id, and it would fail only when a call starts. Instead the CLI stops with exit 1 and not_in_catalog, naming the closest ids. telenow models list shows them all (--kind llm|stt|tts|…, --provider, --full for prices and limits). --skip-catalog-check sends it anyway.
  • telenow flows validate --file flow.json checks a flow graph with the server, as calls will, and saves nothing.

Commands

GroupCommandsAPI reference
Agentsagents list, get, create, update, delete, versions, diff, restore, statsAgents, Agent versioning
Promptsprompts pull, diff, pushabove
Flowsflows validateFlow agents API
Modelsmodels listCatalog
Callscalls list, get, transcript, cost, insights, trace, recording, live, export, export-columns; call placeSessions & calls, Recordings
Chatchat <agent-id>, chat send, messages, endChat API
Campaignscampaigns list, get, create, add-targets, start, pause, cancel, calls, results, analyticsCampaigns
Knowledge baseskb list, get, create, add-doc, docs, attach, attached, detach, deleteKnowledge bases
Voicesvoices list, clonesGetting a provider's voices
Numbersnumbers list, get, search, providers, requirements, buy, release, assign, unassign, default-outbound, inbound-action, renewal-risk, reclaimable, reclaimPhone numbers
Do-not-calldnc list, add, import, removeDo-Not-Call (DNC)
Webhookswebhooks list, create, test, deliveries, replay, disableWebhook management, events
AnalyticsanalyticsCall analytics
Sub-accountssub-accounts list, get, usage, create, update, suspend, resume, rotate-key, closeSub-accounts
Your loginlogin, logout, whoami, profiles list, profiles useAuthentication

Agents

  • agents list — filter with --search <text>, --status draft|published (draft = has unpublished dashboard changes), --kind flow|single and --active true|false. Page with --limit, --offset or --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 example active, 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 / --to are 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 json gives the turns as the server sends them.
  • calls live — how many calls are going on now, per agent (--agent for 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 is settled: false for a few minutes after the call ends. The CLI tells that apart from a call still going and from an unknown call (exit 3).
  • calls insights — the summary, sentiment, topics and word counts. They exist only once made: --generate makes them now from the transcript. Telenow pays for that, not your organization; it needs an owner, admin or developer key.
  • calls trace — the call's debug timeline, when the agent's debug mode recorded one. It needs an owner or admin key. 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 marked transfer_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 as calls list, plus --channel, --disposition, --campaign and --call-mode. --columns a,b picks the columns, and calls export-columns lists 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 (--yes without a terminal).
  • The session is always ended for you. chat <agent-id> ends it on /end, Ctrl-D or Ctrl-C. chat send ends 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 --end to the last one (or run telenow chat end <id>), and read it back with chat messages <id> --format text.
  • When a reply offers options, type a number in the terminal, or pass --option <id> to chat 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=value sets variables on the first message only. A session the server has ended answers exit 1 with SESSION_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=value sets a call variable (repeat for more); --first-response <text> sets the agent's opening line for this call.
  • --machine-detection <mode> — hangup / drop, or on for voicemail detection.
  • --queue lets 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 create always makes a draft; nothing dials until start. Set it with --name, --agent, --concurrency, --window HH:MM-HH:MM, --timezone (IANA, default UTC), --from / --from-number-id and --max-attempts, or from --file (JSON, camelCase; flags override it).
  • campaigns add-targets --file takes a .csv with a phone_number column, or a .json list of {phoneNumber, id?, variables?}. The file is checked before anything is sent. JSON goes in batches, and rows are deduplicated by id within --source (default cli): give each person an id, and a re-run adds nobody twice. Adding people to a running or completed campaign gets them called, so it asks first.
  • start, pause (start resumes it) and cancel (for good; people not yet called are skipped) each read the campaign back and report its real status. start and cancel ask first.
  • campaigns get also says why a campaign is not dialling, if it is not.
  • campaigns calls lists each person and where they are (--status pending|dialing|completed|failed|skipped). campaigns results gives the outcome per person (--outcome, --since, --cursor, --all). campaigns analytics gives 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-doc takes 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; --full includes the document's text.
  • kb docs shows whether each document is ready; wait for embedded before relying on it.
  • kb attach <kb-id> --agent <agent-id> --doc <document-id> (repeat --doc for more) lets the agent answer from those documents only. --whole widens 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, with document_ids (null means the whole base).
  • kb detach <kb-id> --agent <agent-id> stops an agent using it; the knowledge base stays. kb delete asks 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's ttsVoice. Narrow it with --model, --language (hi, hi-IN or Hindi), --gender and --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 list lists your organization's numbers (--include-released adds 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, --type and --region. numbers providers lists 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-key buys 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 unassign asks 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-risk shows renewals ahead and whether your balance covers them. numbers reclaimable lists numbers lost for non-payment that can still be bought back, and numbers 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 --file adds many (one number per line, or CSV number,reason). dnc list --search <text>. dnc remove <entry-id> allows calls again, so it asks first.
  • webhooks create --url https://… --events call.ended,call.analyzed sends your events to a URL. Event names are checked against the server's own list (Webhook events); the default is all of them. --agent limits it to one agent; --include-recording and --include-transcript add 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 (--full adds payloads and response bodies). webhooks disable asks 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 an owner or admin key, and it shares an hourly budget with webhooks 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 create needs a spend limit (--monthly-limit and/or --total-limit, in USD) or --no-limit said out loud, and a --key-role (admin, developer or viewer). --max-concurrent caps 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 raw included. Create and rotate send no idempotency key, so the server never keeps the secret for a retry.
  • sub-accounts list (--status, --external-id), get and usage (--from / --to as instants, for example 2026-11-01T00:00:00Z) show its limits, calls and spend. update changes its name, your id for it, or its limits (none removes a limit).
  • suspend ends its live calls at once, so it asks first, saying how many; resume undoes it.
  • rotate-key makes a new key, and by default every key it has now stops working, so it asks first. --keep-existing keeps them.
  • close cannot 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, and campaigns add-targets to a running or completed campaign;
  • a chat with an agent that can phone people;
  • numbers: numbers buy, release and reclaim, and numbers assign / unassign when they move a number;
  • dnc remove, webhooks disable and webhooks replay;
  • sub-accounts: sub-accounts suspend, rotate-key and close;
  • 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 and dnc import) go without the key, because the server refuses a keyed body over 5 MiB and documents may be larger. --idempotency-key with --file is 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 create and rotate-key send no key, so that the server never keeps a new key's secret for a retry. Use --external-id on 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 whoami first, and ask you to run telenow login — never to ask for a key in the chat or put one on a command line;
  • pass --fields, and read calls with calls transcript;
  • change prompts with prompts pull, diff and push, 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-key unless you ask, since the secret would land in the conversation;
  • run --dry-run first, and add --yes only 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:

CommandWhat it doesDocs
telenow app init <name>Scaffold a new appBuild your first app
telenow dev [--port N]Live preview with hot reloadRun it locally
telenow validateCheck telenow.app.jsonManifest reference
telenow buildValidate, bundle and zip the appPackaging & distribution