Después de que se complete la inserción del navegador, su servidor debe verificar el resultado con una clave API del sitio antes de permitir el registro, el pago o cualquier acción protegida.
El campo de formulario oculto puede llamarse checkify_token, pero su valor es el request_id Checkify.
En el panel de su empresa, abra Desarrollador y cree una clave API de sitio para el sitio que está integrando. Guárdelo únicamente en las variables de entorno del servidor; nunca lo envíe al navegador.
No es necesario llamar manualmente a GET /v1/qr/pass/{PASS_ID}/start. El elemento incrustado Checkify JavaScript inicia la sesión, recibe el request_id y lo escribe en su formulario automáticamente.
Tu backend solo necesita:
El frontend SDK escribe el request_id Checkify en un campo de formulario oculto después de que el usuario complete la verificación. El nombre de campo predeterminado es checkify_token; ese es el nombre del campo, no un tipo de token independiente. Envíe el valor del campo al endpoint de verificación como request_id.
La transferencia de la aplicación móvil puede devolver checkify_request_id en la URL de la página. SDK lo lee al cargar la página; su servidor sigue verificando el mismo valor de request_id.
Formulario POST desde tu frontend
{
"email": "user@example.com",
"checkify_token": "56a57761-ff5b-42f0-9c97-6c13e223e017"
}
Verifica la solicitud desde tu backend.
{
"request_id": "56a57761-ff5b-42f0-9c97-6c13e223e017",
"required_claims": ["human_verified"],
"required_fields": [],
"consume": true
}
Integración manual del frontend únicamente
Los desarrolladores de backend normalmente no llaman a /start. Utilice esta sección solo cuando cree un frontend personalizado o realice pruebas sin la incrustación. En zsh/bash, entrecomille las URL para que ? no se trate como un comodín.
Pass Start solo funciona desde un dominio web registrado. Los navegadores envían el origen automáticamente; cURL no. Envíe el origen (o X-Checkify-Site-Url) con un nombre de host que aparezca en Sitios → dominios permitidos. El nombre de host debe coincidir exactamente: checkify.me y www.checkify.me son diferentes.
Problema de prueba más común
Si /start devuelve HTTP 403, su ID de contraseña aún podría ser válido. La causa habitual es que el dominio de la solicitud no esté incluido en la lista de dominios permitidos, o que www.example.com esté registrado pero se haya utilizado example.com (o viceversa).
Utilice un sitio de prueba dedicado y pase su panel de control Checkify para realizar pruebas de integración. Las claves del sitio API utilizan el prefijo csk_; no existe un formato de clave de prueba independiente. No realice pruebas con el proceso de producción hasta que haya confirmado los flujos de éxito y denegación.
# Step A — start a session (replace PASS_ID and YOUR_REGISTERED_DOMAIN)
curl -sS \
-H "Accept: application/json" \
-H "Origin: https://YOUR_REGISTERED_DOMAIN" \
"https://checkify.me/v1/qr/pass/chk_live_YOUR_PASS_ID/start?request_type=human"
# Response includes request_id and qr_url — open qr_url and complete verification
# Step B — verify on your server (after the user completes verification)
curl -sS -X POST "https://checkify.me/v1/qr/results/verify" \
-H "Authorization: Bearer $CHECKIFY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"request_id": "PASTE_request_id_FROM_STEP_A",
"required_claims": ["human_verified"],
"consume": true
}'
Utilice su clave API del sitio únicamente en la llamada de verificación. Complete la verificación en la aplicación antes de llamar a la función de verificación; de lo contrario, recibirá el estado "pendiente".
Configure required_claims para que coincida con la prueba que requiere su acción protegida. Los nombres de las reclamaciones deben coincidir con los que Checkify aprobó para esa sesión de verificación. Las comprobaciones de edad utilizan age_over_{N} (por ejemplo, age_over_18). Se admiten umbrales dinámicos de 10 a 110 cuando la solicitud integrada utiliza el tipo de solicitud correspondiente.
| Caso de uso | Reclamaciones requeridas sugeridas | Notas |
|---|---|---|
| Bot / Reemplazo de CAPTCHA | ["human_verified"] |
Confirma que un usuario real completó el flujo Checkify. |
| Contenido para mayores de 13 años o productos orientados al público juvenil. | ["age_over_13"] |
Úselo cuando su pase solicite age_over_13. |
| Contenido para mayores de 16 años o normas regionales de edad | ["age_over_16"] |
Úselo cuando su pase solicite age_over_16. |
| Vapeo, alcohol o pago para mayores de 18 años | ["age_over_18"] |
Adapta el rango de edad a tu producto y mercado. |
| Productos restringidos para mayores de 21 años (cuando corresponda) | ["age_over_21"] |
Úselo cuando su pase solicite age_over_21. |
| Política de venta minorista Challenge 25 o más estricta | ["age_over_25"] |
Úselo cuando su pase solicite age_over_25. |
| umbral de edad personalizado | ["age_over_N"] |
Utilice age_over_N, donde N es de 10 a 110, en consonancia con el tipo de solicitud de inserción. |
El tipo de solicitud del navegador debe coincidir con la declaración que usted verifica en el servidor.
| Tipo de solicitud de inserción | reclamaciones requeridas |
|---|---|
human | ["human_verified"] |
age_over_13 | ["age_over_13"] |
age_over_16 | ["age_over_16"] |
age_over_18 | ["age_over_18"] |
age_over_21 | ["age_over_21"] |
age_over_25 | ["age_over_25"] |
age_over_N | ["age_over_N"] (N = 10–110) |
Además de required_claims, puede exigir campos de identidad aprobados concretos (por ejemplo país o franja de edad) cuando su integración los haya recogido. Pase los nombres en required_fields; si faltan en el resultado aprobado, verify devuelve missing_required_fields con missing_fields en error.details.
Llama a POST /v1/qr/results/verify con la clave API de tu sitio antes de otorgar acceso. Trata la referencia del navegador (request_id o token de sondeo heredado) como no confiable hasta que Checkify confirme el resultado.
POST https://checkify.me/v1/qr/results/verify
Authorization: Bearer YOUR_SITE_API_KEY
Content-Type: application/json
{
"request_id": "56a57761-ff5b-42f0-9c97-6c13e223e017",
"required_claims": ["human_verified"],
"consume": true
}
También puedes enviar
token
en lugar de
request_id.
La clave API está limitada a su sitio Checkify; no debe enviar site_id en el cuerpo de la solicitud.
# checkify_token from your form POST is sent as request_id
curl -sS -X POST "https://checkify.me/v1/qr/results/verify" \
-H "Authorization: Bearer $CHECKIFY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"request_id": "56a57761-ff5b-42f0-9c97-6c13e223e017",
"required_claims": ["human_verified"],
"consume": true
}'
import express from "express";
const app = express();
app.use(express.json());
const CHECKIFY_API_KEY = process.env.CHECKIFY_API_KEY;
const CHECKIFY_BASE_URL = process.env.CHECKIFY_BASE_URL || "https://checkify.me";
async function verifyCheckifyResult(requestId, requiredClaims = ["human_verified"]) {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 10000);
try {
const res = await fetch(`${CHECKIFY_BASE_URL}/v1/qr/results/verify`, {
method: "POST",
headers: {
Authorization: `Bearer ${CHECKIFY_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
request_id: requestId,
required_claims: requiredClaims,
consume: true,
}),
signal: controller.signal,
});
let body = null;
try {
body = await res.json();
} catch {
body = null;
}
if (!res.ok) {
const details = body?.error?.details || {};
console.warn("Checkify verification failed", {
requestId,
code: body?.error?.code,
missingClaims: details.missing_claims,
missingFields: details.missing_fields,
reason: details.reason,
});
return {
allow: false,
reason: body?.error?.code || "verification_failed",
userMessage: "Verification could not be completed for this action.",
};
}
if (!body || body.status === "pending" || body.success === false) {
return {
allow: false,
reason: body?.status || "pending",
userMessage: "Verification could not be completed for this action.",
};
}
const approved = requiredClaims.every(
(claim) => body.approved_claims?.[claim] === true
);
return {
allow: approved,
reason: approved ? "approved" : "verification_failed",
userMessage: approved
? null
: "Verification could not be completed for this action.",
result: body,
};
} finally {
clearTimeout(timeout);
}
}
app.post("/signup", async (req, res) => {
const requestId = (req.body.checkify_token || "").trim();
if (!requestId) {
return res.status(403).json({ error: "Verification required" });
}
try {
const verdict = await verifyCheckifyResult(requestId);
if (!verdict.allow) {
return res.status(403).json({ error: verdict.userMessage });
}
return res.json({ ok: true });
} catch (err) {
console.error("Checkify verification unavailable", err);
return res.status(403).json({
error: "Verification is temporarily unavailable. Please try again.",
});
}
});
import { Checkify } from "@checkify/server";
const checkify = new Checkify({
apiKey: process.env.CHECKIFY_SITE_API_KEY,
});
app.post("/signup", async (req, res) => {
const requestId = String(req.body.checkify_token || "").trim();
if (!requestId) {
return res.status(403).json({ error: "Verification required" });
}
try {
const result = await checkify.verifyHuman({ requestId, consume: true });
if (!result.success || !result.approved) {
return res.status(403).json({
error: "Verification could not be completed for this action.",
});
}
return res.json({ ok: true });
} catch (err) {
console.error("Checkify verification failed", err);
return res.status(403).json({
error: "Verification is temporarily unavailable. Please try again.",
});
}
});
import os
import httpx
from fastapi import FastAPI, HTTPException
app = FastAPI()
CHECKIFY_API_KEY = os.environ["CHECKIFY_API_KEY"]
CHECKIFY_BASE_URL = os.getenv("CHECKIFY_BASE_URL", "https://checkify.me")
USER_MESSAGE = "Verification could not be completed for this action."
UNAVAILABLE = "Verification is temporarily unavailable. Please try again."
def verify_checkify_result(request_id: str, required_claims=None) -> dict:
required_claims = required_claims or ["human_verified"]
try:
response = httpx.post(
f"{CHECKIFY_BASE_URL}/v1/qr/results/verify",
headers={
"Authorization": f"Bearer {CHECKIFY_API_KEY}",
"Content-Type": "application/json",
},
json={
"request_id": request_id,
"required_claims": required_claims,
"consume": True,
},
timeout=10.0,
)
except httpx.RequestError as exc:
print("Checkify verification unavailable", exc)
return {"allow": False, "reason": "unavailable", "user_message": UNAVAILABLE}
try:
body = response.json()
except ValueError:
body = None
if response.status_code >= 400:
error = (body or {}).get("error") if isinstance(body, dict) else None
details = (error or {}).get("details", {})
print(
"Checkify verification failed",
{
"request_id": request_id,
"code": (error or {}).get("code"),
"missing_claims": details.get("missing_claims"),
"missing_fields": details.get("missing_fields"),
"reason": details.get("reason"),
},
)
return {
"allow": False,
"reason": (error or {}).get("code", "verification_failed"),
"user_message": USER_MESSAGE,
}
if not isinstance(body, dict) or body.get("status") == "pending" or body.get("success") is False:
return {"allow": False, "reason": "pending", "user_message": USER_MESSAGE}
approved = all((body.get("approved_claims") or {}).get(claim) is True for claim in required_claims)
return {
"allow": approved,
"reason": "approved" if approved else "verification_failed",
"user_message": None if approved else USER_MESSAGE,
"result": body,
}
@app.post("/signup")
def signup(email: str, checkify_token: str):
request_id = (checkify_token or "").strip()
if not request_id:
raise HTTPException(status_code=403, detail="Verification required")
verdict = verify_checkify_result(request_id)
if not verdict["allow"]:
raise HTTPException(status_code=403, detail=verdict["user_message"])
return {"ok": True}
<?php
$checkifyApiKey = getenv('CHECKIFY_API_KEY');
$baseUrl = getenv('CHECKIFY_BASE_URL') ?: 'https://checkify.me';
function verify_checkify_result(string $requestId, array $requiredClaims = ['human_verified']): array {
global $checkifyApiKey, $baseUrl;
$payload = json_encode([
'request_id' => $requestId,
'required_claims' => $requiredClaims,
'consume' => true,
]);
$ch = curl_init($baseUrl . '/v1/qr/results/verify');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $checkifyApiKey,
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_TIMEOUT => 10,
]);
$raw = curl_exec($ch);
$status = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($raw === false) {
error_log('Checkify verification unavailable');
return ['allow' => false, 'user_message' => 'Verification is temporarily unavailable. Please try again.'];
}
$body = json_decode($raw ?: 'null', true);
if ($status >= 400) {
$error = is_array($body) ? ($body['error'] ?? null) : null;
$details = is_array($error) ? ($error['details'] ?? []) : [];
error_log('Checkify verification failed: ' . json_encode([
'code' => is_array($error) ? ($error['code'] ?? null) : null,
'missing_claims' => $details['missing_claims'] ?? null,
]));
return ['allow' => false, 'user_message' => 'Verification could not be completed for this action.'];
}
if (!is_array($body) || ($body['status'] ?? '') === 'pending' || ($body['success'] ?? true) === false) {
return ['allow' => false, 'user_message' => 'Verification could not be completed for this action.'];
}
foreach ($requiredClaims as $claim) {
if (($body['approved_claims'][$claim] ?? false) !== true) {
return ['allow' => false, 'user_message' => 'Verification could not be completed for this action.'];
}
}
return ['allow' => true, 'result' => $body];
}
$requestId = trim($_POST['checkify_token'] ?? '');
if ($requestId === '') {
http_response_code(403);
echo json_encode(['error' => 'Verification required']);
exit;
}
$verdict = verify_checkify_result($requestId);
if (!$verdict['allow']) {
http_response_code(403);
echo json_encode(['error' => $verdict['user_message']]);
exit;
}
// continue protected action...
package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"os"
"strings"
"time"
)
type verifyResponse struct {
Success bool `json:"success"`
Status string `json:"status"`
Message string `json:"message"`
ApprovedClaims map[string]bool `json:"approved_claims"`
}
type errorResponse struct {
Error struct {
Code string `json:"code"`
Message string `json:"message"`
Details map[string]interface{} `json:"details"`
} `json:"error"`
}
func verifyCheckifyResult(requestID string, requiredClaims []string) (bool, string, error) {
apiKey := os.Getenv("CHECKIFY_API_KEY")
baseURL := os.Getenv("CHECKIFY_BASE_URL")
if baseURL == "" {
baseURL = "https://checkify.me"
}
payload, _ := json.Marshal(map[string]interface{}{
"request_id": requestID,
"required_claims": requiredClaims,
"consume": true,
})
req, err := http.NewRequest(http.MethodPost, baseURL+"/v1/qr/results/verify", bytes.NewReader(payload))
if err != nil {
return false, "", err
}
req.Header.Set("Authorization", "Bearer "+apiKey)
req.Header.Set("Content-Type", "application/json")
client := &http.Client{Timeout: 10 * time.Second}
res, err := client.Do(req)
if err != nil {
return false, "", err
}
defer res.Body.Close()
if res.StatusCode >= 400 {
var errBody errorResponse
_ = json.NewDecoder(res.Body).Decode(&errBody)
fmt.Printf("Checkify verification failed code=%s details=%v\n", errBody.Error.Code, errBody.Error.Details)
return false, "Verification could not be completed for this action.", nil
}
var body verifyResponse
if err := json.NewDecoder(res.Body).Decode(&body); err != nil {
return false, "Verification could not be completed for this action.", nil
}
if body.Status == "pending" || !body.Success {
return false, "Verification could not be completed for this action.", nil
}
for _, claim := range requiredClaims {
if !body.ApprovedClaims[claim] {
return false, "Verification could not be completed for this action.", nil
}
}
return true, "", nil
}
func signupHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
return
}
if err := r.ParseForm(); err != nil {
http.Error(w, "invalid form", http.StatusBadRequest)
return
}
requestID := strings.TrimSpace(r.FormValue("checkify_token"))
if requestID == "" {
http.Error(w, "Verification required", http.StatusForbidden)
return
}
allowed, message, err := verifyCheckifyResult(requestID, []string{"human_verified"})
if err != nil {
fmt.Println("Checkify verification unavailable", err)
http.Error(w, "Verification is temporarily unavailable. Please try again.", http.StatusForbidden)
return
}
if !allowed {
http.Error(w, message, http.StatusForbidden)
return
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte(`{"ok":true}`))
}
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
var apiKey = Environment.GetEnvironmentVariable("CHECKIFY_API_KEY");
var baseUrl = Environment.GetEnvironmentVariable("CHECKIFY_BASE_URL") ?? "https://checkify.me";
async Task<(bool Allow, string UserMessage)> VerifyCheckifyAsync(string requestId)
{
using var client = new HttpClient { Timeout = TimeSpan.FromSeconds(10) };
using var req = new HttpRequestMessage(HttpMethod.Post, $"{baseUrl}/v1/qr/results/verify");
req.Headers.Authorization = new AuthenticationHeaderValue("Bearer", apiKey);
req.Content = new StringContent(JsonSerializer.Serialize(new
{
request_id = requestId,
required_claims = new[] { "human_verified" },
consume = true,
}), Encoding.UTF8, "application/json");
HttpResponseMessage res;
try
{
res = await client.SendAsync(req);
}
catch (Exception ex)
{
Console.Error.WriteLine($"Checkify verification unavailable: {ex.Message}");
return (false, "Verification is temporarily unavailable. Please try again.");
}
var raw = await res.Content.ReadAsStringAsync();
using var doc = JsonDocument.Parse(string.IsNullOrWhiteSpace(raw) ? "{}" : raw);
var root = doc.RootElement;
if (!res.IsSuccessStatusCode)
{
if (root.TryGetProperty("error", out var err) && err.TryGetProperty("details", out var details))
{
Console.WriteLine($"Checkify verification failed details={details}");
}
return (false, "Verification could not be completed for this action.");
}
if (root.TryGetProperty("status", out var status) && status.GetString() == "pending")
{
return (false, "Verification could not be completed for this action.");
}
if (root.TryGetProperty("approved_claims", out var claims)
&& claims.TryGetProperty("human_verified", out var human)
&& human.GetBoolean())
{
return (true, string.Empty);
}
return (false, "Verification could not be completed for this action.");
}
// In your signup endpoint:
// var requestId = form["checkify_token"];
// var (allow, message) = await VerifyCheckifyAsync(requestId);
// if (!allow) return Results.Forbid();
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.net.URI;
import java.net.http.*;
import java.time.Duration;
public class CheckifyVerify {
private static final String API_KEY = System.getenv("CHECKIFY_API_KEY");
private static final String BASE_URL =
System.getenv().getOrDefault("CHECKIFY_BASE_URL", "https://checkify.me");
private static final ObjectMapper MAPPER = new ObjectMapper();
static boolean verifyCheckifyResult(String requestId) {
String payload = "{\"request_id\":\"" + requestId + "\","
+ "\"required_claims\":[\"human_verified\"],\"consume\":true}";
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(BASE_URL + "/v1/qr/results/verify"))
.timeout(Duration.ofSeconds(10))
.header("Authorization", "Bearer " + API_KEY)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(payload))
.build();
try {
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
JsonNode root = MAPPER.readTree(response.body() == null ? "{}" : response.body());
if (response.statusCode() >= 400) {
System.err.println("Checkify verification failed: " + root);
return false;
}
if ("pending".equals(root.path("status").asText()) || !root.path("success").asBoolean(false)) {
return false;
}
return root.path("approved_claims").path("human_verified").asBoolean(false);
} catch (Exception ex) {
System.err.println("Checkify verification unavailable: " + ex.getMessage());
return false;
}
}
}
Utilice consume: true para acciones protegidas finales. — pago, registro, restablecimiento de contraseña, compra con restricción de edad, acceso a contenido protegido
Use consumir: false solo para — Pruebas, depuración o comprobaciones no definitivas cuyo resultado debe verificarse posteriormente.
Para acciones reguladas o de alto riesgo, se recomienda usar consume: true para que el mismo resultado de verificación no pueda reutilizarse para múltiples decisiones protegidas.
Si su sistema backend no puede acceder a Checkify, no permita el pago con restricción de edad, el acceso a juegos de azar, el acceso a contenido para adultos, la compra de cigarrillos electrónicos o alcohol, u otras acciones protegidas sin un resultado confirmado del servidor.
Utilice tiempos de espera HTTP cortos (por ejemplo, 10 segundos). Reintente una o dos veces en caso de errores transitorios 5xx o de red, y luego deniegue el acceso. Registre los incidentes en el servidor y muestre mensajes seguros para el usuario. No exponga los detalles internos del error Checkify a los clientes.
try {
const verdict = await verifyCheckifyResult(requestId);
if (!verdict.allow) {
return res.status(403).json({ error: "Verification required" });
}
// Continue protected action
} catch (err) {
console.error("Checkify verification unavailable", err);
return res.status(403).json({
error: "Verification is temporarily unavailable. Please try again.",
});
}
The Checkify monorepo includes @checkify/server (Node.js) and checkify-server (Python) packages for server-side verify calls. There is no published OpenAPI spec or Postman collection yet — use the examples on this page and POST /v1/qr/results/verify directly.
@checkify/server v1.0.0 está publicado en npm. El paquete Python checkify-server se incluye en el monorepo Checkify SDK.
Las verificaciones completadas caducan después de QR_RESULT_MAX_AGE_SECONDS (valor predeterminado: 900 segundos / 15 minutos). Tras la caducidad, verify devuelve result_expired; solicite al usuario que vuelva a verificar.
Los parámetros JavaScript y SDK gestionan el estado de la sesión para las incrustaciones estándar. Úselos únicamente cuando cree una interfaz personalizada que llame manualmente a GET /v1/qr/pass/{pass_id}/start.
GET /v1/qr/status?token={poll_token}GET /v1/qr/status/request/{request_id}?status_token={status_token}En caso de éxito, Checkify devuelve HTTP 200 con success: true y status: completed. Permita el acceso solo cuando se cumplan los requisitos (por ejemplo, human_verified: true).
{
"success": true,
"status": "completed",
"message": "Verification result confirmed",
"request_id": "56a57761-ff5b-42f0-9c97-6c13e223e017",
"site_id": "YOUR_SITE_ID",
"business_id": "YOUR_BUSINESS_ID",
"approved_claims": {
"human_verified": true
},
"approved_fields": [],
"signed_result": {
"payload": { "...": "..." },
"signature": "...",
"signature_algorithm": "EdDSA",
"key_id": "checkify:default"
}
}
El objeto signed_result permite a su backend conservar un registro de auditoría a prueba de manipulación de que Checkify aprobó el claim requerido en el momento de la verificación. La mayoría de integraciones solo necesitan approved_claims. Negocios regulados o de alto riesgo también pueden almacenar signed_result. proof_metadata anidado puede incluir evidencia criptográfica u operativa (por ejemplo metadatos de assurance, tipos de evidencia o hashes de consentimiento), no documentos de identidad ni fecha de nacimiento en bruto. No trate los campos de assurance anidados como certificación ni garantía de confianza; revise certification_status y su propia política. No almacene más información personal de la necesaria.
Si el cliente aún no ha finalizado en la aplicación, Checkify devuelve HTTP 200 con success: false y status: pending. Se deniegan las acciones protegidas y se solicita al usuario que complete la verificación.
{
"success": false,
"status": "pending",
"message": "Verification is not completed yet",
"request_id": "56a57761-ff5b-42f0-9c97-6c13e223e017",
"approved_claims": {},
"signed_result": null
}
| Campo | Significado |
|---|---|
success | true cuando se completa la verificación y requisitos coincidente |
status | completed o pending |
approved_claims | Reclamaciones Checkify aprobadas, p.e. human_verified: true |
signed_result | Carga útil firmada opcional para pistas de auditoría |
Cuando no se puede realizar la verificación, Checkify devuelve un código HTTP 4xx/5xx con un cuerpo estructurado JSON. Compruebe el código de error y registre los detalles del error en el servidor. Devuelva mensajes genéricos a los usuarios finales.
{
"success": false,
"error": {
"code": "verification_failed",
"message": "The verification did not include all required claims.",
"details": {
"missing_claims": ["age_over_18"]
}
}
}
| Código | HTTP | Significado | Acción recomendada |
|---|---|---|---|
missing_authorization | 401 | No se envió ningún encabezado de autorización. | Enviar autorización: Bearer YOUR_SITE_API_KEY solo desde el código del servidor. |
invalid_token | 401 | Falta el token de portador, está mal formado o no es una clave API de sitio válida. | Verifique la clave en el panel de control de su empresa y guárdela en las variables de entorno. |
expired_token | 401 | La clave del sitio API ha sido revocada. | Crea una nueva clave de sitio API y rótala en tus servidores. |
missing_required_field | 400 / 422 | El request_id o token no aparece en el cuerpo de JSON. | Pasa el request_id del campo oculto de tu formulario, o el token de sondeo si tu integración aún lo utiliza. |
invalid_request_id | 400 | La referencia no se pudo analizar o está vacía después de la normalización. | Asegúrese de que su interfaz de usuario envíe el request_id Checkify sin cambios. |
result_not_found | 404 | No existe ninguna solicitud de verificación para esa referencia. | Rechazar la acción. Es posible que el usuario haya manipulado el campo oculto o enviado una sesión antigua. |
result_expired | 410 | La verificación se completó hace demasiado tiempo como para confiar en ella para esta acción. | Pídele al usuario que vuelva a escanear y que llame a verify con el nuevo request_id. |
verification_failed | 403 / 409 | Desajuste de sitio/negocio, resultado ya consumido (HTTP 409) o un fallo genérico de verificación. | Deniegue el acceso. Inspeccione error.details. Para huecos de claims/campos, vea missing_required_claims y missing_required_fields. |
missing_required_claims | 403 | developers_server.err_missing_required_claims_meaning | developers_server.err_missing_required_claims_action |
missing_required_fields | 403 | developers_server.err_missing_required_fields_meaning | developers_server.err_missing_required_fields_action |
unknown_required_attributes | 400 | developers_server.err_unknown_required_attributes_meaning | developers_server.err_unknown_required_attributes_action |
business_not_operational | 403 | La cuenta comercial está bloqueada, archivada o no está operativa. | Comuníquese con el propietario del negocio o con el soporte de Checkify. No permita acciones protegidas hasta que la cuenta esté activa. |
validation_errors | 422 | El cuerpo JSON no superó la validación del esquema (HTTP 422). | Inspeccione error.details.validation_errors para detectar mensajes a nivel de campo. Corrija los tipos request_id, required_claims o required_fields antes de volver a intentarlo. |
rate_limited | 429 | Demasiadas llamadas de verificación en un corto período de tiempo. | Reintentar con retroceso exponencial. Verificar solo en acciones protegidas, no en cada visualización de página. |
server_error | 500+ | Checkify no pudo completar la verificación debido a un problema temporal del servidor. | Inténtalo de nuevo una o dos veces, luego cierra la sesión si falla y registra el incidente. |
Actualmente, la verificación del servidor es síncrona: el backend verifica el request_id cuando el usuario envía la acción protegida. Para los flujos de pago, registro y control de acceso, este es el enfoque recomendado. No se requieren webhooks de verificación de propósito general para las integraciones estándar. GoHighLevel y otras integraciones de socios pueden usar una configuración de webhook saliente independiente en el panel de control empresarial.
Trate el campo oculto como una referencia no confiable, no como una prueba. Verifique siempre con la clave API de su sitio en el servidor antes de otorgar acceso. Usar
consume: true
para acciones únicas como registrarse o restablecer contraseña.