API HTTP

Récupérez vos produits, leur configuration de widget et leurs contenus audio depuis une API publique en lecture seule. Toutes les réponses sont au format JSON.

URL de base
https://app.ekoo.co
Authentification
Aucune
Format
JSON
Accès
Public · lecture seule

Avant de commencer

Aucune authentification n'est requise. Le seul identifiant nécessaire est celui de votre site.

curl -s "https://app.ekoo.co/ws/websites/{websiteId}/products?limit=5" \
-H "Accept: application/json"

Le websiteId est un identifiant public disponible sous Paramètres → Site dans le backoffice, ou dans l'attribut data-ekoo du widget déjà installé sur vos pages.

Cas d'usage typiques

Rendre les données produit et les transcriptions côté serveur
SEO
Afficher les contenus audio dans une application mobile
Apps
Générer des pages statiques ou synchroniser un catalogue externe
Batch

Récupérer un produit

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

GET/ws/websites/{websiteId}/products/{productRef}
curl -s "https://app.ekoo.co/ws/websites/{websiteId}/products/{productRef}" \
-H "Accept: application/json"

Requête

GET https://app.ekoo.co/ws/websites/{websiteId}/products/{productRef}
Champ requis : websiteId, productRef
Exemple de réponseStructure JSON complète, sans exécuter la requête
{
"id": "9b0c2f3e-8e4d-4d9b-9c8a-0f3a6b7e1d22",
"external_id": "SKU-1234",
"name": "Eau de parfum 50ml",
"seo_name": "Eau de parfum 50ml — Acme",
"seo_description": "Les avis audio de nos clients.",
"audio_url": "https://cdn.ekoo.co/audios/...",
"website": {
"id": "f7c1...",
"name": "Acme Cosmetics"
},
"features": { "ctaAnalytics": true },
"config": { /* configuration du widget */ },
"experiment": { /* expérimentation A/B éventuelle */ },
"reviews": [
{
"id": "5d2a...",
"type": "review",
"label": "Marie, 34 ans",
"rating": 5,
"audio_url": "https://cdn.ekoo.co/audios/abc.mp3",
"is_active": true,
"is_default": true,
"created_at": "2026-03-14T09:12:00.000Z",
"transcript": "J'ai acheté ce parfum pour ma femme...",
"user": {
"id": "u_8f...",
"firstname": "Marie",
"lastname": "D.",
"image_url": "https://cdn.ekoo.co/avatars/marie.jpg"
}
}
]
}

Paramètres de chemin

websiteIdstring (UUID)
Identifiant de votre site Ekoo, identique à la valeur de data-ekoo.
productRefstring
Référence produit telle que configurée dans le backoffice. Sensible à la casse.

Paramètres de requête

localestringoptionnel
Code de locale BCP-47 (ex. « fr », « en »). Filtre strictement les avis sur cette locale. Si le paramètre est omis, toutes les locales sont retournées.
typestringoptionnel
Type de widget. Vaut « standalone » par défaut.
variantstringoptionnel
Référence stable d'une configuration widget. Le produit et ses audios restent résolus par productRef ; seul l'habillage change. Compatibilité legacy : si aucune référence ne correspond, le backend tente encore une correspondance par nom.
ℹ️

Choisir entre productRef et variant

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.

Champs de la réponse

idstring (UUID)
Identifiant interne du produit.
external_idstring
Référence produit, identique à productRef.
namestring
Nom du produit configuré dans le backoffice.
seo_namestring
Nom optimisé pour le référencement, si renseigné dans le backoffice.
seo_descriptionstring
Description optimisée pour le référencement, si renseignée.
audio_urlstring
URL audio par défaut. Champ de commodité, généralement égal à reviews[0].audio_url.
websiteobject
Site propriétaire (id, name).
featuresobject
Options actives pour ce site, lues par le widget. Contient aujourd’hui ctaAnalytics.
configobject
Configuration du widget résolue : thème, textes du CTA, animation, etc.
experimentobject | undefined
Présent uniquement si une expérimentation A/B est en cours sur ce produit.
reviews[]array
Avis 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_urlstring
URL du fichier audio (mp3).
reviews[].transcriptstring
Transcription textuelle de l'audio.
reviews[].is_defaultboolean
True pour l'avis affiché par défaut par le widget.
reviews[].is_activeboolean
False pour les avis désactivés ou supprimés en soft-delete.
reviews[].created_atstring (ISO 8601)
Date de création de l’audio.
reviews[].userobject
Métadonnées de l'auteur : firstname, lastname, image_url, description.
Types TypeScriptDéclarations couvrant les deux endpoints
// À copier dans votre projet — typage complet des réponses
export type EkooReviewType = 'review' | 'testimony'
export interface EkooReviewUser {
id: string
firstname?: string
lastname?: string
image_url?: string
description?: string
}
export interface EkooReview {
id: string
type: EkooReviewType
label?: string
rating?: number
audio_url?: string
is_active: boolean
is_default: boolean
transcript?: string
created_at?: string
user: EkooReviewUser
}
export interface EkooExperimentVariation {
id: string
name: string
widgetEnabled: boolean
widgetConfigId: string | null
audioSelection: string
audioId: string | null
audioPosition: number | null
audioIdsByLocale?: Record<string, string>
allocation: number
position: number
config?: unknown
}
export interface EkooExperiment {
id: string
name: string
variations: EkooExperimentVariation[]
}
export interface EkooProduct {
id: string
external_id: string
name?: string
seo_name?: string
seo_description?: string
audio_url?: string
website: { id: string; name?: string }
features?: { ctaAnalytics: boolean }
config?: Record<string, unknown>
experiment?: EkooExperiment
reviews: EkooReview[]
}
export interface EkooProductList {
limit: number
offset: number
count_item: number
items: EkooProduct[]
}

