Skip to content

Latest commit

 

History

History
1004 lines (778 loc) · 19.6 KB

File metadata and controls

1004 lines (778 loc) · 19.6 KB

⚠️ 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


Yoto API Reference Documentation

This document provides comprehensive reference information about the Yoto API for use in developing Yoto smart streaming skills and applications.

Table of Contents

  1. Overview
  2. Authentication
  3. REST API Endpoints
  4. MQTT Communication
  5. Device Models and Data Structures
  6. Code Examples
  7. Useful Libraries
  8. Official Resources

Overview

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

Base URLs

REST API: https://api.yotoplay.com
Auth: https://login.yotoplay.com

Getting Started

  1. Obtain a Client ID from: https://yoto.dev/get-started/start-here/
  2. Authenticate using OAuth2 Device Flow
  3. Use REST API for device management and content
  4. Connect via MQTT for real-time control

Authentication

OAuth2 Device Flow

The recommended authentication method for CLI/server applications.

Step 1: Request Device Code

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
}

Step 2: Poll for Token

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"
}

Step 3: Refresh Token

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

Authentication Headers

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

REST API Endpoints

Devices

Get All Devices

GET /device-v2/devices/mine

Response:

{
  "devices": [
    {
      "deviceId": "string",
      "name": "string",
      "deviceType": "v3",
      "online": true
    }
  ]
}

Get Device Status

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: Unknown
  • 1: AC Power
  • 2: Battery
  • 3: Wireless Charging

Get Device Config

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": []
    }
  }
}

Update Device Config

PUT /device-v2/{deviceId}/config
Content-Type: application/json

{
  "deviceId": "string",
  "config": {
    "name": "Bedroom Player",
    "dayTime": "07:00",
    "nightTime": "19:00",
    "maxVolumeLimit": "80"
  }
}

Send Device Command (via HTTP)

POST /device-v2/{deviceId}/command
Content-Type: application/json

{
  "volume": 50
}

Content (Cards)

Get User's MYO Content

GET /card/mine

Response:

{
  "cards": [
    {
      "cardId": "string",
      "metadata": {
        "title": "string",
        "description": "string",
        "author": "string",
        "cover": {
          "imageL": "https://url"
        }
      }
    }
  ]
}

Get Family Library

GET /card/family/library

Get Card Details

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"
            }
          ]
        }
      ]
    }
  }
}

Family

Get Family Info

GET /user/family

Groups (Family Library Groups)

Get All Groups

GET /groups

Create Group

POST /groups
Content-Type: application/json

{
  "name": "Bedtime Stories",
  "imageId": "fp-cards",
  "items": [
    { "contentId": "cardId1" },
    { "contentId": "cardId2" }
  ]
}

Media

Get Audio Upload URL

POST /media/audio/upload-url
Content-Type: application/json

{
  "sha256": "hash",
  "filename": "story.mp3"
}

Upload Cover Image

POST /media/cover-image
Content-Type: multipart/form-data

image: [binary data]
coverType: default
autoConvert: true

MQTT Communication

Connection

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"
}

Topics

Subscribe Topics

Status Updates:

yoto/{deviceId}/status

Playback Events:

yoto/{deviceId}/events

Command Responses:

yoto/{deviceId}/response

Publish Topics

Send Commands:

yoto/{deviceId}/command

MQTT Messages

Status Message

{
  "volume": 50,
  "batteryLevel": 100,
  "charging": false,
  "online": true,
  "temperature": 24
}

Events Message (Playback)

{
  "trackTitle": "Story Title",
  "cardTitle": "Card Name",
  "playbackStatus": "playing",
  "position": 120,
  "trackLength": 300
}

Command Message

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 }

Device Models and Data Structures

YotoPlayer

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: datetime

YotoPlayerConfig

class 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]

Card

class Card:
    id: str  # Card ID
    title: str
    description: str
    author: str
    category: str
    cover_image_large: str  # URL
    chapters: dict[str, Chapter]

Chapter

class Chapter:
    key: str  # "01-INT"
    title: str
    icon: str  # URL
    duration: int  # seconds
    tracks: dict[str, Track]

Track

class Track:
    key: str
    title: str
    duration: int
    format: str  # "aac", "mp3"
    channels: str  # "mono", "stereo"
    type: str  # "audio"
    trackUrl: str  # Signed URL

Code Examples

Python (using yoto_api library)

Basic Setup and Authentication

from 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)

Using Saved Refresh Token

# 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()

Device Control

# 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}%")

Node.js (using yoto-nodejs-client)

Authentication and Setup

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)
  }
})

Device Management

// 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'
    }
  }
})

MQTT Real-time Control

// 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' })

Stateful Device Model

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()

Account Manager (Multiple Devices)

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()

Useful Libraries

Python

cdnninja/yoto_api

Installation:

pip install yoto-api

Node.js/TypeScript

bcomnes/yoto-nodejs-client

Installation:

npm install yoto-nodejs-client

libraryfm/yoto-js

Other Tools

cdnninja/yoto_ha

yotoplay/examples


Official Resources

Documentation

Key API Sections

  1. Authentication

    • POST /oauth/device/code - Start device flow
    • POST /oauth/token - Exchange tokens
    • GET /authorize - Browser-based flow
  2. 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
  3. 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
  4. Groups

    • GET /groups - List groups
    • POST /groups - Create group
    • GET /groups/{id} - Get group
    • PUT /groups/{id} - Update group
    • DELETE /groups/{id} - Delete group
  5. 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

Common Use Cases

Create MYO (Make Your Own) Cards

See the complete guide: Creating MYO Cards

Quick overview:

  1. Authenticate with Yoto API
  2. Calculate SHA-256 hash of audio file
  3. Get audio upload URL with SHA256 hash
  4. Upload audio file to signed URL (if not already uploaded)
  5. Upload cover image (optional)
  6. Create card content with upload ID
  7. Play card on device via MQTT

Stream Custom Audio

  1. Get audio upload URL with SHA256 hash
  2. Upload audio file to signed URL (if not already uploaded)
  3. Create card content with upload ID
  4. Play card on device via MQTT

Monitor Device Status

  1. Authenticate and get devices
  2. Connect to MQTT for real-time updates
  3. Subscribe to status and events topics
  4. Process battery, temperature, playback events

Create Interactive Card

  1. Upload audio tracks
  2. Upload cover image
  3. Create card with chapters and tracks
  4. Assign icons to chapters
  5. Test playback

Control Multiple Devices

  1. Use YotoAccount (Node.js) or iterate devices (Python)
  2. Listen to unified events
  3. Send commands to specific devices
  4. Handle online/offline states

Tips for Development

Authentication

  • 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

API Best Practices

  • 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

MQTT Tips

  • 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

Error Handling

  • Token expiration - Implement automatic refresh
  • Network failures - Retry with exponential backoff
  • Device offline - Queue commands or notify user
  • Rate limiting - Respect API limits

Version History

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

Contributing

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.


License Note

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.