Login, sessions and profiles
A login has two parts:
- the app: an
api_idandapi_hashfrom my.telegram.org. They identify the program to Telegram.tgkeeps them in the OS keyring. - the session: what Telegram hands out once you log in. It is a file in the state directory, and it is as good as your password.
tg session start gets both. It asks questions, so run it in a terminal.
The app from my.telegram.org
Every user registers their own app. Telegram gives one app per phone number, so an app id is never
shared or built into tg. You need it once per profile; tg session start asks only when the profile
has none.
Two ways to get it:
--app browser(the default) opens my.telegram.org/apps. Log in there, create an app if you have none (any title and short name, platform Desktop), and pasteApp api_idandApp api_hashwhentgasks. The hash is not shown as you type.--app autofills in the site for you. It asks your phone number and the code my.telegram.org sends you as a message in Telegram, then reads your app, or creates one if you have none. The site has no API, sotgfollows its web form; a change on Telegram's side can break this.--app browserstill works then.
The app is stored only after Telegram has accepted the login.
Logging in
tg session start # a QR code in the terminal
tg session start phone # a phone number, the code Telegram sends, and your 2FA passwordQR: scan the code in the Telegram app: Settings → Devices → Link Desktop Device. The code is renewed while you wait.
Phone: type the number in international format, then the login code. If the account has a
cloud password (2FA), tg asks for it without showing what you type. With --app auto, the phone
number is asked only once.
After either, Telegram lists a new device in the app's list of sessions.
Logged in as <your name> (@<username>, id <id>) — profile default.
Session: ~/.local/share/tg-cli/sessions/default.session
App keys: in the keyring
Next: tg chats list · tg server install to keep the archive currentWhen an agent runs the login
An agent has no terminal to draw the QR code in. --qr-file writes it as a PNG instead, for the
agent to show you:
tg session start --qr-file login.pngThe file is replaced when Telegram renews the code, and removed when the login ends, whether it worked or not. Without a terminal this works only when the profile already has its app and the account has no 2FA password: anything else has to be typed.
Check and log out
tg account show # who this profile is logged in as
tg account sessions list # every device and app logged in to the account; ends nothing
tg session end # log out on Telegram's side, and delete the session file heretg session end ends the session on Telegram's side too: the device disappears from the list in
the app. It does not delete the app id and hash from the keyring, and it leaves the local store as
it is.
How long a session lives
Until it is ended: by tg session end, from the list of devices in a Telegram app, or by Telegram
itself once the session has gone unused for longer than the account's limit for inactive sessions
(authorization_ttl_days in
Telegram's API), which the apps let
you set in the list of devices. A profile that runs every day never reaches it. When a session has
ended, every command answers "not logged in, or the session was ended" with exit code 4; log in
again with tg session start
(troubleshooting.md).
Profiles
A profile is a separate login: its own session, app, settings, recipients and runs. It is named by the first word, not by an option:
tg chats list # profile "default"
tg work chats list # profile "work"
export TG_PROFILE=work # the same for the whole shell sessionThe first word is the profile when it is not a command. A profile therefore cannot be called
chats, messages or any other command word; tg session start refuses such a name. Names are
letters, digits, dot, dash and underscore, starting with a letter or a digit.
The order, first match wins: the first word, TG_PROFILE, defaultProfile in the config file, then
default.
A word that is not a command and has no command after it is an error that says so:
"nonsense" is not a command, so it was read as a profile name — and no command followed it.TG_PROFILE_LOCK pins a process to one profile. Set it where an agent runs, and a first word or
TG_PROFILE naming any other profile is refused (exit code 5). Without it, an agent could pick a
profile with fewer limits.
Where the parts are kept
| What | Where |
|---|---|
| the session | sessions/<profile>.session in the state directory |
| the app id and hash | the OS keyring, service tg-cli, entry <profile>:api |
| the app id and hash, with no keyring | credentials.json in the settings directory |
| the app id and hash, for CI | TG_API_ID and TG_API_HASH; they win over the keyring |
The directories are listed in installation.md.
On a machine with no keyring (a container, often), the app goes into credentials.json beside the
settings, and tg says so once on stderr.
⚠
TG_CONFIG_DIR,TG_STATE_DIRandTG_CACHE_DIRmove the keyring entry too. With any of them set, the service name includes the settings directory. A login made without them is invisible with them, and the other way round. Set them always, or never.
⚠ On Linux the keyring is reached through
XDG_RUNTIME_DIR. cron, ssh and some MCP clients starttgwithout it, and every command then says the app credentials were not found "although it has logged in on this machine". Do not log in again: that adds another device and does not fix the environment. SetXDG_RUNTIME_DIR(troubleshooting.md).
Next
- usage.md — the first commands
- configuration.md — settings and the order they resolve in
- security.md — what reaches the disk and what never does