⚠️ DEPRECATED: This documentation has been consolidated into the yoto-smart-stream skill. Please refer to the skill for current information.New location:
.github/skills/yoto-smart-stream/reference/yoto_api_reference.md
This document provides comprehensive reference information about the Yoto API for use in developing Yoto smart streaming skills and applications.
- Overview
- Authentication
- REST API Endpoints
- MQTT Communication
- Device Models and Data Structures
- Code Examples
- Useful Libraries
- Official Resources
Yoto is an audio player system for children that uses physical cards to control content playback. The Yoto API provides:
- REST API for managing devices, content (cards), and configuration
- MQTT for real-time device control and status monitoring
- OAuth2 authentication with device flow and refresh tokens
REST API: https://api.yotoplay.com
Auth: https://login.yotoplay.com
- Obtain a Client ID from: https://yoto.dev/get-started/start-here/
- Authenticate using OAuth2 Device Flow
- Use REST API for device management and content
- Connect via MQTT for real-time control
The recommended authentication method for CLI/server applications.
POST https://login.yotoplay.com/oauth/device/code
Content-Type: application/x-www-form-urlencoded
audience=https://api.yotoplay.com
client_id=YOUR_CLIENT_ID
scope=offline_access
Response:
{
"device_code": "CODE_FOR_POLLING",
"user_code": "XXXX-XXXX",
"verification_uri": "https://login.yotoplay.com/activate",
"verification_uri_complete": "https://login.yotoplay.com/activate?user_code=XXXX-XXXX",
"expires_in": 300,
"interval": 5
}POST https://login.yotoplay.com/oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=urn:ietf:params:oauth:grant-type:device_code
device_code=CODE_FROM_STEP_1
client_id=YOUR_CLIENT_ID
audience=https://api.yotoplay.com
Success Response:
{
"access_token": "JWT_TOKEN",
"refresh_token": "REFRESH_TOKEN",
"token_type": "Bearer",
"expires_in": 86400,
"scope": "openid profile offline_access"
}Pending Response (403):
{
"error": "authorization_pending"
}POST https://login.yotoplay.com/oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token
refresh_token=YOUR_REFRESH_TOKEN
client_id=YOUR_CLIENT_ID
audience=https://api.yotoplay.com
All API requests require:
Authorization: Bearer ACCESS_TOKEN
Content-Type: application/json
User-Agent: Yoto/2.73 (com.yotoplay.Yoto; build:10405; iOS 17.4.0) Alamofire/5.6.4
GET /device-v2/devices/mine
Response:
{
"devices": [
{
"deviceId": "string",
"name": "string",
"deviceType": "v3",
"online": true
}
]
}GET /device-v2/{deviceId}/status
Response:
{
"device": {
"deviceId": "string",
"status": {
"activeCard": "cardId or 'none'",
"batteryLevelPercentage": 100,
"isCharging": true,
"userVolumePercentage": 50,
"systemVolumePercentage": 50,
"temperatureCelsius": 24,
"isBluetoothAudioConnected": false,
"isAudioDeviceConnected": false,
"firmwareVersion": "v2.17.5-5",
"wifiStrength": -54,
"playingSource": "card",
"nightlightMode": "0x194a55",
"dayMode": true,
"ambientLightSensorReading": 100,
"powerSource": "battery"
}
}
}Power Source Values:
0: Unknown1: AC Power2: Battery3: Wireless Charging
GET /device-v2/{deviceId}/config
Response:
{
"device": {
"deviceId": "string",
"name": "string",
"config": {
"dayTime": "06:30",
"nightTime": "18:20",
"maxVolumeLimit": "16",
"nightMaxVolumeLimit": "8",
"dayDisplayBrightness": "auto",
"nightDisplayBrightness": "100",
"ambientColour": "#40bfd9",
"nightAmbientColour": "#f57399",
"timezone": "",
"hourFormat": "12",
"alarms": []
}
}
}PUT /device-v2/{deviceId}/config
Content-Type: application/json
{
"deviceId": "string",
"config": {
"name": "Bedroom Player",
"dayTime": "07:00",
"nightTime": "19:00",
"maxVolumeLimit": "80"
}
}
POST /device-v2/{deviceId}/command
Content-Type: application/json
{
"volume": 50
}
GET /card/mine
Response:
{
"cards": [
{
"cardId": "string",
"metadata": {
"title": "string",
"description": "string",
"author": "string",
"cover": {
"imageL": "https://url"
}
}
}
]
}GET /card/family/library
GET /card/{cardId}
Response:
{
"card": {
"cardId": "string",
"title": "string",
"content": {
"chapters": [
{
"key": "01-INT",
"title": "Chapter Title",
"duration": 349,
"display": {
"icon16x16": "https://url"
},
"tracks": [
{
"key": "01-INT",
"title": "Track Title",
"duration": 349,
"format": "aac",
"channels": "mono",
"trackUrl": "https://signed-url"
}
]
}
]
}
}
}GET /user/family
GET /groups
POST /groups
Content-Type: application/json
{
"name": "Bedtime Stories",
"imageId": "fp-cards",
"items": [
{ "contentId": "cardId1" },
{ "contentId": "cardId2" }
]
}
POST /media/audio/upload-url
Content-Type: application/json
{
"sha256": "hash",
"filename": "story.mp3"
}
POST /media/cover-image
Content-Type: multipart/form-data
image: [binary data]
coverType: default
autoConvert: true
MQTT provides real-time device control and status monitoring.
Getting MQTT Credentials:
GET /device-v2/{deviceId}/mqtt-credentials
Response:
{
"host": "mqtt.broker.url",
"port": 8883,
"username": "string",
"password": "string",
"clientId": "string"
}Status Updates:
yoto/{deviceId}/status
Playback Events:
yoto/{deviceId}/events
Command Responses:
yoto/{deviceId}/response
Send Commands:
yoto/{deviceId}/command
{
"volume": 50,
"batteryLevel": 100,
"charging": false,
"online": true,
"temperature": 24
}{
"trackTitle": "Story Title",
"cardTitle": "Card Name",
"playbackStatus": "playing",
"position": 120,
"trackLength": 300
}Set Volume:
{
"volume": 50
}Set Ambient Color:
{
"ambient": "#FF0000"
}Set Sleep Timer:
{
"sleepTimer": 30
}Play Card:
{
"card": {
"cardId": "5WsQg",
"chapterKey": "01",
"trackKey": "01"
}
}Pause/Resume/Stop:
{ "pause": true }
{ "resume": true }
{ "stop": true }class YotoPlayer:
id: str # Device ID
name: str # Device name
device_type: str # "v3", "v2", etc.
online: bool
# Status
is_playing: bool
active_card: str # Card ID or "none"
battery_level_percentage: int
charging: bool
user_volume: int
system_volume: int
temperature_celcius: int
wifi_strength: int
firmware_version: str
# Configuration
config: YotoPlayerConfig
# Timestamps
last_updated_api: datetime
last_updated_at: datetimeclass YotoPlayerConfig:
day_mode_time: time # "07:00"
day_display_brightness: int
day_ambient_colour: str # "#40bfd9"
day_max_volume_limit: int
night_mode_time: time # "19:00"
night_display_brightness: int
night_ambient_colour: str # "#f57399"
night_max_volume_limit: int
alarms: list[Alarm]class Card:
id: str # Card ID
title: str
description: str
author: str
category: str
cover_image_large: str # URL
chapters: dict[str, Chapter]class Chapter:
key: str # "01-INT"
title: str
icon: str # URL
duration: int # seconds
tracks: dict[str, Track]class Track:
key: str
title: str
duration: int
format: str # "aac", "mp3"
channels: str # "mono", "stereo"
type: str # "audio"
trackUrl: str # Signed URLfrom yoto_api import YotoManager
import time
# Initialize with Client ID
ym = YotoManager(client_id="YOUR_CLIENT_ID")
# Start device code flow
device_code = ym.device_code_flow_start()
print(f"Visit: {device_code['verification_uri']}")
print(f"Code: {device_code['user_code']}")
# Wait for user to authorize
time.sleep(15)
# Complete authentication
ym.device_code_flow_complete()
# Update player status
ym.update_player_status()
print(ym.players)# Save refresh token after first auth
refresh_token = ym.token.refresh_token
# Later, restore session
ym = YotoManager(client_id="YOUR_CLIENT_ID")
ym.set_refresh_token(refresh_token)
ym.check_and_refresh_token()# Connect to MQTT for real-time control
ym.connect_to_events()
# Get first player ID
player_id = next(iter(ym.players))
# Control playback
ym.pause_player(player_id)
ym.resume_player(player_id)
# Update library
ym.update_cards()
print(ym.library)
# Access player properties
for player_id, player in ym.players.items():
print(f"{player.name}: {player.battery_level_percentage}%")import { YotoClient } from 'yoto-nodejs-client'
// Start device flow
const deviceCode = await YotoClient.requestDeviceCode({
clientId: 'YOUR_CLIENT_ID'
})
console.log(`Visit: ${deviceCode.verification_uri_complete}`)
console.log(`Code: ${deviceCode.user_code}`)
// Wait for authorization (simple approach)
const tokens = await YotoClient.waitForDeviceAuthorization({
deviceCode: deviceCode.device_code,
clientId: 'YOUR_CLIENT_ID',
initialInterval: deviceCode.interval * 1000,
expiresIn: deviceCode.expires_in,
onPoll: (result) => {
if (result.status === 'pending') process.stdout.write('.')
}
})
// Create client with auto-refresh
const client = new YotoClient({
clientId: 'YOUR_CLIENT_ID',
refreshToken: tokens.refresh_token,
accessToken: tokens.access_token,
onTokenRefresh: async (event) => {
// MUST save tokens
await saveTokens(event)
}
})// Get devices
const { devices } = await client.getDevices()
console.log('Devices:', devices)
// Get device status
const status = await client.getDeviceStatus({
deviceId: devices[0].deviceId
})
console.log('Battery:', status.batteryLevelPercentage, '%')
// Update config
await client.updateDeviceConfig({
deviceId: devices[0].deviceId,
configUpdate: {
config: {
maxVolumeLimit: '80'
}
}
})// Create MQTT client
const mqtt = await client.createMqttClient({
deviceId: devices[0].deviceId
})
// Listen for events
mqtt.on('events', (message) => {
console.log('Playing:', message.trackTitle)
})
mqtt.on('status', (message) => {
console.log('Volume:', message.volume)
console.log('Battery:', message.batteryLevel)
})
// Connect and control
await mqtt.connect()
await mqtt.setVolume(50)
await mqtt.setAmbientHex('#FF0000')
await mqtt.startCard({ cardId: '5WsQg' })import { YotoDeviceModel } from 'yoto-nodejs-client'
// Create stateful device client
const deviceClient = new YotoDeviceModel(client, devices[0], {
httpPollIntervalMs: 600000 // Poll every 10 minutes
})
// Listen for updates
deviceClient.on('statusUpdate', (status, source, changedFields) => {
console.log(`Battery: ${status.batteryLevelPercentage}% (${source})`)
})
deviceClient.on('online', (metadata) => {
console.log('Device online:', metadata.reason)
})
deviceClient.on('playbackUpdate', (playback) => {
console.log('Playing:', playback.trackTitle)
})
// Start managing device
await deviceClient.start()
// Access current state
console.log('Status:', deviceClient.status)
console.log('Config:', deviceClient.config)
// Control device
await deviceClient.updateConfig({ maxVolumeLimit: 14 })
// Stop when done
await deviceClient.stop()import { YotoAccount } from 'yoto-nodejs-client'
const account = new YotoAccount({
clientOptions: {
clientId: 'YOUR_CLIENT_ID',
refreshToken: 'YOUR_REFRESH_TOKEN',
accessToken: 'YOUR_ACCESS_TOKEN',
onTokenRefresh: async (event) => {
await saveTokens(event)
}
}
})
// Unified event handling for all devices
account.on('statusUpdate', ({ deviceId, status, source }) => {
console.log(`${deviceId}: ${status.batteryLevelPercentage}%`)
})
account.on('online', ({ deviceId }) => {
console.log(`${deviceId} online`)
})
// Start managing all devices
await account.start()
// Access individual device
const device = account.getDevice('abc123')
console.log('Battery:', device.status.batteryLevelPercentage)
// Stop all
await account.stop()cdnninja/yoto_api
- Repository: https://github.com/cdnninja/yoto_api
- Full Python wrapper for Yoto API
- Includes authentication, device control, MQTT support
- Used by Home Assistant integration
Installation:
pip install yoto-apibcomnes/yoto-nodejs-client
- Repository: https://github.com/bcomnes/yoto-nodejs-client
- NPM: https://www.npmjs.com/package/yoto-nodejs-client
- Comprehensive Node.js client
- Automatic token refresh
- Full TypeScript support
- Stateful device management
- CLI tools included
Installation:
npm install yoto-nodejs-clientlibraryfm/yoto-js
- Repository: https://github.com/libraryfm/yoto-js
- Unofficial Node SDK
- TypeScript support
cdnninja/yoto_ha
- Repository: https://github.com/cdnninja/yoto_ha
- Home Assistant Integration
- 175+ stars
- Production-ready implementation
yotoplay/examples
- Repository: https://github.com/yotoplay/examples
- Official examples from Yoto
- React, Next.js, Node.js, Vanilla JS examples
- MQTT examples
- Yoto API Documentation: https://yoto.dev/api/
- Get Started: https://yoto.dev/get-started/start-here/
- MQTT Documentation: https://yoto.dev/players-mqtt/mqtt-docs/
- Developer Portal: https://yoto.dev/
-
Authentication
- POST /oauth/device/code - Start device flow
- POST /oauth/token - Exchange tokens
- GET /authorize - Browser-based flow
-
Devices
- GET /device-v2/devices/mine - List devices
- GET /device-v2/{id}/status - Device status
- GET /device-v2/{id}/config - Device config
- PUT /device-v2/{id}/config - Update config
- POST /device-v2/{id}/command - Send command
-
Content
- GET /card/{cardId} - Get card details
- GET /card/mine - User's MYO content
- GET /card/family/library - Family library
- POST /card - Create/update content
- DELETE /card/{cardId} - Delete content
-
Groups
- GET /groups - List groups
- POST /groups - Create group
- GET /groups/{id} - Get group
- PUT /groups/{id} - Update group
- DELETE /groups/{id} - Delete group
-
Media
- POST /media/audio/upload-url - Get upload URL
- POST /media/cover-image - Upload cover
- GET /media/displayIcons/public - Public icons
- GET /media/displayIcons/user/me - User icons
See the complete guide: Creating MYO Cards
Quick overview:
- Authenticate with Yoto API
- Calculate SHA-256 hash of audio file
- Get audio upload URL with SHA256 hash
- Upload audio file to signed URL (if not already uploaded)
- Upload cover image (optional)
- Create card content with upload ID
- Play card on device via MQTT
- Get audio upload URL with SHA256 hash
- Upload audio file to signed URL (if not already uploaded)
- Create card content with upload ID
- Play card on device via MQTT
- Authenticate and get devices
- Connect to MQTT for real-time updates
- Subscribe to status and events topics
- Process battery, temperature, playback events
- Upload audio tracks
- Upload cover image
- Create card with chapters and tracks
- Assign icons to chapters
- Test playback
- Use YotoAccount (Node.js) or iterate devices (Python)
- Listen to unified events
- Send commands to specific devices
- Handle online/offline states
- Always persist refresh tokens - Access tokens expire, refresh tokens are long-lived
- Implement token refresh callback - Save new tokens immediately
- Handle authorization_pending - Poll with proper intervals (usually 5 seconds)
- Respect slow_down responses - Increase polling interval when requested
- Use MQTT for real-time control - Lower latency than HTTP
- Poll HTTP API periodically - For config sync (every 10 minutes is common)
- Cache device state - Reduce API calls
- Handle offline devices - Check online status before commands
- Maintain persistent connection - Automatic reconnection is important
- Subscribe to all relevant topics - status, events, response
- Handle connection drops - Devices may go offline/online
- Use QoS appropriately - QoS 1 for important commands
- Token expiration - Implement automatic refresh
- Network failures - Retry with exponential backoff
- Device offline - Queue commands or notify user
- Rate limiting - Respect API limits
Document Version 1.0 (January 2026)
- Initial comprehensive reference
- Based on Yoto API as of January 2026
- Includes Python and Node.js examples
- MQTT documentation
- REST API endpoints
This document will be updated as the Yoto API evolves and new features are added. Contributions and updates are welcome to keep this reference current and useful for Copilot skill development.
This documentation is for educational and development purposes. Yoto, Yoto Player, and related trademarks are property of Yoto Ltd. This is unofficial documentation compiled from public sources and community projects.