> ## Documentation Index
> Fetch the complete documentation index at: https://3to1go.docs.thesteau.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Station hooks

> Run your own commands before and after Station stores an upload.

Hooks run a shell command at two points while Station stores an upload. Use them to trigger an offsite sync, write to a log, or ping a monitoring service.

```mermaid theme={null}
flowchart LR
    A[Upload complete] --> B{Checksum OK?}
    B -->|No| X[Scout retries upload<br/>no hooks run]
    B -->|Yes| PRE[Pre command]
    PRE --> S[Store archive<br/>and apply retention]
    S --> POST[Post command<br/>status ok or error]
    POST --> N[ntfy notification]
```

* **Pre command**: after the upload passes its checksum, before Station stores it. Station waits for it to finish.
* **Post command**: after storing, with `THREETOONEGO_STATUS` set to `ok` or `error`. Retrying an upload that already finished doesn't rerun hooks.

## Set up

<Steps>
  <Step title="Open Custom Scripts">
    In Station, click **Custom Scripts**.
  </Step>

  <Step title="Upload a script (optional)">
    Under **Uploaded Items**, upload up to **3** files. Only `.sh` scripts and `.txt` helper files are allowed, and they must be UTF-8 text. `.sh` files are made executable, and Windows line endings are converted.
  </Step>

  <Step title="Set the commands">
    Enter a **Pre Command** and/or **Post Command**. Either:

    * the name of an uploaded script, such as `after-upload.sh`, or
    * any shell command, such as `curl -fsS https://hc-ping.com/your-id`.
  </Step>
</Steps>

Commands run with `sh -c` inside the Station container, from the hook scripts folder (`/hook-scripts`). Each command has a **5-minute** timeout. Output and non-zero exit codes are written to Station's logs. A failing hook doesn't stop the upload.

## Environment variables

Every hook gets these variables:

| Variable | Value |
| - | - |
| `THREETOONEGO_APP` | `station` |
| `THREETOONEGO_HOOK_PHASE` | `pre` or `post` |
| `THREETOONEGO_HOOK_SCRIPTS_DIR` | The hook scripts folder |
| `THREETOONEGO_SCOUT_ID` | Sending Scout's ID |
| `THREETOONEGO_SCOUT_INSTANCE_ID` | Sending Scout's instance ID |
| `THREETOONEGO_JOB_NAME` | Backup job name |
| `THREETOONEGO_UPLOAD_ID` | Upload session ID |
| `THREETOONEGO_NAMESPACE` | Storage path, `scout_id/instance_id/job_name` |
| `THREETOONEGO_FILENAME` | Uploaded filename |
| `THREETOONEGO_FINGERPRINT` | Snapshot fingerprint |
| `THREETOONEGO_TIMESTAMP` | Snapshot timestamp |
| `THREETOONEGO_ARCHIVE_SHA256` | SHA-256 of the encrypted archive |
| `THREETOONEGO_ARCHIVE_SIZE_BYTES` | Archive size |
| `THREETOONEGO_SOURCE_ADDRESS` | IP the upload came from |
| `THREETOONEGO_ADVERTISED_URL` | URL the Scout advertises, if set |
| `THREETOONEGO_STAGED_PATH` | Where the upload is staged before storing |

The **post** command also gets:

| Variable | Value |
| - | - |
| `THREETOONEGO_STATUS` | `ok` or `error` |
| `THREETOONEGO_STORED_AS` | Final stored filename |
| `THREETOONEGO_PRUNED` | Number of old snapshots removed by retention |
| `THREETOONEGO_DUPLICATE` | `true` if Station already had this snapshot |
| `THREETOONEGO_UNUSUAL` | Why the archive's size looks unusual, or empty. See [Unusual sizes](/station/snapshots#unusual-sizes). |

## Example: log each stored snapshot

```sh after-upload.sh theme={null}
#!/bin/sh
[ "$THREETOONEGO_STATUS" = "ok" ] || exit 0
echo "stored $THREETOONEGO_NAMESPACE/$THREETOONEGO_STORED_AS"
```

Set **Post Command** to `after-upload.sh`.

<Note>
  The Station image is a minimal Alpine image with `sh` and `curl`. Tools like `rclone` aren't included. Run those on the host or in another container that reads the same backup folder.
</Note>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.