01 · Start
From install to local search.
Authenticate once in an interactive terminal, discover a stable chat identifier, then choose whether to read online or build a local message history.
-
Install the CLI
Install it globally from npm so
tgis available in any directory.npm install -g @will-17173/telegram-cli tg --version -
Add an account
The login flow may ask for a phone number, Telegram code, and 2FA password. Run it directly in a TTY; never pipe credentials.
tg account add tg status tg whoami -
Discover a chat
Names and usernames work interactively. For scripts, prefer the numeric ID returned by structured output. Chat types are
user,group,supergroup,channel, andunknown.tg chats --json tg chats --type supergroup tg info @team --json -
Sync, then search locally
syncstores new messages in the selected account’s SQLite database. A chat not yet present locally is capped at 500 messages on its firstsync, even when a larger limit is requested.tg sync @team tg search "release" --chat @team tg search "release" --account work --json
02 · Boundaries
Five scopes, one command surface.
A command’s scope tells you whether it needs Telegram, changes local state, or mutates remote state. Check the scope before choosing a command for a script.
Read from Telegram
Connects with the selected session and returns current server state. Message results are not inserted into SQLite.
inbox · read · search-online
Telegram → SQLite
Fetches Telegram history and stores it in the selected account’s local message database for later queries.
history · sync · sync-all · refresh
Read local SQLite
Works without Telegram. Freshness is exactly the last data you synchronized for that account.
search · recent · stats · export
Change Telegram
Mutates messages, notifications, folders, or group state. The local write-access gate must be enabled.
send · edit · delete · group actions
Write a Markdown archive
Reads Telegram history into incremental Markdown and optional media files. It does not populate SQLite.
archive
| Task | Network | Destination | Key behavior |
|---|---|---|---|
| Online read | Telegram | Output only | read and search-online do not populate SQLite. |
| Synchronization | Telegram | SQLite | history backfills older messages from the local oldest row; sync commands update from the local newest row. |
| Local analysis | None | Output or export file | Account databases stay isolated; synchronize first when freshness matters. |
| Archive | Telegram | Markdown + media files | Does not populate SQLite; later runs resume from archive state. |
| Remote write | Telegram | Telegram | Blocked before mutation when write access is off. |
03 · Recipes
Everyday workflows.
These recipes keep the account explicit and use structured output where a machine will consume the result.
Triage without storing
List unread dialogs without marking them read, inspect one chat, or search Telegram globally.
tg inbox --limit 50 --account work --json
tg read @ops --since 2h --account work --json
tg search-online "incident" --until 2026-07-14T10:00:00+08:00 --json
Relative time bounds use s, m, h, d, or w. Absolute timestamps require an ISO 8601 zone.
Build a searchable memory
Backfill a bounded number of messages, increment one chat, or refresh many chats before local analysis.
tg history @ops --limit 1000 --delay 1 --account work
tg sync @ops --limit 5000 --account work
tg refresh --max-chats 20 --delay 1 --account work --json
tg search "incident" --chat @ops --hours 168 --json
refresh can be top-level successful while individual chats fail; inspect data.failures. sync-all omits failure details, so prefer refresh when partial failures matter.
Analyze without reconnecting
Query the selected account’s SQLite database by keyword, sender, time, regular expression, or aggregate.
tg search "error|failed" --regex --account work --json
tg recent --sender alice --hours 6 --limit 100
tg top --chat @ops --hours 168
tg timeline --chat @ops --by hour --json
An empty result may simply mean the correct chat or account has not been synchronized yet.
Send, then synchronize
Check the safety gate, send text or a media group, then sync if the sent message must be available to local queries.
tg config write-access status --json
tg send @team "Release is ready" --account work --json
tg send @team "Build artifacts" --file ./app.zip --file ./checks.txt
tg sync @team --account work
send has no confirmation prompt and does not insert its result into SQLite. Repeat --file to preserve media-group order.
Listen continuously
Listen globally or to selected chats. Interactive TTY mode uses Ink; plain mode emits streaming text.
tg listen @team --persist --retry-seconds 5
tg listen @team @ops --send-to @team
tg listen --no-interactive --auto-download --no-media
--persist means reconnect automatically; it is not a database option. listen exposes no JSON, YAML, or Markdown format flags. Auto-download saves normalized attachments under ~/Downloads/telegram-cli, even when --no-media hides rendered media rows.
Archive to Markdown
Select explicit chats or --all, never both. The first unbounded run defaults to the preceding seven days.
tg archive @team --download-media --account work
tg archive @team --since 30d --until 2026-07-14T00:00:00Z
tg archive --all --full --output ./telegram-archive --json
Later runs are incremental. --rebuild replaces chat files; --download-media retries referenced files that are still missing. Archive files do not populate SQLite. Custom --output directories are not reset automatically.
04 · Identity
Accounts and configuration.
Each registered account owns an isolated Telegram session and SQLite database. API credentials and proxy configuration are shared across registered accounts.
Authenticate a new account
The first account becomes current. Adding later accounts does not switch the default.
tg account add
Keep scripts explicit
Use --account for one invocation without changing the current account.
tg chats --account work --json
Retain local history
Logout invalidates the Telegram session but keeps registration and SQLite messages.
tg account logout work --yes
Delete local account data
Removal deletes the account’s local session and data; --force skips confirmation.
tg account remove work --force
Account lifecycle
tg account list --json
tg account current
tg account switch work
tg account logout work --yes
tg account login work
tg account remove work --force
account login reauthenticates an existing logged-out registration and replaces its session safely while retaining its database. Login and ownership transfer require interactive input; adding --json does not make authentication non-interactive. If a failed replacement reports recovery_path or recovery_paths, preserve those files during recovery.
Credential and proxy precedence
| Setting | One-command override | Persisted setting | Fallback |
|---|---|---|---|
| API credentials | TG_API_ID + TG_API_HASH |
tg config set --api-id … --api-hash … |
Restricted built-in credentials |
| Telegram proxy | TG_PROXY |
tg config set --proxy <url> |
Direct connection |
| Storage root | DATA_DIR=/path/to/data |
OS-specific default | — |
Set TG_API_ID and TG_API_HASH together; supplying only one is an error. Supported proxy schemes are SOCKS4, SOCKS5, HTTP, HTTPS, and MTProxy. The proxy applies to account login and every Telegram-backed command.
tg config set --proxy socks5://127.0.0.1:1080
TG_PROXY=http://127.0.0.1:8080 tg status
tg config list --json
tg config list --show-secrets
Local layout
DATA_DIR changes the root for configuration, the account registry, sessions, databases, and default archives. Important paths are:
config.json
accounts.json
accounts/<name>/session
accounts/<name>/messages.db
accounts/<name>/archive/
05 · Reference
All 33 top-level commands.
Examples show the representative route, not every option. Run tg <command> --help for the installed version’s exact arguments and flags.
| Command | Scope | Purpose and representative syntax |
|---|---|---|
search |
Local | Search stored messages by keyword, chat, sender, time, or regex.tg search "release" --chat @team --hours 168 --json |
recent |
Local | Show recently stored messages; defaults to 24 hours and 50 results.tg recent --sender alice --hours 6 --limit 100 |
stats |
Local | Summarize local message and chat statistics.tg stats --account work --json |
top |
Local | Rank the most active stored-message senders.tg top --chat @team --hours 168 --limit 20 |
timeline |
Local | Aggregate local message activity by day or hour.tg timeline --chat @team --by hour --json |
today |
Local | Show messages stored today, optionally for one chat.tg today --chat @team |
filter |
Local | Filter stored messages using space-separated keywords.tg filter "release urgent" --hours 24 --json |
archive |
Files | Build incremental Markdown archives from Telegram history; SQLite is unchanged.tg archive @team --download-media --json |
export |
Local | Export one chat’s stored messages to text, JSON, or YAML.tg export @team --format json --output ./messages.json |
purge |
Local | Delete one chat’s locally stored messages only; requires confirmation.tg purge @team --yes --json |
data |
Local reset | Manage account-scoped local data. Breaking media-schema upgrades require deleting old databases/default archives, then syncing again.tg data reset --yes --json |
status |
Live | Check whether the selected Telegram account is authenticated.tg status --account work --json |
whoami |
Live | Show identity information for the authenticated account.tg whoami --account work --json |
chats |
Live | List Telegram dialogs and filter by user, group, supergroup, or channel.tg chats --type channel --json |
history |
Persist | Fetch and store older messages before the local oldest row; default limit 1000.tg history @team --limit 1000 --delay 1 |
sync |
Persist | Incrementally store new messages for one chat; default limit 5000.tg sync @team --delay 1 --json |
sync-all |
Persist | Incrementally synchronize multiple dialogs; projected output omits failure details.tg sync-all --max-chats 20 --limit 5000 |
refresh |
Persist | Bulk-sync dialogs and retain per-chat failures in data.failures.tg refresh --max-chats 20 --json |
info |
Live | Show metadata for a Telegram chat.tg info @team --account work --json |
send |
Write | Send text, files, a captioned media group, or a reply; no confirmation prompt.tg send @team "Hello" --file ./photo.jpg --json |
edit |
Write | Edit a Telegram message’s text.tg edit @team 814 "Updated text" --json |
delete |
Write | Delete one or more Telegram messages.tg delete @team 814 815 --json |
download |
Files | Download all media from one message, a selected attachment, a grouped album, a range, a date, or an entire chat. Attachment numbers are one-based; albums flatten by message ID and local index. Stable media errors include attachment_changed and media_access_denied.tg download @channel 42 --attachment 2 |
listen |
Live stream | Stream incoming messages interactively or as plain text; no finite-output flags. Persisted structured rows use ordered lowercase attachments[].tg listen @team --persist --auto-download |
contact |
Live | List Telegram contacts or resolve one by ID, username, or phone.tg contact info @alice --json |
inbox |
Live | List unread dialogs without marking their messages read.tg inbox --limit 50 --markdown |
read |
Live | Read a bounded online message range without persisting it.tg read @team --since 7d --until 2d --json |
search-online |
Live | Search Telegram globally or in one chat without storing results.tg search-online "incident" --chat @team --json |
dialog |
Live | Alternate family for inbox, read, online search, and managed groups.tg dialog search "incident" --chat @team --json |
group |
Inspect Manage | Inspect groups, members, and audit events, or run the 48 management actions below.tg group members @team --type admins --json |
folder |
Inspect Manage | List and inspect folders, or add/remove explicit chat membership.tg folder chat add 3 @team --json |
notification |
Inspect Manage | Inspect, mute, or unmute a chat’s notification settings.tg notification mute @team 8h --json |
account |
Registry Session | Add, list, select, log out, reauthenticate, or remove isolated accounts.tg account list --json |
config |
Local | Save credentials/proxy settings, inspect effective config, and control write access.tg config write-access status --json |
web |
Local | Start the local-only React management UI on 127.0.0.1 to browse stored messages and trigger read-only sync for the selected chat.tg web --port 8734 |
06 · Groups
Inspect first. Manage deliberately.
The group surface combines read-only inspection with generated actions for members, administrators, chat settings, invitations, forum topics, and messages.
Group and member state
tg group info @team --json
tg group list --admin --limit 100
tg group members @team --type admins --query alice --limit 50
tg group member info @team @alice --json
Audit events
tg group audit @team \
--user @alice \
--type member_invited \
--type invite_changed \
--limit 100 --json
group members defaults to type recent and 100 results, with a maximum of 200. Its seven types are recent, all, admins, banned, restricted, bots, and contacts. Telegram may return fewer rows than its reported total.
group audit requires administrator rights, defaults to 100 events, and accepts up to 500. Repeat --user for action authors and --type for event groups including chat/settings changes, member actions, admin changes, message changes, invites, topics, and other.
Generated action grammar
tg group <family> <action> <chat> [arguments] [options]
Every action below is part of the v0.6.1 catalog. “Confirm” actions reject execution without --yes. “Direct” writes have no built-in prompt, but still require authorization and enabled write access. The four actions labeled “Read” do not mutate Telegram.
member
Targets must be explicit @username values or numeric user IDs.
-
Direct
member add <chat> <users...>Add one or more members. -
Confirm
member kick <chat> <user>Remove a member. -
Confirm
member ban <chat> <user>Ban a member. -
Confirm
member unban <chat> <user>Lift a member ban. -
Confirm
member mute <chat> <user> [duration]Restrict sending temporarily or indefinitely. -
Confirm
member unmute <chat> <user>Restore member permissions. -
Confirm
member purge <chat> <user>Delete all messages from a member.
admin
Requires administrator rights; ownership transfer requires the creator role.
-
Confirm
admin promote <chat> <user> [permissions]Promote a member. -
Confirm
admin demote <chat> <user>Remove administrator privileges. -
Confirm
admin rank <chat> <user> <text...>Set a custom administrator title. -
Confirm
admin transfer-owner <chat> <user>Transfer ownership after a secure 2FA prompt.
chat
Capabilities vary: some settings require a supergroup, creator, or administrator role.
-
Direct
chat title <chat> <text...>Change the chat title. -
Direct
chat description <chat> <text...>Change the description. -
Direct
chat username <chat> <username>Change the public username. -
Direct
chat photo <chat> <path>Set a photo from a local file. -
Direct
chat slowmode <chat> <duration>Set slow mode; useoffto disable. -
Confirm
chat ttl <chat> <duration>Set message auto-delete. -
Direct
chat protect <chat> <enabled>Toggle content protection. -
Direct
chat join-requests <chat> <enabled>Toggle approval for new members. -
Direct
chat join-to-send <chat> <enabled>Require membership before posting. -
Confirm
chat default-permissions <chat> <permissions>Set default member rights. -
Direct
chat sticker-set <chat> <sticker|off>Set or remove the sticker set. -
Confirm
chat leave <chat>Leave the selected chat. -
Title + yes
chat delete <chat>Permanently delete the chat.
invite
Inspection is read-only; creation, changes, revocation, and join-request decisions are writes.
-
Read
invite list <chat>List invite links. -
Read
invite show <chat> <invite>Show invite details. -
Direct
invite create <chat> [options]Create a link with optional title, expiry, limit, or approval. -
Direct
invite edit <chat> <invite> [options]Edit an existing link. -
Confirm
invite revoke <chat> <invite>Revoke a link. -
Read
invite members <chat> <invite>List members who used a link. -
Direct
invite approve <chat> <user>Approve one join request. -
Direct
invite decline <chat> <user>Decline one join request. -
Confirm
invite approve-all <chat>Approve every pending request. -
Confirm
invite decline-all <chat>Decline every pending request.
For create/edit, options include --title, --expire, --limit, and --request-needed.
topic
Forum-only capabilities. Topic deletion also removes that topic’s messages.
-
Read
topic list <chat>List forum topics. -
Direct
topic create <chat> <title...>Create a topic. -
Direct
topic edit <chat> <id> <title...>Edit a topic title. -
Direct
topic close <chat> <id>Close a topic. -
Direct
topic reopen <chat> <id>Reopen a topic. -
Direct
topic pin <chat> <id>Pin a topic. -
Direct
topic unpin <chat> <id>Unpin a topic. -
Direct
topic reorder <chat> <ids...>Order pinned topics. -
Confirm
topic delete <chat> <id>Delete a topic and its messages. -
Direct
topic general-hidden <chat> <hidden>Toggle the general topic’s visibility.
message
Administrative message pinning and deletion inside the selected chat.
-
Direct
message pin <chat> <id>Pin a message. -
Direct
message unpin <chat> <id>Unpin one message. -
Confirm
message unpin-all <chat>Unpin every message. -
Confirm
message delete <chat> <ids...>Delete messages from the chat.
Use slash commands while listening
Type / in interactive listen to open one command menu. Reply to a message or run a group action without repeating the selected chat:
/reply <message-id> <content>
/member mute @alice 2h
Matching checks exact paths, prefixes, then ordered fuzzy matches. /rep and /rpy find /reply. /ban finds /member ban.
When listening to more than one chat, set --send-to <chat> before group actions. Permission and availability checks remain active. Risky actions open a confirmation modal.
07 · Machines
Structured output and exit behavior.
Use JSON or YAML envelopes for automation. Human tables and Markdown are designed to be read, not parsed.
Explicit flag
Choose exactly one format on a finite command.
--json · --yaml · --markdown
Environment default
An explicit flag wins over the environment.
OUTPUT=json|yaml|markdown|rich
Automatic default
TTY output is rich; non-interactive output defaults to YAML.
OUTPUT=markdown tg inbox
Stable success envelope
{
"ok": true,
"schema_version": "2",
"data": {}
}
Stable failure envelope
{
"ok": false,
"schema_version": "2",
"error": {
"code": "chat_not_found",
"message": "..."
}
}
Trust exit status
Every failure sets a nonzero process status. Successful finite output goes to stdout.
Read by code
Branch on error.code, not the human message. Inspect operation-specific error.details.
Know the stream
listen is unbounded and has no JSON, YAML, or Markdown flags; use --no-interactive for plain streaming text.
JSON/YAML structured failures are written to stdout in the requested format. Human-readable and Markdown failures go to stderr. Output-format conflicts also fail nonzero and use a stable YAML envelope on stdout. Do not combine output flags.
08 · Guardrails
Keep remote writes intentional.
The write-access switch is a local preflight gate for Telegram mutations. It does not replace explicit authorization, Telegram permissions, or command-specific confirmation.
Inspect, enable, or disable
When disabled, gated commands fail with write_access_disabled before connecting for the mutation.
tg config write-access status --json
tg config write-access off
tg config write-access on
| Operation | Write gate | Built-in confirmation | Important caveat |
|---|---|---|---|
send, edit, delete |
Required | None | Sending does not update SQLite. |
| Notification mute/unmute | Required | None | Omitted mute duration means forever. |
| Folder chat add/remove | Required | None | Prefer numeric folder IDs; titles can be ambiguous. |
| Group direct actions | Required | None | “No prompt” does not mean low impact. |
| Group risky actions | Required | --yes |
Chat deletion also needs exact --confirm-title. |
purge |
Not applicable | --yes |
Deletes local SQLite rows, not Telegram messages. |
What the gate does not block
Online reads, synchronization, history, archives, local queries, exports, local purge, account lifecycle, and configuration changes remain available. The gate is scoped only to remote mutations by message, notification, folder, and non-read-only group actions.
Protect credentials
Never commit .env, API values, authenticated sessions, proxy secrets, SQLite databases, or archive exports.
Respect rate limits
Large global searches, histories, and media archives can trigger FLOOD_WAIT. Respect the reported delay and keep request spacing.
Verify the target
Use numeric chat, folder, message, and user identifiers in automation to avoid ambiguous names.
Keep secrets interactive
Login codes, account 2FA, and ownership-transfer passwords belong only in secure TTY prompts—not arguments, environment values, or logs.
09 · Recovery
Troubleshoot by error code.
Structured errors are stable enough for branching. Human messages add context, but code—not message text—should select the recovery path.
account_required or account_not_found
Add the first account, or inspect registered names and correct --account. The first account becomes current automatically.
tg account add
tg account list --json
tg account switch <name>
account_logged_out or interaction_required
Local SQLite commands still work for a logged-out registration. Run login directly in an interactive terminal; never pipe the phone number, code, or 2FA password.
tg account login <name>
telegram_account_session_expired or AUTH_KEY_UNREGISTERED
The local session is no longer valid. Remove that registration and authenticate again. Removal deletes its local session/data, so preserve anything needed first.
tg account remove <name> --force
tg account add
account_identity_mismatch
The authenticated identity does not match the registered account. Stop, verify the intended account, and rerun interactive login for the correct name. Preserve any reported recovery paths.
chat_not_found or ambiguous_chat
For a local query, synchronize the chat into the same account database first. If a name matches more than one stored dialog, use the numeric chat ID from tg chats --json.
tg chats --account work --json
tg sync <numeric-chat-id> --account work
tg search "keyword" --chat <numeric-chat-id> --account work
contact_not_found
Telegram could not resolve the supplied numeric ID, @username, or phone number. Verify the value and remember that chats --type user lists private dialogs, while contact list lists contacts.
invalid_notification_duration
Use a positive integer with s, m, h, d, or w, or use forever. An omitted duration also means forever.
tg notification mute @team 8h
tg notification mute @team forever
folder_not_found, ambiguous_folder, or folder_operation_unsupported
List folders and use the returned numeric ID. If Telegram reports the operation unsupported, do not retry the same change automatically.
tg folder list --json
tg folder info 3 --json
archive_account_mismatch, archive_failed, or archive_partial_failure
An archive root belongs to one account. Use a separate output directory after an account mismatch. For partial failures, keep completed files, inspect completed/failed/warnings in structured details, and retry only failed work or missing media.
write_access_disabled
The local gate stopped a Telegram mutation before it connected for that write. Obtain authorization before enabling the gate, then obtain separate authorization for the specific mutation.
tg config write-access status --json
tg config write-access on
password_required, password_invalid, password_too_fresh, or session_too_fresh
Ownership transfer must be completed by the creator in an interactive TTY. Prompt again after missing/invalid credentials. For freshness errors, report Telegram’s wait details and do not retry early.
flood_wait or FLOOD_WAIT
Stop aggressive retries, respect the reported delay, retain page/chat delays, narrow the request, and configure personal API credentials if you are using the restricted defaults.