Nadat de browser-embed is voltooid, moet je server het resultaat verifiëren met een site-API-sleutel voordat je signup, checkout of een andere beschermde actie toestaat.
Het verborgen formulierveld kan checkify_token heten, maar de waarde is de Checkify request_id.
Open in je bedrijfsdashboard Developer en maak een site-API-sleutel aan voor de site die je integreert. Bewaar die alleen in serveromgevingsvariabelen — stuur hem nooit naar de browser.
Je hoeft GET /v1/qr/pass/{PASS_ID}/start niet handmatig aan te roepen. De Checkify JavaScript-embed start de sessie, ontvangt de request_id en schrijft die automatisch in je formulier.
Je backend hoeft alleen:
De frontend SDK schrijft de Checkify request_id in een verborgen formulierveld nadat de gebruiker verificatie heeft voltooid. De standaardveldnaam is checkify_token — dat is de veldnaam, geen apart tokentype. Stuur de veldwaarde naar het verify-endpoint als request_id.
Mobile app-handoff kan checkify_request_id teruggeven in de pagina-URL. De SDK leest die bij het laden; je server verifieert nog steeds dezelfde request_id-waarde.
Form POST vanaf je frontend
{
"email": "user@example.com",
"checkify_token": "56a57761-ff5b-42f0-9c97-6c13e223e017"
}
Verify-verzoek vanaf je backend
{
"request_id": "56a57761-ff5b-42f0-9c97-6c13e223e017",
"required_claims": ["human_verified"],
"required_fields": [],
"consume": true
}
Alleen handmatige frontend-integratie
Backend-ontwikkelaars roepen /start normaal niet aan. Gebruik dit gedeelte alleen bij een eigen frontend of bij testen zonder embed. Plaats URL’s tussen aanhalingstekens in zsh/bash zodat ? niet als glob wordt behandeld.
Pass start werkt alleen vanaf een geregistreerd websitedomein. Browsers sturen Origin automatisch; cURL doet dat niet. Stuur Origin (of X-Checkify-Site-Url) met een hostnaam die onder Sites → toegestane domeinen staat. De hostnaam moet exact overeenkomen — checkify.me en www.checkify.me zijn verschillend.
Meest voorkomende testprobleem
Als /start HTTP 403 teruggeeft, kan je Pass ID nog steeds geldig zijn. Meestal staat het verzoekdomein niet in toegestane domeinen, of is www.example.com geregistreerd terwijl example.com werd gebruikt (of andersom).
Gebruik een dedicated testsite en Pass in je Checkify-dashboard voor integratietests. Site-API-sleutels gebruiken het voorvoegsel csk_; er is geen apart test-sleutelformaat. Test niet tegen productie-checkout tot je zowel succes- als weigeringsflows hebt bevestigd.
# 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
}'
Gebruik je site-API-sleutel alleen bij de verify-aanroep. Voltooi verificatie in de app vóór je verify aanroept — anders krijg je status pending.
Stel required_claims in zodat die overeenkomen met de proof die je beschermde actie nodig heeft. Claimnamen moeten overeenkomen met wat Checkify voor die verificatiesessie heeft goedgekeurd. Leeftijdschecks gebruiken age_over_{N} (bijvoorbeeld age_over_18). Dynamische drempels van 10 tot en met 110 worden ondersteund wanneer de embed het bijbehorende verzoektype vraagt.
| Usecase | Voorgestelde required_claims | Notities |
|---|---|---|
| Bot-/CAPTCHA-vervanging | ["human_verified"] |
Bevestigt dat een echte gebruiker de Checkify-flow heeft voltooid. |
| 13+ content of jeugdgerichte producten | ["age_over_13"] |
Gebruik wanneer je Pass age_over_13 vraagt. |
| 16+ content of regionale leeftijdsregels | ["age_over_16"] |
Gebruik wanneer je Pass age_over_16 vraagt. |
| Vape, alcohol of 18+ checkout | ["age_over_18"] |
Laat de leeftijdsdrempel aansluiten op je product en markt. |
| 21+ beperkte producten (waar van toepassing) | ["age_over_21"] |
Gebruik wanneer je Pass age_over_21 vraagt. |
| Challenge 25 of strenger retailbeleid | ["age_over_25"] |
Gebruik wanneer je Pass age_over_25 vraagt. |
| Aangepaste leeftijdsdrempel | ["age_over_N"] |
Gebruik age_over_N waarbij N 10–110 is, afgestemd op je embed-verzoektype. |
Het browser-verzoektype moet overeenkomen met de claim die je server-side verifieert.
| Embed-verzoektype | required_claims |
|---|---|
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) |
Naast required_claims kun je specifieke goedgekeurde identiteitsvelden vereisen (bijvoorbeeld land of leeftijdsband) wanneer je integratie die heeft verzameld. Geef veldnamen door in required_fields — ontbreekt er een in het goedgekeurde resultaat, dan retourneert verify verification_failed met missing_fields in error.details.
Roep POST /v1/qr/results/verify aan met je site-API-sleutel voordat je toegang verleent. Behandel de browserreferentie (request_id of legacy poll-token) als onbetrouwbaar tot Checkify het resultaat bevestigt.
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
}
Je kunt ook
token
sturen in plaats van
request_id.
De API-sleutel is gekoppeld aan je Checkify-site — je stuurt geen site_id in de request body.
# 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;
}
}
}
Gebruik consume: true voor definitieve beschermde acties — checkout, signup, wachtwoordreset, leeftijdsgebonden aankoop, toegang tot beschermde content
Gebruik consume: false alleen voor — testen, debuggen of niet-definitieve checks waarbij het resultaat later opnieuw moet worden geverifieerd
Voor gereguleerde of risicovolle acties geef je de voorkeur aan consume: true zodat hetzelfde verificatieresultaat niet voor meerdere beschermde beslissingen kan worden hergebruikt.
Als je backend Checkify niet kan bereiken, sta geen leeftijdsbeperkte checkout, goktoegang, adult content, vape- of alcoholaankoop of andere beschermde acties toe zonder een bevestigd server-side resultaat.
Gebruik korte HTTP-timeouts (bijvoorbeeld 10 seconden). Probeer een of twee keer opnieuw bij tijdelijke 5xx- of netwerkfouten en weiger daarna toegang. Log incidenten server-side en toon gebruikersveilige berichten. Stel geen interne Checkify-foutdetails bloot aan klanten.
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.",
});
}
Gebruik @checkify/server (npm) of checkify-server (Python) voor getypeerde verify-helpers, of roep POST /v1/qr/results/verify rechtstreeks aan. Er is nog geen gepubliceerde OpenAPI-spec — gebruik de voorbeelden op deze pagina.
@checkify/server v1.0.0 is gepubliceerd op npm. Het Python-pakket checkify-server zit in de Checkify SDK-monorepo.
Voltooide verificaties verlopen na QR_RESULT_MAX_AGE_SECONDS (standaard 900 seconden / 15 minuten). Na verstrijken retourneert verify result_expired — vraag de gebruiker opnieuw te verifiëren.
De JavaScript SDK beheert sessiestatus voor standaardembeds. Gebruik deze alleen wanneer je een eigen frontend bouwt die GET /v1/qr/pass/{pass_id}/start handmatig aanroept.
GET /v1/qr/status?token={poll_token}GET /v1/qr/status/request/{request_id}?status_token={status_token}Bij succes retourneert Checkify HTTP 200 met success: true en status: completed. Sta toegang alleen toe wanneer vereiste claims aanwezig zijn (bijvoorbeeld 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"
}
}
Het signed_result-object laat je backend een manipulatiebestendig auditrecord bewaren dat Checkify de vereiste claim op het moment van verificatie heeft goedgekeurd. De meeste integraties hebben alleen approved_claims nodig. Gereguleerde of risicovolle bedrijven kunnen signed_result ook opslaan voor audit. Sla niet meer persoonsgegevens op dan nodig.
Als de klant nog niet klaar is in de app, retourneert Checkify HTTP 200 met success: false en status: pending. Weiger beschermde acties en vraag de gebruiker verificatie te voltooien.
{
"success": false,
"status": "pending",
"message": "Verification is not completed yet",
"request_id": "56a57761-ff5b-42f0-9c97-6c13e223e017",
"approved_claims": {},
"signed_result": null
}
| Veld | Betekenis |
|---|---|
success | true wanneer verificatie is voltooid en requirements overeenkomen |
status | completed of pending |
approved_claims | Claims die Checkify heeft goedgekeurd, bijv. human_verified: true |
signed_result | Optionele ondertekende payload voor audittrails |
Wanneer verificatie niet kan doorgaan, retourneert Checkify HTTP 4xx/5xx met een gestructureerde JSON-body. Controleer error.code en log error.details server-side. Geef generieke berichten terug aan eindgebruikers.
{
"success": false,
"error": {
"code": "verification_failed",
"message": "The verification did not include all required claims.",
"details": {
"missing_claims": ["age_over_18"]
}
}
}
| Code | HTTP | Betekenis | Aanbevolen actie |
|---|---|---|---|
missing_authorization | 401 | Er is geen Authorization-header gestuurd. | Stuur Authorization: Bearer YOUR_SITE_API_KEY alleen vanuit server-side code. |
invalid_token | 401 | Bearer-token ontbreekt, is ongeldig of is geen geldige site-API-sleutel. | Controleer de sleutel in je bedrijfsdashboard en bewaar die in omgevingsvariabelen. |
expired_token | 401 | De site-API-sleutel is ingetrokken. | Maak een nieuwe site-API-sleutel aan en roteer die op je servers. |
missing_required_field | 400 / 422 | request_id of token ontbreekt in de JSON-body. | Geef de request_id uit je verborgen formulierveld door, of het poll-token als je integratie dat nog gebruikt. |
invalid_request_id | 400 | De referentie kon niet worden geparseerd of is leeg na normalisatie. | Zorg dat je frontend de Checkify request_id ongewijzigd meestuurt. |
result_not_found | 404 | Er bestaat geen verificatieverzoek voor die referentie. | Weiger de actie. De gebruiker kan het verborgen veld hebben gemanipuleerd of een oude sessie hebben ingestuurd. |
result_expired | 410 | De verificatie is te lang geleden voltooid om voor deze actie te vertrouwen. | Vraag de gebruiker opnieuw te scannen en roep verify aan met de nieuwe request_id. |
verification_failed | 403 / 409 | Verificatie is afgerond maar voldeed niet aan je vereiste claims/velden, hoort bij een andere site, of is al geconsumeerd. | Weiger toegang. Inspecteer error.details op missing_claims, missing_fields of reason. |
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 | Het bedrijfsaccount is vergrendeld, gearchiveerd of niet operationeel. | Neem contact op met de bedrijfseigenaar of Checkify-support. Sta geen beschermde acties toe tot het account actief is. |
validation_errors | 422 | De JSON-body is niet geslaagd voor schema-validatie (HTTP 422). | Inspecteer error.details.validation_errors voor veldberichten. Corrigeer request_id-, required_claims- of required_fields-types vóór opnieuw proberen. |
rate_limited | 429 | Te veel verify-aanroepen in een kort tijdsvenster. | Probeer opnieuw met exponentiële backoff. Verifieer alleen bij beschermde acties, niet bij elke paginaweergave. |
server_error | 500+ | Checkify kon verificatie niet voltooien door een tijdelijk serverprobleem. | Probeer een of twee keer opnieuw, fail closed daarna en log het incident. |
Momenteel is serververificatie synchroon: je backend verifieert de request_id wanneer de gebruiker de beschermde actie indient. Voor checkout, signup en toegangscontrole is dit de aanbevolen aanpak. Algemene verificatiewebhooks zijn niet vereist voor standaardintegraties. GoHighLevel en andere partnerintegraties kunnen aparte outbound-webhookconfiguratie in het bedrijfsdashboard gebruiken.
Behandel het verborgen veld als een onbetrouwbare referentie, niet als bewijs. Verifieer altijd met je site-API-sleutel op de server voordat je toegang verleent. Gebruik
consume: true
voor eenmalige acties zoals signup of wachtwoordreset.