Base URL and methods
https://<gateway>/api/v2/<action>, with query parameters as usual
(/api/v2/search?q=news). Every response is application/json.
POST, form-encoded body (application/x-www-form-urlencoded):login,logout,logout-all,refresh-session,history-update,history-removeandhistory-clear. All of them exceptrefresh-sessionanswer405to any other method.GETfor everything else.
Headers
| Header | Who sends it | Where |
|---|---|---|
X-API-KEY |
the gateway | every request |
X-XAI-Client-IP |
the gateway | every request |
Authorization: Bearer <session_token> |
the app | every request except identity and login |
Pagination, sorting and filters
Lists accept offset (default 0) and limit (default 50, 1-200;
larger values are clamped to 200) and answer:
{ "total": 1234, "offset": 0, "limit": 50, "next_offset": 50, "items": [ ... ] }
next_offset is null on the last page. total counts what this line can
see, after filters.
sort_by:name(natural, case-insensitive),addedorrating. Without it, the panel's own order (the channelnumorder for channels). Series have noadded: sort them bynameorrating.sort_order:asc(default) ordesc.genre_ids(movies and series): comma-separated ids fromGET /genres;genre_mode:or(default, any of them) orand(all of them).category_id:0or omitted means all categories.
List fields and compact series: fields
With capability list_fields. vod-movies, series, search and
recently-added accept fields, a comma-separated list of extra fields for
movie and series items, so a row of posters does not need a vod-info per
title:
| Field | Type | Value |
|---|---|---|
plot |
string | Synopsis, HTML entities decoded, tags removed, at most 300 characters cut at a word boundary with …; "" if none |
backdrop |
string or null |
The first backdrop |
year |
int or null |
Release year |
duration_secs |
int or null |
Movie length; for a series, the typical length of an episode |
genres |
string | As in the detail, entities decoded; "" if none |
- Only the fields you ask for are added, in the order of the table, before
is_adult. Channels get nothing. - With
fieldspresent (even empty,fields=), series items are compact:id,type,name,poster,rating,tmdb_id,category_ids,last_modified,is_adultand the fields asked for.cast,director,youtube_trailer,backdrops,release_date,episode_run_timeand the fullplotstay inseries-info. Withfull_info=1the compact item also carriesseasonsandepisodes. - Without
fieldsevery item is exactly as before. - An unknown or repeated name answers
400 invalid_parameter. Other endpoints ignorefields.
Images: images=relative
With capability relative_images. Any endpoint accepts images=relative:
every item image the panel itself serves (icon, poster, backdrop,
backdrops, image, series_poster and the season poster) comes as a path
(/images/1f3a….jpg, no host and no query) to request through your gateway;
any other image stays an absolute URL from a third party (TMDB, a logo
host…), to load directly. Without the parameter, URLs come as stored. Any
other value answers 400 invalid_parameter. brand-info and actor-images
are not affected.
Ids and types
- Every item has
idandtype(channel,movie,series,episode). Channels, movies and series have independent id spaces: channel123and movie123are different items, so keep the type next to the id. - Detail endpoints take
id:vod-info?id=,series-info?id=,episode-info?id=,short-epg?id=,epg?id=,upcoming-matches?id=. - Playback uses its own type names:
channel,vod(a movie) andsaga(an episode), inget-playback-url,playback-routesand the playback token (section 7). - Types are exact: ids, counts and dates are integers;
ratingis a number (0-10) ornull;tmdb_idis an integer ornull; booleans aretrue/false;category_idsandbackdropsare arrays (possibly empty). Text fields that may be missing arenull(icon,poster,epg_id) or""(descriptions). - Text lists:
genres,castanddirectorare display text with commas. - Adult content: every category and every item (channel, movie, series,
episode) carries
is_adult. An item is adult when any of its categories is. The panel does not hide anything: hide or lock adult content in the app (for example behind a parental PIN), also in search, recently added and history. A change made by the reseller can take a few minutes to show. - Hidden categories: the reseller can hide categories for a line. They
are left out everywhere: category lists,
category_ids, lists, search, recently added, events,epg-nowand history. An item whose categories are all hidden is left out (one without categories is shown), and its detail endpoint answers404 not_found.numstays the channel's position in the whole package, so hiding a category does not renumber the others. - Dates: unix seconds (UTC) everywhere, except
exp_user/exp_token(ISO 8601 UTC,2027-01-15T00:00:00Z;exp_userisnullfor a line that never expires) andrelease_date/air_date(YYYY-MM-DDor""). - New fields may be added at any time: ignore unknown ones.
Every endpoint has a real example in examples/: the test suite
generates them from the code and fails if they drift, so you can use them as
fixtures.