---
type: how-to
title: Intégration côté serveur
summary: Prends le caputchin-token d'un envoi et confirme-le avec /siteverify avant de faire confiance à la requête.
---

Le widget remet au navigateur un token à usage unique. Ce token ne prouve rien tant que ton serveur ne le confirme pas auprès de Caputchin. C'est l'étape qui transforme une épreuve visible en vraie protection, et c'est un unique appel HTTPS.

Il te faut un backend capable de faire une requête sortante, et le secret de ta clé de site (`cpt_sec_...`) disponible là, jamais dans le navigateur.

## 1. Lis le token

Quand le formulaire est envoyé, ton handler reçoit un champ supplémentaire aux côtés des tiens : `caputchin-token`. Le widget l'a injecté dans le formulaire quand le visiteur a réussi l'épreuve.

## 2. Confirme-le avec /siteverify

POSTe le token et ton secret. La requête et la réponse ont la même forme que le `siteverify` de reCAPTCHA, donc une intégration existante se transpose avec peu de changement :

```http
POST https://verify.caputchin.com/v1/siteverify
Content-Type: application/json

{ "secret": "cpt_sec_...", "response": "<caputchin-token>" }
```

```js
// Node 18+
const verdict = await fetch("https://verify.caputchin.com/v1/siteverify", {
  method: "POST",
  headers: { "content-type": "application/json" },
  body: JSON.stringify({
    secret: process.env.CAPUTCHIN_SECRET,
    response: token,
  }),
}).then((r) => r.json());

if (!verdict.success) {
  // reject the submission
}
```

Le même appel dans d'autres langages est sur [exemples d'intégration backend](/docs/integration-guides/backend-examples).

## 3. Lis la réponse

```json
{
  "success": true,
  "error-codes": [],
  "platform": { "game_id": "caputchin/games/leaf-memory", "score": 847, "duration_ms": 4200 }
}
```

- **`success`** est la seule chose sur laquelle brancher. `true` veut dire qu'un vrai visiteur a réussi l'épreuve ; fais confiance à la requête. `false` veut dire la rejeter.
- **`error-codes`** apparaît quand `success` est `false`, nommant ce qui n'a pas marché (un token manquant, expiré ou déjà utilisé, etc.).
- **`platform`** est une métadonnée revendiquée par le client (quel jeu, le score, la durée). Elle est nullable et **n'est pas un signal de sécurité**. Enregistre-la pour l'analytique si tu veux, mais ne fonde jamais la confiance sur le score.

## Les règles qui te protègent

- **Usage unique.** Un token se vérifie une fois. Soumettre le même à nouveau renvoie `success: false`, donc tu n'as pas à suivre les rejeux toi-même.
- **Courte durée de vie.** Un token expire dix minutes après son émission, donc vérifie-le pendant le traitement de l'envoi, pas dans une tâche différée.
- **Le secret reste sur le serveur.** Il ne va jamais au navigateur, et c'est lui qui authentifie l'appel.

## Erreurs courantes

- **Vérifier côté client.** L'événement `pass` et la présence du token sont purement UX. Chaque décision de confiance se prend ici, côté serveur, avec ton secret.
- **Gater sur le `score`.** C'est une métadonnée, pas une valeur de confiance.

## Questions fréquentes

### Combien de temps un token est-il valide ?

Dix minutes après son émission. Vérifie-le pendant que tu traites l'envoi, pas depuis une tâche différée ni une file qui pourrait tourner plus tard.

### Puis-je vérifier deux fois le même token ?

Non. Un token est à usage unique. Soumettre le même à nouveau renvoie `success: false`, donc tu n'as pas à suivre les rejeux toi-même.

### Dois-je faire confiance au `score` ou au `game_id` ?

Non. Le bloc `platform` (jeu, score, durée) est une métadonnée revendiquée par le client et il est nullable. Branche uniquement sur `success`, et ne fonde jamais la confiance sur le score.

### Ai-je besoin d'un SDK pour cela ?

Non. La vérification est un unique POST HTTPS vers `/v1/siteverify`. Le même appel en Node, Python, PHP, Go, Ruby, .NET et curl est sur [exemples d'intégration backend](/docs/integration-guides/backend-examples).

## Voir aussi

- [Exemples d'intégration backend](/docs/integration-guides/backend-examples) pour cet appel dans ton langage.
- [Vérification hébergée](/docs/hosted-verification/overview) pour sauter le backend entièrement.
