music-assistant-server

33.5 KBHTML
api_docs.html
33.5 KB894 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>tasks_updated</code> - Background tasks changed</li>
579                        <li><code>music_sync_completed</code> - Music sync finished</li>
580                        <li><code>application_shutdown</code> - Server shutting down</li>
581                    </ul>
582                </div>
583            </div>
584
585            <div class="section">
586                <h2>Client Libraries</h2>
587                <p>
588                    Don't want to implement the API from scratch? Use our official client libraries:
589                </p>
590
591                <h3>Python Client</h3>
592                <p>
593                    Official Python client library with full type hints and async support:
594                </p>
595                <div class="code-block">
596<span class="comment"># Install</span>
597pip install music-assistant-client
598
599<span class="comment"># Usage</span>
600<span class="keyword">from</span> music_assistant_client <span class="keyword">import</span> MusicAssistantClient
601
602<span class="keyword">async with</span> MusicAssistantClient(<span class="string">"{SERVER_HOST}"</span>) <span class="keyword">as</span> client:
603    <span class="comment"># Get all players</span>
604    players = <span class="keyword">await</span> client.get_players()
605
606    <span class="comment"># Play media</span>
607    <span class="keyword">await</span> client.play_media(
608        queue_id=<span class="string">"player_123"</span>,
609        media=[<span class="string">"library://track/456"</span>]
610    )
611                </div>
612                <div class="client-links">
613                    <a href="https://github.com/music-assistant/client" class="client-link">
614                        📦 GitHub Repository
615                    </a>
616                    <a href="https://pypi.org/project/music-assistant-client/" class="client-link">
617                        🐍 PyPI Package
618                    </a>
619                </div>
620
621                <h3>TypeScript/JavaScript</h3>
622                <p>
623                    Reference implementation in the Music Assistant frontend:
624                </p>
625                <div class="code-block">
626<span class="comment">// Example from frontend code</span>
627<span class="keyword">import</span> { MusicAssistantApi } <span class="keyword">from</span> <span class="string">'./api'</span>;
628
629<span class="keyword">const</span> api = <span class="keyword">new</span> MusicAssistantApi(<span class="string">'{SERVER_HOST}'</span>);
630
631<span class="comment">// Connect</span>
632<span class="keyword">await</span> api.connect();
633
634<span class="comment">// Subscribe to events</span>
635api.subscribe(<span class="string">'player_updated'</span>, (event) => {
636  console.log(<span class="string">'Player updated:'</span>, event.data);
637});
638
639<span class="comment">// Call commands</span>
640<span class="keyword">const</span> players = <span class="keyword">await</span> api.getPlayers();
641                </div>
642                <div class="client-links">
643                    <a href="https://github.com/music-assistant/frontend/tree/main/src/plugins/api" class="client-link">
644                        📦 Frontend API Code
645                    </a>
646                </div>
647                <div class="info-box">
648                    <strong>Coming Soon:</strong> A dedicated TypeScript client library is in development!
649                    For now, you can use the frontend's API implementation as a reference.
650                </div>
651            </div>
652
653            <div class="section">
654                <h2>Best Practices</h2>
655                <p><strong>✓ Do:</strong></p>
656                <ul>
657                    <li>Use WebSocket API for real-time applications</li>
658                    <li>Handle connection drops and reconnect automatically</li>
659                    <li>Subscribe to relevant events instead of polling</li>
660                    <li>Use unique message IDs for WebSocket commands</li>
661                    <li>Implement proper error handling</li>
662                </ul>
663                <p><strong>✗ Don't:</strong></p>
664                <ul>
665                    <li>Poll the REST API frequently for updates (use WebSocket events instead)</li>
666                    <li>Send commands without waiting for previous responses</li>
667                    <li>Ignore error responses</li>
668                    <li>Hardcode server URLs (make them configurable)</li>
669                </ul>
670            </div>
671
672            <div class="section">
673                <h2>Authentication</h2>
674                <p>
675                    As of API Schema Version 28, <span class="highlight">authentication is now mandatory</span> for all API access
676                    (except when accessed through Home Assistant Ingress).
677                </p>
678
679                <h3>Authentication Overview</h3>
680                <p>Music Assistant supports the following authentication methods:</p>
681                <ul>
682                    <li><strong>Username/Password</strong> - Built-in authentication provider</li>
683                    <li><strong>Home Assistant OAuth</strong> - OAuth flow for HA users (optional)</li>
684                    <li><strong>Bearer Tokens</strong> - Token-based authentication for HTTP and WebSocket</li>
685                </ul>
686                <p>
687                  When using tokens to automatically authenticate users, clients should be careful to validate that the server
688                  ID for the server they are communicating with matches the ID observed when the user logged in explicitly. Otherwise,
689                  there is a risk of sending tokens and attempting to authenticate with a different server that has the same local address
690                  and port (<code>homeassistant.local:8095</code> for example).
691                </p>
692
693                <h3>HTTP Authentication Endpoints</h3>
694                <p>The following HTTP endpoints are available for authentication (no auth required):</p>
695                <div class="code-block">
696<span class="comment"># Get server info (includes onboard_done status)</span>
697GET {BASE_URL}/info
698
699<span class="comment"># Get available login providers</span>
700GET {BASE_URL}/auth/providers
701
702<span class="comment"># Login with credentials (built-in provider is default)</span>
703POST {BASE_URL}/auth/login
704{
705  <span class="string">"credentials"</span>: {
706    <span class="string">"username"</span>: <span class="string">"your_username"</span>,
707    <span class="string">"password"</span>: <span class="string">"your_password"</span>
708  }
709}
710
711<span class="comment"># Or specify a different provider (e.g., Home Assistant OAuth)</span>
712POST {BASE_URL}/auth/login
713{
714  <span class="string">"provider_id"</span>: <span class="string">"homeassistant"</span>,
715  <span class="string">"credentials"</span>: {<span class="comment">...provider-specific...</span>}
716}
717
718<span class="comment"># Response includes access token</span>
719{
720  <span class="string">"success"</span>: <span class="keyword">true</span>,
721  <span class="string">"token"</span>: <span class="string">"your_access_token"</span>,
722  <span class="string">"user"</span>: { <span class="comment">...user info...</span> }
723}
724
725<span class="comment"># First-time setup (only if no users exist)</span>
726POST {BASE_URL}/setup
727{
728  <span class="string">"username"</span>: <span class="string">"admin"</span>,
729  <span class="string">"password"</span>: <span class="string">"secure_password"</span>
730}
731                </div>
732
733                <h3>Using Bearer Tokens</h3>
734                <p>Once you have an access token, include it in all HTTP requests:</p>
735                <div class="code-block">
736curl -X POST {BASE_URL}/api \
737  -H <span class="string">"Authorization: Bearer your_access_token"</span> \
738  -H <span class="string">"Content-Type: application/json"</span> \
739  -d <span class="string">'{
740    "command": "players/all",
741    "args": {}
742  }'</span>
743                </div>
744
745                <h3>WebSocket Authentication</h3>
746                <p>
747                    After establishing a WebSocket connection, you <strong>must</strong> send an
748                    <code>auth</code> command as the first message:
749                </p>
750                <div class="code-block">
751<span class="comment"># Login with username and password...</span>
752{
753  <span class="string">"message_id"</span>: <span class="string">"auth-123"</span>,
754  <span class="string">"command"</span>: <span class="string">"auth"</span>,
755  <span class="string">"args"</span>: {
756    <span class="string">"username"</span>: <span class="string">"your_username"</span>
757    <span class="string">"password"</span>: <span class="string">"your_password"</span>
758    <span class="string">"device_name"</span>: <span class="string">"your_device_name"</span>
759  }
760}
761
762<span class="comment"># ...or use token from previous login</span>
763{
764  <span class="string">"message_id"</span>: <span class="string">"auth-123"</span>,
765  <span class="string">"command"</span>: <span class="string">"auth"</span>,
766  <span class="string">"args"</span>: {
767    <span class="string">"token"</span>: <span class="string">"your_access_token"</span>
768  }
769}
770
771<span class="comment"># Response on success</span>
772{
773  <span class="string">"message_id"</span>: <span class="string">"auth-123"</span>,
774  <span class="string">"result"</span>: {
775    <span class="string">"authenticated"</span>: <span class="keyword">true</span>,
776    <span class="string">"user"</span>: {
777      <span class="string">"user_id"</span>: <span class="string">"..."</span>,
778      <span class="string">"username"</span>: <span class="string">"your_username"</span>,
779      <span class="string">"role"</span>: <span class="string">"admin"</span>
780    }
781  }
782}
783                </div>
784
785                <div class="info-box">
786                    <strong>Token Types:</strong>
787                    <ul style="margin-top: 12px;">
788                        <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>
789                        <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>
790                    </ul>
791                    Use the <code>auth/tokens</code> and <code>auth/token/create</code> WebSocket commands to manage your tokens.
792                </div>
793
794                <h3>User Management Commands</h3>
795                <p>The following WebSocket commands are available for authentication management:</p>
796                <ul>
797                    <li><code>auth/users</code> - List all users (requires the users.read scope)</li>
798                    <li><code>auth/user</code> - Get user by ID (requires the users.read scope)</li>
799                    <li><code>auth/user/create</code> - Create a new user (admin only)</li>
800                    <li><code>auth/user/update</code> - Update user profile, password, or role (admin for other users)</li>
801                    <li><code>auth/user/enable</code> - Enable user (admin only)</li>
802                    <li><code>auth/user/disable</code> - Disable user (admin only)</li>
803                    <li><code>auth/user/delete</code> - Delete user (admin only)</li>
804                    <li><code>auth/tokens</code> - List your tokens</li>
805                    <li><code>auth/token/create</code> - Create a new long-lived token</li>
806                    <li><code>auth/token/revoke</code> - Revoke a token</li>
807                </ul>
808                <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>
809            </div>
810
811            <div class="section">
812                <h2>Remote Access (WebRTC)</h2>
813                <p>
814                    Music Assistant supports <span class="highlight">remote access via WebRTC</span>, enabling you to connect to your
815                    Music Assistant instance from anywhere without port forwarding or VPN configuration.
816                </p>
817
818                <h3>How It Works</h3>
819                <p>
820                    Remote access uses WebRTC technology to establish a secure, peer-to-peer connection between
821                    your remote client (PWA) and your local Music Assistant server:
822                </p>
823                <ul>
824                    <li><strong>WebRTC Data Channel:</strong> Establishes encrypted connection through NAT/firewalls</li>
825                    <li><strong>Signaling Server:</strong> Cloud-based server coordinates connection setup</li>
826                    <li><strong>Remote ID:</strong> Unique identifier (format: MA-XXXX-XXXX) to connect to your instance</li>
827                    <li><strong>API Bridge:</strong> Remote commands work identically to local WebSocket API</li>
828                </ul>
829
830                <h3>Getting Your Remote ID</h3>
831                <p>Use the <code>remote_access/info</code> WebSocket command to get your Remote ID and connection status:</p>
832                <div class="code-block">
833<span class="comment"># Get remote access information</span>
834{
835  <span class="string">"message_id"</span>: <span class="string">"remote-123"</span>,
836  <span class="string">"command"</span>: <span class="string">"remote_access/info"</span>,
837  <span class="string">"args"</span>: {}
838}
839
840<span class="comment"># Response</span>
841{
842  <span class="string">"message_id"</span>: <span class="string">"remote-123"</span>,
843  <span class="string">"result"</span>: {
844    <span class="string">"enabled"</span>: <span class="keyword">true</span>,
845    <span class="string">"connected"</span>: <span class="keyword">true</span>,
846    <span class="string">"remote_id"</span>: <span class="string">"MA-K7G3-P2M4"</span>,
847    <span class="string">"signaling_url"</span>: <span class="string">"wss://signaling.music-assistant.io/ws"</span>
848  }
849}
850                </div>
851
852                <h3>Connecting Remotely</h3>
853                <p>To connect from outside your local network:</p>
854                <ol style="margin-left: 24px; color: var(--text-secondary);">
855                    <li>Get your Remote ID from the <code>remote_access/info</code> command</li>
856                    <li>Open the Music Assistant PWA from any device (https://app.music-assistant.io)</li>
857                    <li>Enter your Remote ID when prompted</li>
858                    <li>Authenticate with your username and password (or OAuth)</li>
859                    <li>Full API access over encrypted WebRTC connection</li>
860                </ol>
861
862                <div class="info-box">
863                    <strong>Availability Notice:</strong> Remote access is currently only available to users who:
864                    <ul style="margin-top: 12px;">
865                        <li>Have the Home Assistant integration configured</li>
866                        <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>
867                    </ul>
868                    This limitation exists because remote access relies on cloud-based STUN/TURN servers provided by Home Assistant Cloud
869                    to establish WebRTC connections through NAT/firewalls. These servers are expensive to host and are made available
870                    as part of the Home Assistant Cloud service.
871                </div>
872
873                <h3>Security</h3>
874                <p>Remote access maintains the same security standards as local access:</p>
875                <ul>
876                    <li><strong>End-to-end encryption:</strong> All data encrypted via WebRTC (DTLS/SRTP)</li>
877                    <li><strong>Authentication required:</strong> Same login and token system as local access</li>
878                    <li><strong>No data inspection:</strong> Signaling server cannot decrypt your commands or data</li>
879                    <li><strong>Role-based access:</strong> Admin and user permissions work identically</li>
880                </ul>
881            </div>
882        </div>
883
884        <div class="footer">
885            <p>
886                Music Assistant {VERSION} •
887                <a href="https://music-assistant.io">music-assistant.io</a> •
888                <a href="https://github.com/music-assistant">GitHub</a>
889            </p>
890        </div>
891    </div>
892</body>
893</html>
894