---
type: how-to
title: 서버 측 연동
summary: 제출에서 caputchin-token을 받아, 요청을 신뢰하기 전에 /siteverify로 확인하세요.
---

위젯은 브라우저에 일회용 토큰을 건넵니다. 그 토큰은 당신의 서버가 Caputchin으로 확인하기 전까지 아무것도 증명하지 않습니다. 이것이 보이는 챌린지를 진짜 보호로 바꾸는 단계이며, 단 한 번의 HTTPS 호출입니다.

당신에게는 아웃바운드 요청을 할 수 있는 백엔드와, 거기서 쓸 수 있는 사이트 키의 시크릿(`cpt_sec_...`)이 필요하며, 절대 브라우저에는 두지 않습니다.

## 1. 토큰 읽기

폼이 POST하면, 당신의 핸들러는 당신 자신의 필드 옆에 추가 필드 하나를 받습니다: `caputchin-token`. 위젯이 방문자가 챌린지를 풀었을 때 그것을 폼에 넣었습니다.

## 2. /siteverify로 확인하기

토큰과 당신의 시크릿을 POST하세요. 요청과 응답은 reCAPTCHA의 `siteverify`와 같은 모양이라, 기존 연동을 거의 손대지 않고 옮길 수 있습니다:

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

다른 언어로 된 같은 호출은 [백엔드 연동 예제](/docs/integration-guides/backend-examples)에 있습니다.

## 3. 응답 읽기

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

- **`success`**가 분기할 유일한 대상입니다. `true`는 진짜 방문자가 챌린지를 풀었다는 뜻이니, 요청을 신뢰하세요. `false`는 버리라는 뜻입니다.
- **`error-codes`**는 `success`가 `false`일 때 나타나, 무엇이 잘못됐는지(없거나, 만료됐거나, 이미 사용된 토큰 등) 이름을 댑니다.
- **`platform`**은 클라이언트가 주장한 메타데이터입니다(어떤 게임인지, 점수, 소요 시간). 이것은 nullable이며 **보안 신호가 아닙니다**. 원하면 분석용으로 기록하되, 절대 점수에 신뢰를 걸지 마세요.

## 당신을 보호하는 규칙

- **일회용.** 토큰은 한 번 검증합니다. 같은 것을 다시 제출하면 `success: false`가 돌아오니, 리플레이를 직접 추적할 필요가 없습니다.
- **짧은 수명.** 토큰은 발급 후 10분이 지나면 만료되니, 지연 작업이 아니라 제출을 처리하는 동안 검증하세요.
- **시크릿은 서버에 머뭅니다.** 그것은 절대 브라우저로 가지 않으며, 호출을 인증하는 것이 바로 그것입니다.

## 흔한 실수

- **클라이언트 측에서 검증하기.** `pass` 이벤트와 토큰의 존재는 UX일 뿐입니다. 모든 신뢰 결정은 여기, 서버 측에서, 당신의 시크릿으로 일어납니다.
- **`score`에 게이트 걸기.** 그것은 신뢰 값이 아니라 메타데이터입니다.

## 자주 묻는 질문

### 토큰은 얼마나 유효한가요?

발급 후 10분입니다. 제출을 처리하는 동안 검증하세요, 지연 작업이나 나중에 돌 수도 있는 큐에서가 아니라.

### 같은 토큰을 두 번 검증할 수 있나요?

아니요. 토큰은 일회용입니다. 같은 것을 다시 제출하면 `success: false`가 돌아오니, 리플레이를 직접 추적할 필요가 없습니다.

### `score`나 `game_id`를 신뢰해야 하나요?

아니요. `platform` 블록(게임, 점수, 소요 시간)은 클라이언트가 주장한 메타데이터이며 nullable입니다. 오직 `success`로만 분기하고, 절대 점수에 신뢰를 걸지 마세요.

### 이걸 하려면 SDK가 필요한가요?

아니요. 확인은 `/v1/siteverify`로 보내는 한 번의 HTTPS POST입니다. Node, Python, PHP, Go, Ruby, .NET, curl에서의 같은 호출은 [백엔드 연동 예제](/docs/integration-guides/backend-examples)에 있습니다.

## 함께 보기

- 당신의 언어로 된 이 호출은 [백엔드 연동 예제](/docs/integration-guides/backend-examples).
- 백엔드를 통째로 건너뛰려면 [호스팅 인증](/docs/hosted-verification/overview).
