⚠️ These docs are currently under construction and may not be fully accurate.
AIOStreamsAIOStreams
Reference

Templates

Complete reference for writing AIOStreams configuration templates.

A template is a shareable JSON file that applies a partial AIOStreams configuration to a user's instance. Templates support dynamic expressions, user inputs, and service-conditional logic — making them powerful enough to cover most configuration scenarios without hardcoding anything.


Template Structure

{
  "metadata": { ... },
  "config": { ... }
}
  • metadata — describes the template (name, author, inputs, etc.)
  • config — a partial UserData object. Only the keys you include are applied; everything absent is left unchanged in the user's existing config.

All dynamic expressions inside config are evaluated at load time, before the config is merged.


metadata Fields

FieldTypeRequiredDescription
idstring (1–100)NoUnique ID. Auto-generates a UUID if omitted. Use namespaced form: author.my-template
namestring (1–100)YesDisplay name shown in the template browser
descriptionstring (1–1000)YesSupports Markdown (links, bold, lists)
authorstring (1–20)YesAuthor name or handle
source"builtin" | "custom" | "external"NoDefaults to "builtin". Use "external" for user-imported remote templates
versionsemver stringNoDefaults to "1.0.0". Used for update comparisons
categorystring (1–20)YesShown in the browser filter bar (e.g. "Debrid", "Usenet")
servicesServiceId[]NoControls the service selection screen — see Service Handling
serviceRequiredbooleanNoWhether a service must be selected before applying
setToSaveInstallMenubooleanNoRedirects the UI to Save & Install after loading. Defaults to true
sourceUrlURL stringNoURL the template was fetched from — enables auto-update
changelogChangelogEntry[]NoInline version history. See Changelog
changelogUrlURL stringNoURL to a remote CHANGELOG.md. See Changelog
inputsInputDefinition[]NoUser-fillable options shown before loading. See Template Inputs

Service Handling

The services field controls whether (and how) the user is asked to select a debrid service.

ScenarioBehaviour
services not setAll services are shown in the selection screen
services: []Service selection is skipped entirely
services: ["realdebrid", "torbox"]Only the listed services are shown
Single service + serviceRequired: trueSelection is skipped; user is prompted for that service's credentials directly
serviceRequired absent or falseA "Skip" button is shown

At load time, selected services are available via services.<serviceId>. The bare services reference (no service ID) is truthy when at least one service is selected and falsy when none are — making it the canonical way to distinguish debrid mode from P2P/no-service mode.


Template Inputs

Inputs are defined in metadata.inputs. They appear in a dialog before the template loads. Values are accessible throughout config via inputs.<id>.

Input Fields

FieldTypeRequiredDescription
idstringYesIdentifier used in inputs.<id> references
namestringYesLabel shown in the UI
descriptionstringYesHelp text (supports Markdown)
typesee belowYesControls the UI widget
requiredbooleanNoPrevents proceeding if the field is empty
defaultanyNoPre-filled value
options{ value, label }[]For select/multi-selectList of choices
showInSimpleModebooleanNoSet false to hide in Simple mode. Defaults to true
advancedbooleanNoShorthand for showInSimpleMode: false
constraints{ min?, max?, forceInUi? }NoMin/max for strings (length) or numbers (value)
__ifstringNoHide this input when the condition is false. Only services.<id> supported
intentsee AlertFor alertControls the colour/icon of an alert banner
socials{ id, url }[]For socialsSocial links to render

Input Types

TypeWidgetNotes
stringText input
passwordMasked inputNot stored in plaintext after import
numberNumber inputReturns a number; blank returns undefined
booleanToggleReturns true or false
selectDropdownRequires options
select-with-customDropdown + textSame as select + "Custom" option with free-text
multi-selectMulti-selectRequires options. Returns an array
urlURL text inputClient-side format validation
alertStyled bannerDisplays info/warning/error. See Alert section
socialsIcon rowRenders social media icon links. See Socials section
subsectionButton → modalGroups sub-inputs. See Subsections section

Subsections

A subsection renders as a button that opens a modal containing subOptions. Values are accessed with dot notation: inputs.<subsectionId>.<subOptionId>.

{
  "id": "proxy",
  "name": "Proxy Settings",
  "description": "Optional proxy configuration.",
  "type": "subsection",
  "subOptions": [
    { "id": "url", "name": "Proxy URL", "type": "url" },
    { "id": "id", "name": "Proxy Service", "type": "select", "options": [...] }
  ]
}

Use in config as {{inputs.proxy.url}}.

Alert

Displays a styled banner — useful for important notes before the user fills in options. Captures no value.

{
  "id": "notice",
  "name": "Important",
  "description": "This template requires an active RealDebrid subscription.",
  "type": "alert",
  "intent": "warning"
}

intent values: info, success, warning, alert (filled background), info-basic, success-basic, warning-basic, alert-basic (neutral card with coloured icon).

Socials

Renders a row of social icon links. Captures no value.

{
  "id": "credits",
  "name": "Credits",
  "type": "socials",
  "socials": [
    { "id": "github", "url": "https://github.com/yourname" },
    { "id": "ko-fi", "url": "https://ko-fi.com/yourname" }
  ]
}

Supported id values: website · github · discord · ko-fi · patreon · buymeacoffee · github-sponsors · donate


Dynamic Expressions

{{}} — String Interpolation

Use inside any string value to substitute at load time:

{
  "addonLogo": "{{inputs.logoUrl}}",
  "preferredLanguages": ["{{inputs.languages}}", "Original", "Unknown"]
}

Type preservation: When the entire string value is a single {{...}} token, the raw value is returned (array stays array, boolean stays boolean). Arrays are spread into the parent array.

