Point de terminaison des données clients : commandes, livraison et factures
Référence technique des données clients : requête signée de reactive.chat vers votre boutique, schéma de réponse, limites, motifs d’échec, points de terminaison complets en PHP et Node et import via l’API.
Avec les données clients, l’assistant IA répond à des questions comme « Où est mon colis ? » ou « Ma facture est-elle payée ? », uniquement pour les visiteurs confirmés et au sujet de leurs propres commandes. Cette page est la référence technique pour la personne qui développe le point de terminaison. L’affichage des données par l’assistant et l’autorisation des champs dans le compte sont décrits dans Données clients dans le chat.
Disponible avec les forfaits payants (y compris pendant l’essai).
Prérequis
- Vérification d’identité avec exp. Le visiteur doit être connecté avec un
userHashvalide etexp(Identifier les visiteurs). Sansexp, il est considéré comme confirmé mais sans preuve récente ; aucune donnée n’est alors récupérée. - Réglage du compte sous Projet → Données clients : choisir le mode, saisir le point de terminaison, autoriser les champs, définir la mention pour les visiteurs non connectés.
Modes :
- Récupération – reactive.chat interroge votre point de terminaison pour chaque question pertinente. La réponse n’est pas stockée. Recommandé.
- Import – votre boutique nous envoie les commandes d’un client via l’API ; elles sont stockées chiffrées chez nous, pour 90 jours maximum.
- Les deux – d’abord les données importées ; si rien n’est trouvé, la récupération.
- Désactivé.
La requête
reactive.chat appelle votre point de terminaison avec POST :
POST /rc-kundendaten HTTP/1.1
Host: shop.your-company.com
Content-Type: application/json
Accept: application/json
User-Agent: reactive.chat-kundendaten/1
X-RC-Zeit: 1792503600
X-RC-Signatur: 5d41402abc4b2a76b9719d911017c592...
{"user_id":"4711","anliegen":"lieferung","referenz":"2026-0815","sprache":"en","nonce":"9f86d081884c7d659a2feaa0c55ad015"}
En-têtes :
- X-RC-Zeit – secondes Unix au moment de la requête.
- X-RC-Signatur –
hex(HMAC_SHA256(endpoint_secret, X-RC-Zeit + "." + raw_body)), 64 caractères minuscules, sans préfixe. La clé est le secret du point de terminaison de votre compte sous forme de chaîne (il commence parrckd_).
Corps :
- user_id (chaîne) – l’identifiant confirmé provenant de
identify. Jamais une information issue du chat. - anliegen (chaîne) –
bestellung(commande),lieferung(livraison),rechnung(facture),retoure(retour) ;testpour le bouton de test. - referenz (chaîne ou
null) – un numéro mentionné par le client dans le chat. Uniquement un filtre parmi les commandes de ceuser_id; le numéro d’un autre client ne doit rien trouver. - sprache (chaîne) – deux lettres, par ex.
de. - nonce (chaîne) – 32 caractères hexadécimaux, nouveaux à chaque requête.
Votre point de terminaison doit :
- vérifier la signature sur le corps brut (en temps constant, par ex.
hash_equals), pas sur un JSON réencodé ; - rejeter les requêtes dont
X-RC-Zeitdiffère de votre horloge de plus de 300 secondes ; - n’accepter chaque
noncequ’une fois (recommandé) ; - renvoyer uniquement les données du
user_idfourni ; - être accessible via
httpssur le port 443 à une adresse publique, sans redirection ni nom d’utilisateur/mot de passe dans l’adresse ; - répondre entièrement en 3 secondes (établissement de la connexion en 2 secondes maximum).
La réponse
Exactement HTTP 200 avec un objet JSON. Sans résultat, renvoyez {"bestellungen":[]}, également avec 200.
{
"bestellungen": [
{
"nummer": "2026-0815",
"datum": "2026-09-28",
"status": "shipped",
"summe": "49.90",
"waehrung": "EUR",
"positionen": [ { "name": "Balance bike blue", "menge": 1 } ],
"versand": { "dienst": "DHL", "sendungsnummer": "00340434161234567890", "status": "out for delivery" },
"rechnung": { "nummer": "R-2026-1201", "status": "paid", "faellig_am": "2026-10-12" }
}
]
}
Champs par commande :
- nummer (chaîne, obligatoire) – une commande sans numéro est écartée.
- datum (chaîne,
YYYY-MM-DD). - status (chaîne) – statut de commande en texte simple.
- summe (chaîne ou nombre), waehrung (ISO 4217, par ex.
EUR). - positionen (liste, 20 maximum) – chaque entrée avec
nameetmenge. - versand (objet) –
dienst,sendungsnummer,status, éventuellementortetplz(uniquement avec autorisation explicite dans le compte). - rechnung (objet) –
nummer,status,faellig_am.
Limites : au maximum 10 commandes, 20 articles par commande, textes de 200 caractères maximum, réponse entière de 16 384 octets maximum. Les champs inconnus et ceux non autorisés dans votre compte sont écartés sans erreur.
Jamais repris, même si vous les envoyez : rue et autres lignes d’adresse, noms, e-mail, téléphone, IBAN/BIC/compte et données de carte, date de naissance, mots de passe/jetons/clés, numéros fiscaux et d’identité. Mieux vaut ne pas les envoyer du tout.
Traitement de la réponse : elle sert uniquement à cette réponse de l’assistant et n’est stockée ni dans la conversation ni dans la base de connaissances ; le journal n’enregistre que le nombre et la durée. Si votre point de terminaison ne répond pas à temps ou renvoie une erreur, l’assistant répond comme si aucune donnée n’était disponible.
Point de terminaison de référence en PHP
<?php
declare(strict_types=1);
$GEHEIMNIS = (string) getenv('RC_KD_GEHEIMNIS'); // rckd_... from the account
function antworte(int $status, array $daten): never {
http_response_code($status);
header('Content-Type: application/json; charset=utf-8');
header('Cache-Control: no-store');
echo json_encode($daten, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
exit;
}
if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') { antworte(405, ['fehler' => 'POST only']); }
$body = (string) file_get_contents('php://input', false, null, 0, 4097);
if (strlen($body) > 4096) { antworte(413, ['fehler' => 'too large']); }
$zeit = (string) ($_SERVER['HTTP_X_RC_ZEIT'] ?? '');
$sig = strtolower((string) ($_SERVER['HTTP_X_RC_SIGNATUR'] ?? ''));
if (!preg_match('/^\d{9,11}$/', $zeit) || abs(time() - (int) $zeit) > 300) {
antworte(401, ['fehler' => 'zeit']);
}
if (!hash_equals(hash_hmac('sha256', $zeit . '.' . $body, $GEHEIMNIS), $sig)) {
antworte(401, ['fehler' => 'signatur']);
}
$j = json_decode($body, true);
$userId = is_string($j['user_id'] ?? null) ? $j['user_id'] : '';
$referenz = is_string($j['referenz'] ?? null) ? trim($j['referenz']) : '';
$nonce = is_string($j['nonce'] ?? null) ? $j['nonce'] : '';
if ($userId === '' || !preg_match('/^[0-9a-f]{32}$/', $nonce)) {
antworte(400, ['fehler' => 'anfrage']);
}
$pdo = new PDO(getenv('SHOP_DB_DSN'), getenv('SHOP_DB_USER'), getenv('SHOP_DB_PASS'),
[PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
// nonce only once: CREATE TABLE rc_nonce (nonce CHAR(32) PRIMARY KEY, zeit INT NOT NULL);
$pdo->prepare('DELETE FROM rc_nonce WHERE zeit < ?')->execute([time() - 600]);
try {
$pdo->prepare('INSERT INTO rc_nonce (nonce, zeit) VALUES (?, ?)')->execute([$nonce, time()]);
} catch (PDOException $e) {
antworte(409, ['fehler' => 'nonce already used']);
}
if ($userId === 'rc-test') { antworte(200, ['bestellungen' => []]); } // test button
// ONLY this customer; the reference is only a filter
$sql = 'SELECT id, nummer, datum, status, summe, waehrung, versand_dienst, sendungsnummer,
versand_status, rechnung_nummer, rechnung_status, faellig_am
FROM bestellungen WHERE kunde_id = :u';
$w = [':u' => $userId];
if ($referenz !== '') {
$sql .= ' AND (nummer = :r1 OR rechnung_nummer = :r2 OR sendungsnummer = :r3)';
$w += [':r1' => $referenz, ':r2' => $referenz, ':r3' => $referenz];
}
$st = $pdo->prepare($sql . ' ORDER BY datum DESC LIMIT 10');
$st->execute($w);
$pos = $pdo->prepare('SELECT name, menge FROM bestell_positionen WHERE bestellung_id = ? LIMIT 20');
$aus = [];
foreach ($st->fetchAll(PDO::FETCH_ASSOC) as $b) {
$pos->execute([$b['id']]);
$aus[] = [
'nummer' => (string) $b['nummer'],
'datum' => $b['datum'],
'status' => $b['status'],
'summe' => $b['summe'],
'waehrung' => $b['waehrung'],
'positionen' => $pos->fetchAll(PDO::FETCH_ASSOC),
'versand' => ['dienst' => $b['versand_dienst'], 'sendungsnummer' => $b['sendungsnummer'],
'status' => $b['versand_status']],
'rechnung' => ['nummer' => $b['rechnung_nummer'], 'status' => $b['rechnung_status'],
'faellig_am' => $b['faellig_am']],
];
}
antworte(200, ['bestellungen' => $aus]);
Point de terminaison de référence en Node.js (Express)
const crypto = require('crypto');
const express = require('express');
const app = express();
const GEHEIM = process.env.RC_KD_GEHEIMNIS;
const gesehen = new Map(); // nonce -> time; a database in production
app.post('/rc-kundendaten', express.raw({ type: 'application/json', limit: '4kb' }), async (req, res) => {
const body = req.body.toString('utf8'); // RAW body
const zeit = String(req.get('X-RC-Zeit') || '');
const sig = String(req.get('X-RC-Signatur') || '').toLowerCase();
if (!/^\d{9,11}$/.test(zeit) || Math.abs(Date.now() / 1000 - Number(zeit)) > 300) {
return res.status(401).json({ fehler: 'zeit' });
}
const soll = crypto.createHmac('sha256', GEHEIM).update(zeit + '.' + body).digest('hex');
if (sig.length !== soll.length || !crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(soll))) {
return res.status(401).json({ fehler: 'signatur' });
}
const j = JSON.parse(body);
if (!/^[0-9a-f]{32}$/.test(j.nonce || '') || gesehen.has(j.nonce)) {
return res.status(409).json({ fehler: 'nonce' });
}
gesehen.set(j.nonce, Date.now());
if (j.user_id === 'rc-test') { return res.json({ bestellungen: [] }); }
const bestellungen = await ladeBestellungen(j.user_id, j.referenz); // ONLY this customer
res.json({ bestellungen: bestellungen.slice(0, 10) });
});
Tester la connexion
Tester la connexion dans votre compte appelle votre point de terminaison avec le user_id rc-test et anliegen test. Renvoyez une liste vide ou une commande d’exemple pour cet appel. En cas d’échec, votre compte indique une de ces raisons :
- kein_endpunkt, kein_geheimnis – un élément manque dans votre compte.
- url_ungueltig, nur_https, port_verboten, zugangsdaten_in_url – adresse non autorisée (uniquement
https, port 443, aucun identifiant d’accès dans l’adresse). - host_unaufloesbar, adresse_privat, adresse_gewechselt – le nom ne se résout pas ou se résout vers une adresse privée.
- tls, verbindung, zeitueberschreitung – certificat, connexion ou délai de 3 secondes.
- umleitung – votre point de terminaison répond avec 3xx.
- http_<code> – statut autre que 200 (par ex.
http_401: votre vérification de signature refuse la requête). - antwort_zu_gross, antwort_kein_json, antwort_ohne_bestellungen – la réponse ne respecte pas le schéma.
- tarif – le projet ne possède pas de forfait payant.
Import via l’API
Si votre boutique ne peut pas proposer de point de terminaison, envoyez-nous les données. Il vous faut une clé d’API avec la portée import des données clients uniquement (kundendaten:schreiben) et le mode Import ou Les deux.
curl -X PUT "https://reactive.chat/v1/kundendaten/4711?project_id=123" \
-H "Authorization: Bearer rc_live_..." \
-H "Content-Type: application/json" \
-d '{"bestellungen":[{"nummer":"2026-0815","datum":"2026-09-28","status":"shipped"}]}'
- PUT /v1/kundendaten/{user_id} – remplace toutes les commandes de ce client (50 maximum ; une liste vide les supprime). Réponse
{"data":{"stored":1,"dropped":0,"max_orders":50,"expires_in_days":90}}. - DELETE /v1/kundendaten/{user_id} – les supprime ; fonctionne toujours, même sans forfait. Réponse
{"data":{"deleted":1}}. - project_id – obligatoire dès que votre espace de travail contient plusieurs projets.
- user_id – le même identifiant que vous signez dans
identify; encodé pour l’URL, 200 caractères maximum. Une barre oblique encodée (%2F) n’est pas possible. - Il n’existe aucun
GET.
Les données importées sont stockées chiffrées (le numéro client uniquement sous forme de hachage) et supprimées 90 jours après le dernier import. Envoyez donc les modifications en continu, par exemple à chaque changement de statut.
Erreurs : 402 plan_required (aucun forfait payant), 409 import_disabled (le mode n’autorise pas l’import, avec kd_modus), 400 bad_request avec param (user_id, project_id, bestellungen), 404 (projet d’un autre utilisateur). Le format général des erreurs est décrit dans API serveur.
Visiteurs non connectés
Si une personne sans identité confirmée pose une question sur une commande, l’assistant ne récupère rien ; il affiche votre mention du compte (texte par langue, lien de connexion facultatif) ou transfère à votre équipe.
Suite : Webhooks · API serveur
Azken eguneratzea: