/
/
/
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