openapi: 3.2.0
info:
title: Devialet IP Control Groups API
version: '1'
summary: Local-network HTTP control API for Devialet Phantom speakers and accessories.
description: 'The Devialet IP Control API is an unauthenticated HTTP API served by Devialet devices themselves on the local network. Starting with DOS 2.14 firmware, the Phantom range (Phantom I, Phantom II, and the Arch and Dialog accessories) exposes device, system, and group state plus playback, volume, equalizer, night-mode, Bluetooth pairing, and power/restart/factory-reset commands over plain HTTP on port 80 under the `/ipcontrol/v1` path prefix. Devices are discovered with mDNS/DNS-SD: they register `_http._tcp` service instances whose TXT record carries `manufacturer=Devialet`, `ipControlVersion=1`, and `path=/ipcontrol/v1`. Queries use GET and are guaranteed not to change device state; commands use POST and require `Content-Type: application/json`. Regular (non-transport) errors are returned inside a `200 OK` response as an `error` object rather than as an HTTP error status.'
x-provenance:
method: derived
generated: '2026-08-04'
derived_from: openapi/_original/devialet-ip-control-r1.pdf
source_document: Devialet IP Control — REFERENCE API DOCUMENTATION, Revision 1, December 2021
source_url: https://help.devialet.com/hc/en-us/articles/4415207423378-Phantom-s-documentation-for-piloting-them-via-IP
attachment_url: https://help.devialet.com/hc/en-us/article_attachments/4415236063506
note: Devialet publishes this API as a PDF reference document, not as a machine-readable specification. This OpenAPI is an API Evangelist derivation that transcribes only what the Revision 1 reference document specifies. It is not published or endorsed by Devialet. Endpoints that the reference mentions in passing but does not specify (`/devices/{deviceId}/identify`, `/groups/{groupId}/sources/current/soundControl/volume`, `/groups/{groupId}/sources/current/playback/position`) are deliberately omitted here and recorded in conventions/devialet-conventions.yml instead.
contact:
name: Devialet Help Center
url: https://help.devialet.com/
license:
name: © 2021 Devialet. All rights reserved.
url: https://www.devialet.com/en-eu/legal/
servers:
- url: http://{deviceAddress}/ipcontrol/v1
description: A Devialet device on the local network. `deviceAddress` is the IPv4 or IPv6 address (or mDNS hostname) of any reachable Devialet device — that device becomes the "dispatcher" and forwards commands to the rest of the installation. The default below is the example address used in Devialet's reference documentation.
variables:
deviceAddress:
default: 192.168.1.20
description: IP address or mDNS hostname of a Devialet device on the local network.
tags:
- name: Groups
description: Sets of one or more systems playing the same content in the multi-room configuration.
paths:
/groups/{groupId}/sources:
parameters:
- $ref: '#/components/parameters/GroupId'
get:
operationId: listGroupSources
tags:
- Groups
summary: List available sources for the group
description: 'Query the list of currently available audio sources for the designated group. The list changes when the group is reconfigured in the Devialet companion app, when devices become unreachable, or when an Arch physical input configuration changes. Minimal firmware: DOS >= 2.14.'
x-devialet-minimal-firmware: DOS >= 2.14
responses:
'200':
description: The available sources, or a regular error object.
content:
application/json:
schema:
oneOf:
- $ref: '#/components/schemas/SourceList'
- $ref: '#/components/schemas/ErrorEnvelope'
'404':
$ref: '#/components/responses/NotFound'
'500':
$ref: '#/components/responses/InternalError'
/groups/{groupId}/sources/current:
parameters:
- $ref: '#/components/parameters/GroupId'
get:
operationId: getGroupCurrentSource
tags:
- Groups
summary: Get the current playback state for the group
description: 'Query the current playback state of the designated group and the capabilities of its current source — the source descriptor, `playingState`, `muteState`, track metadata when the source provides it, and the list of currently available playback operations. If there is no current source, `NoCurrentSource` is reported. Minimal firmware: DOS >= 2.14.'
x-devialet-minimal-firmware: DOS >= 2.14
responses:
'200':
description: The current playback state, or a regular error object.
content:
application/json:
schema:
oneOf:
- $ref: '#/components/schemas/CurrentSourceState'
- $ref: '#/components/schemas/ErrorEnvelope'
examples:
playing:
summary: From the Devialet reference documentation
value:
source:
sourceId: 213a3ed0-1fb9-4da2-bcf4-066da0f7b27e
deviceId: 13531594-b1c1-42c7-8d5a-18fa9e5d7cd4
type: spotifyconnect
playingState: playing
muteState: unmuted
metadata:
artist: Michael Jackson
album: Thriller
title: Billie Jean
coverArtUrl: https://cdn.spotify.com/covers/4729028427.png
availableOperations:
- play
- pause
- seek
'404':
$ref: '#/components/responses/NotFound'
'500':
$ref: '#/components/responses/InternalError'
/groups/{groupId}/sources/{sourceId}/playback/play:
parameters:
- $ref: '#/components/parameters/GroupId'
- $ref: '#/components/parameters/SourceId'
post:
operationId: playGroupSource
tags:
- Groups
summary: Play or resume a source
description: 'Start or resume playback of the designated source on the group and set its `playingState` to `playing`. If the designated source is not the current source it is selected first and the previous source is paused and unselected. The call may have asynchronous effects — clients should re-read `getGroupCurrentSource` rather than trust the response. If playback cannot start, `PlaybackNoStream` is reported and the source still becomes current with no sound. If the source is already the current, playing source, the call succeeds. Selecting an AirPlay 2 or Roon Ready source removes the host system from its current group. Minimal firmware: DOS >= 2.14.'
x-devialet-minimal-firmware: DOS >= 2.14
requestBody:
$ref: '#/components/requestBodies/NoParameters'
responses:
'200':
$ref: '#/components/responses/EmptyOrError'
'400':
$ref: '#/components/responses/BadRequest'
'404':
$ref: '#/components/responses/NotFound'
'415':
$ref: '#/components/responses/UnsupportedMediaType'
'500':
$ref: '#/components/responses/InternalError'
/groups/{groupId}/sources/current/playback/pause:
parameters:
- $ref: '#/components/parameters/GroupId'
post:
operationId: pauseGroupPlayback
tags:
- Groups
summary: Pause the current source
description: 'Pause playback of the group''s current source and set its `playingState` to `paused`. All sources accept this command; sources that cannot semantically pause (for example `optical`) mute the output instead, leaving `playingState` as `playing` and setting `muteState` to `muted`. If already paused, the call succeeds. Minimal firmware: DOS >= 2.14.'
x-devialet-minimal-firmware: DOS >= 2.14
requestBody:
$ref: '#/components/requestBodies/NoParameters'
responses:
'200':
$ref: '#/components/responses/EmptyOrError'
'400':
$ref: '#/components/responses/BadRequest'
'404':
$ref: '#/components/responses/NotFound'
'415':
$ref: '#/components/responses/UnsupportedMediaType'
'500':
$ref: '#/components/responses/InternalError'
/groups/{groupId}/sources/current/playback/mute:
parameters:
- $ref: '#/components/parameters/GroupId'
post:
operationId: muteGroupPlayback
tags:
- Groups
summary: Mute the current source
description: 'Mute playback of the group''s current source and set its `muteState` to `muted`. Mute state is independent of play/pause state. If already muted, the call succeeds. Minimal firmware: DOS >= 2.14.'
x-devialet-minimal-firmware: DOS >= 2.14
requestBody:
$ref: '#/components/requestBodies/NoParameters'
responses:
'200':
$ref: '#/components/responses/EmptyOrError'
'400':
$ref: '#/components/responses/BadRequest'
'404':
$ref: '#/components/responses/NotFound'
'415':
$ref: '#/components/responses/UnsupportedMediaType'
'500':
$ref: '#/components/responses/InternalError'
/groups/{groupId}/sources/current/playback/unmute:
parameters:
- $ref: '#/components/parameters/GroupId'
post:
operationId: unmuteGroupPlayback
tags:
- Groups
summary: Unmute the current source
description: 'Unmute playback of the group''s current source and set its `muteState` to `unmuted`. If already unmuted, the call succeeds. Note that every volume command also unmutes the current source. Minimal firmware: DOS >= 2.14.'
x-devialet-minimal-firmware: DOS >= 2.14
requestBody:
$ref: '#/components/requestBodies/NoParameters'
responses:
'200':
$ref: '#/components/responses/EmptyOrError'
'400':
$ref: '#/components/responses/BadRequest'
'404':
$ref: '#/components/responses/NotFound'
'415':
$ref: '#/components/responses/UnsupportedMediaType'
'500':
$ref: '#/components/responses/InternalError'
/groups/{groupId}/sources/current/playback/next:
parameters:
- $ref: '#/components/parameters/GroupId'
post:
operationId: nextGroupTrack
tags:
- Groups
summary: Skip to the next track
description: 'Start playback of the next track on the group''s current source. If the current source does not offer the next operation, `PlaybackOperationNotAvailable` is reported — check `availableOperations` from `getGroupCurrentSource` first. Minimal firmware: DOS >= 2.14.'
x-devialet-minimal-firmware: DOS >= 2.14
requestBody:
$ref: '#/components/requestBodies/NoParameters'
responses:
'200':
$ref: '#/components/responses/EmptyOrError'
'400':
$ref: '#/components/responses/BadRequest'
'404':
$ref: '#/components/responses/NotFound'
'415':
$ref: '#/components/responses/UnsupportedMediaType'
'500':
$ref: '#/components/responses/InternalError'
/groups/{groupId}/sources/current/playback/previous:
parameters:
- $ref: '#/components/parameters/GroupId'
post:
operationId: previousGroupTrack
tags:
- Groups
summary: Skip to the previous track
description: 'Start playback of the previous track on the group''s current source. If the current source does not offer the previous operation, `PlaybackOperationNotAvailable` is reported. "Restart the current track" behaviour is not supported by this endpoint and must be implemented client-side. Minimal firmware: DOS >= 2.14.'
x-devialet-minimal-firmware: DOS >= 2.14
requestBody:
$ref: '#/components/requestBodies/NoParameters'
responses:
'200':
$ref: '#/components/responses/EmptyOrError'
'400':
$ref: '#/components/responses/BadRequest'
'404':
$ref: '#/components/responses/NotFound'
'415':
$ref: '#/components/responses/UnsupportedMediaType'
'500':
$ref: '#/components/responses/InternalError'
components:
responses:
InternalError:
description: Unexpected internal error. The body, when present, follows the regular error format, but its content is not part of the officially supported API.
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
BadRequest:
description: Malformed JSON request. The message body is empty.
EmptyOrError:
description: Success (an empty JSON object) or a regular error. Devialet returns regular errors with HTTP status 200 and an `error` object in the body.
content:
application/json:
schema:
oneOf:
- $ref: '#/components/schemas/EmptyObject'
- $ref: '#/components/schemas/ErrorEnvelope'
examples:
success:
summary: Successful command
value: {}
regularError:
summary: Regular error returned with HTTP 200
value:
error:
code: UnreachableDevices
NotFound:
description: Non-existing endpoint — for example a `/systems/...` or `/groups/...` request performed against a non-speaker accessory. The message body is empty.
UnsupportedMediaType:
description: Invalid or missing `Content-Type` header on a POST request. The only allowed value is `application/json`.
parameters:
GroupId:
name: groupId
in: path
required: true
description: Group identifier. The only value supported today is `current`, which refers to the group of the dispatcher.
schema:
type: string
default: current
examples:
- current
SourceId:
name: sourceId
in: path
required: true
description: Source identifier in UUIDv4 format, as returned by `listGroupSources`. An unrecognised value results in a 200 response carrying a regular error object, not a 404.
schema:
type: string
examples:
- 213a3ed0-1fb9-4da2-bcf4-066da0f7b27e
schemas:
CurrentSourceState:
type: object
description: The current playback state of a group and the capabilities of its current source.
required:
- playingState
- muteState
- availableOperations
properties:
source:
$ref: '#/components/schemas/Source'
playingState:
type: string
enum:
- playing
- paused
description: Playback state. Independent of `muteState` — all four combinations are possible.
muteState:
type: string
enum:
- muted
- unmuted
description: Mute state. Independent of `playingState`.
metadata:
$ref: '#/components/schemas/TrackMetadata'
availableOperations:
type: array
description: Operations currently available on the current source. `mute` and `unmute` are always available and are never listed here. The list can change without the source changing.
items:
type: string
enum:
- play
- pause
- next
- previous
- seek
SourceList:
type: object
description: The sources currently available to a group.
required:
- sources
properties:
sources:
type: array
items:
$ref: '#/components/schemas/Source'
Source:
type: object
description: An audio source hosted by a device in the installation.
required:
- sourceId
- deviceId
- type
properties:
sourceId:
type: string
description: Unique source identifier in UUIDv4 format.
deviceId:
type: string
description: Unique identifier of the host device in UUIDv4 format. May be a speaker or an accessory such as Dialog or Arch. For stereo pairs, use this to distinguish left from right inputs.
type:
type: string
description: The source type. Physical sources are `phono`, `line`, `digital_left`, `digital_right` (Arch only), `optical` (Phantom I, Dialog), and `opticaljack` (Phantom II only). Non-physical sources are `spotifyconnect`, `airplay2`, `bluetooth`, `upnp`, and `raat`.
enum:
- phono
- line
- digital_left
- digital_right
- optical
- opticaljack
- spotifyconnect
- airplay2
- bluetooth
- upnp
- raat
EmptyObject:
type: object
description: An empty JSON object.
additionalProperties: false
TrackMetadata:
type: object
description: Metadata for the current track. Absent when the source provides none; retained while the source is paused. All strings are UTF-8.
properties:
artist:
type: string
description: Artist name. Always present, but may be an empty string.
album:
type: string
description: Album name. Always present, but may be an empty string.
title:
type: string
description: Track name. Always present, but may be an empty string.
coverArtUrl:
type: string
format: uri
description: URL from which the client can download the cover art image. Absent when the data are not available as a URL.
ErrorEnvelope:
type: object
description: The regular error envelope. Returned with HTTP status 200 for application-level errors, and with 500 for unexpected internal errors.
required:
- error
properties:
error:
type: object
required:
- code
properties:
code:
type: string
description: A predefined error identifier. Unknown codes must be handled gracefully by the client and shown as a generic error message.
examples:
- Error
- UnreachableDevices
- Timeout
- NoCurrentSource
- InvalidValue
details:
type: object
description: Structured data whose shape depends on the error identifier, for building a localized user-facing message.
additionalProperties: true
message:
type: string
description: Debug-only text. Not intended for display to end users or for programmatic processing.
requestBodies:
NoParameters:
required: false
description: 'This command takes no parameters. The body may be empty or an empty JSON object (`{}`), but the `Content-Type: application/json` header is mandatory.'
content:
application/json:
schema:
$ref: '#/components/schemas/EmptyObject'
examples:
empty:
value: {}