Skip to content

Connecting to players

Our API lets you connect to your Yoto devices, perform specific actions on them, and receive updates on their state.

This happens via the MQTT protocol. Here’s how it works:

First, fetch your devices from our API:

const deviceResponse = await fetch(
"https://api.yotoplay.com/device-v2/devices/mine",
{
headers: {
Authorization: `Bearer ${accessToken}`,
},
}
);
const { devices } = await deviceResponse.json();
// select a device to connect to
const deviceId = devices[0].deviceId;

The MQTT broker is available at wss://aqrphjqbp3u2z-ats.iot.eu-west-2.amazonaws.com. You’ll need to authenticate using a JWT token with the family:devices:control scope.

const MQTT_URL = "wss://aqrphjqbp3u2z-ats.iot.eu-west-2.amazonaws.com/mqtt";
const clientId = `DASH${deviceId}`;
const mqttClient = mqtt.connect(MQTT_URL, {
keepalive: 300,
port: 443,
protocol: "wss",
username: `${deviceId}?x-amz-customauthorizer-name=PublicJWTAuthorizer`,
password: accessToken,
clientId,
ALPNProtocols: ["x-amzn-mqtt-ca"],
});

Subscribe to the device topics on connect:

const topics = [
`device/${deviceId}/data/events`,
`device/${deviceId}/data/status`,
`device/${deviceId}/response`,
];
mqttClient.on("connect", function () {
mqttClient.subscribe(topics, function (subscribeError) {
if (subscribeError) {
throw subscribeError;
}
});
});

You can now send commands to your device. For example, to set the volume:

const volumeTopic = `device/${deviceId}/command/volume/set`;
const volumePayload = { volume: 3 };
mqttClient.publish(volumeTopic, JSON.stringify(volumePayload));

The client will receive messages on the subscribed topics:

const eventTopic = `device/${deviceId}/data/events`;
const statusTopic = `device/${deviceId}/data/status`;
const responseTopic = `device/${deviceId}/response`;
mqttClient.on("message", (topic, message) => {
const payload = JSON.parse(message.toString());
if (topic === eventTopic) {
// Handle device events
} else if (topic === statusTopic) {
// Handle status updates
} else if (topic === responseTopic) {
// Handle command responses
}
});

For a complete status request example, see Getting player status.

Don’t forget to check if the device you’re trying to connect to is online before attempting to connect.

If you just need to ask a player something once (read status, send a command, disconnect), you can stop here.

For apps that stay connected to a player, there’s more to handle. Yoto closes idle MQTT connections after about 5 minutes, so a long-running client will drop and need to reconnect. Add two options to your mqtt.connect call, and publish an events request every 4m 55s to keep the connection alive:

const mqttClient = mqtt.connect(MQTT_URL, {
keepalive: 300,
port: 443,
protocol: "wss",
username: `${deviceId}?x-amz-customauthorizer-name=PublicJWTAuthorizer`,
password: accessToken,
reconnectPeriod: 300,
clientId,
queueQoSZero: true,
ALPNProtocols: ["x-amzn-mqtt-ca"],
});
const eventsRequestTopic = `device/${deviceId}/command/events/request`;
let eventsRequestInterval;
mqttClient.on("connect", function () {
mqttClient.subscribe(topics, function (subscribeError) {
if (subscribeError) throw subscribeError;
mqttClient.publish(eventsRequestTopic, "{}", { qos: 1 });
clearInterval(eventsRequestInterval);
eventsRequestInterval = setInterval(function () {
mqttClient.publish(eventsRequestTopic, "{}", { qos: 1 });
}, 295_000);
});
});
mqttClient.on("reconnect", function () {
console.log("Reconnecting to Yoto MQTT broker");
});

What each highlighted bit does:

  • reconnectPeriod: 300 — if the connection drops, MQTT.js retries every 300ms.
  • queueQoSZero: true — messages you publish while offline are queued and sent once the client reconnects. Without this, commands published during a disconnect are lost.
  • The connect handler fires every time a connection is established — both the first one and any that follow a drop. Subscribing inside it means your subscriptions are restored automatically after a drop, instead of being lost.
  • The reconnect handler fires while MQTT.js is still trying to get a connection back. It’s only useful for logging — resubscribing here would run too early, before the connection exists.
  • The setInterval republishes an events request every 4m 55s, keeping the connection from being closed by the broker for inactivity.