Skip to main content
Version: Latest

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​

  1. autobrr sees a new release from a tracker
  2. 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/check endpoint via an external filter
  3. 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-seed
    • 202 Accepted: a matching torrent exists but is still downloading; autobrr retries later
    • 404 Not Found: no matching torrent exists
  4. On 200 OK, an autobrr action sends the torrent file to qui's /api/cross-seed/apply endpoint - qui reads the actual size from the torrent metadata, runs the match again against your current torrents, then adds it to qBittorrent
Filterall checks passed ✓EXTERNAL · WEBHOOKPOST /api/cross-seed/webhook/checkqui · X-API-Key200202404Cross-seed ✓action sends to qui applyStill downloadingautobrr retries laterNo match ✗release rejected

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​

info

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:

FieldValue
TypeWebhook
Namequi
On ErrorReject
Endpointhttp://localhost:7476/api/cross-seed/webhook/check
HTTP MethodPOST
HTTP Request HeadersX-API-Key=YOUR_QUI_API_KEY
Expected HTTP Status Code200

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 announced
  • size (optional): the size in bytes from the announce or feed data autobrr already has; omitted or 0 means no size is available
  • instanceIds (optional): qBittorrent instance IDs to scan; omit to search all instances
  • indexer (optional): autobrr indexer identifier (e.g. hdb); on /check qui just writes it to the debug log
  • findIndividualEpisodes (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.

Docker Compose

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​

caution

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:

FieldValue
Action TypeWebhook
Namequi cross-seed
Endpointhttp://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 bytes
  • torrentName (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 behavior
  • instanceIds (optional): target instances; omit to apply to any matching instance
  • indexer (optional): autobrr indexer identifier (e.g. hdb); when qui's "Use indexer name as category" mode is enabled, qui uses this as the category
  • tags (optional): override webhook tags from qui's settings
  • category (optional): override category; takes precedence over indexer
  • startPaused (optional): override whether torrents are added paused
  • skipIfExists (optional): skip adding if the torrent already exists
  • findIndividualEpisodes (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:

  1. Confirm you added the /apply action. The external webhook (/check) does not add torrents.
  2. Fix Docker networking. http://localhost:7476/... only works if autobrr can reach qui on its own localhost. In Docker Compose, use the qui service hostname, e.g. http://qui:7476/api/cross-seed/apply?apikey=....
  3. Double-check auth. /check uses the header X-API-Key=..., while /apply uses the query string ?apikey=....
  4. Verify qui can talk to qBittorrent. In the qui UI: Settings → Instances → Test Connection.
  5. 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/check and /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