Checkify
مستندات المطور

التحقق على الواجهة الخلفية لديك

بعد اكتمال تضمين المتصفح، يجب على الخادم الخاص بك التحقق من النتيجة باستخدام مفتاح الموقع API قبل السماح بالتسجيل أو الخروج أو أي إجراء محمي.

Server verification in 5 minutes

  1. Add the Checkify browser embed to your page.
  2. The embed starts the Checkify session and writes the verification reference into your form.
  3. Your backend reads the hidden field value.
  4. Your backend sends that value to POST /v1/qr/results/verify as request_id.
  5. If the required claims are approved, allow the protected action.
  6. Never trust frontend state alone.

The hidden form field may be named checkify_token, but its value is the Checkify request_id.

End-to-end flow

1. قم بإنشاء موقع API مفتاح

في لوحة معلومات عملك، افتح Developer وقم بإنشاء مفتاح موقع API للموقع الذي تقوم بدمجه. قم بتخزينه في متغيرات بيئة الخادم فقط — لا تقم أبدًا بشحنه إلى المتصفح.

Using the frontend SDK?

You do not need to manually call GET /v1/qr/pass/{PASS_ID}/start. The Checkify JavaScript embed starts the session, receives the request_id, and writes it into your form automatically.

Your backend only needs to:

2. Receive the request_id on your server

The frontend SDK writes the Checkify request_id into a hidden form field after the user completes verification. The default field name is checkify_token — that is the field name, not a separate token type. Send the field value to the verify endpoint as request_id.

Mobile app handoff may return checkify_request_id in the page URL. The SDK reads it on load; your server still verifies the same request_id value.

Form POST from your frontend

{
  "email": "user@example.com",
  "checkify_token": "56a57761-ff5b-42f0-9c97-6c13e223e017"
}

Verify request from your backend

{
  "request_id": "56a57761-ff5b-42f0-9c97-6c13e223e017",
  "required_claims": ["human_verified"],
  "required_fields": [],
  "consume": true
}

Manual frontend integration only

3. Manual testing with cURL

Backend developers normally do not call /start. Use this section only when building a custom frontend or testing without the embed. Quote URLs in zsh/bash so ? is not treated as a glob.

Origin header is required for /start

Pass start only works from a registered website domain. Browsers send Origin automatically; cURL does not. Send Origin (or X-Checkify-Site-Url) with a hostname listed under Sites → allowed domains. The hostname must match exactly — checkify.me and www.checkify.me are different.

Most common testing issue

If /start returns HTTP 403, your Pass ID may still be valid. The usual cause is that the request domain is not listed in allowed domains, or www.example.com was registered but example.com was used (or vice versa).

Use a dedicated test site and Pass in your Checkify dashboard for integration testing. Site API keys use the csk_ prefix; there is no separate test-key format. Do not test against production checkout until you have confirmed both success and denial flows.

# 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
  }'

Use your site API key only on the verify call. Complete verification in the app before calling verify — otherwise you will get status pending.

Required claims by use case

Set required_claims to match the proof your protected action needs. Claim names must match what Checkify approved for that verification session. Age checks use age_over_{N} (for example age_over_18). Dynamic thresholds from 10 through 110 are supported when the embed requests the matching request type.

Use case Suggested required_claims Notes
Bot / CAPTCHA replacement ["human_verified"] Confirms a real user completed the Checkify flow.
Vape, alcohol, or 18+ checkout ["age_over_18"] Match the age threshold to your product and market.
21+ restricted products (where applicable) ["age_over_21"] Use when your Pass requests age_over_21.
Challenge 25 or stricter retail policy ["age_over_25"] Use when your Pass requests age_over_25.
Custom age threshold ["age_over_N"] Use age_over_N where N is 10–110, aligned with your embed request type.

Embed request type → server claim

The browser request type must match the claim you verify server-side.

Embed request typerequired_claims
human["human_verified"]
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)

Optional required_fields

Besides required_claims, you may require specific approved identity fields (for example country or age band) when your integration collected them. Pass field names in required_fields — if any are missing from the approved result, verify returns verification_failed with missing_fields in error.details.

4. Call verify on your server

Call POST /v1/qr/results/verify with your site API key before granting access. Treat the browser reference (request_id or legacy poll token) as untrusted until Checkify confirms the result.

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
}

You can also send token instead of request_id. The API key is scoped to your Checkify site — you do not send site_id in the request body.

Implementation examples

# 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
  }'

When to use consume

Use consume: true for final protected actions — checkout, signup, password reset, age-gated purchase, protected content access

Use consume: false only for — testing, debugging, or non-final checks where the result must be verified again later

