music-assistant-server

32.4 KBHTML
api_docs.html
32.4 KB876 lines • xml
1<!DOCTYPE html>
2<html lang="en">
3<head>
4    <meta charset="UTF-8">
5    <meta name="viewport" content="width=device-width, initial-scale=1.0">
6    <title>Music Assistant API Documentation</title>
7    <link rel="stylesheet" href="../resources/common.css">
8    <style>
9        body {
10            min-height: 100vh;
11            padding: 40px 20px;
12        }
13
14        .container {
15            max-width: 1100px;
16            margin: 0 auto;
17            overflow: hidden;
18        }
19
20        .header {
21            text-align: center;
22            padding: 48px 40px;
23            border-bottom: 1px solid var(--border);
24        }
25
26        .header h1 {
27            color: var(--fg);
28            font-size: 32px;
29            font-weight: 600;
30            letter-spacing: -0.5px;
31            margin-bottom: 8px;
32        }
33
34        .header .version {
35            color: var(--text-tertiary);
36            font-size: 14px;
37        }
38
39        .content {
40            padding: 40px;
41        }
42
43        .section {
44            margin-bottom: 48px;
45        }
46
47        .section:last-child {
48            margin-bottom: 0;
49        }
50
51        .section h2 {
52            color: var(--primary);
53            font-size: 24px;
54            font-weight: 600;
55            margin-bottom: 16px;
56            padding-bottom: 12px;
57            border-bottom: 2px solid var(--primary);
58        }
59
60        .section h3 {
61            color: var(--fg);
62            font-size: 18px;
63            font-weight: 600;
64            margin: 32px 0 16px 0;
65        }
66
67        .section p {
68            color: var(--text-secondary);
69            margin-bottom: 16px;
70            font-size: 15px;
71        }
72
73        .api-boxes {
74            display: grid;
75            grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
76            gap: 20px;
77            margin: 24px 0;
78        }
79
80        .api-box {
81            background: var(--input-bg);
82            padding: 28px;
83            border-radius: 12px;
84            border: 1px solid var(--border);
85            transition: all 0.2s ease;
86        }
87
88        .api-box:hover {
89            transform: translateY(-2px);
90            box-shadow: 0 8px 24px var(--primary-glow);
91            border-color: var(--primary);
92        }
93
94        .api-box h4 {
95            color: var(--fg);
96            font-size: 18px;
97            font-weight: 600;
98            margin-bottom: 12px;
99        }
100
101        .api-box p {
102            color: var(--text-secondary);
103            font-size: 14px;
104            margin-bottom: 20px;
105        }
106
107        .api-box .btn {
108            display: inline-block;
109            background: var(--primary);
110            color: white;
111            padding: 12px 24px;
112            border-radius: 10px;
113            text-decoration: none;
114            font-weight: 600;
115            font-size: 14px;
116            transition: all 0.2s ease;
117        }
118
119        .api-box .btn:hover {
120            filter: brightness(1.1);
121            box-shadow: 0 4px 12px var(--primary-glow);
122            transform: translateY(-1px);
123        }
124
125        .code-block {
126            white-space: pre-wrap;
127        }
128
129        .code-block .comment {
130            color: var(--code-comment);
131        }
132
133        .code-block .string {
134            color: var(--code-string);
135        }
136
137        .code-block .keyword {
138            color: var(--code-keyword);
139        }
140
141        .highlight {
142            background: var(--input-focus-bg);
143            padding: 2px 6px;
144            border-radius: 4px;
145            font-weight: 500;
146            color: var(--primary);
147        }
148
149        code {
150            background: var(--input-bg);
151            color: var(--primary);
152            padding: 2px 6px;
153            border-radius: 4px;
154            font-size: 0.9em;
155            font-family: 'Monaco', 'Courier New', monospace;
156        }
157
158        .info-box {
159            background: var(--info-bg);
160            border: 1px solid var(--info-border);
161            border-left: 4px solid var(--primary);
162            padding: 20px;
163            margin: 20px 0;
164            border-radius: 10px;
165        }
166
167        .info-box strong {
168            color: var(--primary);
169        }
170
171        .events-list {
172            background: var(--input-bg);
173            padding: 24px;
174            border-radius: 10px;
175            border: 1px solid var(--border);
176            margin: 20px 0;
177        }
178
179        .events-list h4 {
180            color: var(--fg);
181            font-weight: 600;
182            margin-bottom: 16px;
183            font-size: 16px;
184        }
185
186        .events-list ul {
187            list-style: none;
188            padding-left: 0;
189        }
190
191        .events-list li {
192            padding: 10px 0;
193            border-bottom: 1px solid var(--border);
194            color: var(--text-secondary);
195        }
196
197        .events-list li:last-child {
198            border-bottom: none;
199        }
200
201        .events-list code {
202            background: var(--primary);
203            color: white;
204            padding: 4px 10px;
205            border-radius: 6px;
206            font-size: 13px;
207            font-weight: 500;
208        }
209
210        .client-links {
211            display: flex;
212            gap: 15px;
213            margin: 20px 0;
214            flex-wrap: wrap;
215        }
216
217        .client-link {
218            background: var(--input-bg);
219            color: var(--fg);
220            padding: 12px 24px;
221            border-radius: 10px;
222            text-decoration: none;
223            font-weight: 600;
224            font-size: 14px;
225            border: 1px solid var(--border);
226            transition: all 0.2s ease;
227            display: inline-flex;
228            align-items: center;
229            gap: 8px;
230        }
231
232        .client-link:hover {
233            background: var(--input-focus-bg);
234            border-color: var(--primary);
235            transform: translateY(-1px);
236        }
237
238        .footer {
239            background: var(--input-bg);
240            padding: 24px 40px;
241            text-align: center;
242            color: var(--text-secondary);
243            border-top: 1px solid var(--border);
244            font-size: 14px;
245        }
246
247        .footer a {
248            color: var(--primary);
249            text-decoration: none;
250            font-weight: 500;
251        }
252
253        .footer a:hover {
254            text-decoration: underline;
255        }
256
257        ul {
258            margin-left: 24px;
259            margin-bottom: 16px;
260        }
261
262        ul li {
263            color: var(--text-secondary);
264            margin-bottom: 8px;
265        }
266
267        .code-block .keyword {
268            color: var(--code-keyword);
269        }
270
271        .highlight {
272            background: var(--input-focus-bg);
273            padding: 2px 6px;
274            border-radius: 4px;
275            font-weight: 500;
276            color: var(--primary);
277        }
278
279        code {
280            background: var(--input-bg);
281            color: var(--primary);
282            padding: 2px 6px;
283            border-radius: 4px;
284            font-size: 0.9em;
285            font-family: 'Monaco', 'Courier New', monospace;
286        }
287
288        .info-box {
289            background: var(--info-bg);
290            border: 1px solid var(--info-border);
291            border-left: 4px solid var(--primary);
292            padding: 20px;
293            margin: 20px 0;
294            border-radius: 10px;
295        }
296
297        .info-box strong {
298            color: var(--primary);
299        }
300
301        .events-list {
302            background: var(--input-bg);
303            padding: 24px;
304            border-radius: 10px;
305            border: 1px solid var(--border);
306            margin: 20px 0;
307        }
308
309        .events-list h4 {
310            color: var(--fg);
311            font-weight: 600;
312            margin-bottom: 16px;
313            font-size: 16px;
314        }
315
316        .events-list ul {
317            list-style: none;
318            padding-left: 0;
319        }
320
321        .events-list li {
322            padding: 10px 0;
323            border-bottom: 1px solid var(--border);
324            color: var(--text-secondary);
325        }
326
327        .events-list li:last-child {
328            border-bottom: none;
329        }
330
331        .events-list code {
332            background: var(--primary);
333            color: white;
334            padding: 4px 10px;
335            border-radius: 6px;
336            font-size: 13px;
337            font-weight: 500;
338        }
339
340        .client-links {
341            display: flex;
342            gap: 15px;
343            margin: 20px 0;
344            flex-wrap: wrap;
345        }
346
347        .client-link {
348            background: var(--input-bg);
349            color: var(--fg);
350            padding: 12px 24px;
351            border-radius: 10px;
352            text-decoration: none;
353            font-weight: 600;
354            font-size: 14px;
355            border: 1px solid var(--border);
356            transition: all 0.2s ease;
357            display: inline-flex;
358            align-items: center;
359            gap: 8px;
360        }
361
362        .client-link:hover {
363            background: var(--input-focus-bg);
364            border-color: var(--primary);
365            transform: translateY(-1px);
366        }
367
368        .footer {
369            background: var(--input-bg);
370            padding: 24px 40px;
371            text-align: center;
372            color: var(--text-secondary);
373            border-top: 1px solid var(--border);
374            font-size: 14px;
375        }
376
377        .footer a {
378            color: var(--primary);
379            text-decoration: none;
380            font-weight: 500;
381        }
382
383        .footer a:hover {
384            text-decoration: underline;
385        }
386
387        ul {
388            margin-left: 24px;
389            margin-bottom: 16px;
390        }
391
392        ul li {
393            color: var(--text-secondary);
394            margin-bottom: 8px;
395        }
396    </style>
397</head>
398<body>
399    <div class="container">
400        <div class="header">
401            <div class="logo">
402                <img src="/logo.png" alt="Music Assistant">
403            </div>
404            <h1>Music Assistant API</h1>
405            <div class="version">Version {VERSION}</div>
406        </div>
407
408        <div class="content">
409            <div class="section">
410                <h2>Welcome</h2>
411                <p>
412                    Music Assistant provides a powerful API to control your music library,
413                    manage players, and stream audio. Whether you're building a custom interface,
414                    integrating with home automation, or creating a music app, our API gives you
415                    complete control.
416                </p>
417                <p>
418                    This documentation will help you get started quickly with examples and best practices.
419                </p>
420            </div>
421
422            <div class="section">
423                <h2>API Documentation</h2>
424                <p>
425                    Explore the Music Assistant API with our documentation tools:
426                </p>
427                <div class="api-boxes">
428                    <div class="api-box">
429                        <h4>📚 Commands Reference</h4>
430                        <p>Complete list of all available commands with parameters, descriptions, working curl examples, and interactive testing</p>
431                        <a href="{BASE_URL}/api-docs/commands" class="btn">View Commands</a>
432                    </div>
433                    <div class="api-box">
434                        <h4>📊 Schemas Reference</h4>
435                        <p>All data models and types with their properties, descriptions, and relationships</p>
436                        <a href="{BASE_URL}/api-docs/schemas" class="btn">View Schemas</a>
437                    </div>
438                    <div class="api-box">
439                        <h4>📘 Swagger UI</h4>
440                        <p>Interactive API explorer with OpenAPI specification and "Try it out" functionality</p>
441                        <a href="{BASE_URL}/api-docs/swagger" class="btn">Open Swagger UI</a>
442                    </div>
443                </div>
444            </div>
445
446            <div class="section">
447                <h2>Quick Start</h2>
448
449                <h3>WebSocket API (Recommended)</h3>
450                <p>
451                    The WebSocket API provides <span class="highlight">real-time bidirectional communication</span>
452                    and automatic event notifications. Perfect for applications that need live updates.
453                </p>
454                <div class="info-box">
455                    <strong>Note:</strong> WebSocket messages require a <code>message_id</code> field to match requests with responses.
456                    This allows multiple concurrent requests over the same connection.
457                </div>
458                <div class="code-block">
459<span class="comment"># Connect to WebSocket</span>
460ws://{SERVER_HOST}/ws
461
462<span class="comment"># Step 1: Authenticate (REQUIRED as first command)</span>
463{
464  <span class="string">"message_id"</span>: <span class="string">"auth-123"</span>,
465  <span class="string">"command"</span>: <span class="string">"auth"</span>,
466  <span class="string">"args"</span>: {
467    <span class="string">"token"</span>: <span class="string">"your_access_token"</span>
468  }
469}
470
471<span class="comment"># Auth response</span>
472{
473  <span class="string">"message_id"</span>: <span class="string">"auth-123"</span>,
474  <span class="string">"result"</span>: {
475    <span class="string">"authenticated"</span>: <span class="keyword">true</span>,
476    <span class="string">"user"</span>: {<span class="comment">...user info...</span>}
477  }
478}
479
480<span class="comment"># Step 2: Send commands (message_id is REQUIRED)</span>
481{
482  <span class="string">"message_id"</span>: <span class="string">"unique-id-123"</span>,
483  <span class="string">"command"</span>: <span class="string">"players/all"</span>,
484  <span class="string">"args"</span>: {}
485}
486
487<span class="comment"># Receive response</span>
488{
489  <span class="string">"message_id"</span>: <span class="string">"unique-id-123"</span>,
490  <span class="string">"result"</span>: [<span class="comment">...player data...</span>]
491}
492
493<span class="comment"># Receive automatic events</span>
494{
495  <span class="string">"event"</span>: <span class="string">"player_updated"</span>,
496  <span class="string">"data"</span>: {<span class="comment">...updated player...</span>}
497}
498                </div>
499
500                <h3>HTTP API (RPC)</h3>
501                <p>
502                    The HTTP API provides a simple RPC-like interface for executing commands.
503                    This allows you to call the same commands available via WebSocket over a simple HTTP POST endpoint.
504                    Perfect for one-off commands without needing real-time updates.
505                </p>
506                <div class="info-box">
507                    <strong>Note:</strong> The <code>message_id</code> field is <strong>optional</strong> for HTTP requests
508                    since each HTTP request is isolated. The response returns the command result directly.
509                </div>
510                <div class="code-block">
511<span class="comment"># Get all players (requires authentication)</span>
512curl -X POST {BASE_URL}/api \
513  -H <span class="string">"Authorization: Bearer your_access_token"</span> \
514  -H <span class="string">"Content-Type: application/json"</span> \
515  -d <span class="string">'{
516    "command": "players/all",
517    "args": {}
518  }'</span>
519
520<span class="comment"># Play media on a player (requires authentication)</span>
521curl -X POST {BASE_URL}/api \
522  -H <span class="string">"Authorization: Bearer your_access_token"</span> \
523  -H <span class="string">"Content-Type: application/json"</span> \
524  -d <span class="string">'{
525    "command": "player_queues/play_media",
526    "args": {
527      "queue_id": "player_123",
528      "media": ["library://track/456"]
529    }
530  }'</span>
531
532<span class="comment"># Get server info (no authentication required)</span>
533curl {BASE_URL}/info
534                </div>
535            </div>
536
537            <div class="section">
538                <h2>WebSocket Events</h2>
539                <p>
540                    When connected via WebSocket, you automatically receive real-time event notifications
541                    for all state changes. No polling required!
542                </p>
543
544                <div class="events-list">
545                    <h4>🔊 Player Events</h4>
546                    <ul>
547                        <li><code>player_added</code> - New player discovered</li>
548                        <li><code>player_updated</code> - Player state changed</li>
549                        <li><code>player_removed</code> - Player disconnected</li>
550                        <li><code>player_config_updated</code> - Settings changed</li>
551                    </ul>
552                </div>
553
554                <div class="events-list">
555                    <h4>🎵 Queue Events</h4>
556                    <ul>
557                        <li><code>queue_added</code> - New queue created</li>
558                        <li><code>queue_updated</code> - Queue state changed</li>
559                        <li><code>queue_items_updated</code> - Content changed</li>
560                        <li><code>queue_time_updated</code> - Playback position updated</li>
561                    </ul>
562                </div>
563
564                <div class="events-list">
565                    <h4>📚 Library Events</h4>
566                    <ul>
567                        <li><code>media_item_added</code> - New media added</li>
568                        <li><code>media_item_updated</code> - Metadata updated</li>
569                        <li><code>media_item_deleted</code> - Media removed</li>
570                        <li><code>media_item_played</code> - Playback started</li>
571                    </ul>
572                </div>
573
574                <div class="events-list">
575                    <h4>⚙️ System Events</h4>
576                    <ul>
577                        <li><code>providers_updated</code> - Provider status changed</li>
578                        <li><code>sync_tasks_updated</code> - Sync progress updated</li>
579                        <li><code>application_shutdown</code> - Server shutting down</li>
580                    </ul>
581                </div>
582            </div>
583
584            <div class="section">
585                <h2>Client Libraries</h2>
586                <p>
587                    Don't want to implement the API from scratch? Use our official client libraries:
588                </p>
589
590                <h3>Python Client</h3>
591                <p>
592                    Official Python client library with full type hints and async support:
593                </p>
594                <div class="code-block">
595<span class="comment"># Install</span>
596pip install music-assistant-client
597
598<span class="comment"># Usage</span>
599<span class="keyword">from</span> music_assistant_client <span class="keyword">import</span> MusicAssistantClient
600
601<span class="keyword">async with</span> MusicAssistantClient(<span class="string">"{SERVER_HOST}"</span>) <span class="keyword">as</span> client:
602    <span class="comment"># Get all players</span>
603    players = <span class="keyword">await</span> client.get_players()
604
605    <span class="comment"># Play media</span>
606    <span class="keyword">await</span> client.play_media(
607        queue_id=<span class="string">"player_123"</span>,
608        media=[<span class="string">"library://track/456"</span>]
609    )
610                </div>
611                <div class="client-links">
612                    <a href="https://github.com/music-assistant/client" class="client-link">
613                        📦 GitHub Repository
614                    </a>
615                    <a href="https://pypi.org/project/music-assistant-client/" class="client-link">
616                        🐍 PyPI Package
617                    </a>
618                </div>
619
620                <h3>TypeScript/JavaScript</h3>
621                <p>
622                    Reference implementation in the Music Assistant frontend:
623                </p>
624                <div class="code-block">
625<span class="comment">// Example from frontend code</span>
626<span class="keyword">import</span> { MusicAssistantApi } <span class="keyword">from</span> <span class="string">'./api'</span>;
627
628<span class="keyword">const</span> api = <span class="keyword">new</span> MusicAssistantApi(<span class="string">'{SERVER_HOST}'</span>);
629
630<span class="comment">// Connect</span>
631<span class="keyword">await</span> api.connect();
632
633<span class="comment">// Subscribe to events</span>
634api.subscribe(<span class="string">'player_updated'</span>, (event) => {
635  console.log(<span class="string">'Player updated:'</span>, event.data);
636});
637
638<span class="comment">// Call commands</span>
639<span class="keyword">const</span> players = <span class="keyword">await</span> api.getPlayers();
640                </div>
641                <div class="client-links">
642                    <a href="https://github.com/music-assistant/frontend/tree/main/src/plugins/api" class="client-link">
643                        📦 Frontend API Code
644                    </a>
645                </div>
646                <div class="info-box">
647                    <strong>Coming Soon:</strong> A dedicated TypeScript client library is in development!
648                    For now, you can use the frontend's API implementation as a reference.
649                </div>
650            </div>
651
652            <div class="section">
653                <h2>Best Practices</h2>
654                <p><strong>✓ Do:</strong></p>
655                <ul>
656                    <li>Use WebSocket API for real-time applications</li>
657                    <li>Handle connection drops and reconnect automatically</li>
658                    <li>Subscribe to relevant events instead of polling</li>
659                    <li>Use unique message IDs for WebSocket commands</li>
660                    <li>Implement proper error handling</li>
661                </ul>
662                <p><strong>✗ Don't:</strong></p>
663                <ul>
664                    <li>Poll the REST API frequently for updates (use WebSocket events instead)</li>
665                    <li>Send commands without waiting for previous responses</li>
666                    <li>Ignore error responses</li>
667                    <li>Hardcode server URLs (make them configurable)</li>
668                </ul>
669            </div>
670
671            <div class="section">
672                <h2>Authentication</h2>
673                <p>
674                    As of API Schema Version 28, <span class="highlight">authentication is now mandatory</span> for all API access
675                    (except when accessed through Home Assistant Ingress).
676                </p>
677
678                <h3>Authentication Overview</h3>
679                <p>Music Assistant supports the following authentication methods:</p>
680                <ul>
681                    <li><strong>Username/Password</strong> - Built-in authentication provider</li>
682                    <li><strong>Home Assistant OAuth</strong> - OAuth flow for HA users (optional)</li>
683                    <li><strong>Bearer Tokens</strong> - Token-based authentication for HTTP and WebSocket</li>
684                </ul>
685
686                <h3>HTTP Authentication Endpoints</h3>
687                <p>The following HTTP endpoints are available for authentication (no auth required):</p>
688                <div class="code-block">
689<span class="comment"># Get server info (includes onboard_done status)</span>
690GET {BASE_URL}/info
691
692<span class="comment"># Get available login providers</span>
693GET {BASE_URL}/auth/providers
694
695<span class="comment"># Login with credentials (built-in provider is default)</span>
696POST {BASE_URL}/auth/login
697{
698  <span class="string">"credentials"</span>: {
699    <span class="string">"username"</span>: <span class="string">"your_username"</span>,
700    <span class="string">"password"</span>: <span class="string">"your_password"</span>
701  }
702}
703
704<span class="comment"># Or specify a different provider (e.g., Home Assistant OAuth)</span>
705POST {BASE_URL}/auth/login
706{
707  <span class="string">"provider_id"</span>: <span class="string">"homeassistant"</span>,
708  <span class="string">"credentials"</span>: {<span class="comment">...provider-specific...</span>}
709}
710
711<span class="comment"># Response includes access token</span>
712{
713  <span class="string">"success"</span>: <span class="keyword">true</span>,
714  <span class="string">"token"</span>: <span class="string">"your_access_token"</span>,
715  <span class="string">"user"</span>: { <span class="comment">...user info...</span> }
716}
717
718<span class="comment"># First-time setup (only if no users exist)</span>
719POST {BASE_URL}/setup
720{
721  <span class="string">"username"</span>: <span class="string">"admin"</span>,
722  <span class="string">"password"</span>: <span class="string">"secure_password"</span>
723}
724                </div>
725
726                <h3>Using Bearer Tokens</h3>
727                <p>Once you have an access token, include it in all HTTP requests:</p>
728                <div class="code-block">
729curl -X POST {BASE_URL}/api \
730  -H <span class="string">"Authorization: Bearer your_access_token"</span> \
731  -H <span class="string">"Content-Type: application/json"</span> \
732  -d <span class="string">'{
733    "command": "players/all",
734    "args": {}
735  }'</span>
736                </div>
737
738                <h3>WebSocket Authentication</h3>
739                <p>
740                    After establishing a WebSocket connection, you <strong>must</strong> send an
741                    <code>auth</code> command as the first message:
742                </p>
743                <div class="code-block">
744<span class="comment"># Send auth command immediately after connection</span>
745{
746  <span class="string">"message_id"</span>: <span class="string">"auth-123"</span>,
747  <span class="string">"command"</span>: <span class="string">"auth"</span>,
748  <span class="string">"args"</span>: {
749    <span class="string">"token"</span>: <span class="string">"your_access_token"</span>
750  }
751}
752
753<span class="comment"># Response on success</span>
754{
755  <span class="string">"message_id"</span>: <span class="string">"auth-123"</span>,
756  <span class="string">"result"</span>: {
757    <span class="string">"authenticated"</span>: <span class="keyword">true</span>,
758    <span class="string">"user"</span>: {
759      <span class="string">"user_id"</span>: <span class="string">"..."</span>,
760      <span class="string">"username"</span>: <span class="string">"your_username"</span>,
761      <span class="string">"role"</span>: <span class="string">"admin"</span>
762    }
763  }
764}
765                </div>
766
767                <div class="info-box">
768                    <strong>Token Types:</strong>
769                    <ul style="margin-top: 12px;">
770                        <li><strong>Short-lived tokens:</strong> Created automatically during login. Expire after 30 days of inactivity but auto-renew on each use (sliding expiration window). Perfect for user sessions.</li>
771                        <li><strong>Long-lived tokens:</strong> Created via <code>auth/token/create</code> command. Expire after 10 years with no auto-renewal. Intended for external integrations (Home Assistant, mobile apps, API access).</li>
772                    </ul>
773                    Use the <code>auth/tokens</code> and <code>auth/token/create</code> WebSocket commands to manage your tokens.
774                </div>
775
776                <h3>User Management Commands</h3>
777                <p>The following WebSocket commands are available for authentication management:</p>
778                <ul>
779                    <li><code>auth/users</code> - List all users (admin only)</li>
780                    <li><code>auth/user</code> - Get user by ID (admin only)</li>
781                    <li><code>auth/user/create</code> - Create a new user (admin only)</li>
782                    <li><code>auth/user/update</code> - Update user profile, password, or role (admin for other users)</li>
783                    <li><code>auth/user/enable</code> - Enable user (admin only)</li>
784                    <li><code>auth/user/disable</code> - Disable user (admin only)</li>
785                    <li><code>auth/user/delete</code> - Delete user (admin only)</li>
786                    <li><code>auth/tokens</code> - List your tokens</li>
787                    <li><code>auth/token/create</code> - Create a new long-lived token</li>
788                    <li><code>auth/token/revoke</code> - Revoke a token</li>
789                </ul>
790                <p>See the <a href="{BASE_URL}/api-docs/commands#auth" style="color: var(--primary); text-decoration: none; font-weight: 500;">Commands Reference</a> for detailed documentation of all auth commands.</p>
791            </div>
792
793            <div class="section">
794                <h2>Remote Access (WebRTC)</h2>
795                <p>
796                    Music Assistant supports <span class="highlight">remote access via WebRTC</span>, enabling you to connect to your
797                    Music Assistant instance from anywhere without port forwarding or VPN configuration.
798                </p>
799
800                <h3>How It Works</h3>
801                <p>
802                    Remote access uses WebRTC technology to establish a secure, peer-to-peer connection between
803                    your remote client (PWA) and your local Music Assistant server:
804                </p>
805                <ul>
806                    <li><strong>WebRTC Data Channel:</strong> Establishes encrypted connection through NAT/firewalls</li>
807                    <li><strong>Signaling Server:</strong> Cloud-based server coordinates connection setup</li>
808                    <li><strong>Remote ID:</strong> Unique identifier (format: MA-XXXX-XXXX) to connect to your instance</li>
809                    <li><strong>API Bridge:</strong> Remote commands work identically to local WebSocket API</li>
810                </ul>
811
812                <h3>Getting Your Remote ID</h3>
813                <p>Use the <code>remote_access/info</code> WebSocket command to get your Remote ID and connection status:</p>
814                <div class="code-block">
815<span class="comment"># Get remote access information</span>
816{
817  <span class="string">"message_id"</span>: <span class="string">"remote-123"</span>,
818  <span class="string">"command"</span>: <span class="string">"remote_access/info"</span>,
819  <span class="string">"args"</span>: {}
820}
821
822<span class="comment"># Response</span>
823{
824  <span class="string">"message_id"</span>: <span class="string">"remote-123"</span>,
825  <span class="string">"result"</span>: {
826    <span class="string">"enabled"</span>: <span class="keyword">true</span>,
827    <span class="string">"connected"</span>: <span class="keyword">true</span>,
828    <span class="string">"remote_id"</span>: <span class="string">"MA-K7G3-P2M4"</span>,
829    <span class="string">"signaling_url"</span>: <span class="string">"wss://signaling.music-assistant.io/ws"</span>
830  }
831}
832                </div>
833
834                <h3>Connecting Remotely</h3>
835                <p>To connect from outside your local network:</p>
836                <ol style="margin-left: 24px; color: var(--text-secondary);">
837                    <li>Get your Remote ID from the <code>remote_access/info</code> command</li>
838                    <li>Open the Music Assistant PWA from any device (https://app.music-assistant.io)</li>
839                    <li>Enter your Remote ID when prompted</li>
840                    <li>Authenticate with your username and password (or OAuth)</li>
841                    <li>Full API access over encrypted WebRTC connection</li>
842                </ol>
843
844                <div class="info-box">
845                    <strong>Availability Notice:</strong> Remote access is currently only available to users who:
846                    <ul style="margin-top: 12px;">
847                        <li>Have the Home Assistant integration configured</li>
848                        <li>Possess an active <a href="https://www.nabucasa.com/" style="color: var(--primary); text-decoration: none; font-weight: 500;">Home Assistant Cloud</a> subscription</li>
849                    </ul>
850                    This limitation exists because remote access relies on cloud-based STUN/TURN servers provided by Home Assistant Cloud
851                    to establish WebRTC connections through NAT/firewalls. These servers are expensive to host and are made available
852                    as part of the Home Assistant Cloud service.
853                </div>
854
855                <h3>Security</h3>
856                <p>Remote access maintains the same security standards as local access:</p>
857                <ul>
858                    <li><strong>End-to-end encryption:</strong> All data encrypted via WebRTC (DTLS/SRTP)</li>
859                    <li><strong>Authentication required:</strong> Same login and token system as local access</li>
860                    <li><strong>No data inspection:</strong> Signaling server cannot decrypt your commands or data</li>
861                    <li><strong>Role-based access:</strong> Admin and user permissions work identically</li>
862                </ul>
863            </div>
864        </div>
865
866        <div class="footer">
867            <p>
868                Music Assistant {VERSION} •
869                <a href="https://music-assistant.io">music-assistant.io</a> •
870                <a href="https://github.com/music-assistant">GitHub</a>
871            </p>
872        </div>
873    </div>
874</body>
875</html>
876