Credential refs: {{services.<id>.<key>}} injects a credential entered by the user during service setup. Resolved at the final save step.

{ "apiKey": "{{services.torbox.apiKey}}" }

{{services}} resolves to the array of all selected service IDs (or comma-joined string when embedded in a larger string).

__if — Conditional Array Items

Add "__if": "<condition>" to any object inside an array. The object is included when the condition is true; omitted when false.

{
  "excludedStreamExpressions": [
    {
      "__if": "inputs.torboxTier == nonPro",
      "expression": "/*TB Non-Pro Download Limit*/ size(uncached(streams), '200GB')",
      "enabled": true
    }
  ]
}

Condition Syntax

FormExampleMeaning
Bare referenceinputs.enableFeatureTruthy if not false, null, "", or []0 is truthy
Negation!inputs.enableFeatureLogical NOT
Equalityinputs.tier == premiumString equals "premium"
Inequalityinputs.tier != noneString not equal to "none"
Array includesinputs.optionalFilters includes dvPassthroughArray contains the string
Numeric > / >= / < / <=inputs.count > 5Numeric comparison
Any service (debrid)servicesTrue when any service is selected
No service (P2P)!servicesTrue when no service is selected
Service checkservices.realdebridSpecific service is enabled
Nested subsectioninputs.proxy.urlSub-option is filled
Compound andinputs.flag and inputs.tier == proBoth sub-conditions true
Compound orservices.torbox or services.realdebridAt least one sub-condition true
Compound xorinputs.a xor inputs.bExactly one sub-condition true

Precedence: and > xor > or

At the Object-Key Level

{ "__if": "cond", "__value": X } as the value of a config key conditionally includes or removes the key:

{
  "formatter": {
    "__if": "!inputs.retainFormatter",
    "__value": { "id": "tamtaro" }
  }
}
  • Condition true → key is set to X
  • Condition false → key is absent from applied config (user's value is kept)

__switch — Object Replacement

Replaces an entire object with a different object depending on an input value:

"formatter": {
  "__switch": "inputs.formatterStyle",
  "cases": {
    "torrentio": { "id": "torrentio" },
    "gdrive":    { "id": "gdrive" }
  },
  "default": { "id": "prism" }
}

Use __switch: "services" with "" as the P2P case key (the resolved value is a comma-joined service ID string):

"preferredStreamTypes": {
  "__switch": "services",
  "cases": { "": ["p2p"] },
  "default": ["cached", "usenet"]
}

__value — Conditional Array Values

Inject values directly into a parent array rather than inserting an object:

{
  "excludedVisualTags": [
    "3D",
    { "__if": "inputs.excludeDV", "__value": "DV" },
    { "__if": "inputs.excludeHdr", "__value": ["HDR", "HDR10", "HDR10+"] }
  ]
}

__remove — Drop a Config Key

Remove a key from the applied config unconditionally (leaves the user's existing value untouched):

{ "formatter": { "__remove": true } }

Most useful as a __switch case value when "leave unchanged" is a selectable outcome.


Template Placeholders

For fields the user should fill in themselves after loading:

PlaceholderMeaning
"<template_placeholder>"Required — must be filled
"<required_template_placeholder>"Same as above
"<optional_template_placeholder>"Optional — can be left blank

The frontend highlights unfilled placeholder values after the template is applied.


Validation Rules

Errors (block loading)

  • metadata.name, description, author, category must be non-empty strings
  • metadata.version must be a valid semver string
  • metadata.source must be "builtin", "custom", or "external"
  • __if condition must be a non-empty string
  • __if namespace must be inputs or services
  • __switch reference must start with inputs. or services.

Warnings (allowed but flagged)

  • {{inputs.<id>}} references an ID not declared in metadata.inputs
  • __if references an inputs.<id> not in metadata.inputs
  • __switch references an input not in metadata.inputs
  • __switch is missing a cases object

Changelog

Templates can expose a version history shown to users and used for update notifications.

Inline (changelog)

"changelog": [
  {
    "version": "1.2.0",
    "date": "2026-03-01",
    "content": "- Added support for multi-audio streams\n- Fixed sort order bug"
  },
  {
    "version": "1.1.0",
    "date": "2025-12-01",
    "content": "- Initial release"
  }
]

List entries in reverse-chronological order (newest first).

Remote (changelogUrl)

"metadata": {
  "sourceUrl": "https://raw.githubusercontent.com/you/repo/main/template.json",
  "changelogUrl": "https://raw.githubusercontent.com/you/repo/main/CHANGELOG.md"
}

When changelogUrl is present it takes full precedence — the inline changelog is ignored.

CHANGELOG.md Format

AIOStreams parses this file with a strict format:

# Changelog

## 1.3.0 (2026-03-04)

### What's new

- Rewrote sort logic for better performance

## 1.2.0 (2026-01-20)

- Added language passthrough option

Rules:

  • File must start with # Changelog (h1)
  • Version headings must be ## <semver> (<YYYY-MM-DD>) — nothing else on that line
  • Use ### or deeper for sub-headings inside entry content; never ## inside entries
  • Entries must be in reverse-chronological order

Sharing Templates

Adding to an instance

Instance hosters can add templates via:

  • The templates folder in the data directory (at /app/data/templates in the Docker container)
  • The TEMPLATE_URLS environment variable — a JSON array of URLs pointing to template JSON files

Send a user directly to the template import flow by appending query parameters to any AIOStreams URL:

https://your-aiostreams.example.com/stremio/configure?template=https://example.com/my-template.json

With a specific template pre-selected from a multi-template file:

https://your-aiostreams.example.com/stremio/configure?template=https://example.com/templates.json&templateId=author.my-template

Users are shown a warning when a template is imported via deep link. Remind users in your documentation to only import templates from sources they trust.

On this page