Developers
Integrations and webhooks
Manage libraries from another service with the integration key, and get webhooks for downloads and streaming.
The integration API is for backend services that manage a Wren TV server automatically, such as a script that creates libraries for a new drive, or a service that reacts when a download finishes.
The integration key
Every server generates an integration key on first start (wtv_…). Find it in Manage → Settings → Integrations,
or as integration_key in wrentv.yaml. Send it on every request:
httpX-WrenTV-Key: wtv_…
Endpoints
| Method | Path | Body |
|---|---|---|
| GET | /api/integration/libraries | |
| POST | /api/integration/libraries | {"name": "Movies", "type": "movies", "paths": ["/media/Movies"]}. type is movies or shows |
| POST | /api/integration/libraries/{id}/scan |
bashcurl -X POST https://<server>/api/integration/libraries \
-H "X-WrenTV-Key: $KEY" \
-d '{"name": "Movies", "type": "movies", "paths": ["/media/Movies"]}'
Webhooks
Add webhooks in Manage → Settings → Integrations, or in wrentv.yaml:
yamlwebhooks:
- url: https://backend.example.com/wrentv
secret: change-me
events: [download.completed, download.failed] # empty means all events
Events
| Event | Sent when |
|---|---|
download.completed | A server download finished and the file is in its library |
download.failed | A server download failed (no space, quota reached, the stream stopped working) |
stream.unavailable | Someone tried to play a streamed title and no cached release was found |
Request
httpPOST https://backend.example.com/wrentv
Content-Type: application/json
X-WrenTV-Event: download.completed
X-WrenTV-Signature: sha256=<hex HMAC-SHA256 of the body, keyed with the webhook secret>
json{
"event": "download.completed",
"id": "…",
"timestamp": 1760000000,
"server": { "id": "…", "name": "Wren TV", "url": "https://wrentv.example.com" },
"data": {
"id": "…", "itemId": "…", "title": "Dune - Part Two (2024)", "quality": "2160p", "status": "done",
"path": "/media/Movies/Dune - Part Two (2024) {tmdb-693134}/Dune - Part Two (2024) - 2160p.mkv",
"bytes": 61234567890, "total": 61234567890
}
}
stream.unavailable sends {"itemId", "title", "error"}. server.url uses the server's public_url, so set one if
your backend runs elsewhere.
Verifying the signature
Compute the HMAC-SHA256 of the raw request body with your secret and compare it to the header in constant time:
jsimport { createHmac, timingSafeEqual } from 'node:crypto';
function verify(body, header, secret) {
const want = 'sha256=' + createHmac('sha256', secret).update(body).digest('hex');
return header.length === want.length && timingSafeEqual(Buffer.from(header), Buffer.from(want));
}