Pour les développeurs

Référence API.

Tout ce qu'il faut pour embarquer un sondage, vérifier sa disponibilité et transmettre des soumissions. Aucun SDK requis — deux endpoints et un URL.

SDK Flutter

Le SDK Flutter (insightdive_sdk) est l'intégration recommandée pour les apps mobiles. Il embarque le sondage dans un webview et diffuse les événements de cycle de vie. Deux modes d'intégration sont disponibles :

Bottom sheet modale

Le SDK gère la sheet — il l'ouvre, traite les événements et la ferme automatiquement après soumission. Configuration minimale :

main.dart
Insightdive.configure(
  tenant: 'acme',
  survey: 'onboarding',
  apiKey: 'ik_…',
  productVersion: '2026.5.1',
  context: {                          // optionnel — voir Champs contextuels ↓
    'plan': 'enterprise',
    'trial_days_remaining': 12,
    'beta_user': true,
  },
);

// partout où un BuildContext est disponible :
final result = await Insightdive.show(context);
if (result.status == FeedbackStatus.completed) { // utilisateur a soumis }

Widget inline

Pose InsightdiveSurvey n'importe où dans ton arbre — une page plein écran, un onglet, une carte dans une liste. Tu contrôles quand l'afficher et le retirer :

my_page.dart
if (_showSurvey)
  InsightdiveSurvey(
    options: Insightdive.options,
    onEvent: (event) {
      if (event is FeedbackCompleted) {
        setState(() => _showSurvey = false);
      }
    },
  ),

Les événements du widget inline ne sont pas automatiquement transmis à Insightdive.events. Pour le faire, appelle Insightdive.addEvent(e) dans onEvent.

Vérifier la disponibilité

Avant de monter InsightdiveSurvey ou d'appeler show(), utilise isAvailable() pour confirmer que le sondage est actif. Évite d'afficher un écran "non publié" aux utilisateurs finaux :

initState
Insightdive.isAvailable().then((active) {
  if (active) setState(() => _showSurvey = true);
});

Appelle GET /api/v1/surveys/{survey}/status — public, sans auth, mis en cache 30 s. Retourne false en cas d'erreur réseau pour que ton point d'entrée reste caché plutôt que de planter.

SDK Web / JavaScript

Installe @insightdive/sdk depuis npm :

Terminal
npm install @insightdive/sdk

Intégration modale :

TypeScript / JavaScript
import { Insightdive } from '@insightdive/sdk';

Insightdive.configure({
  tenant: 'acme',
  survey: 'onboarding',
  apiKey: 'ik_abc123...',
  context: {                          // optionnel — voir Champs contextuels ↓
    plan: 'enterprise',
    trialDaysRemaining: 12,
    betaUser: true,
  },
});

const available = await Insightdive.isAvailable();
if (available) {
  const result = await Insightdive.show();
  // result.status === 'completed' | 'dismissed'
}

Widget inline :

TypeScript / JavaScript
// le container doit avoir une hauteur explicite en CSS
const container = document.getElementById('survey-slot');
Insightdive.embed(container);

Événements du cycle de vie :

TypeScript / JavaScript
Insightdive.on('completed', (e) => {
  analytics.track('feedback_completed', { id: e.submissionId });
});
Insightdive.on('dismissed', () => {
  analytics.track('feedback_dismissed');
});

Référence complète : Page intégrations · npm.

SDK .NET / C#

Le SDK .NET (Insightdive) est l'intégration recommandée pour les applications desktop Avalonia. Le sondage s'ouvre dans un AvaloniaWebView à l'intérieur d'une fenêtre 460×680 — jamais plein écran, jamais bloquant. Deux cibles sont incluses dans le même package :

CibleNotes
net8.0Interface Avalonia complète — dialog modale, contrôle inline, capture d'écran optionnelle
netstandard2.0API de vérification uniquement — sans dépendance UI, pour les backends ou projets non-Avalonia

Installation

Terminal
dotnet add package Insightdive

Configuration

Appelle InsightdiveSDK.Configure() une seule fois au démarrage de l'application, avant l'affichage de toute fenêtre :

App.axaml.cs
InsightdiveSDK.Configure(new InsightdiveOptions
{
    Tenant            = "acme",
    Survey            = "nps-q1",
    ApiKey            = "ik_…",               // Admin → Paramètres → API
    ProductVersion    = Assembly.GetEntryAssembly()
                                ?.GetName().Version?.ToString(),
    ProductIdentifier = "myapp-desktop",
    Locale            = CultureInfo.CurrentCulture.Name,
    Theme             = "dark",               // "light" | "dark"
    Context           = new Dictionary<string, object>  // optionnel — voir Champs contextuels ↓
    {
        ["plan"]                 = "enterprise",
        ["trial_days_remaining"] = 12,
        ["beta_user"]            = true,
    },
});

Afficher en dialog modale

Appelle IsAvailableAsync() avant ShowAsync() pour confirmer que le sondage est actif et que le cooldown est écoulé. En cas d'erreur réseau, IsAvailableAsync retourne true (fail-open) pour qu'une panne passagère ne masque pas définitivement le sondage :

MainWindow.axaml.cs
if (await InsightdiveSDK.Instance.IsAvailableAsync())
{
    var result = await InsightdiveSDK.Instance.ShowAsync(this);
    if (result?.Status == FeedbackStatus.Completed)
        Console.WriteLine($"Soumis : {result.SubmissionId}");
}

Déclenchement par événement

Utilise TriggerAsync(nomEvénement) pour faire correspondre un événement SDK nommé à la liste Événements déclencheurs du sondage (Admin → Insight → Paramètres → Livraison). Si l'événement correspond — ou si la liste est vide — la méthode retourne true :

C#
// S'affiche uniquement quand "onboarding_completed" correspond à la liste du sondage
if (await InsightdiveSDK.Instance.TriggerAsync("onboarding_completed"))
    await InsightdiveSDK.Instance.ShowAsync(this);

Événements du cycle de vie

Souscris à FeedbackEventOccurred pour réagir à chaque étape de la session de sondage :

C#
InsightdiveSDK.Instance.FeedbackEventOccurred += (_, e) =>
{
    switch (e)
    {
        case FeedbackViewedEvent v:
            analytics.Track("survey_viewed", v.SessionId); break;
        case FeedbackCompletedEvent c:
            analytics.Track("survey_completed", c.SubmissionId); break;
        case FeedbackDismissedEvent d:
            analytics.Track("survey_dismissed"); break;
    }
};

Contrôle inline

Intègre le sondage directement dans ta mise en page au lieu d'une dialog. Utilise InsightdiveSurveyControl du namespace Insightdive.Avalonia_ :

C# — net8.0 uniquement
using Insightdive.Avalonia_;

var opts    = InsightdiveSDK.Instance.Options;
var token   = await InsightdiveSDK.Instance.FetchEmbedTokenAsync();
var url     = UrlBuilder.SurveyUrl(opts, token);
var control = new InsightdiveSurveyControl(InsightdiveSDK.Instance, url);
control.EventOccurred += (_, e) => { /* gérer les événements */ };
MonPanneau.Children.Add(control);

.NET pur — vérification uniquement

La cible netstandard2.0 expose uniquement l'API de disponibilité sans dépendance UI — utile pour du code serveur ou des projets non-Avalonia qui ont seulement besoin de savoir si un sondage est actif :

C# — netstandard2.0
InsightdiveSDK.Configure(new InsightdiveOptions {
    Tenant = "acme", Survey = "nps-q1", ApiKey = "ik_…"
});

bool disponible  = await InsightdiveSDK.Instance.IsAvailableAsync();
bool déclenché   = await InsightdiveSDK.Instance.TriggerAsync("app_launched");

Référence complète des options : Page intégrations · NuGet.

Champs contextuels

Les trois SDKs acceptent un objet context optionnel lors de configure(). Le contexte est transmis à chaque soumission et stocké en JSON opaque aux côtés des réponses. Les admins peuvent visualiser les valeurs dans le détail d'une réponse, filtrer les réponses par clé de contexte et les exporter sous forme de colonnes context.* dans les exports CSV.

Anonyme by design — Insightdive stocke le blob de contexte tel quel, sans en interpréter les clés. Ne mets jamais d'identifiants utilisateurs, d'emails ou d'autres données personnelles dans les champs de contexte. Si tu as besoin de corréler des réponses avec tes utilisateurs, utilise plutôt le pattern Respondent Token.

Contraintes

ContrainteLimite
Clés max20
Format des clés[a-z0-9_], max 64 chars
Types de valeursstring (max 255 chars), number ou boolean
Taille JSON totale4 Ko

Les clés ou valeurs invalides sont ignorées silencieusement — la soumission n'est jamais rejetée à cause de métadonnées de contexte incorrectes.

Exemples d'utilisation

Exemples de contexte
// Segmenter par plan d'abonnement
context: { plan: 'enterprise', trial_days_remaining: 12 }

// Contexte de feature flags
context: { feature_vault_v2: true, feature_export: false }

// Contexte de surface / flux
context: { surface: 'onboarding', step: 'invite_team' }

Token répondant

Un token répondant est une chaîne opaque générée par ton système et transmise au SDK lors de configure(). Insightdive le stocke tel quel aux côtés de la soumission — il n'est jamais décodé, jamais exposé aux répondants et jamais utilisé pour identifier quiconque. Il permet à ton équipe de corréler une réponse avec tes propres enregistrements internes sans envoyer un identifiant PII à Insightdive.

Pattern : hash plutôt qu'identifiant

L'approche recommandée est de hasher l'identifiant utilisateur avec un secret côté serveur (sel de workspace) pour que le token soit opaque pour Insightdive tout en restant recalculable de ton côté pour retrouver toutes les réponses d'un utilisateur :

Dérivation du token — TypeScript / Node.js
import { createHmac } from 'node:crypto';

// WORKSPACE_SALT est un secret de workspace stocké côté serveur — jamais exposé au client.
const token = createHmac('sha256', process.env.WORKSPACE_SALT)
  .update(userId)
  .digest('hex');

Insightdive.configure({
  tenant: 'acme',
  survey: 'nps-q1',
  apiKey: '...',
  respondentToken: token,   // opaque pour Insightdive, recalculable par toi
});
Dérivation du token — Dart / Flutter
import 'dart:convert';
import 'package:crypto/crypto.dart';

final key = utf8.encode(workspaceSalt);   // fourni par le serveur, jamais dans l'app
final bytes = utf8.encode(userId);
final token = Hmac(sha256, key).convert(bytes).toString();

Insightdive.configure(
  tenant: 'acme',
  survey: 'nps-q1',
  apiKey: '...',
  respondentToken: token,
);
Dérivation du token — C# / .NET
using System.Security.Cryptography;
using System.Text;

var keyBytes = Encoding.UTF8.GetBytes(WorkspaceSalt);  // secret côté serveur
var msgBytes = Encoding.UTF8.GetBytes(userId);
using var hmac = new HMACSHA256(keyBytes);
var token = Convert.ToHexString(hmac.ComputeHash(msgBytes)).ToLower();

InsightdiveSDK.Configure(new InsightdiveOptions
{
    Tenant          = "acme",
    Survey          = "nps-q1",
    ApiKey          = "...",
    RespondentToken = token,
});

Contraintes

ContrainteLimite
Longueur max128 caractères
Caractères autorisés[a-zA-Z0-9-_] uniquement
IndexéOui — filtre exact-match dans l'admin et l'API

Les tokens invalides sont ignorés silencieusement — la soumission n'est jamais rejetée à cause d'un token incorrect.

Ce qu'Insightdive voit vs. ce que tu peux faire

Contact opt-in (Tier 3)

Le contact opt-in est une fonctionnalité initiée par l'utilisateur et relayée par l'opérateur. Quand elle est activée, l'éditeur de sondages permet d'ajouter une question de type contact_opt_in. Le répondant voit une case à cocher (« Je suis ouvert(e) à un suivi ») et un champ de saisie. S'il consent, Insightdive :

  1. Chiffre la valeur de contact (AES-256-GCM) avant de la stocker.
  2. Inclut la valeur en clair dans le payload webhook pour que ton système la reçoive immédiatement.
  3. Purge la copie chiffrée du stockage Insightdive après 7 jours (ou plus tôt, une fois la livraison webhook confirmée).

Anonyme by design : la valeur de contact n'est jamais affichée en clair dans l'interface admin d'Insightdive. L'admin affiche uniquement le statut de consentement, le nom du champ et le statut de livraison webhook. Insightdive joue le rôle de relais transitoire, pas de carnet de contacts.

Activation

Le contact opt-in est désactivé par défaut. Active-le dans ton workspace sous Paramètres → Contact Opt-in. Une fois activé, un nouveau type de question contact_opt_in apparaît dans l'éditeur.

Configuration de la question

Dans l'éditeur, ajoute une question contact_opt_in. Configure :

La question contact opt-in est toujours optionnelle pour le répondant (il peut décocher et soumettre sans fournir d'info de contact).

Payload webhook

Quand une soumission inclut une réponse opt-in, le payload du webhook submission.created contient un champ contactOptIn :

Payload webhook — submission.created (avec opt-in)
{
  "event": "submission.created",
  "data": {
    "submissionId": "clxxx...",
    "projectName": "Onboarding NPS",
    "receivedAt":  "2026-05-15T14:32:00.000Z",
    "preview":     "Recommanderiez-vous → 9",
    "contactOptIn": {
      "consented": true,
      "fieldName": "email",
      "value":     "user@example.com"
    }
  }
}

Quand le répondant n'a pas consenti, consented est false et value est null. Quand le sondage ne contient pas de question opt-in, contactOptIn est null.

Rétention des données

DonnéeStockée par InsightdiveTTL
Statut de consentementOui (toujours)Avec la soumission (pas de TTL)
Nom du champOui (pas de données perso)Avec la soumission (pas de TTL)
Valeur de contactChiffrée AES-256-GCM7 jours (supprimée après livraison webhook)
Valeur en clairJamais stockée

Si ton endpoint webhook est inaccessible plus de 7 jours, les données chiffrées sont purgées et la valeur de contact est définitivement perdue. Veille à maintenir ton webhook opérationnel.

Architecture

Insightdive est basé sur des URLs : le sondage vit à un URL public que tu ouvres dans une popup. L'app embarquante n'a que deux choses à faire :

  1. Vérifier la disponibilité. Avant d'afficher le bouton « Donner mon feedback », appelle GET /api/v1/surveys/{slug}/status. Si enabled est false, masque le bouton silencieusement — l'utilisateur ne voit jamais de page d'erreur.
  2. Ouvrir le sondage. Si actif, ouvre /s/{slug} avec les query params requis dans une petite popup (bottom sheet sur mobile, fenêtre ~460×680 sur desktop). Toute la logique du sondage — questions, suivi AI, soumission — tourne à l'intérieur.

Aucun SDK n'est requis côté client. Les soumissions sont anonymes par défaut : seuls productIdentifier, productVersion et les réponses sont collectés. Les opérateurs peuvent optionnellement enrichir avec des champs context ou un respondentToken pseudonyme. Aucun userId, email ou adresse IP n'est jamais collecté.

Authentification

L'endpoint status est public — aucune authentification n'est nécessaire. Tous les endpoints qui lisent ou écrivent des données privées requièrent une clé API workspace envoyée comme bearer token :

Header HTTP
Authorization: Bearer <ta-clé-api>

Trouve ta clé API dans ton workspace sous Paramètres → API. Elle est scopée à ton tenant — ne l'expose jamais dans du code côté client.

Status du sondage

GET /api/v1/surveys/{slug}/status Public · sans auth CORS ouvert Cache 30 s

Vérifie si un sondage est actif et prêt à être affiché. Renvoie toujours 200 — tu branches uniquement sur le booléen enabled. Toute erreur réseau ou réponse non-200 doit être traitée comme enabled: false pour que le bouton reste masqué.

Réponse — activé

200 OK
{
  "slug":          "mon-projet",
  "enabled":       true,
  "name":          "Mon projet",
  "url":           "https://<tenant>.insightdive.com/s/mon-projet",
  "acceptsLocale": true,   // passe ?locale= si true
  "acceptsTheme":  true    // passe ?theme= si true
}

Réponse — désactivé

200 OK (sondage non disponible)
// reason: "disabled" | "not_published" | "not_found"
{ "slug": "mon-projet", "enabled": false, "reason": "disabled" }

URL du sondage

URL /s/{slug}

Quand enabled est true, ouvre cet URL dans une popup discrète — jamais plein écran. Sur mobile, un bottom sheet à ~75% de hauteur. Sur desktop, un window.open à 460×680 px convient bien.

Format de l'URL complète
https://<tenant>.insightdive.com/s/mon-projet
  ?productIdentifier=myapp-desktop  # requis
  &productVersion=2026.5.1          # requis
  &locale=fr                        # optionnel — uniquement si acceptsLocale: true
  &theme=dark                       # optionnel — uniquement si acceptsTheme: true
Paramètre Description
productIdentifierrequis Identifie quel produit embarqué est à l'origine du feedback (ex : myapp-desktop, myapp-mobile). Permet de filtrer les réponses par surface dans l'admin quand plusieurs produits partagent le même sondage.
productVersionrequis Version du build du produit embarqué (ex : 2026.5.1). Stocké avec chaque réponse pour un filtrage par version.
localeoptionnel Code de langue à passer au sondage (ex : fr, en). Ignoré si acceptsLocale est false dans la réponse status.
themeoptionnel Thème visuel : light ou dark. Ignoré si acceptsTheme est false dans la réponse status.

Soumettre une réponse

POST /api/v1/submissions Bearer token Server-to-server

Crée une soumission directement. Réservé aux intégrations server-to-server — backends qui collectent déjà du feedback et veulent le transmettre à Insightdive. Les clients côté utilisateur doivent utiliser l'URL /s/{slug} à la place, qui gère automatiquement l'interface du sondage et la soumission.

Corps de la requête

Corps POST (application/json)
{
  "schemaVersion":     1,                   // optionnel, actuellement 1
  "projectId":         "<project-id>",      // requis — visible dans Admin → Projet
  "productIdentifier": "myapp-desktop",     // recommandé
  "productVersion":    "2026.5.1",          // recommandé
  "context": {                              // optionnel — voir Champs contextuels ↑
    "plan": "enterprise",
    "trial_days_remaining": 12
  },
  "respondentToken": "a3f2...e9b1",         // optionnel — voir Token répondant ↑
  "summary":           "J'adore le nouveau sélecteur de vault.",
  "transcript": [
    {
      "questionId":   "q1",
      "questionText": "Comment l'évaluez-vous ?",
      "type":         "number",
      "value":        "9"
    }
  ]
}

Réponse

201 Created
{
  "id":         "clxxxxxxxxxxxxxxxxxxxxxxx",
  "receivedAt": "2026-05-15T14:32:00.000Z",
  "status":     "new"
}

Lister les réponses

GET /api/v1/submissions Bearer token

Retourne les soumissions du workspace authentifié, de la plus récente à la plus ancienne. Limité à 100 par appel.

Query param Description
projectIdoptionnel Filtre sur un projet spécifique par ID.
statusoptionnel Filtre par statut : new | reviewed | archived
limitoptionnel Nombre maximum de résultats (1–100, défaut 20).
200 OK
{
  "count": 2,
  "submissions": [
    {
      "id":                "clxxxxxxxxxxxxxxxxxxxxxxx",
      "receivedAt":       "2026-05-15T14:32:00.000Z",
      "status":           "new",
      "productIdentifier": "myapp-desktop",
      "productVersion":   "2026.5.1",
      "summary":          "J'adore le nouveau sélecteur de vault.",
      "project":          { "id": "...", "name": "Mon projet", "slug": "mon-projet" }
    }
  ]
}

Erreurs & rate limits

Tous les endpoints renvoient des erreurs JSON structurées. Les rate limits s'appliquent par adresse IP.

Status Erreur Signification
400 Invalid body Le corps de la requête échoue la validation de schéma. Consulte le champ details pour le chemin exact.
401 Unauthorized Bearer token manquant ou invalide.
402 plan_limit_reached Le workspace a atteint sa limite mensuelle de soumissions. Passe à un plan supérieur ou attends le prochain cycle.
403 workspace_suspended Workspace suspendu. Contacte le support.
404 Project not found Le projectId n'existe pas dans ce workspace.
429 rate_limited Trop de requêtes. Consulte le header Retry-After (en secondes). Limites : 120 req/min sur /status, 60 req/min sur /submissions.

Prêt à intégrer ?

Le guide d'intégration dans ton admin génère des snippets de code adaptés à ton slug de projet et à l'URL de ton tenant.

Créer ton workspace