YotoShelf
Reference

Yoto API Findings

Verified findings from direct exploration of the Yoto API: endpoint behaviour, quirks, and implementation notes.

Source yoto.dev developer portal Verified against live API

This page documents verified behaviour of the Yoto upstream API based on direct exploration. These findings informed the design of go-yoto and YotoShelf's publishing pipeline. Findings are supplemented by the Yoto developer portal.

API base URLs

ServiceBase URL
Authhttps://login.yotoplay.com
APIhttps://api.yotoplay.com
Token audiencehttps://api.yotoplay.com

Authentication

The Yoto API uses Authorization Code + PKCE. YotoShelf registers a redirect URI at dashboard.yoto.dev.

EndpointNotes
GET /authorize Auth Code + PKCE (S256). Params: audience, scope, response_type=code, code_challenge, code_challenge_method=S256.
POST /oauth/token Exchange code or refresh. Refresh tokens are single-use — always persist the new token after a refresh.
GET /userinfo Returns 401 unless the openid scope is requested. Use GET /user/family to get the account email instead.

The token exchange returns: access_token, token_type, expires_in, refresh_token, scope, id_token, expires_at.

Devices

EndpointBehaviour
GET /device-v2/devices/mine Returns {devices: [...]}. Use this — not /devices.
GET /device-v2/{id}/config Returns full device info including a nested status object with activeCard, batteryLevel, charging, cardInserted, etc.
GET /device-v2/{id}/status Returns 403 (deprecated). Status is embedded in the config response.
GET /device-v2/{id}/shortcuts Returns 403 for read. Write (PUT) works.

Useful device fields: deviceType (minie/v3e), deviceFamily (mini/v3), formFactor (mini/standard), generation (all current devices return "gen3").

Content library

EndpointBehaviour
GET /content/mine Returns ALL cards: MYO, purchased physical, and streaming/podcast. Distinguish by config.onlineOnly and presence of editSettings.rssUrl.
GET /content/{id}?playable=true&signingType=s3 Returns time-limited signed S3 URLs for audio. Must be consumed promptly.
POST /content Creates or updates a card. Used for both create and update.
GET /user/family Returns family info including member emails. Use to obtain the account's display email.

MYO card structure

A MYO card fetched with ?playable=true&signingType=s3 has this shape:

card: {
  cardId, title, slug, sortkey, userId, createdAt, updatedAt, lastCheckedAt
  content: {
    activity: "yoto_Player"
    playbackType: "linear"
    restricted: true/false
    version: "1"
    config: { onlineOnly: false }
    chapters: [
      {
        key, title, overlayLabel, duration, fileSize
        availableFrom (optional)
        display: { icon16x16: "https://..." }
        tracks: [
          {
            key, title, overlayLabel, duration, fileSize
            format: "aac"        // MYO cards use aac, not mp3
            type: "audio"        // vs "stream" for podcasts
            trackUrl: "https://s3.amazonaws.com/..."  // signed S3 URL
            channels (optional)
            display: { icon16x16: "..." }
          }
        ]
      }
    ]
  }
  metadata: { ... }
}

Fields not in the original go-yoto types (since fixed)

  • chapter.availableFrom
  • chapter.fileSize
  • track.fileSize
  • track.channels
  • card.lastCheckedAt

Cover images

Upload endpoint: POST /media/coverImage/user/me/upload. The Yoto API calls this a "cover image" — YotoShelf uses the same term. "Label" refers to the printable sticker output, which is a separate concept. See Hardware Constraints for dimensions.

Media deduplication

Yoto deduplicates audio server-side by SHA256 hash on upload. Identical audio uploaded to multiple accounts is stored once on Yoto's infrastructure. YotoShelf tracks content hashes locally to enable smart re-publish decisions.

Full card import

YotoShelf can import existing MYO cards from a linked Yoto account:

  • GET /content/mine lists all MYO cards.
  • GET /content/{id}?playable=true&signingType=s3 provides signed S3 URLs for audio download.
  • Cover images are available via metadata.cover.imageL.
  • Track icons are available via chapter.display.icon16x16.
  • Custom icons are listed at GET /media/displayIcons/user/me.

Downloaded audio is already transcoded to AAC 128 Kbps (Yoto's re-encode output). Re-uploading would result in a second lossy transcode. YotoShelf marks imported audio as transcoded; users can optionally replace it with original sources.

Rate limiting

No documented rate limit figures from Yoto. YotoShelf applies a 500 ms delay between track operations and exponential backoff on HTTP 429 responses. A weekly CI smoke run is deliberately low-frequency to avoid triggering limits.

Scope reference

Scopes used by YotoShelf: offline_access, family:devices:*, family:library:*, user:content:*, user:icons:*.

The openid scope is not requested by default (see /userinfo note above). The family:* family admin endpoints return 403 in all configurations tested; all required data is available via /content/mine and /user/family.