Authorization: Bearer <api_key>, a partner API key created from the admin’s Utilisateurs tab. Each key carries its own named scopes — none are implicit — and its own access modes (api today, mcp reserved for a future MCP layer built on this same key system; a key without the right mode for a channel can never authenticate on it, regardless of its scopes). Base URL: https://es-epgdoctor.cedric-monnier.workers.dev/v1/api. Full machine-readable spec: /v1/api/openapi.json.
GET /channels
Liste des chaînes mémorisées
Required scope: channels:read
Renvoie chaque chaîne mémorisée disposant d’un channel_id réel (une source jamais vérifiée n’en a pas encore). Ne renvoie jamais l’URL du flux XML source. channel_id (valeur brute du provider) et channel_uuid (identifiant canonique de l’app, persistance garantie même si le provider renomme un jour la chaîne) sont tous deux exposés — chaque route channels/* accepte l’un ou l’autre.
Aucun paramètre de requête.
Exemple de réponse
{
"data": {
"channels": [
{
"channel_id": "raw_provider_id_123",
"channel_uuid": "3f1a2b4c-5d6e-7f89-a0b1-c2d3e4f56789",
"name": "Example Channel",
"provider": "OKAST",
"logo_url": "https://…",
"format": "xmltv",
"last_checked_at": "2026-08-24T10:00:00.000Z",
"has_errors": false
}
]
},
"meta": {
"count": 1
}
}
GET /channels/programs
Grille EPG d’une chaîne sur une plage donnée
Required scope: channels:read
Programmes connus pour une chaîne entre since et until (ISO 8601). Plage maximale : 31 jours. Un scope content:read supplémentaire (jamais requis, jamais un gate sur l’accès aux programmes eux-mêmes) enrichit chaque programme d’un champ iab_tags — les tags IAB Content Taxonomy 2.2 associés au TITRE de ce programme (pas à cette diffusion précise), destinés à enrichir le contexte passé au décision process de l’Ad Server pour le ciblage contextuel. Absent du tout sans ce scope ; un tableau vide (pas absent) si le scope est présent mais que le titre n’a encore aucun tag connu.
| Paramètre | Type | Requis | Note |
|---|---|---|---|
channel_id | string | non | channel_id ou channel_uuid requis (l’un des deux) |
channel_uuid | string | non | identifiant canonique de la chaîne, persistance garantie contrairement à channel_id — préférable pour un stockage long terme côté partenaire |
since | string | oui | |
until | string | oui |
Exemple de réponse
{
"data": {
"channel_id": "raw_provider_id_123",
"programs": [
{
"title": "Example Show",
"subtitle": null,
"description": null,
"start": "2026-08-24T10:00:00.000Z",
"end": "2026-08-24T10:30:00.000Z",
"category": [
"Documentaire"
],
"image_url": "https://example.test/programme.jpg",
"iab_tags": [
{
"taxonomy_version": "3.1",
"code": "640",
"label": "Television"
}
]
}
]
},
"meta": {
"count": 1,
"since": "2026-08-24T00:00:00.000Z",
"until": "2026-08-25T00:00:00.000Z"
}
}
POST /channels/refresh
Relance un contrôle sur une chaîne déjà mémorisée
Required scope: channels:write
Refait un fetch + parse réel de la source associée à ce channel_id et persiste le résultat — une vraie action avec effet de bord, pas une lecture passive. Nécessite le scope channels:write, jamais accordé par channels:read seul. channel_id ou channel_uuid requis dans le corps (l’un des deux).
Aucun paramètre de requête.
Corps de la requête
{
"channel_id": "raw_provider_id_123"
}
Exemple de réponse
{
"data": {
"channel_id": "raw_provider_id_123",
"checked_at": "2026-08-24T10:00:00.000Z",
"program_count": 42,
"issues_count": 0,
"valid": true,
"latest_epg_date": "2026-09-06T18:00:00.000Z"
}
}
GET /channels/quality
Analyse ADN d’une chaîne (structure de grille, diversité de titres, catégories)
Required scope: quality:read
range=week nécessite week_start (une date ISO, alignée automatiquement sur le lundi de cette semaine-là) ; range=90d couvre toujours la fenêtre de rétention complète, aucun paramètre supplémentaire.
| Paramètre | Type | Requis | Note |
|---|---|---|---|
channel_id | string | non | channel_id ou channel_uuid requis (l’un des deux) |
channel_uuid | string | non | identifiant canonique de la chaîne, persistance garantie contrairement à channel_id — préférable pour un stockage long terme côté partenaire |
range | string (week | 90d) | oui | |
week_start | string | non | requis si range=week |
Exemple de réponse
{
"data": {
"channel_id": "raw_provider_id_123",
"dna": {
"catalogue_size": 128,
"top_category": {
"name": "Documentaire",
"pct": 62
}
},
"range": {
"since": "2026-08-24T00:00:00.000Z",
"until": "2026-08-31T00:00:00.000Z"
}
},
"meta": {
"truncated": false
}
}
GET /channels/heatmap
Grille hebdomadaire densité/fraîcheur/qualité, jour par jour et heure par heure
Required scope: quality:read
Même moteur que la «Vue hebdomadaire» de l’onglet Analyse.
| Paramètre | Type | Requis | Note |
|---|---|---|---|
channel_id | string | non | channel_id ou channel_uuid requis (l’un des deux) |
channel_uuid | string | non | identifiant canonique de la chaîne, persistance garantie contrairement à channel_id — préférable pour un stockage long terme côté partenaire |
week_start | string | oui |
Exemple de réponse
{
"data": {
"channel_id": "raw_provider_id_123",
"heatmap": {
"days": [
"Lun 24 août",
"…"
],
"cells": []
},
"range": {
"since": "2026-08-24T00:00:00.000Z",
"until": "2026-08-31T00:00:00.000Z"
}
},
"meta": {
"truncated": false
}
}
GET /channels/ad-load
Statistiques de charge publicitaire (quotidien/horaire)
Required scope: ad-load:read
Historique complet mergé (import quotidien et/ou coupures détaillées), pas une plage bornée — mêmes données que la carte «Ad Load Profile».
| Paramètre | Type | Requis | Note |
|---|---|---|---|
channel_id | string | non | channel_id ou channel_uuid requis (l’un des deux) |
channel_uuid | string | non | identifiant canonique de la chaîne, persistance garantie contrairement à channel_id — préférable pour un stockage long terme côté partenaire |
Exemple de réponse
{
"data": {
"channel_id": "raw_provider_id_123",
"stats": {
"period": {
"start": "2026-08-01",
"end": "2026-08-24",
"days": 24
},
"avails_per_day_avg": 96
},
"hourly_trend": {
"series": [],
"avg_avails_per_hour": 4
}
}
}
GET /channels/ad-fill-rate
Taux de remplissage Ad Server (coupures programmées vs décisions réelles)
Required scope: ad-load:read
Best-effort : si l’Ad Server est injoignable, une heatmap réelle revient quand même (tout en NO_REQUEST), avec ad_serving_warning expliquant pourquoi plutôt qu’une erreur.
| Paramètre | Type | Requis | Note |
|---|---|---|---|
channel_id | string | non | channel_id ou channel_uuid requis (l’un des deux) |
channel_uuid | string | non | identifiant canonique de la chaîne, persistance garantie contrairement à channel_id — préférable pour un stockage long terme côté partenaire |
week_start | string | oui |
Exemple de réponse
{
"data": {
"channel_id": "raw_provider_id_123",
"heatmap": {
"days": [],
"cells": []
},
"range": {
"since": "2026-08-24T00:00:00.000Z",
"until": "2026-08-31T00:00:00.000Z"
}
},
"meta": {
"ad_serving_warning": null
}
}
GET /channels/context
Snapshot complet programmes + coupures + tags IAB, pour préchargement par un décision process externe
Required scope: channels:read
Conçu pour le préchargement — jamais appelé pendant une requête VAST en direct. Retourne un instantané COMPLET, non paginé et autoritatif de [since, until[ : tout programme ou coupure connu qui chevauche la fenêtre est inclus (un programme commencé avant since mais qui se termine après since est inclus). Plage maximale : 7 jours. Une correction ou dépublication de grille est observable dès le prochain appel — un programme qui n\u2019a pas été reconfirmé par la vérification la plus récente de la chaîne n\u2019est plus jamais retourné, quelle que soit la fenêtre demandée. program_id et break_id sont des identifiants publics stables (voir program_id : assignation interne UUID + correspondance titre/horaire tant qu\u2019aucun fournisseur EPG n\u2019expose son propre identifiant persistant ; break_id : identifiant interne stable, ou l\u2019identifiant persistant OKAST lui-même dès qu\u2019il sera disponible dans les exports réels). Les tags IAB (taxonomie IAB Content 3.1) sont une classification publicitaire structurée, distincte des catégories éditoriales. keywords (Ad Server, 2026-09-08) : termes éditoriaux extraits du titre/sous-titre/description de CE programme — jamais mappés vers IAB ici, ce mapping reste entièrement du côté Ad Server, à partir de son propre dictionnaire keyword → IAB Content 3.1 ; les tags IAB natifs (champ iab), quand ils existent, restent prioritaires. series_id (optionnel) : identifiant stable de série/émission récurrente quand la source en fournit un ; null pour la grande majorité des flux aujourd\u2019hui — jamais dérivé du titre. Supporte ETag/If-None-Match (304 si la fenêtre n\u2019a pas changé) — keywords et series_id sont inclus dans ce calcul, un changement de l\u2019un ou l\u2019autre invalide le cache.
| Paramètre | Type | Requis | Note |
|---|---|---|---|
channel_id | string | non | channel_id ou channel_uuid requis (l’un des deux) |
channel_uuid | string | non | identifiant canonique de la chaîne, persistance garantie contrairement à channel_id — préférable pour un stockage long terme côté partenaire |
since | string | oui | |
until | string | oui |
Exemple de réponse
{
"data": {
"channel_id": "raw_provider_id_123",
"programs": [
{
"program_id": "3f1a2b4c-5d6e-7f89-a0b1-c2d3e4f56789",
"series_id": null,
"start": "2026-08-25T18:00:00.000Z",
"end": "2026-08-25T19:00:00.000Z",
"title": "Example Show",
"subtitle": null,
"description": "An optional editorial synopsis.",
"categories": [
"Documentaire"
],
"image_url": "https://example.test/programme.jpg",
"iab": {
"taxonomy": "IAB_CONTENT",
"version": "3.1",
"cattax": 9,
"tags": [
{
"term_id": "653",
"label": "Travel",
"source": "epg_doctor_manual",
"confidence": null
}
]
},
"keywords": [
"synopsis",
"editorial",
"documentaire"
],
"updated_at": "2026-08-25T10:12:30.000Z"
}
],
"ad_breaks": [
{
"break_id": "7c8d9e0f-1a2b-3c4d-5e6f-7a8b9c0d1e2f",
"program_id": "3f1a2b4c-5d6e-7f89-a0b1-c2d3e4f56789",
"start": "2026-08-25T18:22:00.000Z",
"end": "2026-08-25T18:24:00.000Z",
"duration_seconds": 120,
"position": "unknown",
"updated_at": "2026-08-25T10:12:30.000Z"
}
]
},
"meta": {
"since": "2026-08-25T00:00:00.000Z",
"until": "2026-09-01T00:00:00.000Z",
"generated_at": "2026-08-25T10:15:00.000Z",
"source_updated_at": "2026-08-25T10:12:30.000Z",
"coverage_until": "2026-09-06T18:00:00.000Z",
"program_count": 1,
"ad_break_count": 1
}
}