
Company News
Socket Joins the OpenJS Foundation
Socket is proud to join the OpenJS Foundation as a Silver Member, deepening our commitment to the long-term health and security of the JavaScript ecosystem.
yoto-nodejs-client
Advanced tools
(Unofficial) Node.js client for the Yoto API with automatic token refresh, MQTT device communication, and TypeScript support
A comprehensive Node.js client for the Yoto API with automatic token refresh, MQTT device communication, and full TypeScript support.
npm install yoto-nodejs-client
import { YotoClient } from 'yoto-nodejs-client'
// Authenticate using device flow (CLI/server applications)
const deviceCodeResponse = await YotoClient.requestDeviceCode({
clientId: 'your-client-id'
})
console.log(`Visit ${deviceCodeResponse.verification_uri_complete}`)
console.log(`Enter code: ${deviceCodeResponse.user_code}`)
// Poll for token
const tokenResponse = await YotoClient.exchangeToken({
grantType: 'urn:ietf:params:oauth:grant-type:device_code',
deviceCode: deviceCodeResponse.device_code,
clientId: 'your-client-id'
})
// Create client with automatic token refresh
const client = new YotoClient({
clientId: 'your-client-id',
refreshToken: tokenResponse.refresh_token,
accessToken: tokenResponse.access_token,
onTokenRefresh: async (event) => {
// REQUIRED: Persist tokens when they refresh
await saveTokens({
accessToken: event.accessToken,
refreshToken: event.refreshToken,
expiresAt: event.expiresAt
})
}
})
// Get devices
const { devices } = await client.getDevices()
console.log('Your devices:', devices)
// Get device status
const status = await client.getDeviceStatus({
deviceId: devices[0].deviceId
})
console.log('Battery:', status.batteryLevelPercentage, '%')
// Get user's MYO content
const myoContent = await client.getUserMyoContent()
console.log('Your content:', myoContent)
// Connect to device via MQTT for real-time control
const mqtt = await client.createMqttClient({
deviceId: devices[0].deviceId
})
mqtt.on('events', (message) => {
console.log('Playing:', message.trackTitle)
})
mqtt.on('status', (message) => {
console.log('Volume:', message.volume, '%')
})
await mqtt.connect()
await mqtt.setVolume(50)
await mqtt.setAmbientHex('#FF0000')
YotoClient.requestDeviceCode({ clientId, [scope], [audience] })Start the OAuth2 Device Authorization flow for CLI/server applications. Returns a device code and user verification URL.
'openid profile offline_access')'https://api.yotoplay.com')const response = await YotoClient.requestDeviceCode({
clientId: 'your-client-id'
})
console.log(`Visit: ${response.verification_uri_complete}`)
console.log(`Or go to ${response.verification_uri} and enter: ${response.user_code}`)
YotoClient.exchangeToken({ grantType, ...params })Exchange authorization code, refresh token, or device code for access tokens.
'authorization_code', 'refresh_token', or 'urn:ietf:params:oauth:grant-type:device_code'authorization_code grant)refresh_token grant)authorization_code grant)// Exchange device code
const tokens = await YotoClient.exchangeToken({
grantType: 'urn:ietf:params:oauth:grant-type:device_code',
deviceCode: response.device_code,
clientId: 'your-client-id'
})
// Refresh token
const refreshed = await YotoClient.exchangeToken({
grantType: 'refresh_token',
refreshToken: tokens.refresh_token,
clientId: 'your-client-id'
})
YotoClient.getAuthorizeUrl({ clientId, redirectUri, responseType, state, ...params })Get authorization URL for browser-based OAuth flow. Returns a URL string to redirect users to.
new YotoClient({ clientId, refreshToken, accessToken, onTokenRefresh, [options] })Create a new Yoto API client with automatic token refresh.
const client = new YotoClient({
clientId: 'your-client-id',
refreshToken: 'stored-refresh-token',
accessToken: 'stored-access-token',
userAgent: 'MyApp/1.0.0', // Optional - identifies your application
defaultRequestOptions: { // Optional - undici request options for all requests
bodyTimeout: 30000, // 30 second timeout
headersTimeout: 10000, // 10 second header timeout
// dispatcher, signal, etc.
},
onTokenRefresh: async ({ accessToken, refreshToken, expiresAt }) => {
// Save to database, file, etc.
await db.saveTokens({ accessToken, refreshToken, expiresAt })
}
})
All API methods accept an optional requestOptions parameter that allows you to override the default undici request options for individual requests. This is useful for setting custom timeouts, using a specific dispatcher, or aborting requests.
// Override timeout for a specific request
const content = await client.getContent({
cardId: '5WsQg',
requestOptions: {
bodyTimeout: 60000, // 60 second timeout for this request only
headersTimeout: 20000
}
})
// Use AbortSignal to cancel requests
const controller = new AbortController()
setTimeout(() => controller.abort(), 5000)
try {
const devices = await client.getDevices({
requestOptions: { signal: controller.signal }
})
} catch (err) {
if (err.name === 'AbortError') {
console.log('Request was aborted')
}
}
// Use custom dispatcher for connection pooling
import { Agent } from 'undici'
const agent = new Agent({ connections: 10 })
const status = await client.getDeviceStatus({
deviceId: 'abc123',
requestOptions: { dispatcher: agent }
})
Available Request Options:
bodyTimeout - Body timeout in millisecondsheadersTimeout - Headers timeout in millisecondssignal - AbortSignal to cancel the requestdispatcher - Custom undici dispatcher (for connection pooling, proxies, etc.)reset - Reset connection after requestthrowOnError - Throw on HTTP error status codesidempotent - Whether the requests can be safely retriedblocking - Whether the response is expected to take a long timeawait client.getContent({ cardId, [timezone], [signingType], [playable] })Get content/card details including metadata, chapters, and optionally playback URLs.
const content = await client.getContent({
cardId: '5WsQg',
playable: true // Include signed playback URLs
})
console.log(content.title)
console.log(content.chapters)
await client.getUserMyoContent({ [showDeleted] })Get user's MYO (Make Your Own) content library.
See Yoto API: Get User's MYO Content
const myoContent = await client.getUserMyoContent({
showDeleted: false
})
myoContent.cards.forEach(card => {
console.log(`${card.metadata.title} - ${card.chapters.length} chapters`)
})
await client.createOrUpdateContent({ content })Create new content or update existing content by cardId.
See Yoto API: Create or Update Content
const newCard = await client.createOrUpdateContent({
content: {
title: 'My Story',
chapters: [
{
title: 'Chapter 1',
tracks: [
{
title: 'Part 1',
key: 'upload-id-from-audio-upload'
}
]
}
]
}
})
console.log('Created card:', newCard.cardId)
await client.deleteContent({ cardId })Delete content/card.
await client.deleteContent({ cardId: '5WsQg' })
await client.getDevices()Get all devices for authenticated user.
const { devices } = await client.getDevices()
devices.forEach(device => {
console.log(`${device.name} (${device.deviceId}) - ${device.online ? 'online' : 'offline'}`)
})
await client.getDeviceStatus({ deviceId })Get current status of a specific device including battery, volume, active card, etc.
See Yoto API: Get Device Status
const status = await client.getDeviceStatus({
deviceId: 'abc123'
})
console.log('Battery:', status.batteryLevelPercentage, '%')
console.log('Charging:', status.isCharging)
console.log('Volume:', status.userVolumePercentage, '%')
console.log('Active card:', status.activeCard)
await client.getDeviceConfig({ deviceId })Get device configuration including settings, timezone, shortcuts, etc.
See Yoto API: Get Device Config
const config = await client.getDeviceConfig({
deviceId: 'abc123'
})
console.log('Name:', config.device.name)
console.log('Day time:', config.device.config.dayTime)
console.log('Night time:', config.device.config.nightTime)
console.log('Max volume:', config.device.config.maxVolumeLimit)
await client.updateDeviceConfig({ deviceId, configUpdate })Update device configuration settings.
See Yoto API: Update Device Config
await client.updateDeviceConfig({
deviceId: 'abc123',
configUpdate: {
name: 'Bedroom Player',
config: {
dayTime: '07:00',
nightTime: '19:00',
maxVolumeLimit: '80'
}
}
})
await client.updateDeviceShortcuts({ deviceId, shortcutsUpdate })Update device shortcuts configuration (beta feature).
See Yoto API: Update Shortcuts
await client.sendDeviceCommand({ deviceId, command })Send MQTT command to device via HTTP API (alternative to MQTT client).
See Yoto API: Send Device Command
await client.sendDeviceCommand({
deviceId: 'abc123',
command: {
volume: 50
}
})
await client.getGroups()Get all family library groups.
const groups = await client.getGroups()
groups.forEach(group => {
console.log(`${group.name}: ${group.items.length} items`)
})
await client.createGroup({ group })Create a new family library group.
const group = await client.createGroup({
group: {
name: 'Bedtime Stories',
imageId: 'fp-cards',
items: [
{ contentId: '5WsQg' },
{ contentId: '7KpLq' }
]
}
})
await client.getGroup({ groupId })Get a specific group by ID.
await client.updateGroup({ groupId, group })Update an existing group.
await client.deleteGroup({ groupId })Delete a group permanently.
await client.getFamilyImages()Get list of uploaded family images.
See Yoto API: Get Family Images
const { images } = await client.getFamilyImages()
images.forEach(image => {
console.log(`${image.name || 'Unnamed'}: ${image.imageId}`)
})
await client.getAFamilyImage({ imageId, size })Get signed URL for a family image.
See Yoto API: Get a Family Image
const { imageUrl } = await client.getAFamilyImage({
imageId: 'abc123hash',
size: '640x480' // or '320x320'
})
console.log('Image URL:', imageUrl)
await client.uploadAFamilyImage({ imageData })Upload a family image for use across Yoto features.
See Yoto API: Upload Family Image
import { readFile } from 'fs/promises'
const imageData = await readFile('./family-photo.jpg')
const result = await client.uploadAFamilyImage({ imageData })
console.log('Image ID:', result.imageId)
await client.getPublicIcons()Get list of public display icons available to all users.
See Yoto API: Get Public Icons
const { displayIcons } = await client.getPublicIcons()
displayIcons.forEach(icon => {
console.log(`${icon.title}: ${icon.displayIconId}`)
})
await client.getUserIcons()Get user's custom uploaded icons.
await client.uploadIcon({ imageData, [autoConvert], [filename] })Upload a custom 16×16px display icon.
See Yoto API: Upload Custom Icon
import { readFile } from 'fs/promises'
const imageData = await readFile('./my-icon.png')
const result = await client.uploadIcon({
imageData,
autoConvert: true, // Auto-resize and process
filename: 'my-custom-icon'
})
console.log('Icon ID:', result.displayIcon.displayIconId)
await client.getAudioUploadUrl({ sha256, [filename] })Get signed URL for uploading audio files. Files are deduplicated by SHA256 hash.
See Yoto API: Get Audio Upload URL
import { createHash } from 'crypto'
import { readFile } from 'fs/promises'
const audioData = await readFile('./story.mp3')
const sha256 = createHash('sha256').update(audioData).digest('hex')
const { upload } = await client.getAudioUploadUrl({
sha256,
filename: 'story.mp3'
})
if (upload.uploadUrl) {
// File doesn't exist, upload it
await fetch(upload.uploadUrl, {
method: 'PUT',
body: audioData
})
}
// Use upload.uploadId in content creation
await client.uploadCoverImage({ [imageData], [imageUrl], [coverType], [autoConvert], [filename] })Upload a cover image for content cards.
See Yoto API: Upload Cover Image
import { readFile } from 'fs/promises'
const imageData = await readFile('./cover.jpg')
const { coverImage } = await client.uploadCoverImage({
imageData,
coverType: 'default', // 638×1011px
autoConvert: true
})
console.log('Cover image ID:', coverImage.mediaId)
await client.createMqttClient({ deviceId, [options] })Create an MQTT client for real-time device communication and control.
const mqtt = await client.createMqttClient({
deviceId: 'abc123',
autoResubscribe: true,
keepAliveSeconds: 1200
})
// Listen for real-time events
mqtt.on('events', (message) => {
console.log('Track:', message.trackTitle)
console.log('Card:', message.cardTitle)
console.log('Status:', message.playbackStatus)
})
// Listen for status updates
mqtt.on('status', (message) => {
console.log('Volume:', message.volume)
console.log('Battery:', message.batteryLevel)
console.log('Charging:', message.charging)
})
// Listen for command responses
mqtt.on('response', (message) => {
console.log('Command response:', message)
})
// Connect to device
await mqtt.connect()
// Control device
await mqtt.setVolume(50)
await mqtt.setAmbientHex('#FF0000')
await mqtt.setSleepTimer(30) // 30 minutes
await mqtt.startCard({ cardId: '5WsQg' })
await mqtt.pauseCard()
await mqtt.resumeCard()
await mqtt.stopCard()
// Disconnect when done
await mqtt.disconnect()
The MQTT client emits three types of messages:
events - Real-time playback events (track changes, play/pause, volume adjustments)status - Device status updates (battery, configuration, online state)response - Command confirmation responsesawait mqtt.connect() - Connect to device MQTT brokerawait mqtt.disconnect() - Disconnect from brokerawait mqtt.setVolume(volume) - Set volume (0-100)await mqtt.setAmbientHex(hex) - Set ambient light color (e.g., '#FF0000')await mqtt.setSleepTimer(minutes) - Set sleep timer (0 to disable)await mqtt.startCard({ cardId, chapterKey, trackKey }) - Start playing a cardawait mqtt.pauseCard() - Pause current playbackawait mqtt.resumeCard() - Resume playbackawait mqtt.stopCard() - Stop playbackawait mqtt.reboot() - Reboot deviceThe library includes CLI tools for authentication and data inspection:
# Get initial tokens (device flow)
node bin/auth.js --output .env
# Refresh tokens
node bin/refresh-token.js
# Show token info
node bin/token-info.js
# List all devices
node bin/devices.js
# Get device details with config
node bin/devices.js --device-id abc123
# Get device status only
node bin/devices.js --device-id abc123 --status
# Connect to MQTT and listen for messages
node bin/devices.js --device-id abc123 --mqtt
# Sample MQTT messages for 10 seconds
node bin/devices.js --device-id abc123 --mqtt --mqtt-stop-after-seconds 10
# List all MYO content
node bin/content.js
# Get specific card details
node bin/content.js --card-id 5WsQg
# Get card with playable URLs
node bin/content.js --card-id 5WsQg --playable
MIT
FAQs
(Unofficial) Node.js client for the Yoto API with automatic token refresh, MQTT device communication, and TypeScript support
We found that yoto-nodejs-client demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Company News
Socket is proud to join the OpenJS Foundation as a Silver Member, deepening our commitment to the long-term health and security of the JavaScript ecosystem.

Security News
npm now links to Socket's security analysis on every package page. Here's what you'll find when you click through.

Security News
A compromised npm publish token was used to push a malicious postinstall script in cline@2.3.0, affecting the popular AI coding agent CLI with 90k weekly downloads.