Verify Endpoint

Server-side verification of CAPTCHA tokens.

Endpoint

POST https://gkcaptcha.gatekeeper.sa/api/v1/token/verify

Request

cURL
curl -X POST https://gkcaptcha.gatekeeper.sa/api/v1/token/verify \
  -H "Content-Type: application/json" \
  -d '{
    "siteKey": "pk_live_...",
    "secret": "sk_live_...",
    "token": "CAPTCHA_TOKEN_FROM_CLIENT"
  }'

Send the JSON body from your server only. Accept the visitor only when success is true and decision is not block.

Headers

HeaderRequiredDescription
Content-TypeYesMust be application/json

Body Parameters

ParameterTypeRequiredDescription
siteKeystringYesYour site key (starts with pk_live_)
secretstringYesYour secret key. Send it from your server only, never from the browser.
tokenstringYesThe token from the widget (the gk-captcha-token form field)
clientIPstringNoThe visitor's IP address, for token binding checks
userAgentstringNoThe visitor's User-Agent header, for token binding checks

Response

Success Response

json
{
  "success": true,
  "score": 0.15,
  "timestamp": 1705314600,
  "decision": "allow"
}

Failed Verification

json
{
  "success": false,
  "score": 0,
  "timestamp": 0,
  "error": "invalid or expired token",
  "reasonCode": "token_expired"
}

Response Fields

FieldTypeDescription
successbooleanWhether the token is authentic and valid for this site
scorefloatRisk score (0.0 = safe, 1.0 = bot). It is 0 when the response carries a decision.
timestampintegerWhen the token was issued (Unix seconds)
decisionstringThe verdict band: allow, borderline or block. Reject the request when it is block, even though success is true.
errorstringWhy the verification failed (only when success is false)
reasonCodestringA machine-readable reason for the failure. See the reason code table.

Error Codes

CodeDescription
invalid request bodyThe body is not valid JSON or misses siteKey, secret or token
invalid credentialsThe site key or the secret key is wrong
site is disabledThe site is disabled in the dashboard
invalid or expired tokenThe token signature is invalid or the token expired
token not valid for this siteThe token was issued for a different site key
token already usedThe token was already verified once. Each token works once.
token binding validation failedThe clientIP or userAgent does not match the visitor who solved the CAPTCHA
verification service errorA temporary server error. Retry the request.

Code Examples

Node.js / Express

server.js
// Never send your secret key to the browser.
const verifyCaptcha = async (token) => {
  const response = await fetch('https://gkcaptcha.gatekeeper.sa/api/v1/token/verify', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      siteKey: process.env.GKCAPTCHA_SITE_KEY,
      secret: process.env.GKCAPTCHA_SECRET_KEY,
      token,
    }),
  });

  // { success, score, timestamp, decision?, error?, reasonCode? }
  return response.json();
};

// Usage in Express route. The form arrives URL-encoded:
// app.use(express.urlencoded({ extended: false }))
app.post('/submit-form', async (req, res) => {
  const token = req.body['gk-captcha-token'];

  const result = await verifyCaptcha(token);

  // success only means the token is authentic. A "block" decision must be rejected.
  if (!result.success || result.decision === 'block') {
    return res.status(400).json({ error: 'CAPTCHA verification failed' });
  }

  // Process the form...
  res.json({ success: true });
});

Go

main.go
package main

import (
    "bytes"
    "encoding/json"
    "net/http"
    "os"
)

type VerifyRequest struct {
    SiteKey string `json:"siteKey"`
    Secret  string `json:"secret"`
    Token   string `json:"token"`
}

type VerifyResponse struct {
    Success    bool    `json:"success"`
    Score      float64 `json:"score"`
    Timestamp  int64   `json:"timestamp"`
    Decision   string  `json:"decision"`
    Error      string  `json:"error"`
    ReasonCode string  `json:"reasonCode"`
}

// verifyCaptcha returns true only when the token is valid and not blocked.
func verifyCaptcha(token string) (bool, error) {
    reqBody, _ := json.Marshal(VerifyRequest{
        SiteKey: os.Getenv("GKCAPTCHA_SITE_KEY"),
        Secret:  os.Getenv("GKCAPTCHA_SECRET_KEY"),
        Token:   token,
    })

    resp, err := http.Post("https://gkcaptcha.gatekeeper.sa/api/v1/token/verify",
        "application/json", bytes.NewBuffer(reqBody))
    if err != nil {
        return false, err
    }
    defer resp.Body.Close()

    var result VerifyResponse
    if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
        return false, err
    }

    // success only means the token is authentic. A "block" decision must be rejected.
    if !result.Success || result.Decision == "block" {
        return false, nil
    }
    return true, nil
}

Python / Flask

app.py
import os
import requests

def verify_captcha(token: str) -> bool:
    response = requests.post(
        'https://gkcaptcha.gatekeeper.sa/api/v1/token/verify',
        json={
            'siteKey': os.environ['GKCAPTCHA_SITE_KEY'],
            'secret': os.environ['GKCAPTCHA_SECRET_KEY'],
            'token': token,
        },
        timeout=10,
    )

    result = response.json()

    # success only means the token is authentic. A "block" decision must be rejected.
    if not result.get('success') or result.get('decision') == 'block':
        return False

    return True

# Usage in Flask
@app.route('/submit', methods=['POST'])
def submit_form():
    token = request.form.get('gk-captcha-token', '')

    if not verify_captcha(token):
        return jsonify({'error': 'Verification failed'}), 400

    # Process form...
    return jsonify({'success': True})

Best Practices

  • Always verify tokens server-side, never trust client-side results
  • Handle errors gracefully - don't block users on API failures
  • Set appropriate timeouts (recommended: 10 seconds)
  • Log verification failures for security monitoring