Aller au contenu

API Capture

Tout entre dans Kilden par un seul endpoint :

POST https://ingest.kilden.io/capture
Content-Type: application/json

Les SDK l’utilisent sous le capot ; vous pouvez l’appeler directement depuis n’importe quel backend, script ou intégration. Il n’existe aucun autre chemin d’écriture.

Le corps est toujours un batch — un événement seul est un batch de un :

{
"write_key": "YOUR_WRITE_KEY",
"sent_at": "2026-07-11T12:00:00.000Z",
"batch": [
{
"uuid": "0197f9d2-5e5a-7cc3-b1a4-1f0e9a2b3c4d",
"event": "order_completed",
"distinct_id": "user_4821",
"properties": { "total": 49.9, "currency": "EUR" },
"timestamp": "2026-07-11T11:59:58.000Z"
}
]
}
Champ Requis Notes
write_key oui Clé publique ou secrète, dans le corps (pas un header). Les clés secrètes marquent les événements comme des faits côté serveur — voir Niveaux de confiance
sent_at non Quand le client a envoyé la requête. Sert uniquement à corriger la dérive d’horloge du client : real_time ≈ timestamp + (server_now − sent_at)
batch oui 1 à 1000 événements
identity_token non Emplacement de repli pour le JWT de vérification d’identité quand un header est impossible (sendBeacon). Le header Authorization gagne si les deux sont présents

Par événement :

Champ Requis Notes
uuid oui Un UUID valide, généré par le client. Utilisez UUID v7. C’est la clé d’idempotence : les réessais avec le même uuid sont dédupliqués, donc réessayer sur n’importe quel échec est toujours sûr
event oui Nom de l’événement, ≤ 200 caractères. Les noms préfixés par $ sont réservés au système
distinct_id oui ≤ 512 caractères. UUID v7 préfixé par anon_ = anonyme ; tout le reste est traité comme un id d’utilisateur identifié
properties non N’importe quel objet JSON. Jamais validé par schéma — l’enveloppe est typée, le payload est à vous
timestamp non ISO 8601. Par défaut, l’heure de réception serveur ; corrigé avec sent_at quand les deux sont présents

Headers :

  • Authorization: Bearer <jwt> — token de vérification d’identité pour tout le batch (un batch appartient à une seule session de navigateur). Max 4096 octets.
  • Content-Encoding: gzip — accepté et recommandé pour les gros batchs.

Limites : corps de requête ≤ 5 MiB, batch ≤ 1000 événements.

200 dès que le batch est validé et mis en file (capture ne bloque jamais sur le traitement en aval) :

{ "status": "ok" }

Les erreurs sont en texte brut, pas en JSON :

Statut Signification
400 Requête malformée : invalid JSON body: …, invalid gzip body, write_key is required, batch must not be empty, batch exceeds 1000 events, batch[i]: uuid is not a valid UUID, batch[i]: event is required, batch[i]: event exceeds 200 chars, batch[i]: distinct_id is required, batch[i]: distinct_id exceeds 512 chars, batch[i]: properties is not valid JSON, identity token too large
401 unknown write_key
403 origin not allowed for this project — voir origines autorisées
405 Méthode autre que POST/OPTIONS

Un échec de validation rejette le batch entier — corrigez et réessayez (les UUID rendent cela sûr).

Terminal window
curl -X POST https://ingest.kilden.io/capture \
-H 'Content-Type: application/json' \
-d '{
"write_key": "sk_your_secret_key",
"sent_at": "2026-07-11T12:00:00Z",
"batch": [{
"uuid": "0197f9d2-5e5a-7cc3-b1a4-1f0e9a2b3c4d",
"event": "subscription_renewed",
"distinct_id": "user_4821",
"properties": {"plan": "pro", "mrr": 49},
"timestamp": "2026-07-11T12:00:00Z"
}]
}'

Les événements envoyés avec une clé secrète sont marqués source=server et verified=true — la clé secrète est l’authentification. N’embarquez jamais une clé secrète dans un navigateur ou une application mobile ; c’est à ça que servent les clés publiques.

/capture est un endpoint d’écriture public, ouvert en CORS par conception :

  • Chaque réponse porte Access-Control-Allow-Origin: * (sans credentials).
  • Le preflight OPTIONS répond 204 avec Access-Control-Allow-Methods: POST, Access-Control-Allow-Headers: Content-Type, Authorization, Content-Encoding, Access-Control-Max-Age: 86400.

Une write key publique est visible dans le source de votre page, donc n’importe qui pourrait la copier. Pour empêcher d’autres sites de polluer vos données, configurez des origines autorisées dans les réglages du projet : quand la liste est non vide, les requêtes navigateur dont l’Origin ne correspond pas reçoivent 403.

  • Motifs : scheme://host[:port], avec wildcards de sous-domaine (https://*.example.com — ne couvre pas l’apex ; listez les deux si nécessaire).
  • Liste vide (défaut) = tout autoriser.
  • Les requêtes sans header Origin (curl, backends, applis mobiles) passent toujours — ce vecteur est couvert par les clés secrètes et la vérification d’identité, pas par le filtrage d’Origin. C’est une réduction de bruit contre la réutilisation réelle de clés dans les navigateurs, pas une frontière de sécurité.