API HTTP

Endpoints HTTP publics, en lecture seule, exposant vos produits, leur configuration de widget et leurs avis audio (URL audio, notes, transcriptions, métadonnées de l'auteur). Il s'agit des endpoints utilisés par le widget Ekoo lui-même, mis à disposition pour tout scénario d'intégration en lecture : rendu côté serveur, application mobile native, génération statique, outillage interne, synchronisation de données.

L'authentification n'est pas requise. Les requêtes et les réponses sont au format JSON. Les réponses sont cachables au niveau de l'edge Ekoo comme côté client.

Cas d'usage courants

  • Afficher les informations produit, notes ou transcriptions dans le HTML avant l'hydratation du widget — pour améliorer l'indexation SEO, l'accessibilité et le first contentful paint.
  • Afficher les avis audio dans une application mobile native, à côté de votre catalogue produit.
  • Générer des pages au build avec un générateur de site statique (Next.js ISR, Astro, Gatsby, Hugo).
  • Synchroniser les métadonnées produit et avis dans votre data warehouse ou votre index de recherche.

Endpoints

MéthodeCheminRetourne
GET/ws/websites/{websiteId}/products/{productRef}Un produit avec ses avis et la config widget.
GET/ws/websites/{websiteId}/productsUne liste paginée de produits.

URL de base

1https://app.ekoo.co

Tous les endpoints ci-dessous sont relatifs à cette URL de base. Les réponses sont au format JSON (Content-Type: application/json).

Authentification

Les requêtes ne sont pas authentifiées. Le paramètre websiteId présent dans l'URL identifie le tenant ; seules les ressources qui lui appartiennent sont retournées. Le websiteId est un identifiant public, déjà présent dans le snippet de widget publié sur votre site.

Votre websiteId est disponible dans le backoffice Ekoo sous Paramètres → Site, ou via l'attribut data-ekoo de toute page où le widget est installé.

Récupérer un produit

Retourne un produit avec sa configuration de widget et ses avis audio actifs.

1GET /ws/websites/{websiteId}/products/{productRef}

Paramètres de chemin

NomTypeDescription
websiteIdstring (UUID)Identifiant de votre site Ekoo (même valeur que data-ekoo).
productRefstringRéférence produit telle que configurée dans le backoffice. Sensible à la casse.

Paramètres de requête

NomTypeRequisDescription
localestringOptionnelCode de locale BCP-47 (ex. "fr", "en"). Filtre les avis sur la locale demandée ; renvoie tous les avis si aucune correspondance.
typestringOptionnelType de widget. Vaut "standalone" par défaut.
variantstringOptionnelRéférence stable d'une configuration widget. Le produit et ses audios restent résolus par productRef ; seul l'habillage change. Compat legacy: si aucune ref ne correspond, le backend essaie encore un match par nom.

Utilisez variant pour réutiliser le même produit avec plusieurs habillages de widget sans dupliquer ses audios ni ses contributeurs. Le productRef continue de porter toute la donnée métier; variant ne sélectionne que la configuration visuelle.

Exemples de code

curl
bash
1curl -s "https://app.ekoo.co/ws/websites/{websiteId}/products/{productRef}?locale=fr&variant=homepage"
Node.js / Bun / Deno
typescript
1// Node.js 18+ / Bun / Deno — fetch global
2const websiteId = process.env.EKOO_WEBSITE_ID!
3const productRef = 'SKU-1234'
4
5const res = await fetch(
6 `https://app.ekoo.co/ws/websites/${websiteId}/products/${productRef}?locale=fr&variant=homepage`,
7 { headers: { Accept: 'application/json' } }
8)
9
10if (!res.ok) throw new Error(`Ekoo API ${res.status}`)
11const product = await res.json()
Python
python
1# Python 3.8+ — requests
2import os, requests
3
4website_id = os.environ["EKOO_WEBSITE_ID"]
5product_ref = "SKU-1234"
6
7res = requests.get(
8 f"https://app.ekoo.co/ws/websites/{website_id}/products/{product_ref}",
9 params={"locale": "fr", "variant": "homepage"},
10 timeout=5,
11)
12res.raise_for_status()
13product = res.json()

Exemple de réponse

200 OK
json
1{
2 "id": "9b0c2f3e-8e4d-4d9b-9c8a-0f3a6b7e1d22",
3 "external_id": "SKU-1234",
4 "name": "Eau de parfum 50ml",
5 "audio_url": "https://cdn.ekoo.co/audios/...",
6 "website": {
7 "id": "f7c1...",
8 "name": "Acme Cosmetics"
9 },
10 "config": { /* configuration du widget */ },
11 "experiment": { /* expérimentation A/B éventuelle */ },
12 "reviews": [
13 {
14 "id": "5d2a...",
15 "type": "review",
16 "label": "Marie, 34 ans",
17 "rating": 5,
18 "audio_url": "https://cdn.ekoo.co/audios/abc.mp3",
19 "is_active": true,
20 "is_default": true,
21 "transcript": "J'ai acheté ce parfum pour ma femme...",
22 "user": {
23 "id": "u_8f...",
24 "firstname": "Marie",
25 "lastname": "D.",
26 "image_url": "https://cdn.ekoo.co/avatars/marie.jpg"
27 }
28 }
29 ]
30}

Champs de la réponse

ChampTypeDescription
idstring (UUID)Identifiant interne du produit.
external_idstringRéférence produit (identique à productRef).
namestringNom du produit configuré dans le backoffice.
audio_urlstringURL audio par défaut (champ de commodité ; généralement égal à reviews[0].audio_url).
websiteobjectSite propriétaire (id, name).
configobjectConfiguration du widget résolue (thème, textes CTA, animation, etc.).
experimentobject | undefinedPrésent uniquement si une expérimentation A/B est en cours sur ce produit.
reviews[]arrayAvis et témoignages audio actifs rattachés au produit.
reviews[].type"review" | "testimony"Indique si l'audio est un avis produit ou un témoignage.
reviews[].ratingnumber (0–5)Note en étoiles, le cas échéant.
reviews[].audio_urlstringURL du fichier audio (mp3).
reviews[].transcriptstringTranscription textuelle de l'audio.
reviews[].is_defaultbooleanTrue pour l'avis affiché par défaut par le widget.
reviews[].is_activebooleanFalse pour les avis désactivés ou supprimés en soft-delete.
reviews[].userobjectMétadonnées de l'auteur (firstname, lastname, image_url, description).

Types TypeScript

Ajoutez les déclarations suivantes à votre projet pour un typage complet des réponses. Elles couvrent à la fois le endpoint produit unique et le endpoint liste.

ekoo-types.ts
typescript
1// À copier dans votre projet — typage complet des réponses
2
3export type EkooReviewType = 'review' | 'testimony'
4
5export interface EkooReviewUser {
6 id: string
7 firstname?: string
8 lastname?: string
9 email?: string
10 image_url?: string
11 description?: string
12}
13
14export interface EkooReview {
15 id: string
16 type: EkooReviewType
17 label?: string
18 rating?: number
19 audio_url?: string
20 is_active: boolean
21 is_default: boolean
22 transcript?: string
23 user: EkooReviewUser
24}
25
26export interface EkooExperimentVariation {
27 id: string
28 name: string
29 widgetEnabled: boolean
30 widgetConfigId: string | null
31 audioSelection: string
32 audioId: string | null
33 audioPosition: number | null
34 audioIdsByLocale?: Record<string, string>
35 allocation: number
36 position: number
37 config?: unknown
38}
39
40export interface EkooExperiment {
41 id: string
42 name: string
43 variations: EkooExperimentVariation[]
44}
45
46export interface EkooProduct {
47 id: string
48 external_id: string
49 name?: string
50 audio_url?: string
51 website: { id: string; name?: string }
52 config?: Record<string, unknown>
53 experiment?: EkooExperiment
54 reviews: EkooReview[]
55}
56
57export interface EkooProductList {
58 limit: number
59 offset: number
60 count_item: number
61 items: EkooProduct[]
62}

Lister les produits

Retourne une liste paginée des produits d'un site. Chaque élément a la même structure que le endpoint produit unique.

1GET /ws/websites/{websiteId}/products

Paramètres de requête

NomTypeRequisDescription
limitintegerOptionnelTaille de page. Vaut 50 par défaut. Toute valeur ≤ 0 est ramenée à 50.
offsetintegerOptionnelOffset des résultats. Vaut 0 par défaut.
withoutstringOptionnelListe de références produit (séparées par des virgules) à exclure de la réponse.

Exemple de requête

curl
bash
1curl -s "https://app.ekoo.co/ws/websites/{websiteId}/products?limit=50&offset=0"

Exemple de réponse

200 OK
json
1{
2 "limit": 50,
3 "offset": 0,
4 "count_item": 124,
5 "items": [
6 { /* même format qu'un produit unique */ }
7 ]
8}

Champs de la réponse

ChampTypeDescription
limitintegerÉcho de la taille de page demandée.
offsetintegerÉcho de l'offset demandé.
count_itemintegerNombre total de produits correspondant à la requête (toutes pages confondues).
items[]arrayPage de produits. Chaque élément a la même structure que la réponse produit unique.

Pour paginer, incrémentez offset de limit jusqu'à ce que offset + items.length >= count_item.

Exemple de rendu côté serveur (Next.js)

Récupérez le produit côté serveur, rendez les champs nécessaires (note, transcription, auteur, mise en page personnalisée) dans votre HTML, puis laissez le widget Ekoo s'hydrater par-dessus une fois chargé. Le pattern est identique dans Nuxt (useFetch), SvelteKit (+page.server.ts), Astro, Remix, ou tout backend capable d'émettre une requête HTTP.

app/products/[ref]/page.tsx
tsx
1// app/products/[ref]/page.tsx — Next.js App Router (Server Component)
2import { notFound } from 'next/navigation'
3import type { EkooProduct } from '@/lib/ekoo-types'
4
5async function fetchEkooProduct(ref: string, locale: string) {
6 const url = `https://app.ekoo.co/ws/websites/${process.env.EKOO_WEBSITE_ID}/products/${ref}?locale=${locale}&variant=homepage`
7 const res = await fetch(url, { next: { revalidate: 300 } })
8 if (!res.ok) return null
9 return (await res.json()) as EkooProduct
10}
11
12export default async function ProductPage({
13 params
14}: { params: Promise<{ ref: string }> }) {
15 const { ref } = await params
16 const product = await fetchEkooProduct(ref, 'fr')
17 if (!product) notFound()
18
19 const primary = product.reviews.find((r) => r.is_default && r.is_active)
20
21 return (
22 <article>
23 {primary?.transcript && (
24 <section aria-label="Transcription de l'avis audio">
25 <h2>Ce que disent nos clients</h2>
26 <blockquote>{primary.transcript}</blockquote>
27 <cite>{primary.user.firstname} {primary.user.lastname}</cite>
28 </section>
29 )}
30
31 {/* Le widget Ekoo s'hydrate côté client et prend le relais */}
32 <ekoo-widget
33 data-ekoo={process.env.EKOO_WEBSITE_ID}
34 data-ekoo-product-id={ref}
35 data-ekoo-locale="fr"
36 data-ekoo-variant="homepage"
37 />
38 </article>
39 )
40}
ℹ️

CORS

Toutes les origines sont autorisées. L'API peut être appelée depuis un navigateur ou depuis un serveur ; l'appel côté serveur reste généralement préférable : les réponses peuvent être mises en cache, le payload ne transite pas par le réseau de l'utilisateur final, et un aller-retour supplémentaire au premier rendu est évité.

Cache & rate limits

Les réponses sont mises en cache au niveau de l'edge Ekoo et invalidées automatiquement lors de la publication de contenu depuis le backoffice. Nous recommandons d'ajouter un cache court côté client (par exemple revalidate: 300 en Next.js, s-maxage sur un CDN) afin d'absorber les pics de trafic et de mitiger les incidents en amont.

Lorsqu'un contenu doit être servi avant la prochaine revalidation, purgez la page concernée sur votre CDN ; l'edge Ekoo servira déjà le payload mis à jour.

Aucun quota par clé n'est appliqué. Les schémas de trafic abusifs — polling haute fréquence non caché depuis une seule IP, par exemple — peuvent être throttlés au niveau de l'edge. Pour un volume de requêtes élevé soutenu, contactez le support afin que la capacité soit provisionnée en conséquence.

Erreurs

StatutSignificationQuand
200OKRessource trouvée et retournée.
404Not foundwebsiteId ou productRef inconnu, ou produit désactivé.
500Server errorErreur base de données ou en amont. Réessayer avec backoff exponentiel est sûr.

Les réponses d'erreur partagent la même structure :

1{
2 "type": "ERR_NOT_FOUND",
3 "message": "Product not found"
4}

Le champ type est un code stable, lisible par machine. Le champ message est destiné aux humains et peut évoluer ; la logique cliente doit s'appuyer sur type, et non sur message.

Versioning & stabilité

Ces endpoints alimentent le widget Ekoo public et sont maintenus comme un contrat stable. De nouveaux champs peuvent être ajoutés aux réponses sans préavis ; les implémentations clientes doivent ignorer les champs inconnus. Les champs existants ne sont ni renommés ni supprimés sans plan de migration documenté et fenêtre de dépréciation.

Les changements cassants, lorsqu'ils sont inévitables, sont publiés sous un nouveau préfixe de chemin ; la version précédente reste disponible pendant au moins six mois.

Ressources liées

API HTTP Ekoo — Documentation — Ekoo