marius
Comment ça marcheCe qu'il rapporteAudit gratuitPersonnaliserTarifsFAQEspace clientEssayerFR/EN
Essayer
Comment ça marcheCe qu'il rapporteAudit gratuitPersonnaliserTarifsFAQEspace clientEnglish

API et webhooks

DémarrerClés et authentificationListes et synchronisationProspectsConversationsCompte, sites et chiffresWebhooksVérifier la signatureZapier, Make, n8nErreurs et limitesDonnées personnellesHistorique

Dernière mise à jour : 15 octobre 2026

Spécification OpenAPI : openapi.json
Une question : bonjour@marius.qd.je

01

Démarrer

Marius qualifie les visiteurs de votre site et en fait des prospects. L’API et les webhooks les envoient dans vos outils : votre CRM (HubSpot, Pipedrive, Salesforce, Axonaut…), un tableur, Zapier, Make, n8n ou votre propre logiciel.

  • Les webhooks envoient chaque nouveau prospect à une adresse de votre choix, dès qu’il arrive. C’est le plus simple pour alimenter un CRM.
  • L’API permet à votre outil de tout relire quand il le veut : prospects, conversations, sites, chiffres du mois. Elle permet aussi de marquer un prospect comme traité et d’ajouter des notes, qui apparaissent dans votre espace Marius.

L’API est ouverte aux clients dont le site est en ligne, toutes formules confondues. Le propriétaire de l’espace crée les clés et les webhooks dans son espace : Mon compte → API et webhooks.

Toutes les adresses commencent par https://marius.qd.je/api/v1. Elles répondent en JSON (UTF-8). Les dates suivent la norme ISO 8601, en UTC.

curl https://marius.qd.je/api/v1/me \
  -H "Authorization: Bearer mr_live_votre_cle"
02

Clés et authentification

Chaque appel porte une clé dans l’en-tête Authorization: Bearer mr_live_…. Une clé commence par mr_live_ et s’affiche une seule fois, à sa création : Marius n’en garde qu’une empreinte, personne ne peut la retrouver. Si elle est perdue, supprimez-la et créez-en une autre.

  • Portée. Une clé ouvre toutes les données de votre espace (tous vos sites), jamais celles d’un autre client.
  • Lecture seule ou modifications. Par défaut, une clé ne fait que lire. Cochez « Autoriser les modifications » à sa création pour qu’elle puisse marquer un prospect comme traité et ajouter des notes.
  • Expiration. Chaque clé a une date d’expiration, choisie à sa création (deux ans au plus). Le propriétaire reçoit un e-mail sept jours avant.
  • Suppression. Une clé supprimée cesse de marcher tout de suite. Elle appartient à votre entreprise, pas à une personne : elle reste valable si le propriétaire de l’espace change.
  • Côté serveur seulement. Une clé se garde comme un mot de passe. Ne la mettez jamais dans le code d’une page web ou d’une application mobile : l’API refuse d’ailleurs les appels faits depuis un navigateur.
03

Listes et synchronisation

Les listes (/leads, /conversations) renvoient une page à la fois :

{
  "object": "list",
  "data": [ … ],
  "has_more": true,
  "next_cursor": "MjAyNi0xMC0xNVQwOToxMzowMS40ODIrMDA6MDB8NmQxYzRiMGUt…"
}
ParamètreRôle
limitÉléments par page, de 1 à 100 (25 par défaut).
cursorPage suivante : la valeur next_cursor de la page précédente, telle quelle.
orderdesc (par défaut) : les plus récents d’abord. asc : les plus anciens d’abord, pour une synchronisation.
updated_sinceSeulement ce qui a changé après cette date (ISO 8601 avec fuseau, par exemple 2026-10-15T09:00:00Z).

Pour tenir un CRM à jour sans webhook, gardez le plus grand updated_at reçu et repartez de là au passage suivant :

# 1re fois : tout l'historique, du plus ancien au plus récent
curl "https://marius.qd.je/api/v1/leads?order=asc&limit=100" -H "Authorization: Bearer $MARIUS_KEY"

# page suivante : reprendre next_cursor tant que has_more vaut true
curl "https://marius.qd.je/api/v1/leads?order=asc&limit=100&cursor=MjAyNi0x…" -H "Authorization: Bearer $MARIUS_KEY"

