# Introduction

Welcome to the Wizarr Documentation

Wizarr is a automatic user invitation system for Plex, Jellyfin and Emby. Create a unique link and share it to a user and they will automatically be invited to your media Server! They will even be guided to download the client and instructions on how to use your requests software!

### Features

* Automatic Invitation to your Media Server (Plex, Jellyfin, Emby...)
* Secured invitation environment
* Plug and Play SSO Support\*
* Multi-tiered Invitations
* Duration for membership
* Guide user on how to download Plex client
* Requests Integration: Guide users on how to request Movies (Seerr, Ombi...)
* Discord Server Integration: Invite users to your Discord Server
* Customizable: Add any Custom HTML


# Installation

## Installation

#### Docker

{% hint style="warning" %}
Be sure to replace`/path/to/appdata/config` in the below examples with a valid host directory path. If this volume mount is not configured correctly, your Wizarr settings/data will not be persisted when the container is recreated (e.g., when updating the image or rebooting your machine).

The `TZ` environment variable value should also be set to the [TZ database name](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) of your time zone!
{% endhint %}

{% tabs %}
{% tab title="Docker Compose (recommended)" %}
**Installation:**

Define the `wizarr` service in your `docker-compose.yml` as follows:

```yaml
---
services:
  wizarr:
    container_name: wizarr
    image: ghcr.io/wizarrrr/wizarr
    ports:
      - 5690:5690
    volumes:
      - /path/to/appdata:/data
    environment:
      - PUID=1000 #Set UID
      - PGID=1000 #Set GID
      - DISABLE_BUILTIN_AUTH=false #Set to true ONLY if you are using another auth provider (Authelia, Authentik, etc)
      - TZ=Europe/London #Set your timezone here
```

Then, start all services defined in the Compose file:

`docker compose up -d` **or** `docker-compose up -d`

**Updating**

Pull the latest image:

`docker compose pull wizarr` or `docker-compose pull wizarr`

Then, restart all services defined in the Compose file:

`docker compose up -d` or `docker-compose up -d`
{% endtab %}

{% tab title="Docker CLI" %}
**Installation**

<pre class="language-bash"><code class="lang-bash"><strong>docker run -d \
</strong>  --name wizarr \
  -e DISABLE_BUILTIN_AUTH=false \
  -e PUID=1000 -e PGID=1000 \
  -e TZ=Europe/London \
  -p 5690:5690 \
  -v /path/to/appdata:/data \
  --restart unless-stopped \
  ghcr.io/wizarrrr/wizarr
</code></pre>

**Updating**

Stop and remove the existing container:

```bash
docker stop wizarr && docker rm wizarr
```

Pull the latest image:

```bash
docker pull ghcr.io/wizarrrr/wizarr
```

Finally, run the container with the same parameters originally used to create the container:

```bash
docker run -d ...
```

{% endtab %}
{% endtabs %}

### Unraid

1. Ensure you have the **Community Applications** plugin installed.
2. Inside the **Community Applications** app store, search for **Wizarr**.
3. Click the **Install Button**.
4. On the following **Add Container** screen, make changes to the **Host Port** and **Host Path 1**(Appdata) as needed, as well as the environment variables.
5. Click apply and access "Wizarr" at your `<ServerIP:HostPort>` in a web browser.

## TrueNas Fangtooth

