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

# Session webhooks

> HTTPS callbacks when API sessions start and end — for ops alerts and agent orchestration.

Register an HTTPS endpoint and Glasswarp will POST when a session starts or
ends. Use this for **ops alerts and agent orchestration** — not for driving the
PC (that's the API / MCP / SDK).

Deliveries are **best-effort** and may be missed. For billing reconciliation,
treat webhooks as a hint and reconcile against the [sessions API](/api-reference/overview)
(source of truth). Retries are not implemented yet.

## Setup

Webhook **management** (create / list / delete) uses your **console login JWT** —
same auth as API Keys in the developer console. Platform API keys (`gw_*`)
authenticate session/control calls; they **cannot** manage webhooks.

1. Open **Console → Webhooks**, or call `POST /v1/webhooks` with
   `Authorization: Bearer <console_access_token>` (the same JWT the console
   uses after you sign in).
2. Enter a public `https://` URL (private / loopback / link-local / cloud-metadata
   addresses are rejected; redirects are not followed).
3. Copy the **signing secret** shown once on create (`whsec_…`).
4. Start a session — your endpoint should receive `session.started`, then
   `session.ended` when it closes (including idle timeout / owner kill).

Max **5** webhooks per account. Events default to both lifecycle events.

## Payload

```json theme={null}
{
  "type": "session.ended",
  "id": "delivery-id",
  "created_at": "2026-07-22T20:00:00.000Z",
  "data": {
    "session_id": "…",
    "rig_id": "…",
    "mode": "desktop",
    "status": "ended",
    "ended_reason": "client",
    "billed_minutes": 3
  }
}
```

### `ended_reason` (on `session.ended`)

| Value          | Meaning                                                     |
| -------------- | ----------------------------------------------------------- |
| `client`       | Caller ended the session (`DELETE` / SDK `end_session`)     |
| `owner_kill`   | Rig owner killed the session (console or host tray)         |
| `idle_timeout` | No API activity for the session idle window (\~15m default) |
| `max_age`      | Session hit the maximum age cap                             |
| `timeout`      | Server-side timeout / orphan cleanup                        |
| `host_gone`    | Host disconnected while the session was active              |
| `unpaired`     | Rig was unpaired mid-session                                |

Idle timeout is still a `session.ended` event — there is no separate
`session.reaped` event. Switch on `ended_reason=idle_timeout`.

## Headers

| Header                  | Meaning                                                    |
| ----------------------- | ---------------------------------------------------------- |
| `Content-Type`          | `application/json`                                         |
| `X-Glasswarp-Event`     | `session.started` or `session.ended`                       |
| `X-Glasswarp-Delivery`  | Unique delivery id (dedupe)                                |
| `X-Glasswarp-Timestamp` | Unix seconds when the delivery was signed                  |
| `X-Glasswarp-Signature` | `sha256=<hex>` — HMAC-SHA256 of `"{timestamp}.{raw_body}"` |

## Verify the signature

Reject deliveries whose timestamp is more than **5 minutes** from your clock
(replay protection).

```python theme={null}
import hmac, hashlib, time

def verify(secret: str, body: bytes, timestamp: str, signature: str, tolerance=300) -> bool:
    ts = int(timestamp)
    if abs(int(time.time()) - ts) > tolerance:
        return False
    got = signature.removeprefix("sha256=")
    signed = f"{ts}.".encode() + body
    expect = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
    return hmac.compare_digest(got, expect)
```

## Delivery behavior

* \~5s timeout; response body is capped — a slow or huge reply cannot tie up the worker.
* Failed deliveries update **last status** in the console.
* Retries are not implemented yet — poll sessions if you need a durable record.

## Related

* [Rigs and sessions](/concepts/rigs-and-sessions)
* [Safe agent sessions](/guides/safe-agent-sessions)
* [API reference](/api-reference/overview)
