What Claude Code Hooks Actually Are
Claude Code hooks are shell commands that run automatically when specific events happen during a session. Unlike git hooks (which only fire on git events), Claude Code hooks cover the entire session lifecycle - from the moment a session starts to every tool call, compaction event, subagent spawn, and session end.
I used to run a mental checklist before every commit: check for secrets, verify tests pass, monitor context usage. It was exhausting, and I forgot steps when deep in a problem. Six months ago I wrote my first hook - a security checker that blocks commands containing API keys. It saved me on day one. Now I run over 20 hooks across my system, and I haven't manually checked a single thing in months.
The breaking point was pushing credentials to a private repo. Nothing catastrophic, but enough to make me automate everything I was relying on memory for. If I catch myself repeating a check three times, it becomes a hook.
TL;DR: Claude Code has 31 hook event types and 18 of them can block. Hooks are shell commands configured in settings.json that receive JSON on stdin. They print JSON back only when they want to change something, and for almost every event exit 0 with no output is how a hook says carry on. WorktreeCreate is the exception, and an expensive one - see below. Start with one high-value hook, then compose.
All 31 Hook Event Types
Claude Code fires hooks at 31 distinct points in the session lifecycle. Understanding the full surface area is critical for designing useful automations.
Counted against the v2.1.233 binary on 2026-08-15, not from the docs summary table - the two disagree, and the callout below the tables says where and why. 18 of the 31 can stop something from happening.
Session Lifecycle
| Event | When It Fires | Can Block? |
|---|---|---|
| SessionStart | Session begins or resumes | No |
| Setup | Repo setup, and only from the CLI: --init-only, --init, or --maintenance. Never on normal startup, and not from typing /init in a session. Receives a trigger of init or maintenance | No |
| SessionEnd | Session terminates | No |
| Stop | Claude finishes responding | Yes |
| StopFailure | API error terminates a response | No |
User Input
| Event | When It Fires | Can Block? |
|---|---|---|
| UserPromptSubmit | User submits a prompt, before processing | Yes |
| UserPromptExpansion | A slash command expands into its prompt | Yes |
| MessageDisplay | Assistant text streams to the screen | No |
UserPromptSubmit is the most underrated hook. It runs before Claude sees your message, so you can inject context, calculate scores, or transform the prompt. My delegation enforcer uses this to score every prompt and inject routing hints.
Tool Execution
| Event | When It Fires | Can Block? |
|---|---|---|
| PreToolUse | Before a tool executes | Yes |
| PostToolUse | After a tool succeeds | Yes |
| PostToolUseFailure | After a tool fails | Yes |
| PostToolBatch | After a batch of parallel tool calls | Yes |
| PermissionRequest | Permission dialog appears | Yes |
| PermissionDenied | Auto-mode classifier denied a tool call | No |
PreToolUse is the gatekeeper, and it is the one event with its own response shape. It blocks by returning hookSpecificOutput.permissionDecision set to "deny". A top-level "decision" field is deprecated for this event rather than inert - it is still honoured - so write the nested form in new hooks, but do not rewrite a working legacy hook on the assumption that it has stopped working. This is where security checks, file protection rules, and command validation live. The post-tool events can block too, but later: they cannot un-run the tool, they stop Claude from continuing after it.
Subagents and Tasks
| Event | When It Fires | Can Block? |
|---|---|---|
| SubagentStart | Subagent spawns | No |
| SubagentStop | Subagent finishes | Yes |
| TaskCreated | New task created | Yes |
| TaskCompleted | Task marked complete | Yes |
| TeammateIdle | Teammate agent about to go idle | Yes |
Context and Configuration
| Event | When It Fires | Can Block? |
|---|---|---|
| PreCompact | Before context compaction | Yes |
| PostCompact | After compaction completes | No |
| InstructionsLoaded | CLAUDE.md or rules file loaded | No |
| ConfigChange | Configuration file changes | Yes |
| CwdChanged | Working directory changes | No |
| DirectoryAdded | A directory is added to the workspace | No |
| FileChanged | Watched file changes on disk | No |
Workspace
| Event | When It Fires | Can Block? |
|---|---|---|
| WorktreeCreate | Git worktree created, via --worktree, isolation: "worktree", or for a background session | Yes |
| WorktreeRemove | A hook-provided worktree is removed. Did not fire for one Claude Code created itself, in any attempt | Yes |
| Notification | System notification sent | No |
| Elicitation | MCP server requests user input | Yes |
| ElicitationResult | User responds to MCP elicitation | Yes |
The 18 Blocking Events, and How They Block
Eighteen of the 31 can stop something from happening, and they do not all use the same field. This is the part that bites people, because a hook that returns the wrong shape fails silently: it exits 0, Claude Code reads no decision, and the action proceeds.
These are not three tidy buckets, and I have published a tidy-bucket version of this twice now and been wrong both times. The mechanisms overlap:
Exit code 2 is the general one. A hook that writes its reason to stderr and exits 2 blocks, for most of the 18. I tested this on UserPromptSubmit, which I had previously filed under "top-level decision only": the run comes back "num_turns": 0 with result: "UserPromptSubmit operation blocked by hook: ...". If you only learn one mechanism, learn this one.
A top-level "decision": "block" also works, for many of the same events - UserPromptSubmit, UserPromptExpansion, PostToolUse, PostToolUseFailure, PostToolBatch, Stop, SubagentStop, PreCompact, and TaskCreated. Same isolated-config test, same result. So for a good number of events, either shape blocks, and the two lists are not complements.
Two events have their own nested shape, and here the shape genuinely matters: PreToolUse uses hookSpecificOutput.permissionDecision, PermissionRequest uses hookSpecificOutput.decision.behavior.
And a few take {"continue": false} - TaskCompleted and TeammateIdle among them.
WorktreeCreate is none of the above, and it is the one that will bite you. It is not a veto hook, it is a provider: its job is to return the path of the worktree it created, on stdout for a command hook or as hookSpecificOutput.worktreePath otherwise. Anything else aborts the operation. I fired three hooks at it against v2.1.233 and all three stopped the worktree being created - exit 1, exit 2, and exit 0 with no output:
Error creating worktree: WorktreeCreate hook failed: hook succeeded but
returned no worktree path (command: echo the path to stdout; ...)
So the general rule above - exit 0 and say nothing to let things proceed - is false here, and expensively so. Register an observe-only logger on WorktreeCreate and you break every worktree creation on the machine, including the ones Claude Code makes for background sessions, with an error that does not mention your hook. If you want to watch worktree creation without owning it, log from inside the worktree after the fact. Do not reach for WorktreeRemove as the observation point either: it cannot tell you a worktree was created, and it only fires for a worktree that a WorktreeCreate hook provided in the first place. That precondition is why it looks dead - three of my attempts had it never fire, because Claude Code had built the worktree itself.
The practical rule: write the event's documented shape, and exit 2 as well if you want belt and braces. What you must not do is assume that because one shape is documented for an event, the other is inert.
The other 13 are observe-only. Your hook runs, its output can enter context as additionalContext, but the action proceeds regardless. Most hooks inform rather than control, and that is still the right default.
The hooks reference's own decision-control table implies 14, because it does not list Elicitation or ElicitationResult as blocking and it files ConfigChange under top-level decision. The v2.1.233 binary says otherwise, in its own words: Exit code 2 - deny the elicitation, Exit code 2 - block the response (action becomes decline), and for ConfigChange Exit code 2 - block the change from being applied to the session. I counted from the binary, because it is the thing that runs. I have now published 3, then 14, then 16 for this number, and each time I had counted rather than tested. WorktreeCreate is what broke 16: I had it marked observe-only until a reviewer refused a worktree with it.
Make that four wrong answers. I wrote a version of this paragraph claiming 17 and naming WorktreeRemove as the row I most distrusted, on the grounds that I could not get it to fire. A reviewer then fired it - and it blocks. The reason mine never fired is a precondition neither of us had thought to vary: WorktreeRemove only dispatches for a worktree that a WorktreeCreate hook provided. When Claude Code builds the worktree itself, the event never comes. Register a providing WorktreeCreate hook and its sibling starts firing, and an exit 2 leaves the worktree in place with could not remove it.
So here is the honest state of 18. Nine of the 31 events have actually had a hook fired at them. Three confirm blocking: UserPromptSubmit (via exit 2 and via a top-level decision), WorktreeCreate, WorktreeRemove. Six confirm the opposite - SessionStart, Setup, SessionEnd, MessageDisplay, InstructionsLoaded, SubagentStart - four of them tested on both mechanisms. The other 22 rows rest on reading the binary and the reference, which is exactly what produced the four wrong answers above. Fire a hook at whatever you are about to build on. If it does not fire, look for the precondition before concluding the event is inert - and if it does block on exit 2, that tells you one thing about one exit code, not what kind of hook it is. WorktreeCreate and WorktreeRemove both block, and they are not the same kind of hook at all: an observe-only logger is fatal on one and harmless on the other. That is the whole lesson here, and it cost four revisions of this one number to learn.
This lives in primeline-ai/evolving-lite - the self-evolving Claude Code plugin. Free, MIT, no build step.
How Hook Configuration Works
Hooks live in your settings.json file. The structure maps event names to arrays of matcher/command pairs:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "python3 .claude/hooks/security-tier-check.py"
}
]
}
],
"UserPromptSubmit": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "python3 .claude/hooks/delegation-enforcer.py"
}
]
}
]
}
}
Matcher Patterns
The matcher field filters when a hook fires:
- Empty string
""- matches every event of that type - Tool name
"Bash"- matches only when that specific tool is used - Pipe-separated
"Edit|Write|MultiEdit"- matches any of the listed tools - Glob patterns - match file paths or other event data
For PreToolUse and PostToolUse, the matcher tests against the tool name. An empty matcher catches all tool calls of that event type.
Where Settings Live
Settings files follow the same scoping as CLAUDE.md:
| Scope | File | Use Case |
|---|---|---|
| User | ~/.claude/settings.json | Hooks you want in every project |
| Project | .claude/settings.json | Hooks specific to this project (gitignored) |
| Project (shared) | .claude/settings.json (committed) | Hooks shared with team |
Multiple hooks on the same event run in parallel. Results are collected and all additionalContext strings are concatenated into Claude's context.
The Hook Response Protocol
Every hook receives JSON on stdin. Returning JSON on stdout is optional, and this is the part people get backwards: you print JSON only when you want to change something. Exit 0 with no output is the normal, correct way to say "carry on". The input always includes session_id and hook_event_name. Tool-related events add tool_name and tool_input.
Input Example (PreToolUse)
{
"session_id": "abc-123",
"cwd": "/Users/dev/my-project",
"hook_event_name": "PreToolUse",
"tool_name": "Bash",
"tool_input": {
"command": "rm -rf ./data"
}
}
Output Schema
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Destructive command on a protected branch",
"additionalContext": "Text injected into Claude's context"
}
}
There is no top-level "decision": "allow". This is the single most common mistake. The top-level decision field takes "approve" or "block" - the v2.1.233 binary throws Unknown hook decision type: allow. Valid types are: approve, block - so "allow" does not silently pass, it errors. In practice you rarely want "approve" either: to let an action through, omit the field, or just exit 0 printing nothing at all.
PreToolUse is the exception with the richer vocabulary, and it lives one level down in hookSpecificOutput:
permissionDecision | Effect |
|---|---|
"allow" | Tool runs, no prompt |
"deny" | Tool call is blocked |
"ask" | User is prompted to confirm |
"defer" | Falls through to the normal permission flow. Print-mode only - in an interactive session the binary logs defer is print-mode only and ignores it, which looks exactly like your hook never ran |
The hooks reference is blunt about the older form: "PreToolUse previously used top-level decision and reason fields, but these are deprecated for this event." The deprecated "approve" and "block" values map to "allow" and "deny". Other events, including PostToolUse and Stop, still use top-level decision as their current format - so the shape genuinely differs by event, and you have to check.
The additionalContext string appears in Claude's context window as a system reminder. This is how hooks communicate with Claude - they inject information, warnings, or instructions that Claude sees on the next turn.
Hook 1: Security Tier Check (PreToolUse)
This hook blocks dangerous commands on production branches. It checks the command being executed against a severity tier and the current git branch.
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "python3 .claude/hooks/security-tier-check.py"
}
]
}
]
}
}
#!/usr/bin/env python3
import json, sys, subprocess
DANGEROUS_PATTERNS = {
"rm -rf": 8,
"drop database": 9,
"git push --force": 7,
"git reset --hard": 7,
"chmod 777": 6,
"curl | bash": 9,
}
PROTECTED_BRANCHES = ["main", "production", "release"]
def get_branch():
try:
result = subprocess.run(
["git", "rev-parse", "--abbrev-ref", "HEAD"],
capture_output=True, text=True, timeout=5
)
return result.stdout.strip()
except Exception:
return "unknown"
data = json.loads(sys.stdin.read())
command = data.get("tool_input", {}).get("command", "")
branch = get_branch()
max_tier = 0
matched = ""
for pattern, tier in DANGEROUS_PATTERNS.items():
if pattern in command.lower():
if tier > max_tier:
max_tier = tier
matched = pattern
if max_tier >= 7 and branch in PROTECTED_BRANCHES:
print(json.dumps({
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": f"BLOCKED: '{matched}' (tier {max_tier}) on protected branch '{branch}'. Switch to a feature branch or remove the dangerous pattern."
}
}))
elif max_tier >= 5:
print(json.dumps({
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "ask",
"permissionDecisionReason": f"WARNING: '{matched}' (tier {max_tier}) detected. Proceed with caution."
}
}))
# Nothing to print in the allow case: exit 0 with no output and the tool runs.
Note the shape. permissionDecision and permissionDecisionReason sit inside hookSpecificOutput, and the allow branch prints nothing at all. An older version of this hook used a top-level {"decision": "deny"}, which is the deprecated form for PreToolUse - it is the kind of thing that keeps working until it quietly does not.
Why Tiers Instead of Block-Everything
My first version blocked all destructive commands everywhere. That lasted two hours before I turned it off - too annoying on feature branches where rm -rf node_modules is routine. The tier system means risk scales with context. Tier 5-6 on a feature branch? Warning. Tier 7+ on main? Hard block.
Since adding this hook, I have been blocked from dangerous operations 23 times. Every single one was a mistake I would have regretted.
Hook 2: Delegation Enforcer (UserPromptSubmit)
This hook scores every prompt for delegation potential and injects routing hints into context. It runs before Claude processes the message, so Claude sees the delegation score as part of the prompt context.
#!/usr/bin/env python3
import json, sys
THRESHOLD = 3
SCORE_FACTORS = {
"explore": 3, "search": 3, "find": 3, "investigate": 3,
"review": 2, "refactor": 2, "research": 2, "audit": 2,
"debug": 2, "test": 2, "bulk": 2,
}
DEDUCTIONS = {
"production": -10, "deploy": -10, "password": -10,
"secret": -10, "credential": -10,
}
def score_prompt(prompt: str) -> int:
score = 0
lower = prompt.lower()
for keyword, points in SCORE_FACTORS.items():
if keyword in lower:
score += points
for keyword, points in DEDUCTIONS.items():
if keyword in lower:
score += points
return score
data = json.loads(sys.stdin.read())
prompt = data.get("prompt", "")
score = score_prompt(prompt)
if score >= THRESHOLD:
output = {
"hookSpecificOutput": {
"hookEventName": "UserPromptSubmit",
"additionalContext": (
f"Delegation score: {score}. "
f"Consider delegating this task to a subagent."
)
}
}
else:
output = {}
print(json.dumps(output))
What the Production Version Adds
The simplified version above shows the core pattern. My production version in Evolving Lite adds:
- Model routing: Complexity 1-2 sends to Haiku, 3-7 to Sonnet, 8+ stays on Opus
- Trait injection: Loads personality profiles (curious for research, cautious for debugging) from config
- Coordination awareness: Reads active agent intents to avoid duplicate work across parallel sessions
- Inline hints: Tags like
[explore]or[debug]that Claude picks up for behavior adjustment
The delegation scoring system explains the full factor table and model routing logic.
Hook 3: Context Warning (PostToolUse)
When Claude reads large files or produces long outputs, context fills up fast. This hook tracks approximate token usage and warns at thresholds.
#!/bin/bash
# context-warning.sh - PostToolUse hook
# Reads tool output size from stdin, maintains running count
INPUT=$(cat)
TOOL_NAME=$(echo "$INPUT" | python3 -c "import json,sys; print(json.load(sys.stdin).get('tool_name',''))")
# Track cumulative context growth in a temp file
TRACKER="/tmp/claude-context-$$"
[ -f "$TRACKER" ] || echo "0" > "$TRACKER"
CURRENT=$(cat "$TRACKER")
# Estimate tokens from tool result (rough: 1 token per 4 chars)
TOOL_OUTPUT_SIZE=$(echo "$INPUT" | wc -c)
ESTIMATED_TOKENS=$((TOOL_OUTPUT_SIZE / 4))
NEW_TOTAL=$((CURRENT + ESTIMATED_TOKENS))
echo "$NEW_TOTAL" > "$TRACKER"
# Threshold warnings
if [ "$NEW_TOTAL" -gt 800000 ]; then
echo "{\"hookSpecificOutput\":{\"hookEventName\":\"PostToolUse\",\"additionalContext\":\"CRITICAL: Context estimated at ${NEW_TOTAL} tokens (80%+). Stop loading new files. Prepare session handoff or run /compact.\"}}"
elif [ "$NEW_TOTAL" -gt 600000 ]; then
echo "{\"hookSpecificOutput\":{\"hookEventName\":\"PostToolUse\",\"additionalContext\":\"WARNING: Context estimated at ${NEW_TOTAL} tokens (60%+). Prefer subagent delegation for remaining research. Load summaries, not full docs.\"}}"
fi
# Under both thresholds: print nothing. Silence is how a hook says "carry on".
Before this hook existed, I regularly hit context limits mid-task and lost work. Now I get early warnings and can plan accordingly. My sessions stay lean because the hook forces cleanup as I go.
Why PostToolUse, Not PreToolUse?
A PreToolUse context warning would block the tool call. But at that point, you have already committed to the action - blocking it just causes confusion. PostToolUse warns after the fact, giving Claude information to adjust its next decision. The hook informs; Claude decides.
Hook 4: Auto-Format on Edit (PostToolUse)
The most common use case from the official docs: automatically format files after Claude edits them.
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write|MultiEdit",
"hooks": [
{
"type": "command",
"command": "bash .claude/hooks/auto-format.sh"
}
]
}
]
}
}
#!/bin/bash
# auto-format.sh - runs formatter on edited files
INPUT=$(cat)
FILE=$(echo "$INPUT" | python3 -c "
import json, sys
data = json.load(sys.stdin)
ti = data.get('tool_input', {})
print(ti.get('file_path', ti.get('path', '')))
")
if [ -z "$FILE" ]; then
exit 0
fi
EXT="${FILE##*.}"
case "$EXT" in
ts|tsx|js|jsx)
npx prettier --write "$FILE" 2>/dev/null
;;
py)
python3 -m black --quiet "$FILE" 2>/dev/null
;;
rs)
rustfmt "$FILE" 2>/dev/null
;;
esac
# No output, exit 0: the edit stands and Claude carries on.
This hook fires on every Edit, Write, or MultiEdit tool call. The matcher "Edit|Write|MultiEdit" catches all three. The script extracts the file path from the tool input, detects the language from the extension, and runs the appropriate formatter.
The result: Claude's edits are always formatted according to your project standards, with zero manual intervention.
Hook 5: Pre-Compact State Save (PreCompact)
When compaction runs, conversation history gets summarized and path-scoped rules are lost. This hook saves critical session state before that happens.
#!/usr/bin/env python3
import json, sys, os
from datetime import datetime
data = json.loads(sys.stdin.read())
session_id = data.get("session_id", "unknown")
cwd = data.get("cwd", os.getcwd())
# Save session state snapshot
state = {
"session_id": session_id,
"timestamp": datetime.now().isoformat(),
"cwd": cwd,
"event": "pre_compact"
}
state_dir = os.path.expanduser("~/.claude/state")
os.makedirs(state_dir, exist_ok=True)
state_file = os.path.join(state_dir, f"pre-compact-{session_id[:8]}.json")
with open(state_file, "w") as f:
json.dump(state, f, indent=2)
print(json.dumps({
"hookSpecificOutput": {
"hookEventName": "PreCompact",
"additionalContext": f"Pre-compact state saved to {state_file}. Path-scoped rules will be lost after compaction - re-read relevant files to reload them."
}
}))
Pairing PreCompact with PostCompact
PreCompact saves state. PostCompact restores it. Together they create a compaction-resilient workflow:
- PreCompact - extract decisions, task state, key file paths
- Compaction runs - conversation history summarized
- PostCompact - re-inject the saved state as
additionalContext
This is the hook-based version of the compact-stuff strategy for preserving session knowledge through compaction events.
- -Check git branch before dangerous commands
- -Estimate task complexity for delegation
- -Monitor context window usage
- -Run formatter after every edit
- -Remember to save state before compaction
- -Validate no sensitive data staged
- +security-tier-check blocks by branch + tier
- +delegation-enforcer scores and routes automatically
- +context-warning alerts at 60% and 80%
- +auto-format runs Prettier/Black on every edit
- +pre-compact saves session state snapshot
- +All hooks: 0.8s combined execution time
Designing Hooks That Compose
Individual hooks solve individual problems. The real power comes from composition - hooks that work together without explicitly knowing about each other.
The Composition Pattern
Each hook follows the same contract: JSON in, JSON out, additionalContext for communication. This means hooks compose naturally through context injection:
A single user prompt triggers this cascade:
- UserPromptSubmit - delegation score calculated, routing hint injected
- Claude processes the prompt, decides to call a tool
- PreToolUse - security check validates the tool call
- Tool executes
- PostToolUse - formatter runs, context warning updates, tracker logs
No hook knows about the others. They all communicate through additionalContext injections that Claude reads naturally.
Performance Budget
Hooks run as shell commands - they add latency. My rule: each hook gets a 200ms budget. If a hook needs more than that, it spawns a background process and returns immediately. Across all my hooks, average combined execution time is 0.8 seconds per session turn.
Keep hooks fast by:
- Avoid network calls in synchronous hooks (use background for API calls)
- Cache expensive computations (git branch doesn't change mid-command)
- Return early when the matcher already filtered to relevant events
Production Results After 6 Months
After six months running 20+ production hooks across multiple projects:
- Manual checks per commit: 6 - 0
- Context limit hits: 12/month - 1/month
- Accidental dangerous commands: 4 - 0
- Delegation decisions: Manual - Automatic (83% of tasks)
- Average hook execution time: 0.8 seconds combined
- Sessions lost to context overflow: 8 - 0
The biggest surprise was not time saved - it was consistency. Hooks do not forget. They do not skip steps when you are tired. That reliability compounds across hundreds of sessions.
Getting Started With Your First Hook
You do not need 20 hooks on day one. Start with one that solves a real pain point:
- If you hit context limits - start with a PostToolUse context warning
- If you have pushed secrets - start with a PreToolUse security check
- If you handle too much manually - start with a UserPromptSubmit delegation enforcer
- If your code style drifts - start with a PostToolUse auto-formatter
The Minimal Hook Template
Every hook follows the same pattern. Copy this and customize:
#!/usr/bin/env python3
import json, sys
data = json.loads(sys.stdin.read())
# Your logic here
print(json.dumps({
"hookSpecificOutput": {
"hookEventName": data.get("hook_event_name", ""),
"additionalContext": "Your message to Claude here"
}
}))
No decision field: omitting it is how you let the action through. Add {"decision": "block", "reason": "..."} only when you actually want to stop something, and only on an event that supports it.
Add it to your settings.json, pick the right event and matcher, and you have a working hook. The Evolving Lite plugin includes 10 production hooks ready to install - security, delegation, context management, and more.
Run /hooks in Claude Code to inspect your current hook configuration and verify everything is wired up correctly.
![Claude Code Hooks: Automate Your Workflow [2026]](/_next/image?url=%2Fblog%2Fhooks-automation-hero.webp&w=3840&q=75)

![Claude Code Persistent Memory: Setup Guide [2026]](/_next/image?url=%2Fblog%2Fmemory-system-hero.webp&w=3840&q=75)