# ensuite, toutes les 15 minutes : seulement ce qui a changé depuis
# le plus grand updated_at déjà reçu
curl "https://marius.qd.je/api/v1/leads?order=asc&updated_since=2026-10-15T09:13:01.482Z" -H "Authorization: Bearer $MARIUS_KEY"

Un paramètre inconnu est refusé (erreur 400) : une faute de frappe ne passe jamais inaperçue.

04

Prospects

Un prospect est un visiteur qui a laissé son e-mail ou son numéro. Les essais faits avec « Tester Marius » n’apparaissent jamais.

GET/leadsListe. Filtres : site (identifiant d’un site), status (new, done), score (hot, warm, cold), plus ceux de la pagination. Triée par updated_at.
GET/leads/{id}Un prospect, avec les notes de votre équipe (notes).
PATCH/leads/{id}Change le statut : {"status": "done"} (traité) ou {"status": "new"} (à traiter). Clé avec modifications.
GET/leads/{id}/notesNotes de l’équipe sur ce prospect.
POST/leads/{id}/notesAjoute une note ({"text": "…"}, 2 000 caractères au plus), signée « API · nom de la clé ». Clé avec modifications.
{
  "id": "6d1c4b0e-2f7a-4c55-9a1e-3b8d2c7f0a19",
  "object": "lead",
  "site": { "id": "290583f4-e9d2-4528-b615-83b8e23fc7e1", "name": "Atelier Martin", "url": "https://www.atelier-martin.fr" },
  "conversation_id": "a3b9e1c2-5d4f-4e8a-b7c6-1f2e3d4c5b6a",
  "status": "new",
  "done_at": null,
  "done_by": null,
  "contact": { "name": "Claire Dubois", "email": "claire.dubois@exemple.fr", "phone": "06 12 34 56 78" },
  "answers": [
    { "key": "projet", "label": "Quel est votre projet ?", "value": "Rénover la cuisine", "answered_at": "2026-10-15T09:12:44Z" },
    { "key": "delai", "label": "Pour quand ?", "value": "Avant Noël", "answered_at": "2026-10-15T09:13:01Z" }
  ],
  "score": {
    "level": "hot",
    "points": 80,
    "criteria": [ { "id": "budget", "label": "Budget indiqué", "points": 25 } ]
  },
  "estimated_value_eur": 8500,
  "summary": {
    "text": "Claire veut rénover sa cuisine avant Noël et a un budget défini.",
    "need": "Rénovation complète de cuisine",
    "next_step": "La rappeler cette semaine pour fixer une visite.",
    "score_reason": "Coordonnées laissées, budget et délai donnés."
  },
  "qualified_at": "2026-10-15T09:45:02.118+00:00",
  "created_at": "2026-10-15T09:11:20.405+00:00",
  "updated_at": "2026-10-15T09:45:02.118+00:00",
  "last_message_at": "2026-10-15T09:14:37.902+00:00",
  "espace_url": "https://marius.qd.je/espace/prospects?p=6d1c4b0e-2f7a-4c55-9a1e-3b8d2c7f0a19"
}
status
new (à traiter) ou done (traité, avec done_at et done_by).
contact
Nom, e-mail et téléphone laissés par le visiteur ; null pour ce qu’il n’a pas donné.
answers
Réponses aux questions de votre questionnaire, dans son ordre.
score
Niveau (hot, warm, cold), points sur 100 et critères remplis. null tant que la fiche n’est pas faite.
estimated_value_eur
Valeur estimée, si vous avez renseigné vos tarifs ; sinon null.
summary
Résumé, besoin, prochaine étape conseillée et raison du score. Fait une fois la conversation retombée (30 minutes sans message), refait si elle reprend.
espace_url
La fiche dans votre espace Marius, à mettre en lien dans votre CRM.
curl -X PATCH https://marius.qd.je/api/v1/leads/6d1c4b0e-2f7a-4c55-9a1e-3b8d2c7f0a19 \
  -H "Authorization: Bearer $MARIUS_KEY" \
  -H "Content-Type: application/json" \
  -d '{"status": "done"}'
