Channel panels
With the panels:manage scope, your app can put one panel on a channel's page, beside the creator's own panels under their About. Use it for things that change: a season's stats, a leaderboard, a "now playing" card, a giveaway's rules.
Panels are data, not code. You send a title, some text, an image, links and stats; Xive draws them in its own panel style. There are no iframes, scripts or HTML, so a panel looks like the rest of the page and can't track or break it. Every panel shows your app's name and icon with an App tag, so viewers know a third party wrote it.
A panel
{
"channel_id": "c0901ae8-6771-4e9a-b40c-b6e72d3ca397",
"application_id": "688c4141-7151-4af3-96f6-154d0412b1e7",
"title": "Season 4 stats",
"body": "**Ranked** grind, every night.\nUpdated after each match.",
"image_url": "https://i.imgur.com/abc123.png",
"links": [
{ "label": "Full leaderboard", "url": "https://example.com/leaderboard" }
],
"stats": [
{ "label": "Wins", "value": "12" },
{ "label": "K/D", "value": "1.4" }
],
"is_hidden": false,
"updated_at": "2026-10-11T16:28:23.136Z"
}
| Field | Notes |
|---|---|
title | Required. Up to 60 characters. |
body | Optional. Up to 1,000 characters of plain text, with the same formatting a creator's text panel takes: **bold**, *italic*, ~~strike~~, # heading, - list, > quote and [link](https://…). Bare URLs become links. HTML is shown as text, never rendered. |
image_url | Optional. An https:// image on one of the allowed hosts, shown under the title. |
links | Optional. Up to 5 {label, url}. label is up to 40 characters (empty shows the address); url must be https://. |
stats | Optional. Up to 8 {label, value}, each up to 30 characters, shown as tiles. value can be a string or a number; it comes back as a string. |
is_hidden | Read-only. true when the creator has hidden your panel. It's still stored and you can still update it, but viewers don't see it. |
updated_at | Read-only. When your app last wrote the panel. |
Every visible string goes through Xive's content filter, the same one that checks stream titles. A refused panel answers 400 with "moderation": {"blocked": true, …}, and nothing is saved.
Get your panel
GET /api/apps/channels/{channelId}/panel returns your app's panel on that channel, or "panel": null if it has none.
{ "success": true, "panel": { "title": "Season 4 stats", "…": "…" } }
Create or replace your panel
PUT /api/apps/channels/{channelId}/panel with the whole panel. It replaces what was there: a field you leave out is cleared. Answers with the saved panel.
curl -X PUT https://api.thexive.com/api/apps/channels/CHANNEL_ID/panel \
-H "Authorization: Bearer $XIVE_APP_SECRET" \
-H "Content-Type: application/json" \
-d '{"title": "Season 4 stats", "body": "Updated after each match.", "stats": [{"label": "Wins", "value": 12}, {"label": "K/D", "value": "1.4"}]}'
Unknown fields are refused with 400, so a typo can't fail silently. You can send back the updated_at you were given; it's ignored.
Take your panel down
DELETE /api/apps/channels/{channelId}/panel removes it from the channel page. Answers {"success": true, "deleted": true}, or "deleted": false if there was nothing to remove.
Limits
- One write every 10 seconds per app per channel.
PUTandDELETEshare it. Sooner answers429withretry_after(seconds) and aRetry-Afterheader. Panels are for things that change every few minutes, not every second: collect updates and send the latest. - One panel per app per channel. A channel page shows up to 10 app panels.
- These calls also count toward your app's normal request budget.
Images
Every viewer's browser loads your image, so it can only come from a short list of image hosts. Anything else is refused with 400:
media.xivecdn.com, i.imgur.com, cdn.discordapp.com, media.discordapp.net, raw.githubusercontent.com, avatars.githubusercontent.com, user-images.githubusercontent.com, static-cdn.jtvnw.net and i.ytimg.com (and their subdomains). Use https://. Images load without a referrer.
What the creator controls
The channel is the creator's. In their Studio, under Channel → Panels, your panel is listed as From <your app>, and they can:
- Hide or show it. Hiding keeps your panel stored but takes it off the page. Deleting and re-creating the panel doesn't undo a hide. Check
is_hiddenif your app wants to know. - Move it before, between or after their own panels. Your writes never change where it sits.
- Remove your app from the channel (Settings → Connected apps). Your panel disappears from the page at once, and your calls answer
403. If they add your app again withpanels:manage, the panel comes back.
Nothing you send can change the panel's look, colour, size or place. That's how every app panel stays readable and the page stays the creator's.
Good practice
- Write only when something changed, and batch bursts into one update.
- Keep the title short and the text scannable. Stats suit numbers; links suit "see more".
- Delete your panel when it no longer applies (an event ended), rather than leaving stale data up.