Cross-seed with qui
qui is our multi-instance qBittorrent WebUI with built-in cross-seed support. It integrates with autobrr through webhook endpoints, enabling real-time cross-seed detection when autobrr sees a new announce.
Unlike the cross-seed third party setup, this is a first party integration: no extra daemon, config file or torznab endpoints are needed. If you run qBittorrent and qui, all you need is a filter in autobrr.
How it works
- autobrr sees a new release from a tracker
- autobrr sends the torrent name, the size it already knows from the announce or feed, and the indexer identifier to qui's
/api/cross-seed/webhook/checkendpoint via an external filter - qui searches your qBittorrent instances for matching content, without downloading the torrent file, and responds with:
200 OK: a matching torrent is complete and ready to cross-seed202 Accepted: a matching torrent exists but is still downloading; autobrr retries later404 Not Found: no matching torrent exists
- On
200 OK, an autobrr action sends the torrent file to qui's/api/cross-seed/applyendpoint - qui reads the actual size from the torrent metadata, runs the match again against your current torrents, then adds it to qBittorrent
Cross-seeds are added paused with skip_checking=true. Title, season, episode and split release-group fallbacks always need a full piece check - qui keeps those paused, runs the check, and only resumes them at 100%. Soft differences like codec, source, HDR, edition or one-sided checksum data take the fast path once the file and layout checks pass, and from there qui's Max auto-start download rule decides: under the limit it starts, over it stays paused for review. If the only missing files are ignorable sidecars there is a 200 MiB exception.
Setup
1. Create an API key in qui
In qui, go to Settings → API Keys, click Create API Key, name it (e.g. autobrr webhook) and copy the generated key. We will refer to it as YOUR_QUI_API_KEY below.
2. Create the filter
Create a new filter dedicated to qui. Select all the indexers you want to cross-seed from, preferably all of them.
Create a filter named e.g. qui cross-seed and set a really high priority to make sure it runs before your other filters.
3. Add the external webhook
Go to the External tab of the filter and add a new external filter:
| Field | Value |
|---|---|
| Type | Webhook |
| Name | qui |
| On Error | Reject |
| Endpoint | http://localhost:7476/api/cross-seed/webhook/check |
| HTTP Method | POST |
| HTTP Request Headers | X-API-Key=YOUR_QUI_API_KEY |
| Expected HTTP Status Code | 200 |
Data (JSON):
{
"torrentName": {{ toRawJson .TorrentName }},
"size": {{ .Size }},
"instanceIds": [1],
"indexer": {{ toRawJson .Indexer }}
}
To search all your qBittorrent instances, omit instanceIds:
{
"torrentName": {{ toRawJson .TorrentName }},
"size": {{ .Size }},
"indexer": {{ toRawJson .Indexer }}
}
Field descriptions:
torrentName(required): the release name as announcedsize(optional): the size in bytes from the announce or feed data autobrr already has; omitted or0means no size is availableinstanceIds(optional): qBittorrent instance IDs to scan; omit to search all instancesindexer(optional): autobrr indexer identifier (e.g.hdb); on/checkqui just writes it to the debug logfindIndividualEpisodes(optional): override qui's global episode matching setting
An exact size counts for more than a rounded one - if it matches the local torrent exactly, qui can approve metadata differences it would otherwise reject. A rounded or mismatched value only passes the normal strict match within the configured size tolerance. If the size is 0, qui falls back to a narrow name-only check, which is enough to let the action download the torrent but not to add it. qui's autobrr integration docs have the details.
If autobrr and qui are both containers, localhost inside autobrr is the autobrr container, not qui. Use the qui container hostname instead (often the Compose service name), for example http://qui:7476/api/cross-seed/webhook/check.
4. Configure retry handling
qui answers 202 Accepted when a match exists but is still downloading. Use the Retry block of the external filter to handle this:
- Retry HTTP status code(s):
202 - Maximum retry attempts:
10 - Retry delay in seconds:
4
5. Add the apply action
The external webhook only answers "is this ready to cross-seed?"; it does not add anything to qBittorrent. You must also add this action, otherwise nothing gets added.
Go to the Actions tab of the filter and add a new action:
| Field | Value |
|---|---|
| Action Type | Webhook |
| Name | qui cross-seed |
| Endpoint | http://localhost:7476/api/cross-seed/apply?apikey=YOUR_QUI_API_KEY |
Payload (JSON):
{
"torrentData": "{{ .TorrentDataRawBytes | toString | b64enc }}",
"torrentName": {{ toRawJson .TorrentName }},
"instanceIds": [1],
"indexer": {{ toRawJson .Indexer }}
}
Field descriptions:
torrentData(required): base64-encoded torrent file bytestorrentName(optional): the original announced name - include it to get reported-size matching and metadata-change detection. Without it you get qui's legacy strict apply behaviorinstanceIds(optional): target instances; omit to apply to any matching instanceindexer(optional): autobrr indexer identifier (e.g.hdb); when qui's "Use indexer name as category" mode is enabled, qui uses this as the categorytags(optional): override webhook tags from qui's settingscategory(optional): override category; takes precedence overindexerstartPaused(optional): override whether torrents are added pausedskipIfExists(optional): skip adding if the torrent already existsfindIndividualEpisodes(optional): override qui's global episode matching setting
Finally, make sure the filter is enabled and you're all set.
Troubleshooting
autobrr shows the filter accepted the release, but nothing shows up in qBittorrent:
- Confirm you added the
/applyaction. The external webhook (/check) does not add torrents. - Fix Docker networking.
http://localhost:7476/...only works if autobrr can reach qui on its ownlocalhost. In Docker Compose, use the qui service hostname, e.g.http://qui:7476/api/cross-seed/apply?apikey=.... - Double-check auth.
/checkuses the headerX-API-Key=..., while/applyuses the query string?apikey=.... - Verify qui can talk to qBittorrent. In the qui UI: Settings → Instances → Test Connection.
- Check paused torrents. Cross-seeds are often added paused; look in qBittorrent's paused list and any cross-seed tag/category you configured.
For anything beyond this, see qui's cross-seed troubleshooting docs.
Going further
- Webhook source filters: by default qui scans all torrents on your instances when looking for matches. You can include/exclude categories and tags in the qui UI under Cross-Seed → Auto → Webhook / autobrr. Excludes win over includes, tag matching is case-sensitive, and if you set both category and tag includes a torrent has to pass both.
- Season packs: qui has a dedicated season pack flow with separate endpoints (
/api/cross-seed/season-pack/checkand/api/cross-seed/season-pack/apply) that links already downloaded episodes into an announced season pack. It requires a separate autobrr filter; see qui's season packs docs for full setup instructions. - Full qui documentation: getqui.com/docs/features/cross-seed/autobrr