curl -X POST https://marius.qd.je/api/v1/leads/6d1c4b0e-2f7a-4c55-9a1e-3b8d2c7f0a19/notes \
  -H "Authorization: Bearer $MARIUS_KEY" \
  -H "Content-Type: application/json" \
  -d '{"text": "Devis envoyé le 16/10."}'

# réponse (201)
{ "id": "f0c1…", "object": "note", "author": "API · HubSpot", "text": "Devis envoyé le 16/10.",
  "created_at": "2026-10-16T08:02:11+00:00", "edited_at": null }
05

Conversations

GET/conversationsListe, triée par date du dernier message. Filtres : site, has_lead (true : seulement celles qui ont donné un prospect), updated_since (celles qui ont un message après cette date), plus la pagination.
GET/conversations/{id}Une conversation avec ses messages (500 au plus), dans l’ordre.

Dans messages, role vaut visitor ou assistant ; unanswered signale une réponse de Marius qui n’avait pas l’information.

{
  "id": "a3b9e1c2-5d4f-4e8a-b7c6-1f2e3d4c5b6a",
  "object": "conversation",
  "site": { "id": "290583f4-…", "name": "Atelier Martin", "url": "https://www.atelier-martin.fr" },
  "started_at": "2026-10-15T09:11:02+00:00",
  "last_message_at": "2026-10-15T09:14:37+00:00",
  "messages_count": 8,
  "first_message": "Bonjour, vous faites les cuisines sur mesure ?",
  "lead_id": "6d1c4b0e-2f7a-4c55-9a1e-3b8d2c7f0a19",
  "messages": [
    { "id": "…", "role": "visitor", "content": "Bonjour, vous faites les cuisines sur mesure ?", "unanswered": null, "created_at": "…" },
    { "id": "…", "role": "assistant", "content": "Oui : …", "unanswered": false, "created_at": "…" }
  ]
}
06

Compte, sites et chiffres

GET/meVotre entreprise, sa formule et ses sites. Pratique pour vérifier une clé.
GET/sitesVos sites : identifiant (pour le filtre site), nom, adresse, état.
GET/statsChiffres d’un mois, heure de Paris, essais exclus. Paramètres : month (AAAA-MM, le mois en cours par défaut), site.
{
  "object": "account",
  "client": { "id": "8c1f…", "name": "Atelier Martin", "plan": "pro", "live_since": "2026-10-02T08:14:00+00:00" },
  "sites": [
    { "object": "site", "id": "290583f4-e9d2-4528-b615-83b8e23fc7e1", "name": "Atelier Martin",
      "url": "https://www.atelier-martin.fr", "status": "active", "created_at": "2026-09-30T09:42:12+00:00" }
  ]
}
{
  "object": "stats",
  "month": "2026-10",
  "site_id": null,
  "conversations": 412,
  "visitor_messages": 1386,
  "leads": 37,
  "hot_leads": 9,
  "done_leads": 21,
  "unanswered_questions": 14
}
07

Webhooks

Dans Mon compte → API et webhooks, le propriétaire indique une adresse en https:// et choisit les événements. Marius y envoie une requête POST en JSON à chaque événement, en moins d’une minute.

ÉvénementQuand
lead.createdUn visiteur vient de laisser son e-mail ou son numéro : un nouveau prospect.
lead.qualifiedSa fiche est prête : résumé, besoin, score, valeur estimée. Peut revenir si la conversation reprend.
lead.updatedSon statut a changé (traité, à traiter), dans l’espace ou par l’API, ou ses coordonnées ont été complétées.
pingLe bouton « Envoyer un test » de Mon compte.

data contient le prospect, sous la même forme que GET /leads/{id} (sans les notes), tel qu’il était au moment de l’événement.