Lister les produits

Retourne une liste paginée des produits d’un site. Les filtres sont appliqués avant la pagination et le calcul du total.

GET/ws/websites/{websiteId}/products
curl -s "https://app.ekoo.co/ws/websites/{websiteId}/products?limit=5&locale=fr" \
-H "Accept: application/json"

Requête

GET https://app.ekoo.co/ws/websites/{websiteId}/products?limit=5&locale=fr
Champ requis : websiteId
Exemple de réponseStructure JSON complète, sans exécuter la requête
{
"limit": 50,
"offset": 0,
"count_item": 124,
"items": [
{ /* même format qu'un produit unique */ }
]
}

Paramètres de requête

limitintegeroptionnel
Taille de page. Vaut 50 par défaut. Toute valeur ≤ 0 est ramenée à 50.
offsetintegeroptionnel
Offset des résultats. Vaut 0 par défaut.
withoutstringoptionnel
Valeur spéciale « inactive » pour exclure les contenus audio inactifs de la réponse.
localestringoptionnel
Locale des contenus audio recherchés et retournés. Vaut « fr » par défaut.
withAudiobooleanoptionnel
Filtre les produits selon la présence d’au moins un fichier audio retournable dans la locale demandée : URL non vide, validé et non en attente de validation.
publishedbooleanoptionnel
Filtre les produits selon la présence d’au moins un audio publié : URL non vide, actif, validé et non en attente de validation.

Filtres audio courants

Produits ayant au moins un audio validé dans la locale demandée, actif ou non
withAudio=true
Produits ayant au moins un audio publié et utilisable par le widget
withAudio=true&published=true
Produits ayant au moins un audio retournable, mais aucun audio publié
withAudio=true&published=false
Produits sans aucun audio retournable dans la locale demandée
withAudio=false
ℹ️

Filtrage et pagination

Les filtres sont appliqués avant limit, offset et le calcul de count_item. Ne filtrez donc pas les résultats côté client à partir de audio_url. Utilisé seul, published=false inclut également les produits sans audio ; combinez-le avec withAudio=true pour obtenir uniquement les produits possédant un audio retournable, mais non publié.

Champs de la réponse

limitinteger
Écho de la taille de page demandée.
offsetinteger
Écho de l'offset demandé.
count_iteminteger
Nombre total de produits correspondant à la requête, toutes pages confondues.
items[]array
Page de produits. Chaque élément a la même structure que la réponse produit unique.

Rendu côté serveur

Rendez les contenus Ekoo dans votre HTML, puis laissez le widget s’hydrater par-dessus.

// app/products/[ref]/page.tsx — Next.js App Router (Server Component)
import { notFound } from 'next/navigation'
import type { EkooProduct } from '@/lib/ekoo-types'
async function fetchEkooProduct(ref: string, locale: string) {
const url = `https://app.ekoo.co/ws/websites/${process.env.EKOO_WEBSITE_ID}/products/${ref}?locale=${locale}&variant=homepage`
const res = await fetch(url, { next: { revalidate: 300 } })
if (!res.ok) return null
return (await res.json()) as EkooProduct
}
export default async function ProductPage({
params
}: { params: Promise<{ ref: string }> }) {
const { ref } = await params
const product = await fetchEkooProduct(ref, 'fr')
if (!product) notFound()
const primary = product.reviews.find((r) => r.is_default && r.is_active)
return (
<article>
{primary?.transcript && (
<section aria-label="Transcription de l'avis audio">
<h2>Ce que disent nos clients</h2>
<blockquote>{primary.transcript}</blockquote>
<cite>{primary.user.firstname} {primary.user.lastname}</cite>
</section>
)}
{/* Le widget Ekoo s'hydrate côté client et prend le relais */}
<ekoo-widget
data-ekoo={process.env.EKOO_WEBSITE_ID}
data-ekoo-product-id={ref}
data-ekoo-locale="fr"
data-ekoo-variant="homepage"
/>
</article>
)
}

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.

ℹ️

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é.

Erreurs

Toutes les réponses d’erreur partagent la même structure.

{
"type": "ERR_DB_MISSING",
"message": "error-message-not-found"
}
⚠️

Testez le corps, pas seulement le statut

Sur GET /products/{productRef}, une erreur est renvoyée avec le statut 200 : c’est un comportement historique demandé par des intégrateurs. Un produit introuvable ne se détecte donc qu’à la présence du champ type dans la réponse.

Codes de statut

200OK
Ressource trouvée. Sur le produit unique, également renvoyé en cas d’erreur : vérifiez le champ type.
400Bad request
Paramètre de requête invalide, sur l’endpoint liste.
500Server error
Erreur base de données ou en amont. Réessayer avec un backoff exponentiel est sûr.

Corps de l’erreur

typestring
Code stable, lisible par machine. La logique cliente doit s’appuyer sur ce champ. Valeurs courantes : ERR_DB_MISSING (produit ou site inconnu), ERR_INPUT_VALIDATION (paramètre invalide), ERR_DB_READ (erreur base de données).
messagestring
Message destiné aux humains. Peut évoluer sans préavis.

Cache et limites

Ce sur quoi vous pouvez vous appuyer en production.

Cache

Les réponses produit individuelles peuvent être mises en cache par Ekoo et ce cache est invalidé lors des mises à jour depuis le backoffice. La réponse de liste ne doit pas être considérée comme mise en cache. 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 également la page concernée sur votre propre CDN.

Limites de débit

L'API n'utilise pas de clé d'authentification et ne publie actuellement pas de quota contractuel. Évitez le polling haute fréquence non mis en cache. Pour un volume de requêtes élevé et soutenu, contactez le support afin que la capacité soit provisionnée en conséquence.