Run a script when Claude Code finishes or a session ends (hooks)
How Claude Code hooks work, the difference between the Stop and SessionEnd events, and how to write a hook that stays fast and out of your way.
2 min read · updated 2026-10-02
Claude Code hooks let you run your own command when something happens: before a tool runs, when Claude finishes answering, when a session ends. They are how tools like showmytokens keep your receipt up to date without you thinking about it. Here is how the two most useful events work.
Where hooks live
Hooks are configured in ~/.claude/settings.json (or in a project's .claude/settings.json) under a hooks key. Each event holds a list of groups, and each group holds one or more hooks. A command hook looks like this:
{
"hooks": {
"SessionEnd": [
{
"hooks": [
{
"type": "command",
"command": "node ~/.showmytokens/showmytokens.cjs hook end",
"timeout": 10
}
]
}
]
}
}The full list of events and options is in the official hooks reference.
Stop vs SessionEnd
- Stop runs each time Claude finishes responding. It fires many times in a session, once per answer.
- SessionEnd runs once when the session terminates, for example when you exit, clear the conversation or resume another one. The event says why it ended.
For a task like syncing usage they do different jobs. Stop keeps things fresh while you work, so a session that runs for hours does not stay invisible until you quit. SessionEnd catches whatever happened in the last stretch before the session closed. showmytokens installs both: Stop syncs at most every 10 minutes, and SessionEnd syncs again after 2 minutes so the tail of a session is not lost.
A session that is killed, or whose terminal is closed abruptly, may never run its SessionEnd hook, so do not rely on it alone. A tool that re-reads everything each time, as showmytokens does, simply catches up at the next sync.
Writing a good hook
Hooks run in the middle of your workflow, so a bad one makes Claude Code feel slow. A few rules:
- Return immediately. Start long work in a detached background process and exit. The hook should finish in milliseconds.
- Throttle.
Stopcan fire every few seconds. Record when you last did the work and skip if it was recent. - Do not overlap. Use a lock file so two sessions do not start the same job at once, and ignore a lock that is old enough to be left over from a crash.
- Exit quietly with code 0. Do not print noise, and do not fail loudly on a network error. A hook that errors can get in your way.
- Set a `timeout`. It is a safety net if your command hangs.
- Make it safe to repeat. Because hooks may run more than once or not at all, the work should be idempotent: running it twice should give the same result as running it once.
Try it and undo it
Run the command from your terminal first and check that it behaves. To see the hooks showmytokens would add, run npx showmytokens login: it shows the exact change and asks before touching your settings, keeps a backup next to the file, and npx showmytokens logout removes the hooks again. More on what is read and sent is in the guide to where Claude Code stores your usage data.