# Account audit log Source: https://docs.torrin.app/api-reference/account/account-audit-log /api-reference/openapi.yaml get /api/me/audit # Delete the account Source: https://docs.torrin.app/api-reference/account/delete-the-account /api-reference/openapi.yaml delete /api/me # End-of-month recap Source: https://docs.torrin.app/api-reference/account/end-of-month-recap /api-reference/openapi.yaml get /api/wrapped A year-in-review style recap. Available only in the last 7 days of each month. # Get the current account Source: https://docs.torrin.app/api-reference/account/get-the-current-account /api-reference/openapi.yaml get /api/me # Monthly ingest usage Source: https://docs.torrin.app/api-reference/account/monthly-ingest-usage /api-reference/openapi.yaml get /api/usage/ingest Your uncached ingest this calendar month against your plan's cap. # Pause the subscription Source: https://docs.torrin.app/api-reference/account/pause-the-subscription /api-reference/openapi.yaml post /api/me/pause Freezes the remaining time on the subscription so it isn't consumed while unused. # Referral code and stats Source: https://docs.torrin.app/api-reference/account/referral-code-and-stats /api-reference/openapi.yaml get /api/referral # Regenerate the API key Source: https://docs.torrin.app/api-reference/account/regenerate-the-api-key /api-reference/openapi.yaml post /api/auth/regenerate-key Rotates the API key. The old key stops working immediately. # Resume the subscription Source: https://docs.torrin.app/api-reference/account/resume-the-subscription /api-reference/openapi.yaml post /api/me/resume # Update email Source: https://docs.torrin.app/api-reference/account/update-email /api-reference/openapi.yaml put /api/me # Watch history Source: https://docs.torrin.app/api-reference/account/watch-history /api-reference/openapi.yaml get /api/history # Your usage stats Source: https://docs.torrin.app/api-reference/account/your-usage-stats /api-reference/openapi.yaml get /api/stats # Begin two-factor enrollment Source: https://docs.torrin.app/api-reference/auth/begin-two-factor-enrollment /api-reference/openapi.yaml post /api/auth/2fa/enroll Returns a secret / otpauth URI to add to an authenticator app, then confirm at /api/auth/2fa/confirm. # Complete a login challenge Source: https://docs.torrin.app/api-reference/auth/complete-a-login-challenge /api-reference/openapi.yaml post /api/auth/login/verify # Confirm two-factor enrollment Source: https://docs.torrin.app/api-reference/auth/confirm-two-factor-enrollment /api-reference/openapi.yaml post /api/auth/2fa/confirm # Create an API key Source: https://docs.torrin.app/api-reference/auth/create-an-api-key /api-reference/openapi.yaml post /api/keys # Disable two-factor Source: https://docs.torrin.app/api-reference/auth/disable-two-factor /api-reference/openapi.yaml post /api/auth/2fa/disable # List API keys Source: https://docs.torrin.app/api-reference/auth/list-api-keys /api-reference/openapi.yaml get /api/keys # Log in with an API key Source: https://docs.torrin.app/api-reference/auth/log-in-with-an-api-key /api-reference/openapi.yaml post /api/auth/login/key # Log in with email and password Source: https://docs.torrin.app/api-reference/auth/log-in-with-email-and-password /api-reference/openapi.yaml post /api/auth/login Returns a challenge to complete at /api/auth/login/verify with the emailed or authenticator code. # Revoke an API key Source: https://docs.torrin.app/api-reference/auth/revoke-an-api-key /api-reference/openapi.yaml delete /api/keys/{id} # Set or change your password Source: https://docs.torrin.app/api-reference/auth/set-or-change-your-password /api-reference/openapi.yaml post /api/auth/password # Verify a two-factor code Source: https://docs.torrin.app/api-reference/auth/verify-a-two-factor-code /api-reference/openapi.yaml post /api/auth/2fa/verify # Authentication Source: https://docs.torrin.app/api-reference/authentication How to get and use your torrin API key. Every torrent and store endpoint is authenticated with your instance **API key** — the same key your Stremio addon and dashboard use. Public `/api/*` endpoints need no auth. ## Get your API key Your API key is issued when your account is created. On the hosted instance it's shown in the dashboard; you can also create an account directly: ```bash theme={null} curl -X POST https://api.torrin.app/api/auth/register \ -H "Content-Type: application/json" \ -d '{ "email": "you@example.com" }' ``` ```json theme={null} { "api_key": "YOUR_API_KEY", "email": "you@example.com", "plan": "free" } ``` Treat the key like a password. Your primary key grants full account access — if it leaks, rotate it from the dashboard. For addons, prefer a dedicated **API-only** key (below) that can be revoked on its own and can't be used to sign in. ## Use it Send the key as a bearer token on every authenticated request: ```bash theme={null} curl https://api.torrin.app/rest/1.0/torrents \ -H "Authorization: Bearer YOUR_API_KEY" ``` For compatibility, two surfaces also accept alternate forms: | Surface | Also accepts | | - | - | | `/rest/1.0/*` | `?auth_token=YOUR_API_KEY` query parameter | | `/v0/store/*` | `X-StremThru-Store-Authorization: Bearer YOUR_API_KEY` header | ## Multiple keys & API-only keys Instead of sharing one key everywhere, you can issue several. Give each addon or integration its own labelled key, so if one leaks you revoke just that key without disturbing the others. Each key is one of: * **Login-enabled** — full account access (dashboard, billing, credentials, key management). * **API-only** — can stream, download, and search, but **cannot sign in or change your account**. This is the default for new keys and the right choice for an addon: a leaked API-only key can't take over your account. Every key belongs to your account, so all of them are bound to your plan, limits, and expiry. Managing keys requires a login-enabled key. API-only keys receive `403` on account routes. ### List your keys ```bash theme={null} curl https://api.torrin.app/api/keys \ -H "Authorization: Bearer YOUR_LOGIN_KEY" ``` ```json theme={null} { "keys": [ { "id": "k_123", "label": "AIOStreams", "login_allowed": false, "created_at": "2026-08-13T00:00:00Z", "last_used_at": "2026-08-13T12:00:00Z" } ] } ``` ### Create a key ```bash theme={null} curl -X POST https://api.torrin.app/api/keys \ -H "Authorization: Bearer YOUR_LOGIN_KEY" \ -H "Content-Type: application/json" \ -d '{ "label": "AIOStreams", "login_allowed": false }' ``` ```json theme={null} { "id": "k_123", "label": "AIOStreams", "key": "YOUR_NEW_KEY", "login_allowed": false } ``` The `key` is returned **once** — store it now. Up to 20 keys per account. ### Revoke a key ```bash theme={null} curl -X DELETE https://api.torrin.app/api/keys/k_123 \ -H "Authorization: Bearer YOUR_LOGIN_KEY" ``` The key stops working immediately. ## Errors | Status | Meaning | | - | - | | `401` | Missing or invalid key. | | `403` | The subscription is paused or expired — resume or renew, then retry. | Both error shapes differ by surface: `/rest/1.0` returns `{ "error": "...", "error_code": 8 }`, and `/v0/store` returns `{ "error": { "code": 401, "message": "..." } }`. # Get a crypto invoice Source: https://docs.torrin.app/api-reference/billing/get-a-crypto-invoice /api-reference/openapi.yaml get /api/billing/crypto/invoice/{id} Poll the status of a crypto checkout invoice. # Get an invoice payment address Source: https://docs.torrin.app/api-reference/billing/get-an-invoice-payment-address /api-reference/openapi.yaml post /api/billing/crypto/invoice/{id}/address Returns a pay-to address for the chosen coin on a crypto invoice. # Open the Bachs billing portal Source: https://docs.torrin.app/api-reference/billing/open-the-bachs-billing-portal /api-reference/openapi.yaml post /api/billing/bachs/portal # Redeem a code Source: https://docs.torrin.app/api-reference/billing/redeem-a-code /api-reference/openapi.yaml post /api/redeem Redeem a reseller/gift code to activate a plan. # Start a card checkout Source: https://docs.torrin.app/api-reference/billing/start-a-card-checkout /api-reference/openapi.yaml post /api/billing/bachs/checkout Creates a Bachs hosted checkout and returns its URL. # Start a crypto checkout Source: https://docs.torrin.app/api-reference/billing/start-a-crypto-checkout /api-reference/openapi.yaml post /api/billing/crypto/checkout Creates a Bitcart invoice; poll the returned invoice id for payment status. # Bring your own key (BYOK) Source: https://docs.torrin.app/api-reference/byok Add your own RealDebrid, AllDebrid, Premiumize, or TorBox key. Torrin can download through its own shared debrid pool, but you can also attach your own provider keys. When you do, torrin checks and pulls through **your** key first, falling back to the shared pool — so you get your provider's cache and limits, not just torrin's. ## Why * **Your cache, your limits.** Availability checks and downloads use your account, so a title cached on your RealDebrid is instant for you even if torrin has never seen it. * **More providers.** Add Premiumize or TorBox and torrin's availability check (`/v0/store/magnets/check`) fans out across all of them. ## The endpoints Each provider has the same three calls, under its own prefix (`/api/rd`, `/api/alldebrid`, `/api/premiumize`, `/api/torbox`): | Call | Does | | - | - | | `GET /api/{provider}/credentials` | Report whether a key is set (returns it **masked**). | | `POST /api/{provider}/credentials` | Validate and save a key. A bad key is rejected with `400`. | | `DELETE /api/{provider}/credentials` | Remove the key. | Set a key: ```bash theme={null} curl -X POST https://api.torrin.app/api/rd/credentials \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "api_key": "YOUR_REALDEBRID_KEY" }' ``` `GET /api/debrid/usage` reports which providers are configured and how they've been used. Keys are stored encrypted at rest and only ever returned masked. Removing a key takes effect immediately — the next request falls back to torrin's shared pool. # Bring your own storage (BYOS) Source: https://docs.torrin.app/api-reference/byos Stream from your own cloud — S3 or an rclone remote. By default torrin caches content in its own shared store and evicts it over time. With **bring-your-own-storage**, content you stream is also written to *your* cloud, encrypted, and served back through torrin — so it's permanent, private, and counts against your space, not torrin's. ## How it works When BYOS is connected, a completed stream is copied to your backend under an encrypted key (torrin never stores your filenames in the clear). Playback still goes through torrin's signed, geo-routed URLs, so the origin and your credentials stay hidden — the client can't tell BYOS content from shared-cache content. ## Connecting Two shapes, depending on the backend: * **S3-compatible** (S3, Backblaze, Wasabi, Garage...) — pass the endpoint and keys directly: ```bash theme={null} curl -X POST https://api.torrin.app/api/storage/connect \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "provider": "s3", "endpoint": "https://s3.example.com", "region": "us-east-1", "bucket": "my-torrin", "access_key": "...", "secret_key": "..." }' ``` * **OAuth backends** (Google Drive, Dropbox, OneDrive, pCloud) — start the consent flow at `GET /api/storage/oauth/{provider}/start` and torrin finishes the connection on callback. ## Managing it | Call | Does | | - | - | | `GET /api/storage` | Whether BYOS is connected, plus the provider count and pool policy. | | `POST /api/storage/connect` | Connect the primary backend. | | `DELETE /api/storage` | Disconnect. Existing content stays in your cloud. | | `PUT /api/storage/providers` | Add providers to the pool and set the policy (`epmfs` pool or `all` mirror). | | `DELETE /api/storage/providers/{idx}` | Remove one provider from the pool. | | `GET /api/storage/rclone-config` | Download your `rclone.conf` to mount the storage yourself. | | `PUT /api/storage/evict-policy` | Set cleanup rules: `evict_after_days`, `evict_max_gb` (0 = keep everything). | | `POST /api/storage/evict` | Remove one release (`info_hash`) from your storage. | ## Pooling and mirroring Add more providers to act as one encrypted pool. `union_policy` picks placement: `epmfs` (default) spreads files across providers by free space; `all` mirrors every file to every provider for redundancy. ```bash theme={null} curl -X PUT https://api.torrin.app/api/storage/providers \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "union_policy": "all", "providers": [ { "endpoint": "https://s3.example.com", "bucket": "backup", "access_key": "...", "secret_key": "..." } ] }' ``` Everything written to your storage is encrypted with a key torrin holds, so a raw browse of your bucket shows opaque blobs, not media. # Cairn Source: https://docs.torrin.app/api-reference/cairn Permanent, Usenet-backed archive of your cached content. Torrin's shared cache is a cache — content gets evicted when it's cold or over capacity. **Cairn** is the escape hatch: it posts a cached item to Usenet, so even after the hot copy is evicted, the item can be re-hydrated on demand instead of re-downloaded from the source. ## Why * **Permanence without the storage bill.** Keeping everything hot is expensive; Usenet retention is cheap and long. Cairn trades an instant hot copy for a durable cold one. * **Survives eviction.** An archived title comes back even if it's since dropped out of the live cache and the original torrent is dead. ## How it works ``` POST /api/cairn archive an item │ torrin posts the cached blobs to Usenet, stores the NZB │ (later) the hot copy is evicted │ POST /api/cairn/{hash}/restore re-hydrate from the stored NZB → back in the cache ``` The NZB (the index needed to pull it back) is kept durably and backed up, so a restore is a Usenet re-download, not a re-leech of the original. ## The endpoints | Call | Does | | - | - | | `GET /api/cairn` | List your archived items. | | `POST /api/cairn` | Archive a cached item by info-hash (queued, `202`). | | `POST /api/cairn/{hash}/restore` | Re-hydrate an archived item back into the cache. | | `DELETE /api/cairn/{hash}` | Drop the archive. | Archiving is queued and happens in the background — posting to Usenet takes minutes per title. The item stays streamable from cache in the meantime. # Archive an item Source: https://docs.torrin.app/api-reference/cairn/archive-an-item /api-reference/openapi.yaml post /api/cairn Posts a cached item to Usenet so it survives cache eviction. # List archives Source: https://docs.torrin.app/api-reference/cairn/list-archives /api-reference/openapi.yaml get /api/cairn Items you've archived to torrin's permanent Usenet-backed store. # Remove an archive Source: https://docs.torrin.app/api-reference/cairn/remove-an-archive /api-reference/openapi.yaml delete /api/cairn/{hash} # Restore from archive Source: https://docs.torrin.app/api-reference/cairn/restore-from-archive /api-reference/openapi.yaml post /api/cairn/{hash}/restore Re-hydrates an archived item back into the live cache from Usenet. # How the API works Source: https://docs.torrin.app/api-reference/concepts Torrin's model — jobs, the shared cache, and signed streams — and how the two API dialects map onto it. Under the RealDebrid-shaped responses, torrin is not a debrid reseller — it downloads on its own box behind a VPN, caches the result, and hands you a signed HTTPS stream. Understanding that model makes the endpoints obvious. ## The lifecycle Every source you add — a magnet, a hash, a release link — becomes a **job**. A job moves through a handful of states, and what you get back depends on where it is: ``` add magnet/hash │ cached in torrin's shared store? │ ├── yes ──> job is "downloaded" immediately ──> links are ready │ └── no ───> job downloads on the box (behind a VPN) │ pending → downloading → uploading → downloaded │ └── once downloaded ──> cached for everyone ──> links ready ``` * **Cached hits are instant.** The bytes are already on disk, so the job is created as `downloaded` and its `links` are populated on the same call. * **Misses download server-side.** You poll the torrent until its status is `downloaded`, then unrestrict a link. No local torrent client, no seeding, no waiting on your own bandwidth. * **The cache is shared.** Once anyone downloads a title, it's cached for every user — which is why the [availability check](#instant-availability) matters before you add. ## Two dialects, one engine Torrin exposes the *same* jobs through two API shapes. Pick the one your client speaks — they sit on the same cache and the same downloads. | | `/rest/1.0` | `/v0/store` | | - | - | - | | **Speaks** | RealDebrid API | StremThru store API | | **Use it for** | Stremio RD addons, RD-compatible clients, direct integration | StremThru clients like Comet | | **Auth header** | `Authorization: Bearer ` | `X-StremThru-Store-Authorization: Bearer ` | | **Add a source** | `POST /torrents/addMagnet` (form) | `POST /magnets` (JSON) | | **Availability** | `GET /torrents/instantAvailability/{hashes}` | `GET /magnets/check` | | **Play a file** | `POST /unrestrict/link` | the file's `link` (already playable) | If you're integrating from scratch, use `/rest/1.0` — it's the most widely supported shape and every RD tutorial applies. ## Instant availability Before adding a magnet, ask whether it's already cached. This is the cheapest call in the API and it decides whether playback is instant or a download: * `/rest/1.0/torrents/instantAvailability/{hashes}` returns a key for **every** hash you asked about — a `rd` array when cached, or an empty `{}` when it isn't. Key *presence* is not enough; check that the `rd` array is non-empty. * `/v0/store/magnets/check` is richer. Each item is one of: | Status | Meaning | | - | - | | `cached` | Streamable right now from torrin's shared store. `files` are populated. | | `acceleratable` | Not in the shared store, but reachable via a connected provider (system AD/RD or your own key) and can be pulled quickly. | | `unknown` | Not available. Adding it starts a fresh download. | ## Streaming A torrent's `links` are `torrin://` references, not URLs. To play one, **unrestrict** it: ```bash theme={null} curl -X POST https://api.torrin.app/rest/1.0/unrestrict/link \ -H "Authorization: Bearer YOUR_API_KEY" \ --data-urlencode "link=torrin://blobs/b_afb2a849" ``` You get back a `download` URL: a **signed HTTPS link valid for 24 hours**, served from the nearest cache node with the origin IP hidden. It supports range requests, so players can seek. (StremThru `files[].link` is already this signed URL — no unrestrict step.) Torrin doesn't know a file's exact byte size until it's read, so `unrestrict` returns `filesize: 0`. Take the real size from the torrent's file entry (`files[].bytes`) instead. ## A typical flow Adding a magnet and playing the biggest file, end to end: `GET /rest/1.0/torrents/instantAvailability/{hash}` — if the `rd` array is non-empty, playback will be instant. `POST /rest/1.0/torrents/addMagnet` with the magnet. You get back an `id`. Poll `GET /rest/1.0/torrents/info/{id}` until `status` is `downloaded`. A cached add is already `downloaded`. Pick a `link` from the torrent, `POST /rest/1.0/unrestrict/link`, and open the returned `download` URL. ## Job statuses The RealDebrid `status` field is torrin's internal job state, mapped to RD's vocabulary: | Torrin state | RD `status` | Meaning | | - | - | - | | pending | `magnet_conversion` | Accepted, resolving the magnet. | | queued | `queued` | Waiting for a download slot. | | downloading / processing | `downloading` | Fetching + unpacking on the box. | | publishing | `uploading` | Writing the finished media to the cache. | | complete / seeding | `downloaded` | Cached and ready — `links` are populated. | | failed | `error` | The download failed. | Your plan's concurrency limit applies to jobs that are actively downloading; cached adds don't consume a slot. Hitting the limit returns `503` (RD) / `429` (store) with a `slot limit reached` message. # Debrid usage Source: https://docs.torrin.app/api-reference/debrid-keys/debrid-usage /api-reference/openapi.yaml get /api/debrid/usage Reports which providers are configured and how they've been used. # Get AllDebrid key status Source: https://docs.torrin.app/api-reference/debrid-keys/get-alldebrid-key-status /api-reference/openapi.yaml get /api/alldebrid/credentials # Get Premiumize key status Source: https://docs.torrin.app/api-reference/debrid-keys/get-premiumize-key-status /api-reference/openapi.yaml get /api/premiumize/credentials # Get RealDebrid key status Source: https://docs.torrin.app/api-reference/debrid-keys/get-realdebrid-key-status /api-reference/openapi.yaml get /api/rd/credentials # Get TorBox key status Source: https://docs.torrin.app/api-reference/debrid-keys/get-torbox-key-status /api-reference/openapi.yaml get /api/torbox/credentials # Remove AllDebrid key Source: https://docs.torrin.app/api-reference/debrid-keys/remove-alldebrid-key /api-reference/openapi.yaml delete /api/alldebrid/credentials # Remove Premiumize key Source: https://docs.torrin.app/api-reference/debrid-keys/remove-premiumize-key /api-reference/openapi.yaml delete /api/premiumize/credentials # Remove RealDebrid key Source: https://docs.torrin.app/api-reference/debrid-keys/remove-realdebrid-key /api-reference/openapi.yaml delete /api/rd/credentials # Remove TorBox key Source: https://docs.torrin.app/api-reference/debrid-keys/remove-torbox-key /api-reference/openapi.yaml delete /api/torbox/credentials # Set AllDebrid key Source: https://docs.torrin.app/api-reference/debrid-keys/set-alldebrid-key /api-reference/openapi.yaml post /api/alldebrid/credentials # Set Premiumize key Source: https://docs.torrin.app/api-reference/debrid-keys/set-premiumize-key /api-reference/openapi.yaml post /api/premiumize/credentials # Set RealDebrid key Source: https://docs.torrin.app/api-reference/debrid-keys/set-realdebrid-key /api-reference/openapi.yaml post /api/rd/credentials # Set TorBox key Source: https://docs.torrin.app/api-reference/debrid-keys/set-torbox-key /api-reference/openapi.yaml post /api/torbox/credentials # qBittorrent add torrent Source: https://docs.torrin.app/api-reference/download-client/qbittorrent-add-torrent /api-reference/openapi.yaml post /api/v2/torrents/add Adds a magnet (urls field) or an uploaded .torrent (torrents field), optionally under a category. Torrin ingests it through its normal pipeline. # qBittorrent delete torrent Source: https://docs.torrin.app/api-reference/download-client/qbittorrent-delete-torrent /api-reference/openapi.yaml post /api/v2/torrents/delete # qBittorrent list torrents Source: https://docs.torrin.app/api-reference/download-client/qbittorrent-list-torrents /api-reference/openapi.yaml get /api/v2/torrents/info Returns your jobs in qBittorrent's torrent-list shape so the *arr app can track progress and detect completion. # qBittorrent login Source: https://docs.torrin.app/api-reference/download-client/qbittorrent-login /api-reference/openapi.yaml post /api/v2/auth/login qBittorrent WebUI auth. Send your Torrin API key as the username or password. Returns the text "Ok." and a session cookie, matching qBittorrent so Sonarr/Radarr connect unchanged. See the Download client guide. # SABnzbd API Source: https://docs.torrin.app/api-reference/download-client/sabnzbd-api /api-reference/openapi.yaml get /api SABnzbd-compatible endpoint (also served at /sabnzbd/api). The mode parameter selects the function. Authenticate with apikey set to your Torrin API key. # SABnzbd API (file upload) Source: https://docs.torrin.app/api-reference/download-client/sabnzbd-api-file-upload /api-reference/openapi.yaml post /api SABnzbd addfile. POST an .nzb as multipart field "name" with mode=addfile. # API Overview Source: https://docs.torrin.app/api-reference/introduction Torrin's HTTP API — RealDebrid-compatible, StremThru store, and public endpoints. Torrin speaks the **RealDebrid API**. Most clients and Stremio addons that already support RealDebrid work by pointing their base URL at your instance and using your torrin API key. On top of that, torrin exposes a **StremThru store** API for clients like Comet, and a few **public** endpoints for signup and instance metadata. ## Base URL All requests go to your instance: ``` https://api.torrin.app ``` If you self-host, use your own base URL instead. Every path below is relative to it. ## The three surfaces `/rest/1.0/*` — user, torrents, and link unrestriction in RealDebrid's exact response shapes. This is what Stremio addons and RD clients use. `/v0/store/*` — the StremThru store contract used by clients such as Comet. `/api/*` — create an account and read instance metadata. No auth required. ## A first request Fetch the authenticated user with your API key: ```bash theme={null} curl https://api.torrin.app/rest/1.0/user \ -H "Authorization: Bearer YOUR_API_KEY" ``` ```json theme={null} { "id": 1779700568426389947, "username": "you", "email": "you@example.com", "type": "premium", "premium": 2419200, "expiration": "2026-08-26T12:00:00Z" } ``` `premium` is the number of seconds left on the subscription — this is how RD clients detect an active account, so torrin returns it in the same shape. ## Conventions * **Auth** is your API key as a bearer token. See [Authentication](/api-reference/authentication). * **Times** are RFC 3339 (`2026-08-26T12:00:00Z`). * **Errors** use the surface's native shape — RealDebrid's `{ error, error_code }` on `/rest/1.0`, and StremThru's `{ error: { code, message } }` on `/v0/store`. Each endpoint below lists the status codes it can return. Use the endpoint pages in the sidebar to see every parameter, response schema, and a runnable example you can try with your own key. # Add a file-host link Source: https://docs.torrin.app/api-reference/jobs/add-a-file-host-link /api-reference/openapi.yaml post /api/jobs/hoster Unrestrict a supported file-host URL. Requires a paid plan. # Add a job Source: https://docs.torrin.app/api-reference/jobs/add-a-job /api-reference/openapi.yaml post /api/jobs Add a magnet, info-hash, or a supported web/file-host URL. Cached content returns `200` with a ready job (stream URLs populated); a new download returns `202`. # Add an NZB Source: https://docs.torrin.app/api-reference/jobs/add-an-nzb /api-reference/openapi.yaml post /api/jobs/nzb Upload an `.nzb` file body to download from Usenet. # Add anything (auto-detected) Source: https://docs.torrin.app/api-reference/jobs/add-anything-auto-detected /api-reference/openapi.yaml post /api/add One endpoint that detects what you send and routes it automatically, so nothing has to pick a type. Send a magnet URI, a 40-char info-hash, or a supported web/file-host URL as the raw request body, or upload a `.torrent` or `.nzb` file as `multipart/form-data`. Cached content returns `200` with a ready job (stream URLs populated); a new download returns `202`. # Delete a job Source: https://docs.torrin.app/api-reference/jobs/delete-a-job /api-reference/openapi.yaml delete /api/jobs/{id} # Download all files as a zip Source: https://docs.torrin.app/api-reference/jobs/download-all-files-as-a-zip /api-reference/openapi.yaml get /api/jobs/{id}/zip # Get a job Source: https://docs.torrin.app/api-reference/jobs/get-a-job /api-reference/openapi.yaml get /api/jobs/{id} # Grab a scene-rls release Source: https://docs.torrin.app/api-reference/jobs/grab-a-scene-rls-release /api-reference/openapi.yaml post /api/scenerls/grab Starts a download of a chosen scene-rls release. Paid plans only. # Grab an HDEncode release Source: https://docs.torrin.app/api-reference/jobs/grab-an-hdencode-release /api-reference/openapi.yaml post /api/hdencode/grab Starts a download of a chosen HDEncode release. Paid plans only. # Import hashes from your debrid library Source: https://docs.torrin.app/api-reference/jobs/import-hashes-from-your-debrid-library /api-reference/openapi.yaml post /api/{provider}/import Bulk-import provider hashes as Torrin download jobs. Paid plans only. Partial failures are reported in `errors`, not the status code. # Job progress Source: https://docs.torrin.app/api-reference/jobs/job-progress /api-reference/openapi.yaml get /api/jobs/{id}/progress Lightweight status/progress for polling an active download. # List jobs Source: https://docs.torrin.app/api-reference/jobs/list-jobs /api-reference/openapi.yaml get /api/jobs # Recheck a job Source: https://docs.torrin.app/api-reference/jobs/recheck-a-job /api-reference/openapi.yaml post /api/jobs/{id}/recheck Re-verifies a completed torrent or Usenet download. # Retry a job Source: https://docs.torrin.app/api-reference/jobs/retry-a-job /api-reference/openapi.yaml post /api/jobs/{id}/retry Requeues a failed or evicted job. # Sign stream URLs Source: https://docs.torrin.app/api-reference/jobs/sign-stream-urls /api-reference/openapi.yaml post /api/jobs/{id}/sign Returns fresh signed, geo-routed stream URLs for a completed job's files. # Upload a .torrent file Source: https://docs.torrin.app/api-reference/jobs/upload-a-torrent-file /api-reference/openapi.yaml post /api/torrents/upload Upload a .torrent (multipart field `torrent`, max 5 MiB) to start a download or a private-tracker seed. # Create an account Source: https://docs.torrin.app/api-reference/public/create-an-account /api-reference/openapi.yaml post /api/auth/register # List plans Source: https://docs.torrin.app/api-reference/public/list-plans /api-reference/openapi.yaml get /api/plans # List supported file hosters Source: https://docs.torrin.app/api-reference/public/list-supported-file-hosters /api-reference/openapi.yaml get /api/hosters # List supported link sites Source: https://docs.torrin.app/api-reference/public/list-supported-link-sites /api-reference/openapi.yaml get /api/sites # Platform-wide wrapped stats Source: https://docs.torrin.app/api-reference/public/platform-wide-wrapped-stats /api-reference/openapi.yaml get /api/wrapped/platform # Public instance configuration Source: https://docs.torrin.app/api-reference/public/public-instance-configuration /api-reference/openapi.yaml get /api/config Feature flags and settings the web app reads before login. # Public instance stats Source: https://docs.torrin.app/api-reference/public/public-instance-stats /api-reference/openapi.yaml get /api/stats/public # Add a magnet Source: https://docs.torrin.app/api-reference/realdebrid-compatible/add-a-magnet /api-reference/openapi.yaml post /rest/1.0/torrents/addMagnet # Delete a torrent Source: https://docs.torrin.app/api-reference/realdebrid-compatible/delete-a-torrent /api-reference/openapi.yaml delete /rest/1.0/torrents/delete/{id} # Get the current user Source: https://docs.torrin.app/api-reference/realdebrid-compatible/get-the-current-user /api-reference/openapi.yaml get /rest/1.0/user # Get torrent info Source: https://docs.torrin.app/api-reference/realdebrid-compatible/get-torrent-info /api-reference/openapi.yaml get /rest/1.0/torrents/info/{id} # Instant availability Source: https://docs.torrin.app/api-reference/realdebrid-compatible/instant-availability /api-reference/openapi.yaml get /rest/1.0/torrents/instantAvailability/{hashes} Returns a key for every hash — a `rd` array when cached, `{}` when not. # List torrents Source: https://docs.torrin.app/api-reference/realdebrid-compatible/list-torrents /api-reference/openapi.yaml get /rest/1.0/torrents # Select files Source: https://docs.torrin.app/api-reference/realdebrid-compatible/select-files /api-reference/openapi.yaml post /rest/1.0/torrents/selectFiles/{id} Compatibility no-op — torrin auto-selects video files server-side. # Unrestrict a link Source: https://docs.torrin.app/api-reference/realdebrid-compatible/unrestrict-a-link /api-reference/openapi.yaml post /rest/1.0/unrestrict/link Turns a `torrin://` file reference into a signed HTTPS URL valid 24h. # Reseller / Partner API Source: https://docs.torrin.app/api-reference/reseller Provision and activate Torrin accounts from your own backend. The reseller API lets a partner create Torrin accounts and activate plans entirely from their own backend — no customer ever visits Torrin or pastes a key. It's how white-label apps bundle Torrin into their own product and billing. ## Getting access Reseller access is issued per partner — contact the operator to get your **partner key**. There's no self-serve signup. Your single key works for both: * **`X-Partner-Key`** — create accounts (skips captcha and per-IP signup limits). * **`X-Reseller-Key`** — mint, list, and settle plan codes. Your key is **scoped to your own codes** — you only ever see and settle codes you minted. ## Provisioning flow Three calls create a customer and put them on a plan: ### 1. Create the account ```bash theme={null} curl -X POST https://api.torrin.app/api/auth/register \ -H "X-Partner-Key: YOUR_PARTNER_KEY" \ -H "Content-Type: application/json" \ -d '{"email":"customer@example.com"}' ``` Returns the customer's API key: ```json theme={null} { "api_key": "YOUR_CUSTOMER_API_KEY", "email": "customer@example.com", "plan": "free" } ``` ### 2. Mint a plan code ```bash theme={null} curl -X POST https://api.torrin.app/api/reseller/codes \ -H "X-Reseller-Key: YOUR_PARTNER_KEY" \ -H "Content-Type: application/json" \ -d '{"plan_id":"standard","period":"monthly","quantity":1}' ``` ```json theme={null} { "codes": ["TRN-XXXX-XXXX-XXXX"], "plan_id": "standard", "period": "monthly" } ``` `plan_id` is `standard` or `pro` — both include system Usenet + indexer with nothing extra to buy (`starter` does not bundle Usenet). `period` is `monthly`, `yearly`, `lifetime`, or `days` (see [Trials](#trials)). ### 3. Activate it on the account ```bash theme={null} curl -X POST https://api.torrin.app/api/redeem \ -H "Authorization: Bearer YOUR_CUSTOMER_API_KEY" \ -H "Content-Type: application/json" \ -d '{"code":"TRN-XXXX-XXXX-XXXX"}' ``` ```json theme={null} { "plan_id": "standard", "expires_at": "2026-11-15T00:00:00Z" } ``` The customer is now on Standard and ready to stream. ## Trials Mint a short **`days`** code for a trial that auto-expires, then upgrade the same account after payment: ```bash theme={null} # 24-hour Standard trial curl -X POST https://api.torrin.app/api/reseller/codes \ -H "X-Reseller-Key: YOUR_PARTNER_KEY" \ -H "Content-Type: application/json" \ -d '{"plan_id":"standard","period":"days","days":1,"quantity":1}' ``` Redeem it exactly as in step 3. After the window expires, redeem a `monthly` code on the **same account** to continue — the new term starts from redemption. ## Billing * **Codes never expire** until redeemed — prepay a batch and use it over time. * **The plan term starts only when a code is redeemed**, not when it's minted. * **Settlement** reconciles the codes you've activated so you can be invoiced. ```bash theme={null} # list your codes (scoped to you) curl "https://api.torrin.app/api/reseller/codes?redeemed=true" \ -H "X-Reseller-Key: YOUR_PARTNER_KEY" # mark your redeemed codes settled curl -X POST https://api.torrin.app/api/reseller/settle \ -H "X-Reseller-Key: YOUR_PARTNER_KEY" \ -H "Content-Type: application/json" \ -d '{"ref":"invoice-2026-10"}' ``` ## Reconciliation Calls are safe to retry after an uncertain response: * **Redeem** is idempotent — redeeming an already-used code returns `409 this code has already been used`, confirming the first attempt worked. * **List your codes** (`GET /api/reseller/codes`) to see exactly what was minted or redeemed. ## Included service & limits | | Standard | Pro | | - | - | - | | System Usenet + indexer | Yes | Yes | | Simultaneous streams | Unlimited | Unlimited | | Device / IP limits | None | None | | Monthly uncached downloads | 4 TB | 8 TB | ## White-label Partner-created accounts can have the Torrin welcome email **suppressed** so the experience stays fully your brand. Ask the operator to enable it on your key. # Add a feed Source: https://docs.torrin.app/api-reference/rss/add-a-feed /api-reference/openapi.yaml post /api/rss/feeds Adds an RSS feed; matching items are auto-added as jobs. # Delete a feed Source: https://docs.torrin.app/api-reference/rss/delete-a-feed /api-reference/openapi.yaml delete /api/rss/feeds/{id} # Enable or disable a feed Source: https://docs.torrin.app/api-reference/rss/enable-or-disable-a-feed /api-reference/openapi.yaml post /api/rss/feeds/{id}/toggle # List feed tags Source: https://docs.torrin.app/api-reference/rss/list-feed-tags /api-reference/openapi.yaml get /api/rss/tags # List feeds Source: https://docs.torrin.app/api-reference/rss/list-feeds /api-reference/openapi.yaml get /api/rss/feeds # Update a feed Source: https://docs.torrin.app/api-reference/rss/update-a-feed /api-reference/openapi.yaml put /api/rss/feeds/{id} # Browse your debrid library Source: https://docs.torrin.app/api-reference/search-&-availability/browse-your-debrid-library /api-reference/openapi.yaml get /api/{provider}/library Paginated view of your existing library on a debrid provider (20 per page). Paid plans only. # Check many hashes Source: https://docs.torrin.app/api-reference/search-&-availability/check-many-hashes /api-reference/openapi.yaml post /api/availability # Check one hash Source: https://docs.torrin.app/api-reference/search-&-availability/check-one-hash /api-reference/openapi.yaml get /api/availability/{hash} Whether a single info-hash is cached in torrin's shared store. # Grab a Usenet result Source: https://docs.torrin.app/api-reference/search-&-availability/grab-a-usenet-result /api-reference/openapi.yaml post /api/usenet/grab # Newznab indexer Source: https://docs.torrin.app/api-reference/search-&-availability/newznab-indexer /api-reference/openapi.yaml get /newznab/api Exposes Torrin's Usenet search as a Newznab indexer for Prowlarr, NZBHydra2, Jackett, and the *arr apps. Add as a "Generic Newznab" indexer with base URL `/newznab` and your Torrin API key. Returns a Newznab RSS/XML feed (or caps XML). # Newznab NZB download Source: https://docs.torrin.app/api-reference/search-&-availability/newznab-nzb-download /api-reference/openapi.yaml get /newznab/getnzb Resolves a search result to its .nzb file (this is the download link inside the feed). # Queue a metadata scrape Source: https://docs.torrin.app/api-reference/search-&-availability/queue-a-metadata-scrape /api-reference/openapi.yaml post /api/scrape Asks torrin to fetch metadata for the given hashes in the background. # Search HDEncode Source: https://docs.torrin.app/api-reference/search-&-availability/search-hdencode /api-reference/openapi.yaml get /api/hdencode/search # Search scene-rls Source: https://docs.torrin.app/api-reference/search-&-availability/search-scene-rls /api-reference/openapi.yaml get /api/scenerls/search # Search the cache Source: https://docs.torrin.app/api-reference/search-&-availability/search-the-cache /api-reference/openapi.yaml get /api/search The public search API. Give an IMDB id (or a title) and get back everything torrin has cached for it, ready to stream — build addons, tools, whatever you want. One person's grab is cached for everyone. Requires an active monthly, yearly, or lifetime plan. # Search Usenet Source: https://docs.torrin.app/api-reference/search-&-availability/search-usenet /api-reference/openapi.yaml get /api/usenet/search Searches the configured Usenet indexer. Requires an indexer to be set. # Torznab indexer Source: https://docs.torrin.app/api-reference/search-&-availability/torznab-indexer /api-reference/openapi.yaml get /torznab/api Exposes Torrin's torrent search as a Torznab indexer for Prowlarr, Jackett, and the *arr apps. Add as a "Generic Torznab" indexer with base URL `/torznab` and your Torrin API key. Returns a Torznab RSS/XML feed of magnets (or caps XML). Cached releases are tagged and ranked first. Requires any paid plan; the operator must set COMET_URL to enable it. # Configure storage (S3-style) Source: https://docs.torrin.app/api-reference/storage/configure-storage-s3-style /api-reference/openapi.yaml post /api/storage Set the primary BYOS credentials directly (S3-compatible bucket, or an rclone backend). For pasting a full rclone config block, use `/api/storage/connect` instead. # Connect storage Source: https://docs.torrin.app/api-reference/storage/connect-storage /api-reference/openapi.yaml post /api/storage/connect Connect a bring-your-own-storage backend (S3-compatible or an rclone remote). Content you stream is written to your storage, encrypted, and served back through torrin. # Disconnect storage Source: https://docs.torrin.app/api-reference/storage/disconnect-storage /api-reference/openapi.yaml delete /api/storage # Export rclone config Source: https://docs.torrin.app/api-reference/storage/export-rclone-config /api-reference/openapi.yaml get /api/storage/rclone-config Download an rclone.conf for your storage so you can mount it yourself. Holds your access keys and encryption passphrase, so keep it private. # Get storage status Source: https://docs.torrin.app/api-reference/storage/get-storage-status /api-reference/openapi.yaml get /api/storage # Remove content from your storage Source: https://docs.torrin.app/api-reference/storage/remove-content-from-your-storage /api-reference/openapi.yaml post /api/storage/evict Remove one release from your storage. It stays in your library and is served from the shared cache if still available. # Remove pool provider Source: https://docs.torrin.app/api-reference/storage/remove-pool-provider /api-reference/openapi.yaml delete /api/storage/providers/{idx} Remove one provider from the pool by its index (0-based over the added providers). # Set storage cleanup rules Source: https://docs.torrin.app/api-reference/storage/set-storage-cleanup-rules /api-reference/openapi.yaml put /api/storage/evict-policy Auto-clean your own storage by age or size. 0 disables a rule. # Set storage pool Source: https://docs.torrin.app/api-reference/storage/set-storage-pool /api-reference/openapi.yaml put /api/storage/providers Add providers to the storage pool and set placement. Submitted providers are merged into the existing pool. `union_policy` is `epmfs` (spread by free space) or `all` (mirror). # Start storage OAuth Source: https://docs.torrin.app/api-reference/storage/start-storage-oauth /api-reference/openapi.yaml get /api/storage/oauth/{provider}/start Begins the OAuth flow for a provider that needs it (e.g. Google Drive). # Add a magnet Source: https://docs.torrin.app/api-reference/stremthru-store/add-a-magnet /api-reference/openapi.yaml post /v0/store/magnets # Add an NZB Source: https://docs.torrin.app/api-reference/stremthru-store/add-an-nzb /api-reference/openapi.yaml post /v0/store/newz Upload an .nzb file (multipart field "file") or pass JSON with a "link" to fetch. # Check cached NZBs Source: https://docs.torrin.app/api-reference/stremthru-store/check-cached-nzbs /api-reference/openapi.yaml get /v0/store/newz/check StremThru usenet store. torz aliases (/v0/store/torz/*) mirror the magnet store. # Check magnets Source: https://docs.torrin.app/api-reference/stremthru-store/check-magnets /api-reference/openapi.yaml get /v0/store/magnets/check Per-magnet status: cached, acceleratable, or unknown. # Delete a magnet Source: https://docs.torrin.app/api-reference/stremthru-store/delete-a-magnet /api-reference/openapi.yaml delete /v0/store/magnets/{id} # Generate a download link for an NZB store item Source: https://docs.torrin.app/api-reference/stremthru-store/generate-a-download-link-for-an-nzb-store-item /api-reference/openapi.yaml post /v0/store/newz/link/generate # Generate link Source: https://docs.torrin.app/api-reference/stremthru-store/generate-link /api-reference/openapi.yaml post /v0/store/link/generate # Get a magnet Source: https://docs.torrin.app/api-reference/stremthru-store/get-a-magnet /api-reference/openapi.yaml get /v0/store/magnets/{id} # Get an NZB store item Source: https://docs.torrin.app/api-reference/stremthru-store/get-an-nzb-store-item /api-reference/openapi.yaml get /v0/store/newz/{id} # Get store user Source: https://docs.torrin.app/api-reference/stremthru-store/get-store-user /api-reference/openapi.yaml get /v0/store/user # List magnets Source: https://docs.torrin.app/api-reference/stremthru-store/list-magnets /api-reference/openapi.yaml get /v0/store/magnets # List your NZB store items Source: https://docs.torrin.app/api-reference/stremthru-store/list-your-nzb-store-items /api-reference/openapi.yaml get /v0/store/newz # Remove an NZB store item Source: https://docs.torrin.app/api-reference/stremthru-store/remove-an-nzb-store-item /api-reference/openapi.yaml delete /v0/store/newz/{id} # Generate a Telegram link code Source: https://docs.torrin.app/api-reference/telegram/generate-a-telegram-link-code /api-reference/openapi.yaml post /api/telegram/link-code Returns a short-lived code (15 min) to link the account from the Telegram bot. # Telegram link status Source: https://docs.torrin.app/api-reference/telegram/telegram-link-status /api-reference/openapi.yaml get /api/telegram/status # Unlink Telegram Source: https://docs.torrin.app/api-reference/telegram/unlink-telegram /api-reference/openapi.yaml delete /api/telegram/link # Usenet Source: https://docs.torrin.app/api-reference/usenet Add a Usenet provider and indexer, search, and grab. Torrin can pull from Usenet as well as torrents. You attach a **provider** (the NNTP server that serves the bytes) and an **indexer** (the Newznab-compatible search that finds releases), then search and grab — the result becomes a normal job and streams like anything else. ## Two pieces | Piece | What it is | Endpoint | | - | - | - | | **Provider** | Your Usenet server (host, port, login, connection count). Serves the actual data. | `/api/usenet/credentials` | | **Indexer** | A Newznab-compatible indexer (URL + API key). Finds releases to grab. | `/api/usenet/indexer` | Each has `GET` (status), `POST` (set), `DELETE` (remove), and a test call (`POST /api/usenet/test`, `POST /api/usenet/indexer/test`) to verify credentials before saving. ## Search and grab Once both are set: ```bash theme={null} # search the indexer curl "https://api.torrin.app/api/usenet/search?imdb=tt1234567" \ -H "Authorization: Bearer YOUR_API_KEY" # grab a result → it becomes a job curl -X POST https://api.torrin.app/api/usenet/grab \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "id": "RESULT_ID", "title": "..." }' ``` You can also skip the indexer and upload an `.nzb` file directly to [`POST /api/jobs/nzb`](/api-reference/jobs/add-an-nzb). A grab returns a `202` with a job — poll `GET /api/jobs/{id}` until it's `complete`, then stream it exactly like a torrent. # Add a Usenet indexer Source: https://docs.torrin.app/api-reference/usenet/add-a-usenet-indexer /api-reference/openapi.yaml post /api/usenet/indexers Add a Newznab indexer (paid plans only). Up to 8 per account. # Add a Usenet provider Source: https://docs.torrin.app/api-reference/usenet/add-a-usenet-provider /api-reference/openapi.yaml post /api/usenet/providers # Built-in indexer state Source: https://docs.torrin.app/api-reference/usenet/built-in-indexer-state /api-reference/openapi.yaml get /api/usenet/system-indexer The built-in Torrin indexer's availability (needs Standard/Pro) and on/off state. # Check cached Usenet releases Source: https://docs.torrin.app/api-reference/usenet/check-cached-usenet-releases /api-reference/openapi.yaml get /api/usenet/cached Returns already-cached, playable files for a title. # Delete a Usenet indexer Source: https://docs.torrin.app/api-reference/usenet/delete-a-usenet-indexer /api-reference/openapi.yaml delete /api/usenet/indexers/{id} # Delete a Usenet provider Source: https://docs.torrin.app/api-reference/usenet/delete-a-usenet-provider /api-reference/openapi.yaml delete /api/usenet/providers/{id} # Edit a Usenet indexer Source: https://docs.torrin.app/api-reference/usenet/edit-a-usenet-indexer /api-reference/openapi.yaml put /api/usenet/indexers/{id} # Enable or disable a Usenet indexer Source: https://docs.torrin.app/api-reference/usenet/enable-or-disable-a-usenet-indexer /api-reference/openapi.yaml post /api/usenet/indexers/{id}/toggle # Enable or disable a Usenet provider Source: https://docs.torrin.app/api-reference/usenet/enable-or-disable-a-usenet-provider /api-reference/openapi.yaml post /api/usenet/providers/{id}/toggle # Get indexer status Source: https://docs.torrin.app/api-reference/usenet/get-indexer-status /api-reference/openapi.yaml get /api/usenet/indexer # Get Usenet provider status Source: https://docs.torrin.app/api-reference/usenet/get-usenet-provider-status /api-reference/openapi.yaml get /api/usenet/credentials # List your Usenet indexers Source: https://docs.torrin.app/api-reference/usenet/list-your-usenet-indexers /api-reference/openapi.yaml get /api/usenet/indexers # List your Usenet providers Source: https://docs.torrin.app/api-reference/usenet/list-your-usenet-providers /api-reference/openapi.yaml get /api/usenet/providers # Remove indexer Source: https://docs.torrin.app/api-reference/usenet/remove-indexer /api-reference/openapi.yaml delete /api/usenet/indexer # Remove Usenet provider Source: https://docs.torrin.app/api-reference/usenet/remove-usenet-provider /api-reference/openapi.yaml delete /api/usenet/credentials # Set indexer Source: https://docs.torrin.app/api-reference/usenet/set-indexer /api-reference/openapi.yaml post /api/usenet/indexer # Set Usenet provider Source: https://docs.torrin.app/api-reference/usenet/set-usenet-provider /api-reference/openapi.yaml post /api/usenet/credentials # Test a Usenet indexer Source: https://docs.torrin.app/api-reference/usenet/test-a-usenet-indexer /api-reference/openapi.yaml post /api/usenet/indexers/test Validates and live-tests an indexer config before saving. # Test an indexer Source: https://docs.torrin.app/api-reference/usenet/test-an-indexer /api-reference/openapi.yaml post /api/usenet/indexer/test # Test the Usenet provider Source: https://docs.torrin.app/api-reference/usenet/test-the-usenet-provider /api-reference/openapi.yaml post /api/usenet/test # Test Usenet provider credentials Source: https://docs.torrin.app/api-reference/usenet/test-usenet-provider-credentials /api-reference/openapi.yaml post /api/usenet/providers/test # Toggle the built-in indexer Source: https://docs.torrin.app/api-reference/usenet/toggle-the-built-in-indexer /api-reference/openapi.yaml post /api/usenet/system-indexer/toggle # Update a Usenet provider Source: https://docs.torrin.app/api-reference/usenet/update-a-usenet-provider /api-reference/openapi.yaml put /api/usenet/providers/{id} # Buy a plan from wallet balance Source: https://docs.torrin.app/api-reference/wallet/buy-a-plan-from-wallet-balance /api-reference/openapi.yaml post /api/wallet/buy-plan # Get wallet balance Source: https://docs.torrin.app/api-reference/wallet/get-wallet-balance /api-reference/openapi.yaml get /api/wallet # Top up the wallet Source: https://docs.torrin.app/api-reference/wallet/top-up-the-wallet /api-reference/openapi.yaml post /api/wallet/topup Starts a checkout to add credit to your balance. # List WebDAV naming overrides Source: https://docs.torrin.app/api-reference/webdav/list-webdav-naming-overrides /api-reference/openapi.yaml get /api/webdav/overrides # Set a WebDAV naming override Source: https://docs.torrin.app/api-reference/webdav/set-a-webdav-naming-override /api-reference/openapi.yaml post /api/webdav/override Changes how a release's folder is named in the WebDAV library. # Accounts & plans Source: https://docs.torrin.app/self-hosting/accounts How users sign up and how plan limits work. ## The first account Registration is open by default — open the web UI at your `PUBLIC_URL` and sign up. The first account is a normal user; there's no separate admin bootstrap. Each account adds its **own debrid key** (RealDebrid / AllDebrid / TorBox) in settings — that's what makes cached content stream instantly. ## Plans Every user is on a **plan** that sets their limits. New accounts land on the built-in **Free** plan: | Limit | Free | | - | - | | Concurrent downloads | 1 | | Max torrent size | 50 GB | | WebDAV / Stremio / shared cache | yes | Plans are defined in code (`shared/plans/plans.go`). For a personal instance the Free limits are often too tight, so you have two options: Assign your user a higher plan directly in the database: ```sql theme={null} UPDATE users SET plan_id='pro', expires_at='2099-01-01' WHERE email='you@example.com'; ``` (Run inside the postgres container: `docker compose exec postgres psql -U torrin -d torrin`.) Change the limits on the Free plan (or add your own) in `shared/plans/plans.go` and rebuild the images. Best if you want everyone on your instance to get the same limits. Billing (Gumroad / Bitcart / etc.) is only relevant if you're running Torrin as a paid service. A private self-host can ignore it entirely and manage plans by hand. # Bring Your Own Storage Source: https://docs.torrin.app/self-hosting/byos Pipe content to your own cloud provider via rclone. BYOS lets a user send their content to **their own cloud storage** instead of (or alongside) the shared cache. It's built on [rclone](https://rclone.org), and everything is **encrypted** before it leaves the server (rclone `crypt`), so the provider only ever sees scrambled files. ## Supported providers Google Drive, Dropbox, OneDrive, and pCloud via OAuth; Mega, PikPak, and Gofile via account login; any S3-compatible bucket (S3, Backblaze B2, Wasabi, R2) via keys; and anything else rclone supports by pasting an rclone remote config. ## Enabling OAuth providers For the OAuth cloud providers, register an app with each provider and set its client ID/secret, plus the api's public base so the OAuth redirect works: | Variable | Description | | - | - | | `API_PUBLIC_URL` | The api's externally-reachable base — the OAuth redirect target. Must be reachable by the provider. | | `GDRIVE_CLIENT_ID` / `GDRIVE_CLIENT_SECRET` | Google Drive app. | | `DROPBOX_CLIENT_ID` / `DROPBOX_CLIENT_SECRET` | Dropbox app. | | `ONEDRIVE_CLIENT_ID` / `ONEDRIVE_CLIENT_SECRET` | OneDrive app. | | `PCLOUD_CLIENT_ID` / `PCLOUD_CLIENT_SECRET` | pCloud app. | An S3-compatible bucket needs no app registration — the user just enters the endpoint, bucket, and keys in settings. ## Using it Once a provider is configured, a user connects their storage in the app's settings. New downloads are encrypted and written to their provider, and playback streams back from it, geo-routed and decrypted on the fly. ## Pooling multiple providers Several providers can act as one encrypted pool. Add extra providers in settings, then choose how files are placed: * **Pool** (default): each file goes to whichever provider has the most free space, so usable space is the sum of all providers. * **Mirror**: every file is written to all providers for redundancy (uses Nx the space). ## Exporting your config Since everything is encrypted with your own key, you can download a ready-made `rclone.conf` from settings and mount your storage yourself, independent of the server. Keep it private: it holds your keys and encryption passphrase. ## Cleanup Content on your storage stays until you remove it. You can remove a single release from the app, or set optional rules to auto-clean by age (remove after N idle days) or size (keep under N GB). The sweep runs daily; see `BYOS_EVICT_HOUR` in [Configuration](/self-hosting/configuration). Content on your own storage is **not** subject to the shared-cache eviction; it stays until you remove it. See [Storage](/self-hosting/storage). # Configuration Source: https://docs.torrin.app/self-hosting/configuration Environment variables for a self-hosted instance. All configuration lives in `deploy/.env` (copied from `deploy/.env.example`). Anything you leave blank falls back to the default baked into the compose files. ## Required | Variable | Description | | - | - | | `SIGNING_KEY` | Secret that signs stream URLs and internal calls. Generate with `openssl rand -hex 32`. **Change from the default.** | | `PUBLIC_URL` | Externally-reachable base for the stream service (your domain or IP). Signed URLs are minted against it. | `DATABASE_URL` and `NATS_URL` are wired to the bundled PostgreSQL and NATS by the compose file, so you don't set them yourself. ## Storage | Variable | Description | | - | - | | `STORAGE_KEY` | Optional hex key to encrypt stored **content** at rest (default off). | | `CREDS_KEY` | Optional hex key to encrypt stored **debrid credentials** at rest. | The cache location is a volume mount, not an env var. See [Storage](/self-hosting/storage). ## Cache limits | Variable | Default | Description | | - | - | - | | `BUDGET_MAX_BYTES` | `1000000000000` | Cap on in-flight (downloading) bytes. | | `EVICTION_CAP_BYTES` | `300000000000` | Soft cap on the cache; oldest content is evicted past it. | | `EVICTION_HOUR` | `4` | Hour of day the eviction sweep runs. | | `BYOS_EVICT_HOUR` | `5` | Hour of day the [BYOS](/self-hosting/byos) cleanup sweep runs (only for users who set cleanup rules). | ## Debrid (optional) Users add their own provider keys in the app. These system keys are only used for the instant "is it cached" probe and can be left empty. | Variable | Description | | - | - | | `RD_API_KEY` | System RealDebrid key for cache checks. | | `AD_API_KEY` | System AllDebrid key for cache checks. | | `TORRENTCLAW_API_KEY` | Non-jamming live cache-check service. | ## Usenet (optional) | Variable | Description | | - | - | | `USENET_HOST` / `USENET_PORT` / `USENET_USER` / `USENET_PASS` | Your Usenet provider. | | `USENET_MAXCONNS` | Max connections (default 20). | | `USENET_INDEXER_URL` / `USENET_INDEXER_KEY` | A Newznab indexer, as a fallback source. | | `INDEXER_USER_AGENT` | Default User-Agent for indexer searches (default `Prowlarr/1.30.2`). Users can override per BYOK indexer. | | `INDEXER_GRAB_USER_AGENT` | Fallback User-Agent for NZB grabs (default `SABnzbd/5.0.4`). Some indexers block unknown agents. | | `SONARR_USER_AGENT` / `RADARR_USER_AGENT` | Grab User-Agent by category when the indexer's grab UA is blank: TV uses Sonarr, movies use Radarr (defaults `Sonarr/4.0.15.2941` / `Radarr/5.26.2.10099`). | ## Torrent search (optional) Enables the [Torznab indexer](/self-hosting/search-indexer) endpoint. Without these, `/torznab` returns "not available". | Variable | Description | | - | - | | `COMET_URL` | Base URL of the addon that backs torrent search (e.g. `https://addon.torrin.app`). | | `COMET_SEARCH_KEY` | Shared secret the addon validates on search requests. Must match the addon's `TORZNAB_API_KEY`. | ## BYOS OAuth (optional) Only needed if users will connect a cloud provider. See [BYOS](/self-hosting/byos). | Variable | Description | | - | - | | `API_PUBLIC_URL` | The api's externally-reachable base (used as the OAuth redirect target). | | `GDRIVE_CLIENT_ID` / `GDRIVE_CLIENT_SECRET` | Google Drive OAuth app. | | `DROPBOX_CLIENT_ID` / `DROPBOX_CLIENT_SECRET` | Dropbox OAuth app. | | `ONEDRIVE_CLIENT_ID` / `ONEDRIVE_CLIENT_SECRET` | OneDrive OAuth app. | | `PCLOUD_CLIENT_ID` / `PCLOUD_CLIENT_SECRET` | pCloud OAuth app. | ## Rate limiting (optional) | Variable | Default | Description | | - | - | - | | `RATE_LIMIT_RPS` | `30` | Per-key requests/sec; `0` disables. | | `RATE_LIMIT_BURST` | `60` | Burst allowance. | # Sonarr / Radarr download client Source: https://docs.torrin.app/self-hosting/download-clients Use Torrin as a drop-in download client in an *arr stack. Torrin can present itself as a **qBittorrent** or **SABnzbd** download client, so Sonarr, Radarr, and the rest of the \*arr stack can hand releases straight to Torrin. Torrin does the downloading (debrid, Usenet, hosters), and the finished files show up on a path the \*arr app imports from over [WebDAV](/self-hosting/webdav). No bridging middleware needed: point the \*arr app at Torrin directly. ## How it fits together Sonarr/Radarr talks to Torrin's fake qBittorrent or SABnzbd API and sends it a magnet or NZB, exactly as it would to a real download client. Torrin resolves and caches the release through its normal pipeline (debrid, Usenet, hosters), subject to your plan limits. Torrin reports the completed download at a folder path. The \*arr app reads that path over a mounted WebDAV share and imports (hardlink/copy) the files. ## 1. Mount Torrin's WebDAV on the \*arr host The \*arr app imports by **file path**, so it needs to *see* Torrin's finished files locally. Mount Torrin's [WebDAV](/self-hosting/webdav) share on the same host that runs Sonarr/Radarr, e.g. with rclone: ```bash theme={null} rclone mount torrin: /mnt/torrin \ --dir-cache-time 10s --vfs-cache-mode off --allow-other ``` where the `torrin:` remote is a `webdav` remote pointing at `http://:9092` with your Torrin account credentials. The WebDAV folder names match the paths Torrin reports to the \*arr app, so the import "just works" once the mount and the path mapping (step 3) line up. ## 2. Add Torrin as a download client In **Sonarr/Radarr → Settings → Download Clients → Add**, pick one: Choose **qBittorrent** and set: | Field | Value | | - | - | | Host | your Torrin host, e.g. `api.torrin.app` (or your instance) | | Port | `443` (or your port) | | Use SSL | on, if your instance is HTTPS | | Username | your Torrin **API key** | | Password | your Torrin **API key** | | Category | e.g. `tv-sonarr` / `radarr` | Torrin serves the qBittorrent WebUI API at `/api/v2/*` on your main API host, so leave **URL Base** empty. Username and password both accept your API key (the password is used if both are set). Choose **SABnzbd** and set: | Field | Value | | - | - | | Host | your Torrin host, e.g. `api.torrin.app` | | Port | `443` (or your port) | | Use SSL | on, if your instance is HTTPS | | API Key | your Torrin **API key** | | URL Base | leave empty (or `/sabnzbd`) | | Category | e.g. `tv` / `movies` | Torrin answers the SABnzbd API at `/api` and `/sabnzbd/api`. Hit **Test**; it should go green. Categories round-trip, so a Sonarr and a Radarr instance pointed at the same Torrin stay separated. ## 3. Map the download path The \*arr app sees the download at Torrin's reported save path (default `/downloads`), but the files actually live on your **WebDAV mount**. Bridge the two with a **Remote Path Mapping**: **Settings → Download Clients → Remote Path Mappings → Add** | Field | Value | | - | - | | Host | the same host you set on the download client | | Remote Path | `/downloads/` | | Local Path | `/mnt/torrin/` (your WebDAV mount) | Torrin's reported save path is set by the `ARR_SAVE_PATH` env on the `api` service (default `/downloads`). If you change it, change the Remote Path here to match. ## That's it Search in Sonarr/Radarr as usual. When it grabs a release, Torrin ingests it, and once it's cached the \*arr app imports it straight from the WebDAV mount. After that it streams from Torrin like anything else. Grabs count against your plan's limits (concurrent jobs, monthly ingest, cold pulls) just like any other Torrin download. A busy \*arr library can burn through those quickly. # Exposing publicly Source: https://docs.torrin.app/self-hosting/exposing Put Torrin behind HTTPS so you can use it remotely and with Stremio. The quickstart runs Torrin on plain HTTP, which is fine on your LAN. To use it from outside your network — and to use the **Stremio addon**, which requires HTTPS — put it behind TLS. The `docker-compose.public.yml` overlay does this for you, two ways. ## Access it locally first (no domain) Before exposing anything, you can reach the dashboard on the machine itself. The web UI runs in the `web` container on port `5000`, but that port isn't published to your host by default. Publish it with a `docker-compose.override.yml` next to your compose files: ```yaml theme={null} services: web: ports: - "127.0.0.1:5000:5000" ``` Re-run your `up -d` command, then open **[http://localhost:5000](http://localhost:5000)** to create your account and add your debrid key. Drop the `127.0.0.1:` prefix to reach it from other devices on your LAN. Local access is HTTP only. For remote use and the Stremio addon (which requires HTTPS), use one of the options below. ## Option A: Caddy with a domain (automatic TLS) Point a domain at your server, then let the bundled Caddy fetch and renew a Let's Encrypt certificate automatically. Create an A/AAAA record for your domain (e.g. `stream.example.com`) pointing at the server's public IP. Open ports **80** and **443**. In `deploy/.env`: ```bash theme={null} STREAM_DOMAIN=stream.example.com ACME_EMAIL=you@example.com PUBLIC_URL=https://stream.example.com ``` ```bash theme={null} docker compose -f docker-compose.yml -f docker-compose.vpn.yml \ -f docker-compose.public.yml up -d ``` Caddy reverse-proxies the public services (see `deploy/Caddyfile`) and manages TLS. Adjust the Caddyfile if you want separate subdomains for the api, addon, or WebDAV. ## Option B: Cloudflare Tunnel (no open ports) If you can't open ports or prefer not to expose your IP, use a Cloudflare Tunnel. Create a tunnel in the Cloudflare dashboard, then: ```bash theme={null} CLOUDFLARE_TUNNEL_TOKEN=your-tunnel-token ``` and bring the stack up with the same `docker-compose.public.yml` overlay. Route the tunnel's hostnames to the internal services in the Cloudflare dashboard. `PUBLIC_URL` must match the HTTPS address you actually serve on — signed stream URLs are minted against it, so a mismatch breaks playback. # Introduction Source: https://docs.torrin.app/self-hosting/introduction What Torrin is and how it works. Torrin is an open-source, self-hostable debrid + streaming service. You add a magnet, NZB, or file-host link, and get back an instant HTTPS stream. No local torrent client, no seeding, no IP exposure. ## What it does Torrin downloads torrents, Usenet, and file-host links on the server (behind a VPN), caches the result on disk, and serves it back as signed HTTPS stream URLs. Cached content is shared across users, so repeat plays are instant. * **Instant streaming** for cached content; downloads behind a VPN when it isn't. * **Multiple sources** — torrents (qBittorrent), debrid providers, Usenet (NZB), file hosters, and scene/HDEncode releases. * **Stremio addon**, **WebDAV** mount (VLC / Infuse / Kodi), and a web UI. * **RSS auto-download** with criteria filters (resolution, source, codec, HDR, group, ...). * **BYOS** — Bring Your Own Storage: pipe downloads to your own cloud via rclone. ## How it works 1. A user submits a magnet / NZB / link, or searches for a title. 2. `api` checks the cache and content providers. If available, it returns signed stream URLs instantly. 3. If not, a job is queued and `ingest` downloads it (behind a VPN). 4. On completion the files are published to the object store. 5. The user streams via a signed HTTPS URL from `stream`, or mounts the library via `webdav`. ## Architecture Torrin is a Go monorepo of small services over [NATS](https://nats.io), backed by PostgreSQL for metadata and a filesystem-backed object store for content. | Service | Role | | - | - | | `api` | REST API — auth, jobs, RSS, storage | | `ingest` | Download pipeline — torrent / debrid / Usenet / hoster → publish | | `stream` | Signed HTTPS streaming of cached files | | `webdav` | WebDAV server for mounting the library | | `catalog` | Content catalog / metadata | | `scheduler` | Background scheduling + cache eviction | | `stremio` | Stremio addon server | | `byos` | Bring Your Own Storage (rclone) | Get a Torrin instance up with Docker Compose. # Quick start Source: https://docs.torrin.app/self-hosting/quickstart Run a Torrin instance with Docker Compose. ## Prerequisites * **Docker** and **Docker Compose** * A **disk** with room for the content cache * A **WireGuard VPN** provider for download egress (recommended, so your host IP is never exposed) ## 1. Clone ```bash theme={null} git clone https://github.com/torrin-app/torrin cd torrin/deploy ``` ## 2. Configure Copy the example env and fill in the required values: ```bash theme={null} cp .env.example .env ``` At minimum, set: Secret that signs stream URLs. Generate one with `openssl rand -hex 32`. Where the stream service is reachable from the outside (your domain or IP), e.g. `https://stream.example.com`. Your WireGuard details, if you use the VPN overlay (recommended). See [VPN](/self-hosting/vpn). See [Configuration](/self-hosting/configuration) for the full list. ## 3. Storage Cached content lives on the local filesystem. The compose mounts the host path `/mnt/cache/store` into the services — mount your cache disk there, or change that volume path in `docker-compose.yml`. See [Storage](/self-hosting/storage). ## 4. Start Base stack plus the VPN overlay (downloads route through gluetun): ```bash theme={null} docker compose -f docker-compose.yml -f docker-compose.vpn.yml up -d ``` Leave off `-f docker-compose.vpn.yml` to run without a VPN — downloads will use your host IP directly, which is not recommended. This brings up PostgreSQL, NATS, qBittorrent, the object store, and the Torrin services (`api`, `ingest`, `stream`, `stremio`, ...). ## 5. Expose it (for remote access + Stremio) The above runs on plain HTTP, fine for a LAN test. To reach it from outside your network — and to use the Stremio addon, which requires HTTPS — add the public overlay (Caddy with automatic TLS on your domain, or a Cloudflare tunnel): ```bash theme={null} docker compose -f docker-compose.yml -f docker-compose.vpn.yml \ -f docker-compose.public.yml up -d ``` See [Exposing publicly](/self-hosting/exposing) for the DNS/domain and `PUBLIC_URL` details. ## 6. First use 1. Open the web UI at your `PUBLIC_URL` and create an account. 2. Add your debrid provider key (RealDebrid / AllDebrid / TorBox) in settings. 3. Paste a magnet, NZB, or link — or connect the [Stremio addon](/self-hosting/stremio). Point the addon at your instance and stream from Stremio. # Requirements Source: https://docs.torrin.app/self-hosting/requirements What you need before installing Torrin. ## Host * A **Linux** server with **Docker** and **Docker Compose**. * **\~2-4 GB RAM** for the base stack (more if you run heavy usenet/torrent loads). * **Disk** sized for your cache: at least `EVICTION_CAP_BYTES` (default 300 GB) plus headroom for the download scratch area. A separate data disk mounted at `/mnt/cache/store` is recommended. ## Accounts & services * A **debrid account** (RealDebrid, AllDebrid, or TorBox) — this is what makes streaming instant. Users add their own key in the app. * A **WireGuard VPN** provider for download egress (recommended). See [VPN](/self-hosting/vpn). * A **domain** if you want to reach it from outside your LAN / use it with Stremio. See [Exposing publicly](/self-hosting/exposing). ## Optional * A **Usenet provider + indexer** for NZB downloads. * **Cloud storage** (Google Drive / Dropbox / OneDrive / pCloud / S3) if you want users to bring their own storage. See [BYOS](/self-hosting/byos). Everything else — PostgreSQL, NATS, qBittorrent, the object store, and the Torrin services — is bundled in the compose stack. You don't install them separately. # Torrin as a search indexer (Newznab + Torznab) Source: https://docs.torrin.app/self-hosting/search-indexer Use Torrin's built-in search as a Newznab (usenet) or Torznab (torrent) indexer in Prowlarr, NZBHydra2, Jackett, and the *arr stack. Torrin can present its search as a **Newznab** (usenet) and a **Torznab** (torrent) indexer, so Prowlarr, NZBHydra2, Jackett, AIOStreams, and the \*arr apps can search Torrin the same way they'd search any indexer. It's the companion to the [download client](/self-hosting/download-clients) integration: use both together and an \*arr app can **search *and* grab** entirely through Torrin. Two endpoints: **`/newznab`** for usenet and **`/torznab`** for torrents. Add whichever you need (or both). Torznab requires the operator to enable it, see [Torznab requirements](#requirements-torznab). ## What you get * A standard **Newznab** endpoint backed by Torrin's usenet search — your own indexers (BYOK) and the built-in indexer both flow through it. * A standard **Torznab** endpoint backed by Torrin's torrent search — results are returned as magnets, and anything already in Torrin's cache is tagged so your \*arr app can prefer instant content. * Works anywhere "Generic Newznab" / "Generic Torznab" is supported (Prowlarr, NZBHydra2, Jackett, the \*arr apps, AIOStreams). ## Newznab (usenet) ### Requirements * A plan with usenet search, and at least one indexer available to your account (a BYOK Newznab indexer or the built-in one enabled) — the same gate as searching inside Torrin. * Your Torrin **API key**. ### Add Torrin in Prowlarr **Settings → Indexers → Add Indexer → Generic Newznab**, then set: | Field | Value | | - | - | | URL | `https:///newznab` (e.g. `https://api.torrin.app/newznab`) | | API Path | `/api` (the default) | | API Key | your Torrin **API key** | | Categories | Movies (2000) and/or TV (5000) | Hit **Test** — it should go green. The same URL + key work in **NZBHydra2** (add a "Newznab" indexer), **Jackett**, or directly in any \*arr app's indexer settings. ## Torznab (torrents) ### Requirements * The operator must enable it by setting `COMET_URL` (and `COMET_SEARCH_KEY`) on the API service, see [Configuration](/self-hosting/configuration). Until then the endpoint returns "not available". * Any **paid plan**, and your Torrin **API key**. ### Add Torrin in Prowlarr **Settings → Indexers → Add Indexer → Generic Torznab**, then set: | Field | Value | | - | - | | URL | `https:///torznab` (e.g. `https://api.torrin.app/torznab`) | | API Path | `/api` (the default) | | API Key | your Torrin **API key** | | Categories | Movies (2000) and/or TV (5000) | Hit **Test** — it should go green. Results carry seeders, size, and infohash, and cached releases get a `[CACHED]` tag on the title plus higher ranking. To auto-prefer them, add a Custom Format in Sonarr/Radarr matching `CACHED` and give it a positive score. ## How it works Prowlarr/\*arr calls `/newznab/api` or `/torznab/api` with `t=search|tvsearch|movie` and your API key. Supported params: `q`, `imdbid`, `season`, `ep`. For usenet, Torrin searches your configured indexers (BYOK + built-in). For torrents, it runs its torrent search and flags anything already in the cache. Usenet results come back as a Newznab RSS feed. Torrent results come back as a Torznab feed of magnets, cached-first, with `[CACHED]` tags on releases already in Torrin. On a usenet grab the link resolves to the real `.nzb` through Torrin; a torrent grab hands the magnet to the download client. Either way it works with any client, including [Torrin itself](/self-hosting/download-clients). ## Endpoints | Endpoint | Purpose | | - | - | | `GET /newznab/api?t=caps` | Usenet capabilities (no key required) | | `GET /newznab/api?t=search&q=&apikey=` | Usenet text search | | `GET /newznab/api?t=tvsearch&imdbid=&season=&ep=&apikey=` | Usenet TV search | | `GET /newznab/api?t=movie&imdbid=&apikey=` | Usenet movie search | | `GET /newznab/getnzb?id=&apikey=` | NZB download passthrough | | `GET /torznab/api?t=caps` | Torrent capabilities (no key required) | | `GET /torznab/api?t=search&q=&apikey=` | Torrent text search | | `GET /torznab/api?t=tvsearch&imdbid=&season=&ep=&apikey=` | Torrent TV search | | `GET /torznab/api?t=movie&imdbid=&apikey=` | Torrent movie search | Pair this with the [Sonarr/Radarr download client](/self-hosting/download-clients) to search **and** download through Torrin end-to-end. # Storage Source: https://docs.torrin.app/self-hosting/storage Where cached content lives and how eviction works. Torrin caches downloaded content on the **local filesystem**. There's no external object store to run for a single-box setup. ## The cache directory The compose mounts the host path `/mnt/cache/store` into the services that read and write cached content. To use a specific disk, mount it there: ```bash theme={null} # example: mount a dedicated disk at the cache path mount /dev/sdX1 /mnt/cache/store ``` Or point the volume somewhere else by editing the mount in `deploy/docker-compose.yml`: ```yaml theme={null} volumes: - /your/big/disk:/mnt/cache/store ``` The download **scratch** area (`/mnt/cache/ingest-scratch`) is separate and temporary — it holds a job's files only until they're published to the cache. ## Encryption at rest Set `STORAGE_KEY` (a hex key from `openssl rand -hex 32`) to encrypt stored content at rest. It's off by default. Stored debrid credentials can be encrypted independently with `CREDS_KEY`. ## Eviction The cache is capped by `EVICTION_CAP_BYTES` (default 300 GB). A daily sweep (at `EVICTION_HOUR`) removes the least-recently-used content once the cache grows past the cap, so it never fills the disk. Popular content stays warm; rarely-watched content is dropped and simply re-fetched on the next play. ## Bring Your Own Storage Instead of (or in addition to) the shared cache, users can pipe their content to their own cloud provider via rclone. See [BYOS](/self-hosting/byos). # Stremio addon Source: https://docs.torrin.app/self-hosting/stremio Stream your Torrin instance from Stremio. Torrin ships a Stremio addon server (the `stremio` service) so you can browse and play cached content directly in Stremio. ## Install the addon The addon is served on port **7000**. Its manifest is at: ``` http://:7000/manifest.json ``` Add it to Stremio one of two ways: Visit `http://:7000/` in a browser and follow the prompt to link it to your Torrin account. Paste the manifest URL into Stremio's **Addons → Add addon** field, or use the **Install** button on the configure page. For anything beyond a LAN setup, put the addon behind HTTPS (a reverse proxy / the `docker-compose.public.yml` overlay). Stremio requires HTTPS for remote addons. ## How it works When you pick a title in Stremio, the addon asks `api` whether it's cached. If so you get an instant signed stream; if not, a download is queued and the stream becomes available once it completes. # Troubleshooting Source: https://docs.torrin.app/self-hosting/troubleshooting Verify your instance and fix common issues. ## Verify it's running ```bash theme={null} docker compose ps # all services healthy? docker compose logs -f api # follow a service's logs docker compose logs -f ingest ``` Then open your `PUBLIC_URL`, sign up, add a debrid key, and paste a magnet — it should start downloading (or return an instant stream if it's already cached). ## Common issues Check gluetun: `docker compose logs -f gluetun`. A bad `VPN_PRIVATE_KEY`, `VPN_ADDRESSES`, or provider name is the usual cause. `ingest` and qBittorrent share gluetun's network, so if the VPN is down, downloads stall. The store path (`/mnt/cache/store`) and scratch dir must be writable by the containers. Make sure the mounted disk exists and isn't read-only. Almost always a `PUBLIC_URL` mismatch — signed URLs are minted against it, so it must be the exact HTTPS address you serve on. See [Exposing publicly](/self-hosting/exposing). Stremio requires HTTPS for remote addons. Put the instance behind TLS first ([Exposing publicly](/self-hosting/exposing)), then add the manifest URL. New accounts are on the Free plan (1 concurrent, 50 GB). Bump your account or edit the plan limits — see [Accounts & plans](/self-hosting/accounts). ## Getting help * Check [CONTRIBUTING.md](https://github.com/torrin-app/torrin/blob/main/CONTRIBUTING.md) * Report security issues via [SECURITY.md](https://github.com/torrin-app/torrin/blob/main/SECURITY.md), not a public issue. # Updating Source: https://docs.torrin.app/self-hosting/updating Move your instance to a newer version. Torrin ships as versioned container images. To update: ```bash theme={null} cd torrin/deploy git pull docker compose pull docker compose up -d ``` Use the same overlay flags you started with, e.g.: ```bash theme={null} docker compose -f docker-compose.yml -f docker-compose.vpn.yml \ -f docker-compose.public.yml pull docker compose -f docker-compose.yml -f docker-compose.vpn.yml \ -f docker-compose.public.yml up -d ``` `docker compose up -d` alone does **not** re-pull images — always run `pull` first, or a service can keep running an old image. ## Notes * **Database** changes apply automatically on startup (the schema is embedded), so there's no manual migration step. * **Config** is your `.env`; new options land in `.env.example` — diff it against your `.env` after pulling to pick up anything new. * To pin a specific version instead of the latest, set `TORRIN_TAG` in `.env`. # VPN Source: https://docs.torrin.app/self-hosting/vpn Route downloads through a VPN so your host IP is never exposed. Torrin runs its download engine (qBittorrent + `ingest`) inside [gluetun](https://github.com/qdm12/gluetun), so all torrent and download traffic egresses through your VPN — your server's real IP is never exposed. This is enabled by the `docker-compose.vpn.yml` overlay: ```bash theme={null} docker compose -f docker-compose.yml -f docker-compose.vpn.yml up -d ``` ## WireGuard config Fill these in `deploy/.env` (values come from your VPN provider's WireGuard config): | Variable | Description | | - | - | | `VPN_PROVIDER` | gluetun provider name (e.g. `airvpn`, `mullvad`, `custom`). | | `VPN_TYPE` | `wireguard` (recommended) or `openvpn`. | | `VPN_PRIVATE_KEY` | Your WireGuard private key. | | `VPN_PRESHARED_KEY` | Preshared key, if your provider uses one. | | `VPN_ADDRESSES` | The address block from your WireGuard config, e.g. `10.x.x.x/32`. | | `VPN_COUNTRIES` | Preferred exit country/countries (default `Germany`). | gluetun supports many providers — see its [wiki](https://github.com/qdm12/gluetun-wiki) for the exact `VPN_PROVIDER` value and any provider-specific settings. ## Running without a VPN Leave off the overlay to run without one: ```bash theme={null} docker compose up -d ``` Without the VPN overlay, downloads use your host's IP directly. Only do this if you understand the exposure — a VPN is strongly recommended. # WebDAV Source: https://docs.torrin.app/self-hosting/webdav Mount your Torrin library in VLC, Infuse, Kodi, or a file manager. The `webdav` service exposes your cached library as a WebDAV share, so you can mount it in players like **VLC**, **Infuse**, **Kodi**, or your OS file manager. ## Connect WebDAV is served on port **9092**: ``` http://:9092 ``` Authenticate with your Torrin account credentials. Add a network share of type **WebDAV (HTTP)**, host ``, port `9092`, and your Torrin login. Add a video source with a **WebDAV server (HTTP)** path: `http://:9092/`. Connect to server `http://:9092` and sign in with your account. For remote access, put WebDAV behind HTTPS via a reverse proxy — most players require it, and it keeps your credentials off the wire. ## Browse in a browser No client needed to look around. Open the WebDAV URL in any browser and sign in with your account to get a clickable file listing you can navigate and play straight from the page. ## What you'll see Your library is presented as a folder per release, each holding its playable files. Streaming pulls from the cache (or your [BYOS](/self-hosting/byos) provider) on demand, and nothing is stored on the client.