For regulated or high-risk actions, prefer consume: true so the same verification result cannot be reused for multiple protected decisions.

Fail closed for regulated actions

If your backend cannot reach Checkify, do not allow age-restricted checkout, gambling access, adult content access, vape or alcohol purchase, or other protected actions without a confirmed server-side result.

Use short HTTP timeouts (for example 10 seconds). Retry once or twice for transient 5xx or network errors, then deny access. Log incidents server-side and show user-safe messages. Do not expose internal Checkify error details to customers.

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.",
  });
}

Server SDKs and tooling

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 is published on npm. The Python checkify-server package ships in the Checkify SDK monorepo.

Result expiry window

Completed verifications expire after QR_RESULT_MAX_AGE_SECONDS (default 900 seconds / 15 minutes). After expiry, verify returns result_expired — ask the user to verify again.

Status polling endpoints (custom frontends)

The JavaScript SDK handles session state for standard embeds. Use these only when you build a custom frontend that calls GET /v1/qr/pass/{pass_id}/start manually.

معالجة الاستجابة

On success, Checkify returns HTTP 200 with success: true and status: completed. Allow access only when required claims are present (for example 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"
  }
}

Signed result

The signed_result object lets your backend keep a tamper-evident audit record that Checkify approved the required claim at verification time. Most integrations only need approved_claims. Regulated or high-risk businesses may also store signed_result for audit. Do not store more personal information than necessary.

If the customer has not finished in the app yet, Checkify returns HTTP 200 with success: false and status: pending. Deny protected actions and ask the user to complete verification.

{
  "success": false,
  "status": "pending",
  "message": "Verification is not completed yet",
  "request_id": "56a57761-ff5b-42f0-9c97-6c13e223e017",
  "approved_claims": {},
  "signed_result": null
}
الحقلالمعنى
successtrue عند اكتمال التحقق ومطابقة المتطلبات
statuscompleted أو pending
approved_claimsالمطالبات Checkify المعتمدة، على سبيل المثال. human_verified: true
signed_resultالحمولة الموقعة الاختيارية لمسارات التدقيق

JSON error payloads

When verification cannot proceed, Checkify returns HTTP 4xx/5xx with a structured JSON body. Check error.code and log error.details server-side. Return generic messages to end users.

{
  "success": false,
  "error": {
    "code": "verification_failed",
    "message": "The verification did not include all required claims.",
    "details": {
      "missing_claims": ["age_over_18"]
    }
  }
}
Code HTTP Meaning Recommended action
missing_authorization401No Authorization header was sent.Send Authorization: Bearer YOUR_SITE_API_KEY from server-side code only.
invalid_token401Bearer token missing, malformed, or not a valid site API key.Verify the key in your business dashboard and store it in environment variables.
expired_token401The site API key has been revoked.Create a new site API key and rotate it on your servers.
missing_required_field400 / 422request_id or token is missing from the JSON body.Pass the request_id from your hidden form field, or the poll token if your integration still uses it.
invalid_request_id400The reference could not be parsed or is empty after normalization.Ensure your frontend submits the Checkify request_id unchanged.
result_not_found404No verification request exists for that reference.Reject the action. The user may have tampered with the hidden field or submitted an old session.
result_expired410The verification completed too long ago to trust for this action.Ask the user to scan again and call verify with the new request_id.
verification_failed403 / 409Verification finished but did not meet your required claims/fields, belongs to another site, or was already consumed.Deny access. Inspect error.details for missing_claims, missing_fields, or reason.
business_not_operational403The business account is locked, archived, or not operational.Contact the business owner or Checkify support. Do not allow protected actions until the account is active.
validation_errors422The JSON body failed schema validation (HTTP 422).Inspect error.details.validation_errors for field-level messages. Fix request_id, required_claims, or required_fields types before retrying.
rate_limited429Too many verify calls in a short window.Retry with exponential backoff. Verify only at protected actions, not on every page view.
server_error500+Checkify could not complete verification due to a temporary server issue.Retry once or twice, then fail closed and log the incident.

Webhooks and asynchronous verification

Today, server verification is synchronous: your backend verifies the request_id when the user submits the protected action. For checkout, signup, and access-control flows, this is the recommended approach. General-purpose verification webhooks are not required for standard integrations. GoHighLevel and other partner integrations may use separate outbound webhook configuration in the business dashboard.

Common mistakes

تذكير الأمان

تعامل مع الحقل المخفي كمرجع غير موثوق به، وليس كدليل. تحقق دائمًا باستخدام مفتاح موقعك API الموجود على الخادم قبل منح الوصول. استخدم consume: true لإجراءات لمرة واحدة مثل الاشتراك أو إعادة تعيين كلمة المرور.

Next steps