Relay
将您的服务连接到 PushWard 的最简便方式。将 webhook 指向 relay.pushward.app —— 无需 Docker 容器、无需自托管、无需逐用户配置。
Supported Providers
Grafana
/grafana事件
- Firing
- Resolved (grouped by alertname)

Public Relay
PushWard hosts a public relay at relay.pushward.app — no deployment required. Just point your services at it with your integration key.
# Grafana Contact Point URL:
https://relay.pushward.app/grafana
# HTTP Header:
Authorization: Bearer hlk_YOUR_INTEGRATION_KEYThe public relay supports all 19 providers listed above. If you prefer to self-host, see the self-hosted setup further down.
How It Works
pushward-relay extracts the hlk_ integration key from each incoming webhook request. The relay infrastructure scales horizontally — one instance can serve many users concurrently. Per-user usage is bounded by your account's plan (free tier monthly quotas; unlimited on paid).
- Receive — external services send webhooks to provider-specific routes
- Authenticate — the integration key is extracted from the
Authorization: Bearer hlk_...header (or Basic Auth password for Radarr/Sonarr/Prowlarr/Bazarr/Komodo, or GenieKey for TrueNAS) - Transform — the provider handler maps the webhook payload to a push notification or Live Activity update depending on the event type
- Forward — the request is sent to PushWard using the caller's integration key
Push Notifications vs Live Activities
Each provider delivers events as either a push notification (banner alert) or a Live Activity (Dynamic Island + Lock Screen), depending on the event type:
| Delivery | Providers | Use case |
|---|---|---|
| Notification only | Grafana, Prowlarr, Bazarr | One-shot alerts — firing/resolved, health, grabs, subtitle downloads |
| Live Activity only | ArgoCD, Uptime Kuma, Backrest, Proxmox, Overseerr, Gatus, Changedetection, Paperless, Unmanic, Gitea, Forgejo | Multi-step progress tracking (syncs, downloads, backups) |
| Both | Radarr, Sonarr, Jellyfin, Komodo, TrueNAS | Radarr/Sonarr: Grab/Download → Live Activity; Health/Rename/Add/Delete → notification. Jellyfin: playback → Live Activity; library adds, scheduled tasks, auth failures → notification. Komodo: resolvable server conditions (CPU, memory, disk, unreachable, version mismatch, swarm) → Live Activity plus a companion notification; container state, build failed, image update → notification only. TrueNAS: each alert → Live Activity plus a companion notification |
Relay requires PostgreSQL for persistent state (sync tracking, download lifecycle, playback progress across restarts and tenants). Stateless providers (Bazarr, Changedetection, Unmanic) still require a database connection to start.
本节介绍的功能来自一个正在等待 App Store 审核的应用更新。更新上线后,此处会自动解锁。
Poster artwork
Four providers carry artwork through to the Live Activity with no configuration on your side. Jellyfin, Radarr, Sonarr and Overseerr all know which film or episode an event is about, so the relay resolves its poster and attaches both the image URL and a ThumbHash to the activity it creates. The Lock Screen shows cover art instead of a generic download glyph.
The ThumbHash matters more than it sounds. Jellyfin in particular is usually reachable only on your own network, and the phone refuses to download an image from a private host -- so the ~25-byte blurred version travelling on the push is the only thing that renders. The relay fetches the artwork once to build that hash, which is the whole reason this works for a self-hosted media server at all.
The webhook waits at most 600ms for a hash it does not already have, then answers without one. The fetch is not cancelled: it carries on under its own ~3s budget and fills the relay-wide cache, so the very first event for a film you have never seen before arrives bare and the next update picks the artwork up. If a poster cannot be resolved at all -- an untagged file, an unreachable image endpoint -- the activity falls back to its icon exactly as before.
Artwork from a media server on your LAN
The relay will not fetch from a private address unless you tell it to. poster.allow_private_hosts is false by default, so on a stock relay a http://192.168.1.10:8096/... or jellyfin.local poster produces no ThumbHash -- and since the phone independently refuses that host too, nothing renders. Self-hosting the relay alongside the media server is the case this setting exists for:
poster:
enabled: true
allow_private_hosts: true| 环境变量 | 说明 | 默认值 |
|---|---|---|
PUSHWARD_POSTER_ALLOW_PRIVATE_HOSTS | Let poster fetches reach loopback, RFC 1918, CGNAT/Tailscale and link-local addresses. It is also what permits a plain http fetch; with it off the relay fetches over https only (YAML: poster.allow_private_hosts) | false |
PUSHWARD_POSTER_ENABLED | Attach poster artwork at all. Turning it off drops both the image URL and the ThumbHash (YAML: poster.enabled) | true |
Leave this off on any relay that accepts webhooks you do not control, including the public relay.pushward.app, which runs with the default. The webhook payload is what carries the image URL, so a relay that will fetch private addresses on request is an SSRF probe of everything it can reach. When it is on, the check runs against the resolved address rather than the hostname, so a DNS name pointing at 127.0.0.1 does not slip past it.
The ThumbHash encoder decodes JPEG, PNG and GIF. Artwork served as WebP or AVIF gets an image_url but no hash, so it renders only when the phone can reach the URL.
Per-request overrides
Append query parameters to any provider webhook URL to override that provider's delivery behavior for a single request. This is handy when one source should behave differently from the provider default — for example forcing a noisy alert to a plain notification, or bumping a deploy's priority. An explicit parameter always wins over the provider-computed value and your static config; omitting a parameter leaves today's behavior unchanged.
| Parameter | Values | Effect |
|---|---|---|
channels | Comma-separated: activity, notification | Restricts which delivery surfaces this request may use. channels=notification suppresses Live Activities and only sends push notifications; channels=activity does the reverse. Must list at least one valid surface. |
priority | Integer 0-10 | Sets the eviction / relevance priority for any Live Activity the request creates. |
level | passive, active, time-sensitive, critical | Sets the notification interruption level for any push notification the request sends. |
https://relay.pushward.app/grafana?channels=notification&level=time-sensitiveAn invalid value is rejected with 400 before the webhook is processed — an unknown channels surface, a priority that isn't an integer 0-10, or a level outside the four allowed values. Fix the URL rather than retrying.
Setup
1. Get Your Integration Key
在 PushWard 应用的 设置 → 集成密钥中复制您的默认集成密钥;登录时会自动创建。要将某个密钥限定到此集成,请参阅创建限定范围的密钥。
2. Self-Hosted (Optional)
If you prefer to run your own relay instance:
services:
pushward-relay:
image: ghcr.io/mac-lucky/pushward-relay:latest
ports:
- "8090:8090"
environment:
PUSHWARD_URL: https://api.pushward.app
PUSHWARD_DATABASE_DSN: postgres://user:pass@db:5432/relay?sslmode=disable
restart: unless-stopped
db:
image: postgres:17-alpine
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
POSTGRES_DB: relay
volumes:
- relay-data:/var/lib/postgresql/data
volumes:
relay-data:For Radarr, Sonarr, and Prowlarr, use Basic Auth with the hlk_ key as the password (username is ignored), since their webhook settings only support Basic Auth.
Authentication
Three patterns depending on the provider:
| Method | Providers | Format |
|---|---|---|
| Bearer token | Most providers | Authorization: Bearer hlk_... |
| Basic Auth | Radarr, Sonarr, Prowlarr, Bazarr, Komodo | hlk_... as password |
| GenieKey | TrueNAS | Authorization: GenieKey hlk_... |
Configuration
| 环境变量 | 说明 | 默认值 |
|---|---|---|
PUSHWARD_URL | PushWard server URL | -- |
PUSHWARD_DATABASE_DSN | PostgreSQL connection string | -- |
PUSHWARD_SERVER_ADDRESS | HTTP listen address | :8090 |
Each provider can be individually enabled/disabled and configured with its own priority, cleanup delay, and stale timeout via environment variables or YAML config.