How HookTray Works

HookTray uses a stateless relay backend, Server-Sent Events for live streaming, and IndexedDB for browser-local history. No database. No retained payloads.

Architecture overview

Webhook provider or curl
        |
        |  ANY /hooks/{token}
        v
.NET Minimal API relay
        |
        |  build bounded request snapshot
        |  broadcast to active SSE subscribers
        v
SSE stream  /api/stream/{token}
        |
        v
Browser UI
        |
        |  render safely (no dangerouslySetInnerHTML)
        |  store locally
        v
IndexedDB

Step by step

1. Token creation

When you open HookTray, the browser calls POST /api/hooks. The backend generates a cryptographically secure random token and registers a session. The token is used as both the webhook receive path and the SSE channel identifier.

2. Webhook receive

Your webhook provider sends a request to /hooks/{token}. The backend accepts GET, POST, PUT, PATCH, and DELETE. It builds a bounded snapshot — method, path, query string, headers, and a size-limited body preview — and broadcasts it to all active SSE subscribers for that token.

3. SSE stream

The browser opens a persistent connection to /api/stream/{token}. The backend keeps this connection alive and pushes each request snapshot as a JSON SSE event. There is no polling — events arrive within milliseconds of the webhook landing.

4. Local storage

Each incoming snapshot is written to IndexedDB in the browser. History is scoped to the token and persists across page reloads. When you reopen HookTray with the same token in the URL (?t=TOKEN), your previous requests are hydrated from IndexedDB.

5. Backend cleanup

Sessions that have been inactive beyond the configured TTL are removed by a background cleanup task. The backend holds no payload data — only the active session registry and subscriber list.

Privacy model

The backend is intentionally a relay, not a history service. When a webhook arrives, HookTray builds a bounded snapshot, delivers it to your browser, and discards the payload server-side. Operational logs use hashed token and IP identifiers — raw values are never written. See the privacy page for the full model.

Tech stack

  • Backend: .NET 10 Minimal API, in-memory session store, Channel<T> for SSE broadcast
  • Frontend: Next.js 16 App Router, React 19, Zustand, Dexie (IndexedDB), Tailwind CSS v4
  • Transport: Server-Sent Events (text/event-stream) — no WebSocket, no polling
  • Deployment: Docker, nginx reverse proxy, static export for the frontend