Events
2 endpoints
/v1/eventsPousser un événement personnalisé qui déclenche les automatisations
Webhook entrant générique par compte : une source externe (Zapier, Make,
n8n, un backend client, un outil tiers) pousse un événement nommé avec
ses données. Capturia résout le contact (external_id → phone →
email), enrichit sa fiche de façon non-destructive, remplit les champs
personnalisés mappés, enregistre l'événement sur la fiche, puis démarre
les automatisations dont le déclencheur « Événement personnalisé »
écoute ce nom d'événement — avec le payload disponible dans les
messages via {{event.<clé>}}.
Résolution du contact
Au moins un identifiant est requis (external_id, email ou phone).
Si aucun contact ne matche, une fiche minimale est créée — sauf si
contact.create_if_missing est false, auquel cas la requête répond
404 contact_not_found sans rien enregistrer. Si les identifiants ne
matchent qu'une fiche archivée, l'événement est enregistré dessus
(traçabilité) mais la fiche n'est pas modifiée et aucune automatisation
n'est enrôlée (contact_archived: true).
Consentement (Loi 25 / LCAP)
La réception d'un événement n'établit aucun consentement de communication. Un contact créé par ce chemin ne peut pas recevoir de SMS/courriel tant qu'un consentement n'est pas capté par un autre flux ; une automatisation déclenchée qui tenterait un envoi sans consentement est bloquée par les gardes du moteur d'exécution.
Signature HMAC optionnelle
En plus de la clé API, la requête peut porter une signature
X-Capturia-Signature: t=<unix>,v1=<hex> — HMAC-SHA256 de
${t}.${corps brut} calculée avec la clé API brute comme secret,
fenêtre anti-replay de 5 minutes. Dès que le header est présent, la
validation est stricte (fail-closed) : signature invalide = 401 invalid_signature. Sans le header, la clé API seule authentifie.
Idempotency
Le header Idempotency-Key est optionnel mais recommandé pour les
retries : une 2e requête avec la même clé et le même payload renvoie la
réponse d'origine sans ré-ingérer l'événement (stockage 24h ; payload
différent = 409 idempotency_conflict). Par ailleurs, l'enrôlement des
automatisations porte sa propre dédup de 5 minutes par contact et par
automatisation.
Codes d'erreur custom
- 429 —
rate_limited_ipquand la limite IP (120 req/min) est atteinte, headerX-RateLimit-Scope: ip. Limite par clé : 60 req/min (modulable par clé).
events:ingestCorps de requête
| Propriété | Type | Obligatoire | Description |
|---|---|---|---|
event | string | obligatoire | Nom de l'événement, dans l'espace de noms du compte. Comparé tel quel (sensible à la casse) au nom configuré sur le déclencheur « Événement personnalisé » des automatisations. |
contact | EventIngestContact | obligatoire | — |
payload | object | optionnel | Données arbitraires de l'événement. Chaque clé de premier niveau est
exposée aux messages des automatisations via {{event.<clé>}} (un
objet imbriqué est sérialisé en JSON). Le contenu persisté est borné
(nombre de clés, profondeur, taille totale). |
custom_fields | object | optionnel | Champs personnalisés du contact, keyés par le name de la définition
(même contrat que /v1/leads/capture). Une clé sans définition
correspondante est ignorée silencieusement. Merge additif : la
nouvelle valeur gagne sur collision. |
source_label | string | optionnel | Étiquette libre identifiant la source (affichée sur la fiche contact). |
Responses
| Statut | Description |
|---|---|
200 | Événement ingéré ( |
400 | La requête est mal formée (header manquant, JSON invalide, paramètre invalide). |
401 | Clé API inconnue ou format invalide. |
403 | Refus d'autorisation. Deux causes, distinguées par |
404 | La ressource demandée n'existe pas (ou pas dans le scope du tenant). |
409 | Une requête précédente avec la même |
422 | Validation des paramètres ou du body a échoué. |
429 | Le quota de requêtes par fenêtre est dépassé pour cette clé API ou cet endpoint. |
500 | Erreur serveur inattendue. Toujours fournir le |
Exemple de requête
curl -X POST 'https://api.capturia.io/v1/events' \
-H 'Authorization: Bearer cap_live_<your-key>' \
-H 'Content-Type: application/json' \
-d '{
"event": "abandon_panier",
"contact": {
"external_id": "user-4821",
"external_source": "mon-backend",
"email": "[email protected]",
"first_name": "Alex"
},
"payload": {
"montant": 249.99,
"produit": "Forfait Pro",
"url_panier": "https://boutique.example.com/panier/abc123"
},
"custom_fields": {
"plan_vise": "pro"
},
"source_label": "Boutique en ligne"
}'/v1/event-registrationsInscrire une fiche à un événement
Inscrit une fiche existante du compte à un événement publié (webinaire, défi, sommet, événement en ligne). La personne reçoit ensuite les messages de l'événement, comme si elle s'était inscrite par sa page.
Une seule inscription par personne et par édition : un second appel
pour la même fiche et la même édition rend la même inscription avec
200 et created: false, sans renvoyer la confirmation. Il met la
langue à jour, et réinscrit une personne qui avait dit qu'elle ne
viendrait pas ; une personne qui a demandé l'arrêt des messages le
reste. L'appel est donc sûr à rejouer, sans en-tête
Idempotency-Key.
Exige le module d'accompagnement d'événements du forfait
(403 feature_not_available sinon) et le scope contacts:write.
Refus
404 resource_not_foundavecdetails.resource:contact(fiche absente, fusionnée ou d'un autre compte),eventouedition.422 business_rule_violationavecdetails.rule:event_not_published(brouillon),event_closed(événement archivé, édition annulée, ou aucune édition à venir),event_edition_ended(édition terminée).400 validation_error: corps invalide, ouevent_idetevent_slugtous deux présents ou tous deux absents.
contacts:writeCorps de requête
| Propriété | Type | Obligatoire | Description |
|---|---|---|---|
contact_id | string | obligatoire | Fiche à inscrire. |
event_id | string | optionnel | Identifiant de l'événement. |
event_slug | string | optionnel | Adresse de l'événement, telle qu'elle paraît dans son lien d'inscription. |
edition_id | string | optionnel | Édition visée. Absente : la prochaine édition ouverte de l'événement, comme sur sa page d'inscription. |
language | string | optionnel | Langue des messages. Absente : la première langue de l'événement. |
Responses
| Statut | Description |
|---|---|
200 | La fiche était déjà inscrite à cette édition ; la même inscription est rendue. |
201 | Inscription créée. |
400 | Validation des paramètres ou du body a échoué. |
401 | Clé API inconnue ou format invalide. |
403 | Refus d'autorisation. Deux causes, distinguées par |
404 | La ressource demandée n'existe pas (ou pas dans le scope du tenant). |
415 | Le header |
422 | La requête est syntaxiquement valide mais viole une règle métier. |
429 | Le quota de requêtes par fenêtre est dépassé pour cette clé API ou cet endpoint. |
500 | Erreur serveur inattendue. Toujours fournir le |
503 | Service temporairement indisponible (maintenance, dépendance externe down, infra dégradée). Le client devrait retry avec backoff exponentiel. |
Exemple de requête
curl -X POST 'https://api.capturia.io/v1/event-registrations' \
-H 'Authorization: Bearer cap_live_<your-key>' \
-H 'Content-Type: application/json' \
-d '{
"contact_id": "9b8a7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
"event_slug": "defi-5-jours",
"language": "fr"
}'