1. Discover Apps

   ![image](https://github.com/user-attachments/assets/a99db34b-34f8-423c-8a56-617c87bf4c6a)
2. Custom App

   ![image](https://github.com/user-attachments/assets/43e9ee74-3430-4dd5-8a82-0907f9877262)
3. Quick install if you know what your doing.

   ![image](https://github.com/user-attachments/assets/fae99bb7-ee49-49cf-a611-007074a9ab5e)

*All other steps below are every single thing that needs to be changed. Also only things that need to be setup.*

4. Use only what is used in pictures following. Don't change anything unless you know what your doing. For storage locations make changes to match your setup.

*Repository:* [ghcr.io/wizarrrr/wizarr](https://github.com/wizarrrr/wizarr/blob/main/docs/getting-started/ghcr.io/wizarrrr/wizarr/README.md)

![image](https://github.com/user-attachments/assets/e4a91eb3-58d8-4ab5-8f1c-fdb775b408a6)

![image](https://github.com/user-attachments/assets/d44dec66-d520-4e2d-a6d3-6ea623c457c5)

![image](https://github.com/user-attachments/assets/f8ebb365-7404-4417-af70-b0f81a1275ba)

![image](https://github.com/user-attachments/assets/5888758f-4c4b-4756-b168-78647d5e6bd5)

![image](https://github.com/user-attachments/assets/d4e05d81-8e49-418c-a86c-1804ed0f002d)

![image](https://github.com/user-attachments/assets/c6737814-b871-46cd-900f-3a51e42bd4b9)

![image](https://github.com/user-attachments/assets/27c6e67b-eabe-4d40-b237-400d237f634e)

5. Click Install

   ![image](https://github.com/user-attachments/assets/41a7a6df-cb5d-4379-8272-f622ac837235)


# Reverse Proxy

## Trusted proxy headers

When Wizarr runs behind a reverse proxy such as Nginx, Traefik, Caddy, SWAG, or Nginx Proxy Manager, the browser may connect to the proxy over HTTPS while the proxy connects to Wizarr over plain HTTP.

Enable trusted proxy headers so Wizarr can use the original public request details when generating URLs and redirects.

Add the following environment variables to the Wizarr service:

```env
TRUST_PROXY_HEADERS=true
PROXY_FIX_X_FOR=1
PROXY_FIX_X_PROTO=1
PROXY_FIX_X_HOST=1
PROXY_FIX_X_PORT=1
PROXY_FIX_X_PREFIX=1
```

For a typical setup with one reverse proxy in front of Wizarr, use `1` for each `PROXY_FIX_*` value.

Only enable `TRUST_PROXY_HEADERS` when Wizarr is reachable only through your trusted reverse proxy. **Do not** enable it if clients can connect directly to the Wizarr container.

If Wizarr is behind multiple trusted proxy hops, increase the relevant `PROXY_FIX_*` value to match the number of trusted proxies.

## Nginx

{% tabs %}
{% tab title="SWAG" %}
Create a new file `wizarr.subdomain.conf` in `proxy-confs` with the following configuration:

```nginx
server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;

    server_name wizarr.*;

    include /config/nginx/ssl.conf;

    client_max_body_size 0;

    location / {
        include /config/nginx/proxy.conf;
        resolver 127.0.0.11 valid=30s;
        set $upstream_app wizarr;
        set $upstream_port 5690;
        set $upstream_proto http;
        proxy_pass $upstream_proto://$upstream_app:$upstream_port;
    }

}
```

{% endtab %}

{% tab title="Nginx Proxy Manager" %}
Add a new proxy host with the following settings:

#### Details

* **Domain Names:** Your desired external wizarr hostname; e.g., `wizarr.example.com`
* **Scheme:** `http`
* **Forward Hostname / IP:** Internal wizarr hostname or IP
* **Forward Port:** `5690`
* **Cache Assets:** yes
* **Block Common Exploits:** no

#### SSL

* **SSL Certificate:** Select one of the options; if you are not sure, pick “Request a new SSL Certificate”
* **Force SSL:** yes
* **HTTP/2 Support:** yes
  {% endtab %}

{% tab title="Subdomain" %}
Add the following configuration to a new file `/etc/nginx/sites-available/wizarr.example.com.conf`:

```nginx
server {
    listen 80;
    server_name wizarr.example.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name wizarr.example.com;

    ssl_certificate /etc/letsencrypt/live/wizarr.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/wizarr.example.com/privkey.pem;

    proxy_set_header Referer $http_referer;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Real-Port $remote_port;
    proxy_set_header X-Forwarded-Host $host;
    proxy_set_header X-Forwarded-Server $host;
    proxy_set_header X-Forwarded-Port $server_port;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Forwarded-Ssl on;

    location / {
        proxy_pass http://127.0.0.1:5690;
    }
}
```

Then, create a symlink to `/etc/nginx/sites-enabled`:

```bash
sudo ln -s /etc/nginx/sites-available/wizarr.example.com.conf /etc/nginx/sites-enabled/wizarr.example.com.conf
```

{% endtab %}
{% endtabs %}

## Traefik (v2)

Add the following labels to the wizarr service in your `docker-compose.yml` file:

```
labels:
  - "traefik.enable=true"
  ## HTTP Routers
  - "traefik.http.routers.wizarr-rtr.entrypoints=https"
  - "traefik.http.routers.wizarr-rtr.rule=Host(`wizarr.domain.com`)"
  - "traefik.http.routers.wizarr-rtr.tls=true"
  ## HTTP Services
  - "traefik.http.routers.wizarr-rtr.service=wizarr-svc"
  - "traefik.http.services.wizarr-svc.loadbalancer.server.port=5690"
```

For more information, please refer to the [Traefik documentation](https://doc.traefik.io/traefik/user-guides/docker-compose/basic-example/).

## Caddy

{% tabs %}
{% tab title="Subdomain" %}
Add the following site block to your Caddyfile:

```
wizarr.example.com {
    reverse_proxy http://127.0.0.1:5690
}
```

{% endtab %}

{% tab title="Path" %}
You need the [response replacement](https://github.com/caddyserver/replace-response) module to use this config.

Add the following site block to your Caddyfile:

```
plex.example.com {
    redir /wizarr /wizarr/admin

    handle_path /wizarr/* {
        replace {
            "href=\"/"      "href=\"/wizarr/"
            "action=\"/"    "action=\"/wizarr/"
            "\"/static"       "\"/wizarr/static"
            "hx-post=\"/"   "hx-post=\"/wizarr/"
            "hx-get=\"/"    "hx-get=\"/wizarr/"
            "scan=\"/"      "href=\"/wizarr/"
            "/scan"         "/wizarr/scan"
            # include in join code path copy
            "navigator.clipboard.writeText(url + \"/j/\" + invite_code);" "navigator.clipboard.writeText(url + \"/wizarr/j/\" + invite_code);"
        }

        # Your wizarr backend
        reverse_proxy http://127.0.0.1:5690
    }
    # Your main service that you want at /
    reverse_proxy http://127.0.0.1:5055
}
```

{% endtab %}
{% endtabs %}


# Single-Sign-On (SSO)

#### **Wizarr supports SSO via disabling its inbuilt authentication**

To Disable Wizarr's inbuilt authentication in order to put it behind a Proxy Provider (Authelia, Authentik...), set the following variable:

`DISABLE_BUILTIN_AUTH=True`

#### Whitelist Public Paths (important!)

In order to make the invitation process available for non signed in users, make sure you whitelist the following paths:

{% tabs %}
{% tab title="Authelia" %}
Authelia matches each expression against the path and the query string, so every expression also accepts a `?`.

```
    - domain: wizarr.domain.com
      resources:
        - '^/j([/?].*)?$'
        - '^/join([/?].*)?$'
        - '^/invitation/process([/?].*)?$'
        - '^/plex/callback([/?].*)?$'
        - '^/wizard([/?].*)?$'
        - '^/reset/.+$'
        - '^/setup([/?].*)?$'
        - '^/static([/?].*)?$'
        - '^/image-proxy([/?].*)?$'
        - '^/cinema-posters([/?].*)?$'
        - '^/favicon\.ico$'

      policy: bypass
```

{% endtab %}

{% tab title="Authentik/Other" %}
Paste these lines into the **Unauthenticated Paths** field of the Proxy Provider. Put one expression on each line. Do not add quotes or a `-` in front of a line. Authentik reads them as part of the expression, and then no path matches.

```
^/j(/.*)?$
^/join([/?].*)?$
^/invitation/process$
^/plex/callback$
^/wizard(/.*)?$
^/reset/.+$
^/setup(/.*)?$
^/static(/.*)?$
^/image-proxy$
^/cinema-posters$
^/favicon\.ico$
```

In **Forward auth (domain level)** mode, Authentik matches the full URL, not only the path. The full URL includes the query string, for example `/join?code=ABC123` from the invitation code page. For this reason, the `/join` expression also accepts a `?`. Add the scheme and the host to each line, for example `^https://wizarr\.domain\.com/j(/.*)?$`.
{% endtab %}
{% endtabs %}

The PWA manifest (`/static/manifest.json`) and the icons are under `/static`, so the `/static` expression covers them.

Do not whitelist `/login`. When `DISABLE_BUILTIN_AUTH=True`, `/login` signs in every visitor as an admin. The `/api` paths are not necessary for invitations. Whitelist `/api` only if external tools call the Wizarr API. The API endpoints then accept only requests with a valid `X-API-Key` header, but the API documentation at `/api/docs/` becomes public.

#### Cloudflare Access: verify the token

`DISABLE_BUILTIN_AUTH=True` trusts whatever reaches Wizarr. Anything that can reach its port directly, such as another container on the same host or a misconfigured firewall, gets the admin pages without going through your proxy.

If Wizarr sits behind Cloudflare Access, it can check Access's signed token itself instead. Set both of these:

```
CF_ACCESS_TEAM_DOMAIN=yourteam.cloudflareaccess.com
CF_ACCESS_AUD=<the application's AUD tag>
```

The AUD tag is in Zero Trust under Access > Applications > your Wizarr app > Additional settings > Application Audience (AUD) Tag.

With both set, every admin request must carry a valid `Cf-Access-Jwt-Assertion` header: signed by your team's key, for this application, issued by your team domain and not expired. A session cookie on its own grants nothing. This takes precedence over `DISABLE_BUILTIN_AUTH` and the built-in login. The invitation paths above stay public, `/health` needs no token, and API keys work as before.

If the signing keys can't be fetched from `https://<team>/cdn-cgi/access/certs`, admin access is refused rather than allowed. To get back in, unset the two variables and restart.


# Discord Integration

#### Find your Discord Server ID

1. In Discord, go to your **Server Settings**
2. Select the **Engagement** tab
3. Enable Server Widget
4. Choose an Invite Channel (`general` for example)
5. Copy your **Server ID**

{% hint style="info" %}
**Why not use an invitation link?**

Enabling the widget and the invite channel makes the Discord API dynamically generate an invitation link for the purpose of the widget.

This means that to use this integration, you don't need to generate a Never expiring invitation, which some users might want to avoid.
{% endhint %}


# Customise The Wizard

Give your new users a personalized onboarding experience with Wizarr's powerful wizard system. Create custom welcome flows that guide users through setting up their media apps, understanding your server, and more.

***

## Overview

Wizarr's wizard system supports multiple approaches for creating and managing onboarding steps:

1. **Database-backed steps** (recommended) - Create and manage steps through the admin interface
2. **Pre-invite and post-invite categories** - Show steps before or after users accept invitations ([Learn more](https://github.com/wizarrrr/wizarr/tree/main/docs/using-wizarr/pre-post-invite-steps.md))
3. **Wizard bundles** - Custom sequences of steps for specific invitation types
4. **Multi-server wizards** - Automatic flows for invitations covering multiple servers
5. **Legacy file-based steps** - Markdown files as fallback (still supported)

***

## 1 · Managing Wizard Steps Through the Admin Interface

### Accessing the Wizard Admin

Navigate to **Settings** → **Wizard** in your Wizarr admin panel to manage wizard steps and bundles.

### Creating Default Steps

Default steps are server-type specific and will be shown to all users invited to that server type.

1. **Click "Default Steps"** in the wizard settings
2. **Click "Create Step"** to add a new step
3. **Select the server type**: `plex`, `jellyfin`, `emby`, `audiobookshelf`, `romm`, or `komga`
4. **Write your content** using Markdown
5. **Set conditional display** (optional) using the `requires` field

### Step Fields

* **Server Type**: Which media server this step applies to
* **Category**: When to show this step - "Before Invite Acceptance" (pre-invite) or "After Invite Acceptance" (post-invite)
* **Title**: Optional override for the step heading
* **Markdown**: The main content of your step
* **Requires**: Comma-separated setting keys that must be truthy for the step to display
* **Require Interaction**: When enabled, users must click a link or button in the step before they can proceed to the next step

> **New Feature**: The **Category** field allows you to control whether steps are shown before or after users accept invitations. [Learn more about pre-invite and post-invite steps](https://github.com/wizarrrr/wizarr/tree/main/docs/using-wizarr/pre-post-invite-steps.md).

### Conditional Step Display

Use the `requires` field to show steps only when certain conditions are met:

```
discord_url,enable_notifications
```

This step will only show if both `discord_url` and `enable_notifications` settings have truthy values.

***

## 2 · Wizard Bundles (Custom Flows)

Wizard bundles allow you to create completely custom onboarding sequences that can be assigned to specific invitations.

### Creating a Bundle

1. **Click "Custom Bundles"** in the wizard settings
2. **Click "Create Bundle"**
3. **Name your bundle** (e.g., "VIP Onboarding", "Family Setup")
4. **Add a description** (optional)
5. **Create and assign steps** to your bundle

### Bundle Steps

Bundle steps are different from default steps:

* **No server type** - they're not tied to a specific media server
* **Flexible ordering** - drag and drop to reorder steps within the bundle
* **Custom content** - can include any markdown content you want
* **Interaction requirements** - can also be configured to require user interaction

### Assigning Bundles to Invitations

When creating an invitation, you can:

1. **Select a wizard bundle** from the dropdown
2. **Leave it as "Automatic"** to use the default server-type steps

***

## 3 · Multi-Server Wizard Flows

For invitations that cover multiple media servers, Wizarr automatically creates a combined wizard flow.

### How It Works

1. **User accepts invitation** for multiple servers
2. **Wizard automatically combines** steps from each server type
3. **Steps are shown in order** based on server type (alphabetical)
4. **User progresses through** all relevant steps

### Server Types Supported

* **Plex** - Media streaming server
* **Jellyfin** - Open-source media server
* **Emby** - Media server platform
* **Audiobookshelf** - Audiobook and podcast server
* **Romm** - ROM and game library manager
* **Komga** - Comic and manga library server

***

## 4 · Writing Effective Wizard Steps

### Markdown Syntax

Wizarr supports standard Markdown with some enhancements:

```markdown
# Step Title

Welcome to our **media server**! Here's what you need to know:

## Getting Started

1. Download the app using the button below
2. Sign in with your username and password
3. Start enjoying your content!

[Download App](https://example.com/download){:target="_blank" .btn}
```

### Styling Options

* **Links as buttons**: Add `{.btn}` to make links look like buttons
* **External links**: Add `{:target="_blank"}` to open in new tab
* **Images**: Use standard Markdown syntax with optional classes
* **Tailwind classes**: Add `{.class-name}` for custom styling

### Recently Added Media Carousel

Use the `recently_added_media` widget to show the posters of the newest items on the media server:

```markdown
{{ widget:recently_added_media }}
{{ widget:recently_added_media libraries="Movies, TV Shows" limit=12 }}
```

The widget reads the newest items of each library. Then it sorts all the items by the date they were added, newest first, and shows the first items. Thus one library cannot fill the carousel.

* `libraries` (optional): the names of the libraries to show, with commas between them. Case does not matter. A name that is not on the server gives no items. If you do not set `libraries`, the widget uses the libraries that are enabled in Wizarr for the server. If Wizarr has no library list for the server, the widget uses all the libraries.
* `limit` (optional): the maximum number of posters. The default is 6.

The **Add... → Recently Added Media** menu item in the wizard-step editor inserts this syntax for you.

### Plex Collection Carousel

For Plex wizard steps, use the `collection_media` widget to show posters from a named collection:

```markdown
{{ widget:collection_media collection="Summer Blockbusters" limit=15 }}
```

The collection name is matched case-insensitively across the Plex server's libraries. The optional `limit` parameter caps the number of posters and defaults to 15. Set it to `0` to show the entire collection. The **Add... → Collection Media** menu item in the wizard-step editor inserts this syntax for you.

### Templates in Shared Bundles

You can import and export third-party bundles. Wizarr stores their content and applies the same rendering rules to imported and edited steps.

Wizarr renders step templates in a Jinja sandbox. A template cannot read Python internals or run commands on the server.

Templates can use `server_name`, `server_type`, `server_url`, `external_url`, and the application settings, for example `discord_id` or `overseerr_url`. The same values are available through `settings`, for example `settings.server_name`. Wizarr does not give templates settings with credential names (passwords, tokens, secrets, and API keys), sessions, or Python objects.

You can use the usual Jinja features: conditions, loops, `set`, `with`, macros, filters, and string methods. For translations, use `{{ _("Welcome") }}`, `{{ _("Welcome to %(name)s", name=server_name) }}`, or `{% trans %}...{% endtrans %}`. `url_for` is available for links to Wizarr pages and static files.

Cards, buttons, and media widgets remain available. For example:

```markdown
|||
## {{ _("Welcome") }}

{{ server_name }}

{{ widget:button url="external_url" text=_("Open") }}
|||
```

Templates cannot include, import, or extend other templates, and cannot use recursive loops. Wizarr also stops a template that runs too many loop iterations or function calls, or that makes very large text. Wizarr shows a step error when this occurs.

Wizarr shows the HTML in a step as you write it, including styles, scripts, and iframes. Titles display as plain text. Before you import a bundle from a third party, read its steps: their HTML and scripts run in the wizard for each invited user.

### Requiring User Interaction

You can force users to engage with step content before allowing them to proceed to the next step. This is useful for ensuring users actually download apps, read important information, or acknowledge terms.

#### How It Works

1. **Enable "Require Interaction"** when creating or editing a step in the admin interface
2. **Add interactive elements** like links or buttons to your step content
3. **Users must click** on at least one link or button before the "Next" button becomes enabled

#### Example Step Content

```markdown
# Download the Plex App

Before you can access our media library, you'll need to download the Plex app for your device.

**Choose your platform:**

[📱 Download for iOS](https://apps.apple.com/app/plex/id383457673){:target="_blank" .btn}
[🤖 Download for Android](https://play.google.com/store/apps/details?id=com.plexapp.android){:target="_blank" .btn}
[💻 Download for Windows](https://www.plex.tv/media-server-downloads/?cat=plex+desktop&plat=windows#plex-app){:target="_blank" .btn}

Click one of the download buttons above to continue.
```

#### Visual Feedback

* **Disabled Next Button**: Appears dimmed with reduced opacity until interaction occurs
* **Visual Indicators**: The button shows "not-allowed" cursor when hovered
* **Automatic Enable**: As soon as the user clicks any link or button, the Next button becomes active

***

## 5 · Legacy File-Based Steps (Fallback)

While the database-backed approach is recommended, Wizarr still supports file-based steps as a fallback.

### File Structure

```
wizard_steps/
├── plex/
│   ├── 01_download.md
│   ├── 02_setup.md
│   └── 99_tips.md
├── jellyfin/
│   ├── 01_download.md
│   └── 02_about.md
├── emby/
│   ├── 01_download.md
│   └── 02_about.md
├── audiobookshelf/
│   └── 01_intro.md
├── romm/
│   └── 01_intro.md
└── komga/
    ├── 01_intro.md
    ├── 02_access.md
    ├── 03_features.md
    └── 04_tips.md
```

### File-Based Front Matter

```yaml
---
title: Download the App
require: true
---

# Your content here
```

### When Files Are Used

* **No database steps exist** for the server type
* **Database is unavailable** (during migrations, etc.)
* **Fallback behavior** ensures the wizard always works

***

## 6 · Access Control

### Wizard Access Control

By default, wizard access is restricted to:

* **Authenticated users** (logged-in admins)
* **Users with valid invitation codes** (stored in session)

### Disabling Access Control

Set the `wizard_acl_enabled` setting to `false` to allow unrestricted access to the wizard.

### Visitors Without Access

When a visitor opens `/wizard/` or a wizard step without access, Wizarr shows a help page at `/wizard/help`. It does not show the admin login.

* If **Protect Wizard Access** is on, the help page tells the visitor to open the link from their invitation. It does not show wizard steps.
* If **Protect Wizard Access** is off, the help page has a link to the wizard steps of each server type. Thus you can send `/wizard/` to users who want to read the steps again after they sign up.

***

## 7 · Previewing and Testing

### Preview Options

1. **Click "Preview"** button when editing steps
2. **Visit `/wizard`** while logged in to test the full flow
3. **Use invitation links** to test the user experience

### Testing Multi-Server Flows

1. **Create an invitation** for multiple servers
2. **Use the invitation link** to test the combined flow
3. **Verify step order** and content display

***

## 8 · Advanced Features

### Step Ordering

* **Database steps**: Automatically ordered by position
* **File-based steps**: Ordered alphabetically (use `01_`, `02_` prefixes)
* **Bundle steps**: Drag and drop to reorder

### Dynamic Content

Steps can reference server settings and configuration:

```markdown
Welcome to **{{ server_name }}**!

Your server is available at: {{ server_url }}
```

### Conditional Logic

Use the `requires` field to show steps conditionally:

```markdown
# Discord Integration

Join our Discord community for support and updates!

[Join Discord]({{ discord_url }}){:target="_blank" .btn}
```

Only shows if `discord_url` is set in the `requires` field.

***

## 9 · Best Practices

### Content Guidelines

* **Keep steps concise** - users want to get started quickly
* **Use clear calls-to-action** - make buttons and links obvious
* **Test on mobile** - many users will access on phones
* **Include screenshots** - visual aids help understanding
* **Use interaction requirements wisely** - only require interaction for critical steps like app downloads or terms acceptance

### Step Organization

* **Start with essentials** - app download, account setup
* **End with optional content** - advanced features, tips
* **Use logical progression** - each step builds on the previous
* **Consider user journey** - think about what they need when

### Bundle Strategy

* **Create specific bundles** for different user types
* **VIP users** might get extended onboarding
* **Family members** might need simpler instructions
* **Power users** might want advanced configuration steps

***

## 10 · Migration from File-Based Steps

If you're upgrading from an older version with file-based steps:

1. **Database steps take precedence** - they'll be used if they exist
2. **File-based steps remain** as fallback
3. **Import existing files** through the admin interface
4. **Gradually migrate** to database-backed approach
5. **Test thoroughly** before removing file-based steps

***

## 11 · Troubleshooting

### Common Issues

| Problem                  | Solution                                                                                                |
| ------------------------ | ------------------------------------------------------------------------------------------------------- |
| Steps not showing        | Check server type matches invitation                                                                    |
| Wrong step order         | Verify position values in database                                                                      |
| Missing steps            | Check `requires` field conditions                                                                       |
| Bundle not working       | Verify bundle assignment on invitation                                                                  |
| Next button won't enable | Ensure step has interactive elements (links/buttons) and interaction requirement is properly configured |

### Debug Tips

1. **Check admin logs** for wizard-related errors
2. **Verify database connectivity** for step loading
3. **Test with different server types** to isolate issues
4. **Use browser dev tools** to inspect step loading

***

## 12 · FAQ

| Question                                 | Answer                                                      |
| ---------------------------------------- | ----------------------------------------------------------- |
| Can I disable the wizard entirely?       | Yes, don't create any steps and remove existing ones        |
| Do I need to know HTML/CSS?              | No, Markdown is sufficient for most use cases               |
| Can I use custom JavaScript?             | Not directly, but you can use interactive Markdown elements |
| How do I backup my wizard steps?         | Export through the admin interface or backup your database  |
| Can I preview changes before publishing? | Yes, use the preview button in the step editor              |

***

That's it! 🎉

Wizarr's wizard system gives you complete control over your user onboarding experience. Whether you use simple default steps or create elaborate custom bundles, your users will have a smooth, guided introduction to your media server.

Start with the admin interface, create your first step, and watch your users get set up faster than ever before! ✨


# Password and Passkey Recovery

This guide helps administrators recover access to their Wizarr instance when locked out due to forgotten passwords or lost passkeys.

## Recovery Tool Overview

The recovery tool is a command-line utility designed to help administrators regain access when locked out. It provides emergency recovery options for common authentication issues.

### Key Features

* **Password Reset**: Reset passwords for admin accounts
* **Passkey Management**: Remove all passkeys for specific admin accounts
* **Emergency Admin**: Create emergency admin accounts
* **Account Listing**: View all admin accounts and their passkey status
* **Container-Friendly**: Designed to work seamlessly in Docker containers

## When to Use the Recovery Tool

Use the recovery tool in these situations:

* **Forgotten Password**: You cannot remember your admin password
* **Lost Passkey**: Your passkey device is lost or broken
* **2FA Lockout**: You're locked out due to passkey 2FA requirements
* **Complete Lockout**: All admin accounts are inaccessible

## Running the Recovery Tool

### Docker Container

For Docker installations:

```bash
# Find your container name
docker ps

# Run it directly
docker exec -it <container_name> uv run recovery_tool.py
```

## Recovery Options

The recovery tool provides five main options:

### 1. List All Admin Accounts

View all administrators and their current status:

* Username and ID
* Number of registered passkeys
* Account creation date
* Legacy admin status

### 2. Reset Admin Password

Change the password for any admin account:

1. Select the admin account from the list
2. Enter a new password (minimum 6 characters)
3. Confirm the password
4. The password is immediately updated

### 3. Remove All Passkeys

Delete all passkeys for a specific admin account:

1. Select the admin account
2. Confirm the operation (this cannot be undone)
3. All passkeys are removed
4. 2FA requirement is disabled for that account

### 4. Create Emergency Admin

Create a new admin account with password authentication:

1. Enter a username (must be unique)
2. Set a password (minimum 6 characters)
3. Confirm the password
4. New admin account is created immediately

### 5. Exit

Close the recovery tool safely.

## Common Recovery Scenarios

### Scenario 1: Forgotten Admin Password

**Problem**: You remember your username but forgot your password.

**Solution**:

1. Run the recovery tool
2. Select option 2 (Reset admin password)
3. Choose your admin account
4. Enter a new password
5. Log in with the new password

### Scenario 2: Lost Passkey Device

**Problem**: Your passkey device is lost, broken, or unavailable.

**Solution**:

1. Run the recovery tool
2. Select option 3 (Remove all passkeys for admin)
3. Choose your admin account
4. Confirm the removal
5. Log in with username/password (2FA disabled)

### Scenario 3: Complete Lockout

**Problem**: All admin accounts are inaccessible.

**Solution**:

1. Run the recovery tool
2. Select option 4 (Create emergency admin account)
3. Create a new admin account
4. Log in with the emergency account
5. Manage other accounts through the web interface
6. Delete the emergency account when no longer needed

### Scenario 4: 2FA Lockout

**Problem**: You're locked out due to passkey 2FA requirements.

**Solution**:

1. Run the recovery tool
2. Select option 3 (Remove all passkeys for admin)
3. Choose your admin account
4. Confirm passkey removal
5. Log in with username/password only

## Security Considerations

{% hint style="warning" %}
**Important Security Notes**

* Only run this tool when you have direct access to the server/container
* The tool requires database access and should only be used by system administrators
* Anyone with server access can use this tool to gain admin privileges
  {% endhint %}

### After Recovery

Once you regain access, consider these security steps:

1. **Update Passwords**: Change passwords for all admin accounts
2. **Re-register Passkeys**: Set up new passkeys for 2FA
3. **Review Admin Access**: Audit who has admin privileges
4. **Delete Emergency Accounts**: Remove temporary accounts when no longer needed
5. **Secure Server Access**: Ensure only authorized personnel can access the server

## System Requirements

The recovery tool requires:

* Access to the server or container running Wizarr
* Read/write access to the database
* Proper Flask environment configuration
* Python execution privileges

## Environment Configuration

The tool automatically uses your existing Wizarr configuration. Ensure these environment variables are set if needed:

* `DATABASE_URL` - Database connection string
* `FLASK_ENV` - Flask environment (development/production)
* `SECRET_KEY` - Flask secret key

## Troubleshooting

### Common Issues

**Import Error**

* Ensure you're running the tool from the Wizarr root directory
* Check that all dependencies are installed

**Database Connection Error**

* Verify the database is accessible
* Check that the Flask environment is properly configured

**Permission Error**

* Ensure you have write access to the database
* Check file permissions on the database file

### Tool Output

The recovery tool provides clear feedback:

* ✅ Success messages for completed operations
* ❌ Error messages for failed operations
* ⚠️ Warning messages for destructive operations
* ℹ️ Information messages for status updates

## Best Practices

1. **Regular Backups**: Keep database backups before making changes
2. **Test Access**: Verify you can log in after making changes
3. **Document Changes**: Keep records of recovery actions taken
4. **Secure Storage**: Store recovery procedures in a secure location
5. **Regular Reviews**: Periodically review admin account access

## Support

If you encounter issues with the recovery tool:

1. Check the troubleshooting section above
2. Verify database connectivity
3. Ensure proper permissions
4. Review Flask application logs for additional details
5. Contact support through the official channels

{% hint style="info" %}
**Remember**: This tool is for emergency recovery only. Regular admin management should be done through the web interface.
{% endhint %}


# Plex


# Obtain a Plex token

## How to Get Your Plex Token

Some Wizarr features require your Plex authentication token. Here’s the simplest way to find it.

### 1. Open Plex in Your Browser

* Go to [https://app.plex.tv](https://app.plex.tv/) and log in.
* Open your **Plex Web App**.

### 2. Right-Click Any Item

* Right-click on a movie, show, or library item.
* Click **View Info**
* Select **View XML**

### 3. Copy the Token

* The XML page will open in a new tab.
* Look at the **URL in the address bar**.
* At the very end you’ll see `X-Plex-Token=...`

Example:

```
https://<server>:32400/library/metadata/12345?X-Plex-Token=abcd1234efgh5678
```

The part after `X-Plex-Token=` is your **Plex token**.

### 4. Keep It Safe

* Do **not** share this token publicly — it grants access to your Plex server.
* If it’s ever compromised, you can revoke it by signing out of all devices in your [Plex account settings](https://plex.tv/account).


# Discord

Join our discord server

{% embed url="<https://discord.gg/wsSTsHGsqu>" %}


# Translate

Thanks for your interest in contributing to Wizarr!

### Weblate

We use Weblate to help translate Wizarr!

{% embed url="<https://hosted.weblate.org/engage/wizarr/>" %}

### Testing Translations

After you have saved a translation, it will be pushed to the `master` branch directly. The `dev` docker image will then be automatically compiled shortly thereafter.

To test it out, simply add the `dev` label to the Docker Image, and you can use the `FORCE_LANGUAGE` environment variable to force a language to Wizarr.

{% tabs %}
{% tab title="Docker Compose" %}

```yaml
---
version: "3.8"
services:
  wizarr:
    container_name: wizarr
    image: ghcr.io/wizarrrr/wizarr:dev
    [...]
    environment:
      - FORCE_LANGUAGE=en
```

{% endtab %}

{% tab title="Docker CLI" %}

```
docker run -d \
  -e FORCE_LANGUAGE=en
  [...]
  ghcr.io/wizarrrr/wizarr:dev
```

{% endtab %}
{% endtabs %}


# Development

If you want to contribute to Wizarr, here is how

### Prerequisites

* [uv](https://docs.astral.sh/uv/)
* Node.js/npm - [nvm](https://github.com/nvm-sh/nvm) recommended

### Development Environment

1. Clone the repository with `git clone https://github.com/wizarrrr/wizarr.git`
2. Move into the directory `cd wizarr`
3. Start Wizarr with `uv run dev.py`
4. Wizarr is now accessible at <http://127.0.0.1:5000>

### Running Tests

To run the test suite, ensure your dependencies (including development dependencies) are installed, then invoke pytest via `uv`:

```bash
uv sync --locked
uv run pytest
```