POST https://votre-outil.exemple.fr/marius
Content-Type: application/json; charset=utf-8
User-Agent: marius-Webhooks/1.0 (+https://marius.qd.je/developpeurs) …
Marius-Event: lead.created
Marius-Event-Id: b7e2c9d0-6a1f-4f3e-8c2d-9e0f1a2b3c4d
Marius-Delivery: 0e9d8c7b-6a5f-4e3d-2c1b-0a9f8e7d6c5b
Marius-Signature: t=1760519581,v1=5f2b0c…

{
  "id": "b7e2c9d0-6a1f-4f3e-8c2d-9e0f1a2b3c4d",
  "object": "event",
  "type": "lead.created",
  "created_at": "2026-10-15T09:13:01.482+00:00",
  "data": { "id": "6d1c4b0e-…", "object": "lead", "contact": { … }, … }
}
  • Répondez vite par un code 2xx, en moins de 10 secondes, puis traitez l’événement. Une redirection (3xx) compte comme un échec : indiquez l’adresse finale.
  • Nouveaux essais. En cas d’échec, Marius réessaie après 1 minute, 5 minutes, 30 minutes, 2 heures, 6 heures et 12 heures. Après 7 tentatives manquées (environ 21 heures), l’événement est abandonné ; si votre adresse n’a rien reçu pendant tout ce temps, le webhook est désactivé et le propriétaire reçoit un e-mail. Il le réactive dans Mon compte.
  • Doublons et ordre. Un même événement peut arriver deux fois, et deux événements peuvent arriver dans le désordre : dédoublonnez avec id (ou l’en-tête Marius-Event-Id) et fiez-vous à data.updated_at.
  • Événements manqués (webhook en pause ou désactivé) : ils ne sont pas renvoyés. Récupérez-les par l’API avec updated_since.
  • Adresses acceptées : https://, un nom de domaine public (pas d’adresse IP, pas de réseau privé), port 443 ou 8443, 5 webhooks par espace au plus.
  • Site plus en ligne : plus aucun événement ne part, et les clés sont refusées.
08

Vérifier la signature

Chaque envoi est signé avec le secret du webhook (whsec_…, montré une seule fois, à l’ajout du webhook). Vérifier la signature garantit que l’envoi vient bien de Marius et n’a pas été modifié.

L’en-tête Marius-Signature vaut t=<horodatage>,v1=<signature>. La signature est le HMAC-SHA256, en hexadécimal, de la chaîne <horodatage>.<corps brut>, avec le secret entier (whsec_ compris) comme clé. Calculez-la sur le corps reçu tel quel, avant de le décoder, et refusez un horodatage de plus de 5 minutes.

Node.js

import crypto from 'node:crypto'

// rawBody : le corps reçu tel quel (texte), AVANT JSON.parse
export function mariusSignatureOk(rawBody, header, secret) {
  const parts = Object.fromEntries(header.split(',').map((p) => p.trim().split('=')))
  const t = Number(parts.t)
  if (!t || Math.abs(Date.now() / 1000 - t) > 300) return false // plus de 5 minutes : refusé
  const expected = crypto.createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex')
  const given = String(parts.v1 ?? '')
  return given.length === expected.length && crypto.timingSafeEqual(Buffer.from(given), Buffer.from(expected))
}

// Express : app.post('/marius', express.raw({ type: 'application/json' }), (req, res) => {
//   if (!mariusSignatureOk(req.body.toString('utf8'), req.get('Marius-Signature') ?? '', process.env.MARIUS_WEBHOOK_SECRET)) return res.sendStatus(401)
//   const event = JSON.parse(req.body)
//   res.sendStatus(204) // répondre vite, traiter ensuite
// })

PHP

function marius_signature_ok(string $rawBody, string $header, string $secret): bool {
    parse_str(str_replace(',', '&', $header), $p);
    $t = (int) ($p['t'] ?? 0);
    if (!$t || abs(time() - $t) > 300) return false;
    $expected = hash_hmac('sha256', $t . '.' . $rawBody, $secret);
    return hash_equals($expected, (string) ($p['v1'] ?? ''));
}

$raw = file_get_contents('php://input');
if (!marius_signature_ok($raw, $_SERVER['HTTP_MARIUS_SIGNATURE'] ?? '', getenv('MARIUS_WEBHOOK_SECRET'))) {
    http_response_code(401);
    exit;
}
$event = json_decode($raw, true);

Python

import hashlib, hmac, time

def marius_signature_ok(raw_body: bytes, header: str, secret: str) -> bool:
    parts = dict(p.strip().split("=", 1) for p in header.split(",") if "=" in p)
    t = int(parts.get("t", "0") or 0)
    if not t or abs(time.time() - t) > 300:
        return False
    expected = hmac.new(secret.encode(), f"{t}.".encode() + raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, parts.get("v1", ""))
09

Zapier, Make, n8n

Pas besoin d’écrire de code pour envoyer vos prospects dans un CRM :

  • Zapier : déclencheur « Webhooks by Zapier », événement « Catch Hook ». Collez l’adresse donnée par Zapier dans Mon compte, envoyez un test, puis ajoutez l’action de votre CRM (créer un contact, une affaire…).
  • Make : module « Webhooks », « Custom webhook ». Même principe : l’adresse de Make dans Mon compte, un test pour que Make découvre les champs.
  • n8n : nœud « Webhook » en méthode POST ; l’adresse de production dans Mon compte.

Pour lire l’API depuis ces outils, utilisez leur module HTTP avec l’en-tête Authorization: Bearer mr_live_…. Les champs utiles se trouvent dans data.contact (nom, e-mail, téléphone), data.summary.need (le besoin) et data.espace_url (lien vers la fiche).

10

Erreurs et limites

Une erreur renvoie un code HTTP et un objet error : code est stable (à tester dans votre programme), message est une phrase en français. Chaque réponse porte un en-tête Marius-Request-Id : donnez-le au support si besoin.

HTTP/1.1 404 Not Found
Marius-Request-Id: 1f0e2d3c-…

{
  "error": { "code": "not_found", "message": "Prospect introuvable." },
  "request_id": "1f0e2d3c-…"
}
HTTPcodeCause
400invalid_parameter, invalid_jsonParamètre inconnu ou invalide, corps qui n’est pas un objet JSON.
401missing_key, invalid_key, key_expiredClé absente, mal formée, inconnue, supprimée ou expirée.
403read_only_keyModification demandée avec une clé en lecture seule.
403not_liveLe site n’est plus en ligne.
404not_foundAdresse inconnue, ou élément introuvable dans votre espace.
405method_not_allowedMéthode non acceptée (l’en-tête Allow donne les bonnes).
413payload_too_largeCorps de plus de 10 000 caractères.
429rate_limitedPlus de 60 appels en une minute avec la même clé : attendez le délai de l’en-tête Retry-After.
500, 502server_error, unavailableErreur de notre côté : réessayez un peu plus tard. Une autre erreur 5xx, parfois sans corps JSON, vient du réseau : même conduite.

Limites : 60 appels par minute et par clé, 10 clés et 5 webhooks par espace. Les 15 derniers appels et envois s’affichent dans Mon compte.

11

Données personnelles

Les prospects sont des données personnelles (nom, e-mail, téléphone, propos tenus). Marius les traite pour votre compte, comme sous-traitant. Quand vous les envoyez vers un autre outil par l’API ou un webhook, ce transfert et ce que cet outil en fait relèvent de votre responsabilité : choisissez des outils qui respectent le RGPD, ne gardez que ce dont vous avez besoin, et supprimez une clé dès qu’elle ne sert plus.

Marius garde le journal des appels de l’API 90 jours et les événements des webhooks 30 jours. Un prospect supprimé de votre espace (ou arrivé au bout de sa durée de conservation) disparaît aussi de l’API et des envois en attente.

12

Historique

Version actuelle : v1. Dans une même version, on ajoute des champs et des événements, on n’en retire jamais et on n’en change jamais le sens : ignorez simplement les champs que vous ne connaissez pas. Un changement incompatible ferait l’objet d’une v2, annoncée aux propriétaires des espaces.

15 octobre 2026
Première version : prospects (lecture, statut, notes), conversations, sites, chiffres du mois ; webhooks lead.created, lead.qualified, lead.updated.
marius

L'assistant qui connaît votre site par cœur et vous ramène des clients.

Essayer Marius sur mon site
ProduitComment ça marcheCe qu'il rapportePersonnaliserTarifsQuestions fréquentes
ClientsSe connecterDemander une démoAPI et webhooksSupport
Contactbonjour@marius.qd.jeRéponse sous 48 h, du lundi au vendrediNantes, France
© 2026 Marius · Une entreprise française, basée à Nantes
Mentions légalesConfidentialitéConditions d'utilisationCGV