Documentation Index
Fetch the complete documentation index at: https://docs.overlayed.gg/llms.txt
Use this file to discover all available pages before exploring further.
Private channels allow you to restrict app updates to specific users. This is useful for beta testing, early access
programs, or paid tiers.
Overview
When a user requests an update from a private channel, your backend must:
- Verify the user has access to the requested channel
- Request an access token from the Overlayed API
- Return the token to the client
The client then uses this token to authenticate with the update server.
Client Setup
Configure the access token fetcher in your Electron main process:
overlay.updater.setAccessTokenFetcher(async (channel) => {
const response = await fetch("https://your-api.com/access-token", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${getUserSessionToken()}`,
},
body: JSON.stringify({ channel }),
});
if (!response.ok) {
return null;
}
const data = await response.json();
return data.access_token;
});
Handle token invalidation to move users back to the public channel:
overlay.updater.on("authTokenInvalid", async () => {
// User lost access, force an update.
// Overlayed will send them to the latest `stable` version.
await overlay.updater.checkForUpdates();
overlay.updater.quitAndInstall(true, true);
});
Server Implementation
Your server must call the Overlayed API to generate access tokens. The endpoint is:
POST https://api.overlayed.dev/v1/applications/{application_id}/channels/{channel_id}/tokens
Headers:
Authorization: Bearer {your_overlayed_api_key}
Content-Type: application/json
Body:
{
"audience": "user_unique_id",
"expires_in_minutes": 60
}
Response:
{
"access_token": "eyJhbGciOiJIUzI1NiIs..."
}
app.post("/access-token", async (req, res) => {
const { channel } = req.body;
const userId = req.user?.id;
// List of channels can be obtained from the Overlayed API
const requestedChannel = channelsCache[channel];
if (!requestedChannel) {
return res.status(404).json({ error: "Channel not found" });
}
if (!requestedChannel.private) {
return res.json({ access_token: null });
}
if (!userId) {
return res.status(401).json({ error: "Unauthorized" });
}
const userChannels = getUserChannels(userId);
if (!userChannels.includes(channel)) {
return res.status(403).json({ error: "No access to this channel" });
}
const response = await fetch(
`https://api.overlayed.dev/v1/applications/${APPLICATION_ID}/channels/${requestedChannel.id}/tokens`,
{
method: "POST",
headers: {
Authorization: `Bearer ${OVERLAYED_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
audience: userId,
expires_in_minutes: ACCESS_TOKEN_EXPIRATION_MINUTES,
}),
},
);
if (!response.ok) {
return res.status(500).json({ error: "Failed to generate token" });
}
const data = await response.json();
return res.json({ access_token: data.access_token });
});
async fn create_access_token(body: CreateAccessTokenRequest, user_id: Option<String>) -> Result<AccessTokenResponse> {
let requested_channel = get_channel(&body.channel)?;
// Public channels don't need tokens
if !requested_channel.private {
return Ok(AccessTokenResponse { access_token: None });
}
// Verify user is authenticated
let user_id = user_id.ok_or(Unauthorized)?;
// Check user has access to this channel
let user_channels = get_user_channels(&user_id);
if !user_channels.contains(&requested_channel.name) {
return Err(Forbidden);
}
// Generate access token from Overlayed API
let response = http_client
.post(format!(
"https://api.overlayed.dev/v1/applications/{}/channels/{}/tokens",
APPLICATION_ID, requested_channel.id
))
.header("Authorization", format!("Bearer {}", OVERLAYED_API_KEY))
.json(&json!({
"audience": user_id,
"expires_in_minutes": 60,
}))
.send()
.await?;
let data: OverlayedTokenResponse = response.json().await?;
Ok(AccessTokenResponse {
access_token: Some(data.access_token),
})
}
Security Considerations
- Store your Overlayed API key securely. Never expose it to clients.
- Validate user authentication before generating tokens.
- Use short token expiration times (30-60 minutes recommended).
- The
audience field should be a unique identifier for the user. This is logged for analytics.