---
type: how-to
title: Integración del lado del servidor
summary: Toma el caputchin-token de un envío y confírmalo con /siteverify antes de fiarte de la petición.
---

El widget le entrega al navegador un token de un solo uso. Ese token no prueba nada hasta que tu servidor lo confirma con Caputchin. Este es el paso que convierte un reto visible en protección de verdad, y es una única llamada HTTPS.

Necesitas un backend que pueda hacer una petición saliente, y el secreto de tu clave de sitio (`cpt_sec_...`) disponible ahí, nunca en el navegador.

## 1. Lee el token

Cuando el formulario se postea, tu handler recibe un campo extra junto a los tuyos: `caputchin-token`. El widget lo inyectó en el formulario cuando el visitante superó el reto.

## 2. Confírmalo con /siteverify

Haz POST del token y tu secreto. La petición y la respuesta tienen la forma del `siteverify` de reCAPTCHA, así que una integración existente se traslada con poco cambio:

```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
}
```

La misma llamada en otros lenguajes está en [ejemplos de integración de backend](/docs/integration-guides/backend-examples).

## 3. Lee la respuesta

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

- **`success`** es lo único sobre lo que ramificar. `true` significa que un visitante real superó el reto; fíate de la petición. `false` significa descártala.
- **`error-codes`** aparece cuando `success` es `false`, nombrando qué salió mal (un token ausente, expirado o ya usado, etcétera).
- **`platform`** es metadato declarado por el cliente (qué juego, la puntuación, la duración). Es nullable y **no es una señal de seguridad**. Regístralo para analítica si quieres, pero nunca condiciones la confianza a la puntuación.

## Las reglas que te protegen

- **De un solo uso.** Un token verifica una vez. Enviar el mismo otra vez devuelve `success: false`, así que no tienes que rastrear los reenvíos tú mismo.
- **De vida corta.** Un token expira diez minutos después de emitirse, así que verifícalo mientras manejas el envío, no en un job diferido.
- **El secreto se queda en el servidor.** Nunca va al navegador, y es lo que autentica la llamada.

## Errores comunes

- **Verificar en el cliente.** El evento `pass` y la presencia del token son solo UX. Cada decisión de confianza ocurre aquí, en el servidor, con tu secreto.
- **Condicionar a `score`.** Es metadato, no un valor de confianza.

## Preguntas frecuentes

### ¿Cuánto tiempo es válido un token?

Diez minutos después de emitirse. Verifícalo mientras manejas el envío, no desde un job diferido ni una cola que podría correr más tarde.

### ¿Puedo verificar el mismo token dos veces?

No. Un token es de un solo uso. Enviar el mismo otra vez devuelve `success: false`, así que no tienes que rastrear los reenvíos tú mismo.

### ¿Debo confiar en el `score` o el `game_id`?

No. El bloque `platform` (juego, puntuación, duración) es metadato declarado por el cliente y es nullable. Ramifica solo en `success`, y nunca condiciones la confianza a la puntuación.

### ¿Necesito un SDK para esto?

No. La comprobación es un único POST HTTPS a `/v1/siteverify`. La misma llamada en Node, Python, PHP, Go, Ruby, .NET y curl está en [ejemplos de integración de backend](/docs/integration-guides/backend-examples).

## Véase también

- [Ejemplos de integración de backend](/docs/integration-guides/backend-examples) para esta llamada en tu lenguaje.
- [Verificación alojada](/docs/hosted-verification/overview) para saltarte el backend por completo.
