How async hooks execute
When an async hook fires, Claude Code starts the hook process and immediately continues without waiting for it to finish. The hook receives the same JSON input via stdin as a synchronous hook.
After the background process exits, Claude Code delivers the additionalContext and systemMessage fields from the hook's JSON response to Claude on the next conversation turn. Unlike a synchronous hook's systemMessage, neither field is shown to you.
Claude Code validates that JSON response against the same output schema as synchronous hooks, and drops any field whose value has the wrong type, such as a systemMessage that isn't a string, instead of delivering it. Run with --debug to see a warning naming each dropped field. Before v2.1.202, malformed JSON output from an async hook could crash the session, and the crash recurred each time the session was resumed.
Async hook completion notifications are suppressed by default. To see them, enable verbose mode with Ctrl+O or start Claude Code with --verbose.