Ekenno Survey API
Créez, administrez, intégrez et analysez des sondages interactifs riches au sein de vos applications web, mobiles ou intranets grâce à notre suite d'APIs REST ultra-rapide et sécurisée.
https://docs.ekenno.com/api/
Ce que vous pouvez construire avec Kenno Survey
Créateur de Sondages Dynamique
Concevez des formulaires avec 32 types de questions : Likert, NPS, Matrice, validation stricte, upload de fichiers et conditions logiques.
Ciblage & Screening Anti-fraude
Filtrez les participants selon leur pays, âge, compétences, ou disqualifiez immédiatement avec des questions de screening.
Embed iFrame, Modal & SDK JS
Embarquez vos sondages sur vos sites web en iframe responsive ou modal popup avec communication bidirectionnelle postMessage.
Analytics & Export CSV / JSON
Récupérez les distributions de réponses agrégées, scores de qualité de réponse, ou exportez l'intégralité brute en CSV ou JSON.
Parcours d'intégration en 3 étapes
Live ou Test avec les permissions souhaitées (surveys:read, surveys:write, etc.).curl -X GET "https://docs.ekenno.com/api/surveys/categories/" \
-H "Authorization: Bearer ek_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Accept: application/json"
import requests
url = "https://docs.ekenno.com/api/surveys/categories/"
headers = {
"Authorization": "Bearer ek_live_xxxxxxxxxxxxxxxxxxxxxxxx",
"Accept": "application/json"
}
response = requests.get(url, headers=headers)
categories = response.json()
print("Catégories disponibles:", categories)
const response = await fetch("https://docs.ekenno.com/api/surveys/categories/", {
headers: {
"Authorization": "Bearer ek_live_xxxxxxxxxxxxxxxxxxxxxxxx",
"Accept": "application/json"
}
});
const categories = await response.json();
console.log("Catégories:", categories);
[
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Technologie & Innovation",
"slug": "technologie",
"icon": "bx-laptop",
"color": "#3b82f6"
},
{
"id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
"name": "Consommation & Habitudes",
"slug": "consommation",
"icon": "bx-shopping-bag",
"color": "#10b981"
}
]
Démarrage Rapide (5 minutes)
Suivez ce guide étape par étape pour authentifier votre application, créer un sondage complet et publier votre premier questionnaire.
Rendez-vous dans la section Manage > API Keys pour générer un jeton. Notez le préfixe :
ek_test_...pour vos environnements de staging et tests locaux.ek_live_...pour vos environnements de production.
Envoyez une requête POST /api/surveys/manage/ avec les métadonnées de base :
Ajoutez des questions via POST /api/surveys/manage/{id}/questions/ puis passez le statut en ligne via POST /api/surveys/manage/{id}/publish/.
curl -X POST "https://docs.ekenno.com/api/surveys/manage/" \
-H "Authorization: Bearer ek_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"title": "Enquête Satisfaction Client Q4",
"description": "Recueil des retours d utilisation",
"survey_type": "internal",
"estimated_time": 5,
"max_responses": 500,
"allow_anonymous": true
}'
{
"id": "7f8e9d0a-2b1c-4e3f-9a8b-1c2d3e4f5a6b",
"title": "Enquête Satisfaction Client Q4",
"status": "draft",
"version_number": 1,
"created_at": "2026-11-09T14:32:00Z"
}
Authentification par Clé API
L'API Ekenno utilise des jetons secrets d'API standards passés via l'en-tête HTTP Authorization: Bearer <votre_cle>.
Règles de sécurité des clés
Vos clés brutes ne sont jamais stockées en clair. Kenno enregistre uniquement un condensé cryptographique SHA-256 irréversible.
Le secret complet n'est affiché qu'une seule fois lors de la création de la clé. Conservez-le dans vos variables d'environnement (.env).
Permissions & Portées (Scopes)
| Portée (Scope) | Description | Endpoints autorisés |
|---|---|---|
| surveys:read | Lecture des sondages et catégories | GET /api/surveys/manage/ |
| surveys:write | Création, modification et publication | POST, PUT, PATCH /api/surveys/manage/ |
| surveys:delete | Clôture et suppression de sondages | DELETE /api/surveys/manage/{id}/ |
| responses:read | Lecture et export des réponses collectées | GET /api/surveys/manage/{id}/responses/ |
| analytics:read | Accès aux agrégats statistiques et métriques | GET /api/surveys/manage/{id}/results/ |
# Exemple avec curl
curl -H "Authorization: Bearer ek_live_a1b2c3d4e5f6..." \
"https://docs.ekenno.com/api/surveys/manage/"
Si la clé est révoquée, expirée ou invalide, l'API renvoie immédiatement :
{
"detail": "Invalid or revoked API key."
}
Les 32 Types de Questions
L'écosystème Kenno Survey propose 32 composants interactifs exhaustifs, regroupés en 10 catégories fonctionnelles. Chaque question dispose de ses propres options de validation, d'étiquetage et de règles de conditionnement logique.
Short Text, Paragraph, Email, Téléphone, URL / Site Web.
Radio buttons, Menu déroulant (Dropdown), Oui / Non.
Cases à cocher (Checkboxes), Liste multi-sélection, Tags cliquables.
Étoiles (1–5), Échelle numérique (1–10), NPS (0–10), Likert, Curseur (Slider).
Date, Date & Heure complète, Heure seule, Date de naissance.
Fichier générique (PDF, docx), Upload d'image avec aperçu.
Nom complet (Prénom + Nom), Genre avec options personnalisées.
Pays (sélecteur ISO), Ville, Adresse complète structurée.
Nombre (entier/décimal), Matrice grille à double entrée, Consentement RGPD.
En-tête de section (Séparateur), Bloc d'instructions & description.
Référentiel Technique des 32 Types de Questions
POST /api/surveys/manage/{id}/questions/| # | Code API & Alias | Catégorie & Libellé | Champs Spécifiques | Format de Réponse |
|---|---|---|---|---|
| 1 |
text
alias: short_text
|
Texte court
A. Texte · Short Text
|
placeholder, min_length, max_length, is_required, help_text | string (ex: " Paiement mobile ") |
| 2 |
textarea
alias: long_text, paragraph
|
Paragraphe
A. Texte · Long Text / Paragraph
|
placeholder, min_length, max_length, is_required, help_text | string multiligne |
| 3 |
email
|
Adresse Email
A. Texte · Email
|
placeholder, is_required, help_text | string (ex: "client@domaine.com") |
| 4 |
phone
|
Téléphone
A. Texte · Phone Number
|
placeholder, is_required, help_text | string (ex: "+2250700000000") |
| 5 |
url
|
Site Web / URL
A. Texte · Website URL
|
placeholder, is_required, help_text | string (ex: "https://ekenno.com") |
| 6 |
radio
alias: single_choice
|
Boutons Radio
B. Choix Unique · Single Choice (Radio)
|
options (min 2), is_screening, disqualify_answers, is_required | string (ex: "Tous les jours") |
| 7 |
dropdown
alias: select
|
Menu Déroulant
B. Choix Unique · Dropdown / Select
|
options (min 2), is_screening, disqualify_answers, is_required | string (ex: "Option B") |
| 8 |
yesno
|
Oui / Non
B. Choix Unique · Yes / No
|
is_screening, disqualify_answers, is_required | string ("Oui" | "Non") |
| 9 |
checkbox
alias: multiple_choice
|
Cases à cocher
C. Choix Multiple · Multiple Choice (Checkboxes)
|
options (min 2), is_required, help_text | array<string> (ex: ["Wave", "Orange Money"]) |
| 10 |
multi_dropdown
alias: multi_select
|
Liste multi-sélection
C. Choix Multiple · Multi-select Dropdown
|
options (min 2), is_required, help_text | array<string> |
| 11 |
tags
|
Sélection par Tags
C. Choix Multiple · Tag Selection
|
options (min 2), is_required, help_text | array<string> |
| 12 |
stars
alias: rating, star_rating
|
Évaluation Étoiles (1–5)
D. Évaluation · Star Rating
|
scale_min (1), scale_max (5), scale_labels, is_required | integer (1..5) |
| 13 |
scale
|
Échelle numérique (1–10)
D. Évaluation · Rating Scale
|
scale_min, scale_max, scale_labels, is_required | integer (scale_min..scale_max) |
| 14 |
nps
|
Net Promoter Score (0–10)
D. Évaluation · Net Promoter Score (NPS)
|
scale_min (0), scale_max (10), scale_labels, is_required | integer (0..10) |
| 15 |
likert
|
Échelle de Likert
D. Évaluation · Likert Scale
|
options, scale_labels, is_required | string ou integer |
| 16 |
slider
|
Curseur (Slider)
D. Évaluation · Slider
|
scale_min, scale_max, number_step, is_required | number |
| 17 |
date
|
Date
E. Date & Heure · Date
|
date_min, date_max, is_required | string ("YYYY-MM-DD") |
| 18 |
datetime
alias: date_time
|
Date & Heure
E. Date & Heure · Date & Time
|
date_min, date_max, is_required | string ("YYYY-MM-DDTHH:MM") |
| 19 |
time
|
Heure
E. Date & Heure · Time
|
is_required, help_text | string ("HH:MM") |
| 20 |
birthdate
alias: birth_date, dob
|
Date de naissance
E. Date & Heure · Date of Birth
|
date_min, date_max, is_required | string ("YYYY-MM-DD") |
| 21 |
file
alias: file_upload
|
Fichier (Document)
F. Fichiers & Médias · File Upload
|
allowed_file_types, max_file_size_mb, is_required | string (URL/nom du fichier) |
| 22 |
image_upload
alias: image
|
Image / Photo
F. Fichiers & Médias · Image Upload
|
allowed_file_types, max_file_size_mb, is_required | string (URL/data de l'image) |
| 23 |
fullname
alias: full_name, name
|
Nom complet
G. Identité · Full Name
|
placeholder, is_required, help_text | string (ex: "Koffi Kouadio") |
| 24 |
gender
|
Genre
G. Identité · Gender
|
gender_options, is_required | string (ex: "Femme") |
| 25 |
country
|
Pays
H. Localisation · Country
|
is_required, help_text | string (ex: "CI" ou "Côte d'Ivoire") |
| 26 |
city
|
Ville
H. Localisation · City
|
placeholder, is_required | string (ex: "Abidjan") |
| 27 |
address
|
Adresse complète
H. Localisation · Full Address
|
placeholder, is_required | string |
| 28 |
number
alias: integer, decimal
|
Nombre
I. Avancé · Number
|
number_min, number_max, number_step, is_required | number (ex: 42) |
| 29 |
matrix_single
alias: matrix, matrix_multi
|
Grille Matrice
I. Avancé · Matrix Grid
|
matrix_rows, matrix_columns, is_required | object {"Ligne 1": "Colonne A", ...} |
| 30 |
consent
|
Consentement RGPD
I. Avancé · Consent / Agreement
|
consent_text, consent_link, is_required | boolean (true) |
| 31 |
section
|
Titre de Section
J. Structure · Section Header
|
question_text, help_text, order | null (élément structurel) |
| 32 |
description
alias: info, banner
|
Bloc de Description
J. Structure · Description / Instruction
|
question_text, help_text, order | null (élément structurel) |
{
"question_text": "Quelle est votre appréciation globale ?",
"question_type": "stars",
"order": 1,
"is_required": true,
"scale_min": 1,
"scale_max": 5,
"scale_labels": {
"1": "Très insatisfait",
"5": "Très satisfait"
},
"is_screening": false
}
Questions Textuelles (5 Types)
Champs de saisie libre avec validation automatique du format (expression régulière, taille minimale/maximale, format email ou URL).
Saisie sur une seule ligne. Idéal pour un titre, une marque, ou un mot-clé.
placeholder, min_length, max_length.Champ multiligne avec redimensionnement automatique. Idéal pour les commentaires libres ou avis détaillés.
Validation stricte RFC 5322 du format d'adresse email avec nettoyage automatique des espaces.
Validation de numéro téléphonique international au format E.164 (+33..., +225...).
Validation de lien hypertexte valide commençant obligatoirement par http:// ou https://.
{
"question_text": "Veuillez renseigner votre email professionnel",
"question_type": "email",
"placeholder": "nom@entreprise.com",
"is_required": true,
"help_text": "Nous ne partagerons jamais votre email."
}
Questions à Choix Unique (3 Types)
L'utilisateur ne peut sélectionner qu'une seule réponse parmi une liste prédéfinie.
Affichage vertical de tous les choix sous forme de boutons radio.
options: ["Option 1", "Option 2"] (minimum 2).Menu sélecteur déroulant idéal pour les listes longues (> 5 options) afin d'économiser l'espace écran.
Boutons binaires Oui / Non à fort contraste pour les questions directes de qualification.
{
"question_text": "À quelle fréquence utilisez-vous notre service ?",
"question_type": "radio",
"is_required": true,
"options": [
"Tous les jours",
"Plusieurs fois par semaine",
"Une fois par mois",
"Rarement"
]
}
Questions à Choix Multiple (3 Types)
Permet de cocher un ou plusieurs éléments dans une liste avec restrictions optionnelles de sélection min/max.
Cases à cocher classiques. La réponse enregistrée est une liste JSON : ["Option A", "Option B"].
Menu déroulant permettant d'ajouter et de retirer des éléments sous forme de badges.
Nuage de pilules cliquables interactives, très intuitif sur mobile et écrans tactiles.
{
"question_text": "Quels réseaux sociaux utilisez-vous ?",
"question_type": "checkbox",
"is_required": true,
"options": [
"LinkedIn",
"Twitter / X",
"Instagram",
"TikTok",
"YouTube"
]
}
Questions d'Évaluation (5 Types)
Idéal pour mesurer la satisfaction client (CSAT), le Net Promoter Score (NPS) et l'accord sur échelle de Likert.
Composant 5 étoiles interactif. Enregistre une valeur entière de 1 à 5.
Échelle horizontale avec bornes configurables (scale_min: 1, scale_max: 10) et étiquettes textuelles aux extrémités.
Calcul automatique des Détracteurs (0-6), Passifs (7-8) et Promoteurs (9-10) dans les analytics.
Évaluation d'accord de « Tout à fait en désaccord » à « Tout à fait d'accord » (5 ou 7 points).
Glissière continue avec pas personnalisable (number_step) et valeur affichée en direct.
{
"question_text": "Recommanderiez-vous Ekenno à un collègue ?",
"question_type": "nps",
"scale_min": 0,
"scale_max": 10,
"scale_labels": {
"0": "Pas du tout probable",
"10": "Extrêmement probable"
},
"is_required": true
}
Questions Date & Heure (4 Types)
Sélecteurs normalisés au standard ISO 8601 pour stocker précisément les dates, horaires et tranches d'âge.
Sélecteur de calendrier. Format retourné : YYYY-MM-DD.
Date avec heure et minute. Format retourné : YYYY-MM-DDTHH:MM:SSZ.
Heure au format 24 heures : HH:MM.
Composant 3 sélecteurs (Jour/Mois/Année) avec calcul automatique de l'âge légal pour le ciblage démographique.
{
"question_text": "Quelle est votre date de naissance ?",
"question_type": "birthdate",
"is_required": true,
"date_max": "2008-01-01"
}
Téléversement de Fichiers (2 Types)
Permet aux participants de joindre des documents, justificatifs ou photos via téléversement sécurisé multipart.
Téléversement générique. Propriétés : allowed_file_types: ["pdf", "docx"], max_file_size_mb: 10.
Téléversement de photos avec prévisualisation immédiate côté client (JPG, PNG, WebP).
{
"question_text": "Téléversez votre CV (Format PDF)",
"question_type": "file",
"allowed_file_types": ["pdf"],
"max_file_size_mb": 5,
"is_required": true
}
Questions d'Identité (2 Types)
Champs normalisés pour recueillir l'identité civique du participant.
Saisie combinée prénom et nom avec dissociation automatique.
Choix inclusif personnalisable via gender_options. Défaut : ["Homme", "Femme", "Autre", "Ne souhaite pas préciser"].
{
"question_text": "À quel genre vous identifiez-vous ?",
"question_type": "gender",
"gender_options": ["Femme", "Homme", "Non-binaire", "Ne souhaite pas répondre"],
"is_required": false
}
Questions de Localisation (3 Types)
Ciblage géographique précis : pays de résidence, ville et adresse postale.
Liste complète des pays avec recherche intégrée et codes ISO à 2 lettres.
Champ ville avec autocomplétion des métropoles mondiales.
Formulaire multiligne : Rue, Code Postal, Ville, Pays.
{
"question_text": "Dans quel pays résidez-vous ?",
"question_type": "country",
"is_required": true
}
Composants Avancés (3 Types)
Nombres avec contrôles de limites, grilles matricielles complexes et clauses de consentement légal.
Saisie numérique avec number_min, number_max, et number_step.
Tableau à double entrée avec lignes (matrix_rows) et colonnes (matrix_columns).
Case d'acceptation obligatoire avec texte de consentement et lien hypertexte légal vers la politique de confidentialité.
{
"question_text": "Évaluez ces aspects de notre service :",
"question_type": "matrix_single",
"matrix_rows": ["Rapidité", "Facilité d utilisation", "Support client"],
"matrix_columns": ["Insatisfaisant", "Moyen", "Très bon"],
"is_required": true
}
Composants Structurels (2 Types)
Éléments non-répondables servant à paginer, segmenter et documenter les différentes parties d'un questionnaire.
En-tête visuel séparant les blocs thématiques. N'attend aucune réponse du répondant.
Bannière informative pour donner des consignes avant une série de questions spécifiques.
{
"question_text": "Partie 2 : Votre expérience avec le support",
"question_type": "section",
"help_text": "Ces questions nous aident à améliorer notre réactivité.",
"order": 5
}
Intégration HTML (iFrame)
Intégrez directement le questionnaire au sein de votre site web en copiant une simple balise <iframe> hautement personnalisable.
Paramètres d'URL supportés
| Paramètre | Valeurs | Description |
|---|---|---|
| theme | dark | light | Applique le thème sombre ou clair au sondage. |
| color | Hex (ex: FFD928) | Couleur d'accentuation des boutons et jauges. |
| progress | 1 | 0 | Afficher ou masquer la barre de progression. |
| branding | 1 | 0 | Afficher ou masquer la mention « Propulsé par Kenno ». |
<iframe
src="https://docs.ekenno.com/survey/7f8e9d0a-2b1c/embed/?theme=dark&color=FFD928&progress=1&branding=1"
width="100%"
height="700px"
frameborder="0"
style="border-radius: 16px; border: 1px solid rgba(255,255,255,0.1);"
allow="camera; microphone">
</iframe>
Lien Direct Partageable
Chaque sondage dispose d'un jeton public unique (share_token) retourné par l'API dans public_url, permettant aux participants de répondre directement sans compte préalable lorsque allow_anonymous=true.
https://docs.ekenno.com/s/{share_token}/
https://docs.ekenno.com/i/{invitation_token}/
Idéal pour les campagnes d'emailing, newsletters, SMS WhatsApp ou QR Codes physiques lors d'événements.
<a href="https://docs.ekenno.com/s/ek98a7b6c5d4/"
target="_blank"
rel="noopener noreferrer"
class="kenno-survey-button">
Participer à l'enquête
</a>
Popup Modal (Lightbox)
Affichez le sondage dans une boîte modale superposée au-dessus de votre application lorsqu'un utilisateur clique sur un bouton ou termine une action clé.
<!-- Bouton déclencheur -->
<button onclick="openKennoModal()">Donner mon avis</button>
<!-- Script du Popup Modal -->
<script>
function openKennoModal() {
const overlay = document.createElement('div');
overlay.id = 'kennoModalOverlay';
overlay.style = 'position:fixed;inset:0;background:rgba(0,0,0,0.7);z-index:99999;display:flex;align-items:center;justify-content:center;backdrop-filter:blur(4px);';
overlay.innerHTML = `
<div style="position:relative;width:90%;max-width:680px;height:85vh;background:#101114;border-radius:20px;overflow:hidden;border:1px solid #222;">
<button onclick="document.getElementById('kennoModalOverlay').remove()" style="position:absolute;top:14px;right:16px;background:none;border:none;color:#fff;font-size:24px;cursor:pointer;z-index:10;">×</button>
<iframe src="https://docs.ekenno.com/survey/7f8e9d0a-2b1c/embed/?theme=dark" width="100%" height="100%" frameborder="0"></iframe>
</div>
`;
document.body.appendChild(overlay);
}
</script>
JavaScript SDK & Événements postMessage
Communiquez en temps réel entre votre page hôte et l'iFrame du sondage grâce aux événements window.postMessage émis par static/survey/js/survey.js.
Événements émis par le sondage embarqué
| event.data.type | Déclenchement | Données (Payload) |
|---|---|---|
| kenno_survey_ready | L'iFrame a terminé son initialisation | { type, event: "ready", survey: { id, title }, timestamp } |
| kenno_survey_complete | Le sondage a été soumis et validé | { type, event: "complete", survey: { id, title }, answers, timestamp } |
window.addEventListener("message", (event) => {
const payload = event.data || {};
if (!payload.type) return;
switch (payload.type) {
case "kenno_survey_ready":
console.log("Sondage prêt :", payload.survey.id, payload.survey.title);
break;
case "kenno_survey_complete":
console.log("Réponses validées :", payload.answers);
// Fermez votre modal ou débloquez la récompense utilisateur
const modal = document.getElementById("kennoModalOverlay");
if (modal) modal.remove();
break;
}
});
Catégories de Sondages
Récupérez la liste de toutes les catégories actives pour classer vos sondages et alimenter des filtres de sélection.
Renvoie un tableau d'objets catégories avec leur identifiant UUID, nom, slug, icône et code couleur hexadécimal.
curl -X GET "https://docs.ekenno.com/api/surveys/categories/" \
-H "Authorization: Bearer ek_live_xxxxxxxxxxxxxxxxxxxxxxxx"
[
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Technologie",
"slug": "technologie",
"icon": "bx-laptop",
"color": "#3b82f6"
}
]
Export CSV & JSON des Réponses
Téléchargez l'intégralité des réponses d'un sondage au format brut tabulaire CSV (avec BOM UTF-8 pour Excel) ou sous forme d'arbre d'objets JSON structuré.
Passez le paramètre ?format=json pour obtenir un payload JSON avec métadonnées, ou omettez-le pour télécharger directement le fichier CSV.
# Téléchargement au format JSON
curl -X GET "https://docs.ekenno.com/api/surveys/manage/7f8e9d0a-2b1c/export/?format=json" \
-H "Authorization: Bearer ek_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
-o reponses.json
Codes d'état HTTP & Erreurs
L'API Kenno Survey utilise des codes de réponse HTTP conformes aux standards RFC pour indiquer le succès ou l'échec d'une requête. Toutes les erreurs retournent un corps JSON structuré avec code, message lisible et détails de validation.
Format standard des erreurs (JSON)
Lorsqu'une erreur survient, le serveur répond avec un objet JSON prévisible :
{
"error": true,
"code": "validation_error",
"message": "Les données envoyées sont invalides.",
"details": {
"title": ["Ce champ est obligatoire."],
"category": ["La catégorie spécifiée n'existe pas."]
}
}
| Code HTTP | Signification | Description & Contexte Ekenno |
|---|---|---|
| 200 OK | Succès standard | La requête a réussi (lecture de sondage, liste, analytics, export). |
| 201 Created | Ressource créée | Création d'un sondage (brouillon), ajout d'une question ou génération d'une clé d'API. |
| 400 Bad Request | Validation invalide | Champs requis manquants, format email erroné ou options de question incohérentes. |
| 401 Unauthorized | Clé API manquante ou invalide | Header Authorization: Bearer <key> absent ou clé révoquée. |
| 403 Forbidden | Permissions insuffisantes | La clé n'a pas le scope requis (ex: tentative d'écriture avec clé surveys:read). |
| 404 Not Found | Ressource inexistante | L'ID de sondage, question ou webhook n'existe pas ou n'appartient pas à votre organisation. |
| 429 Too Many Requests | Quota de débit dépassé | Dépassement de la limite de requêtes par minute (RPM) ou par jour (RPD). Vérifiez l'en-tête Retry-After. |
| 500 Server Error | Erreur interne serveur | Incident inattendu. Contactez le support développeur avec l'ID de corrélation de la requête. |
{
"error": true,
"code": "invalid_parameters",
"status": 400,
"message": "Erreur de validation lors de la création du sondage.",
"errors": {
"title": [
"Le titre du sondage ne peut pas être vide."
],
"category": [
"La catégorie spécifiée n'existe pas."
]
}
}
Cycle de Vie d'un Sondage
Un sondage Kenno évolue à travers des statuts stricts garantissant l'intégrité des réponses et le versioning automatique lorsqu'un sondage en production est modifié.
Diagramme d'état des transitions
Édition libre. Aucune réponse acceptée. Questions et critères modifiables.
En ligne et accessible. Collecte de réponses et rétribution en KEN.
Temporairement gelé. Les participants voient un message d'attente.
Terminé (quota atteint ou date dépassée). Analytics et export complets.
Moteur de Versioning Automatique (Audit-Proof)
Dès lors qu'un sondage passe à l'état Active et collecte sa première réponse, toute modification structurelle (ajout/suppression d'une question) génère une nouvelle version brouillon liée (version_number + 1). Cela préserve la validité mathématique des réponses passées.
curl -X PATCH "https://docs.ekenno.com/api/surveys/manage/550e8400-e29b-41d4-a716-446655440000/" -H "Authorization: Bearer ek_live_xxxxxxxxxxxxxxxxxxxxxxxx" -H "Content-Type: application/json" -d '{"status": "active"}'
/api/surveys/manage/
Créer un nouveau sondage
Initialise un nouveau sondage à l'état draft. Vous pourrez ensuite lui ajouter des questions, définir ses critères d'éligibilité et le publier.
Paramètres du corps de requête (JSON)
| Champ | Type | Obligatoire | Description |
|---|---|---|---|
| title | string | Oui | Titre public du sondage affiché aux répondants. |
| description | string | Non | Instructions ou contexte introductif pour les participants. |
| category | UUID | Oui | Identifiant UUID valide d'une catégorie active (obtenue via /categories/). |
| max_responses | integer | Non (optionnel) | Plafond de réponses. Le sondage se clôture automatiquement à ce seuil. |
| allow_anonymous | boolean | Non (défaut: true) | Autoriser la participation sans compte Kenno préalable. |
| collect_email | boolean | Non (défaut: false) | Exiger la saisie d'une adresse email avant de commencer le sondage. |
curl -X POST "https://docs.ekenno.com/api/surveys/manage/" -H "Authorization: Bearer ek_live_xxxxxxxxxxxxxxxxxxxxxxxx" -H "Content-Type: application/json" -d '{
"title": "Enquête Habitudes Mobiles 2026",
"description": "Étude sur les paiements et services financiers",
"category": "550e8400-e29b-41d4-a716-446655440000",
"max_responses": 500,
"allow_anonymous": true,
"collect_email": true
}'
/api/surveys/manage/
Lister vos sondages
Retourne la liste paginée de tous les sondages créés par votre organisation, avec filtres par statut, recherche textuelle et pagination.
Filtres de requête (Query String)
| Paramètre | Type | Exemple | Description |
|---|---|---|---|
| status | string | ?status=active | Filtrer par statut : draft, active, paused, closed. |
| search | string | ?search=finance | Recherche insensible à la casse dans le titre et la description. |
| page | integer | ?page=2 | Numéro de page pour la pagination des résultats (20 par défaut). |
curl -X GET "https://docs.ekenno.com/api/surveys/manage/?status=active" -H "Authorization: Bearer ek_live_xxxxxxxxxxxxxxxxxxxxxxxx" -H "Accept: application/json"
/api/surveys/manage/{id}/
Récupérer un sondage
Retourne l'arborescence intégrale d'un sondage, y compris toutes ses questions ordonnées, leurs options et les règles d'éligibilité configurées.
{
"id": "368de50f-5df6-4b15-b6cf-f5ff6d123c90",
"title": "Parlons de votre utilisation de l’argent mobile",
"status": "active",
"category": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "E-commerce & Marché"
},
"current_responses": 142,
"max_responses": 500,
"question_count": 12,
"allow_anonymous": true,
"share_token": "a1b2c3d4e5",
"created_at": "2026-05-22T17:03:59Z"
}
Actions sur un Sondage
Pilotage opérationnel de l'état d'un sondage en temps réel sans rechargement.
Rend le sondage immédiatement accessible aux participants et démarre les webhooks.
PATCH /manage/{id}/ {"status": "active"}
Gèle momentanément la collecte de réponses en conservant toutes les données existantes.
PATCH /manage/{id}/ {"status": "paused"}
Arrête définitivement la collecte et consolide les métriques analytiques.
PATCH /manage/{id}/ {"status": "closed"}
curl -X PATCH "https://docs.ekenno.com/api/surveys/manage/{survey_id}/" -H "Authorization: Bearer ek_live_xxxxxxxxxxxxxxxxxxxxxxxx" -H "Content-Type: application/json" -d '{"status": "active"}'
Ciblage Démographique & Questions de Screening
L'API Kenno intègre un moteur de qualification double niveau : pré-qualification démographique basée sur les attributs vérifiés des participants et questions de screening éliminatoires en cours de parcours.
7 Types de règles d'éligibilité supportés
| Type de règle (rule_type) | Opérateurs | Exemple de valeur | Effet |
|---|---|---|---|
| country | eq, in | "BJ", ["CI", "SN", "BJ"] | Restreint l'accès aux pays spécifiés (ISO-2). |
| age_min / age_max | gte, lte | 18, 45 | Tranche d'âge minimale et maximale requise. |
| gender | eq | "female", "male" | Ciblage de genre spécifique si nécessaire. |
| city | eq, in | "Cotonou", "Abidjan" | Localité urbaine ciblée. |
| has_skill | in | "flutter", "react" | Compétence vérifiée dans le profil du répondant. |
| level | gte | 2 | Niveau de réputation minimum du répondant sur la plateforme. |
{
"rule_type": "country",
"operator": "in",
"value": ["BJ", "CI", "SN"],
"is_screening": true,
"disqualify_answers": ["none", "other"]
}
/api/surveys/manage/{id}/responses/
Réponses & Détection de Fraude
Chaque soumission est enregistrée avec son empreinte digitale cryptographique (fingerprint), son temps de réponse effectif et un score de qualité algorithmique (0 à 100) pour filtrer les réponses hâtives ou automatisées.
{
"id": "e9b5a882-9477-44c0-9989-11f4d9c7e112",
"survey": "368de50f-5df6-4b15-b6cf-f5ff6d123c90",
"status": "completed",
"respondent_email": "participant@example.com",
"time_spent": 184,
"quality_score": 96.5,
"answers": [
{
"question_id": "8e3678fb-2917-4824-9b88-c711aa110022",
"question_type": "stars",
"answer": 5
}
],
"completed_at": "2026-11-10T14:32:10Z"
}
/api/surveys/manage/{id}/results/
Résultats & Métriques Analytiques
Calcule à la volée les distributions de fréquences, les scores NPS (Net Promoter Score avec répartition Promoteurs/Passifs/Détracteurs), les moyennes Likert et le taux de complétion global.
{
"total_responses": 142,
"completion_rate": 88.4,
"average_time_seconds": 185,
"average_quality_score": 94.2,
"nps_score": 62.5,
"breakdown": {
"promoters": 72.0,
"passives": 18.5,
"detractors": 9.5
}
}
Personnalisation de l'Intégration
Adaptez le widget de sondage à la charte graphique de votre application via les attributs d'URL ou les options du SDK JavaScript.
| Paramètre d'URL | Valeurs | Défaut | Description |
|---|---|---|---|
| theme | dark, light, auto | dark | Force le thème sombre ou clair du formulaire. |
| primary_color | hex code (ex: FFD928) | FFD928 | Couleur d'accentuation des boutons et jauges. |
| progress_bar | true, false | true | Afficher ou masquer la barre de progression en haut. |
| brand | true, false | true | Afficher ou masquer le badge "Powered by Kenno". |
<iframe
src="https://docs.ekenno.com/api/../s/550e8400-e29b-41d4-a716-446655440000?theme=dark&primary_color=FFD928&progress_bar=true"
width="100%"
height="600"
frameborder="0">
</iframe>
Webhooks & Événements HTTP
Recevez une requête HTTP POST en temps réel à chaque fois qu'un participant démarre, valide ou abandonne un sondage. Tous les payloads sont signés par HMAC SHA-256 via votre secret de webhook.
Événements disponibles (Modèle réel SurveyWebhook)
Démarrage de session
Déclenché dès qu'un participant ouvre la première question du sondage.
Sondage complété
Déclenché avec le payload complet des réponses et le score qualité calculé.
Abandon ou Disqualification
Déclenché si le répondant quitte avant la fin ou échoue au screening.
Vérification de la signature HMAC SHA-256
Chaque appel contient l'en-tête X-Kenno-Signature. Pour vous assurer que la requête provient bien d'Ekenno, calculez le hash HMAC-SHA256 du corps brut avec votre clé secrète et comparez-le en temps constant.
{
"event": "on_complete",
"survey_id": "368de50f-5df6-4b15-b6cf-f5ff6d123c90",
"response_id": "e9b5a882-9477-44c0-9989-11f4d9c7e112",
"respondent_email": "user@example.com",
"quality_score": 96.5,
"answers": [
{
"question_id": "8e3678fb-2917-4824-9b88-c711aa110022",
"answer": "Option A"
}
],
"timestamp": "2026-11-10T14:32:10Z"
}
Cycle de vie & Rotation des Clés API
Administrez vos clés d'API depuis Manage > API Keys ou programmatiquement via /api/developer/keys/.
Révélation unique du secret
Lors d'un POST /api/developer/keys/, le champ raw_key est retourné une seule fois. Seul son hash SHA-256 est stocké en base.
Régénération instantanée
L'endpoint POST /api/developer/keys/{id}/roll/ invalide l'ancien secret et génère un nouveau secret tout en conservant l'ID et les scopes.
Désactivation immédiate
Un appel DELETE /api/developer/keys/{id}/ passe is_active=false. Tout appel ultérieur renvoie 401 Unauthorized.
curl -X POST "https://docs.ekenno.com/api/developer/keys/9f8e7d6c-5b4a-4c3d-8e9f-0a1b2c3d4e5f/roll/" \
-H "Authorization: Bearer ek_live_xxxxxxxxxxxxxxxxxxxxxxxx"
Environnements Live & Sandbox (Test)
Ekenno distingue deux modes d'exécution identifiés par le préfixe de votre clé d'API et exposés dans l'en-tête de réponse X-Developer-Environment.
ek_live_...
Destiné à vos sondages en production. Les réponses complétées enregistrent les soumissions des répondants éligibles et déclenchent vos webhooks de production.
ek_test_...
Idéal pour vos tests d'intégration automatisés et environnements de staging. Permet de tester les 32 types de questions sans impacter vos campagnes Live.
HTTP/1.1 200 OK
Content-Type: application/json
X-Request-Id: e9b5a882-9477-44c0-9989-11f4d9c7e112
X-Developer-Environment: live
X-RateLimit-Limit-Hourly: 1000
X-RateLimit-Limit-Daily: 20000
Serveur MCP de Gestion & Automatisation IA
MCP v1.0Le protocole Model Context Protocol (MCP) permet à n'importe quel agent d'intelligence artificielle autonome (Cursor, Claude Desktop, Windsurf, LangChain, AutoGPT) de piloter intégralement le système de gestion d'eKenno : création de sondages, ajout des 32 types de questions, publication, analyse des résultats, gestion des webhooks et des clés API.
Deux Transports Disponibles
Endpoint distant accessible par n'importe quel agent ou service cloud via requête POST JSON-RPC ou Server-Sent Events.
https://docs.ekenno.com/mcp/manage
Exécution locale via un script Python autonome pour connecter instantanément Claude Desktop ou Cursor sans configuration réseau.
python apps/mcp/manage_mcp_cli.py
Catalogue des 13 Outils de Gestion Disponibles
Liste tous vos sondages avec filtres de statut, recherche et pagination.
Crée un nouveau sondage (titre, description, catégorie, ciblage).
Insère n'importe quel composant parmi les 32 types du Question Builder.
Publie et active le sondage pour le rendre accessible en ligne.
Met en pause la collecte des réponses temporairement.
Clôture définitivement la campagne de sondage.
Extrait les métriques agrégées, scores NPS et distributions des réponses.
Configure une URL de notification HTTP signée par HMAC-SHA256.
Consulte la liste des clés d'API développeur actives.
Génère une nouvelle clé d'accès programmatique.
Configuration pour Cursor & Claude Desktop
Ajoutez simplement cette configuration dans votre fichier claude_desktop_config.json ou .cursor/mcp.json :
{
"mcpServers": {
"ekenno-manage": {
"command": "python",
"args": [
"c:/Users/admin/Documents/project/ekenno/backend/apps/mcp/manage_mcp_cli.py"
],
"env": {
"EKENNO_API_KEY": "ek_live_votre_cle_api_ici"
}
}
}
}
curl -X POST https://docs.ekenno.com/mcp/manage \
-H "Authorization: Bearer ek_live_..." \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "manage_list_surveys",
"arguments": {
"limit": 10,
"status": "active"
}
}
}'
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"structured_content": {
"count": 1,
"surveys": [
{
"id": "747bf9d6-68c1-44e7-837f-06175672ad52",
"title": "Enquête Satisfaction Client",
"status": "active",
"version_number": 1,
"question_count": 10,
"response_count": 42
}
]
},
"isError": false
}
}
Architecture du Survey Builder & Logique Conditionnelle
Chaque question d'un sondage est ordonnée via order, peut être marquée comme éliminatoire (is_screening) et peut dépendre de la réponse à une question précédente grâce à conditional_logic.
Logique conditionnelle (Show / Hide)
Affichez une question uniquement lorsqu'une condition sur une question précédente est remplie (equals, not_equals, contains).
Screening éliminatoire automatique
Activez is_screening: true et listez les réponses disqualifiantes dans disqualify_answers pour filtrer instantanément les profils hors-cible.
{
"question_text": "Pourquoi avez-vous attribué une note inférieure à 3 ?",
"question_type": "textarea",
"order": 2,
"is_required": true,
"conditional_logic": {
"action": "show",
"source_question_order": 1,
"operator": "lte",
"value": 2
}
}
Quotas horaires, quotidiens & Gestion du 429
Chaque clé d'API active dispose d'un quota horaire de 1 000 requêtes / heure et d'un plafond quotidien de 20 000 requêtes / 24h. Vous pouvez suivre votre consommation en direct dans Manage > Rate Limits.
| En-tête HTTP | Valeur type | Description |
|---|---|---|
| X-RateLimit-Limit-Hourly | 1000 | Plafond maximal de requêtes autorisées par fenêtre glissante d'une heure. |
| X-RateLimit-Limit-Daily | 20000 | Plafond maximal de requêtes autorisées par période de 24 heures. |
| X-Request-Id | UUID v4 | Identifiant unique de traçabilité enregistré dans vos Request Logs. |
import time
import requests
def call_ekenno_with_backoff(url, headers, max_retries=3):
for attempt in range(max_retries):
resp = requests.get(url, headers=headers, timeout=10)
if resp.status_code != 429:
return resp
wait_sec = int(resp.headers.get("Retry-After", 2 ** attempt))
time.sleep(wait_sec)
raise RuntimeError("Rate limit exceeded after retries")
Guide de Dépannage (Troubleshooting)
Solutions immédiates aux cas les plus fréquemment rencontrés lors de l'intégration de l'API Survey et des widgets embarqués.
POST /api/surveys/manage/{id}/publish/ renvoie-t-il 400 Bad Request ?Un sondage ne peut être publié que s'il est à l'état draft ou paused et qu'il comporte au moins 1 question. Ajoutez d'abord une question via POST /api/surveys/manage/{id}/questions/.
radio ou checkbox est-elle rejetée en 400 ?Les types à choix (radio, dropdown, checkbox, multi_dropdown, tags, likert) exigent un tableau options contenant au moins 2 choix distincts.
/s/{share_token}/ et /survey/{id}/embed/ ?La route /s/{share_token}/ affiche la page publique autonome complète du sondage, tandis que /survey/{id}/embed/ génère la vue allégée optimisée pour une balise <iframe> avec support des événements window.postMessage.
import crypto from "crypto";
export function verifyKennoWebhook(rawBody, signatureHeader, secretKey) {
const expected = crypto
.createHmac("sha256", secretKey)
.update(rawBody, "utf8")
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(signatureHeader || "", "utf8"),
Buffer.from(expected, "utf8")
);
}