ادخلوا إلى لوحة التحكم
يفتح فريق Sekyro AI مؤسستكم. يدعوكم مدير بدور محدد؛ اقبلوا الدعوة بالعنوان المدعو.
جميع الأدوار
رابط مستضاف دون برمجة، أو الـ API من خادمكم: اختاروا التكامل المناسب لفريقكم، واختبروه في Sandbox، ثم انتقلوا إلى Live.
الطرق الأربع لدمج Sekyro AI جنباً إلى جنب. ابدؤوا بأبسطها: يمكنكم إضافة طريقة أخرى لاحقاً على التطبيق نفسه.
| المعيار | الروابط المستضافة | واجهة API للخادم | الويب وReact | iOS وAndroid |
|---|---|---|---|---|
| التوفر | متاح | متاح | قيد التأكيد | مخطط |
| البرمجة المطلوبة | لا شيء | خادمكم | خادمكم وموقعكم | خادمكم وتطبيقكم |
| أين يتحقق الشخص | صفحة مستضافة من Sekyro AI تُفتح من رابط | الصفحة المستضافة للجلسة | داخل موقعكم | داخل تطبيقكم |
| المنصة الواجب تسجيلها | لا شيء — التطبيق دون منصة يخدم الروابط المستضافة | لا شيء | الويب: النطاقات المصرّح بها | iOS: Team ID وbundle ID · Android: الحزمة وبصمات SHA-256 |
| مفتاح الـ API | لا: تُنشأ الروابط في لوحة التحكم | نعم، على الخادم فقط | نعم، على الخادم فقط | نعم، على الخادم فقط |
مرّر الجدول أفقياً
مسار التكامل بالترتيب الذي تقترحه لوحة التحكم. وتبيّن كل خطوة من يمكنه تنفيذها.
يفتح فريق Sekyro AI مؤسستكم. يدعوكم مدير بدور محدد؛ اقبلوا الدعوة بالعنوان المدعو.
جميع الأدوار
سمّوه واختاروا منصاته. يتطلب الويب نطاقات مصرّحاً بها، ويتطلب iOS وAndroid معرّفات. ودون منصة، يخدم التطبيق الـ API والروابط المستضافة.
مديرمطوّر
انطلقوا من ملف بداية وراجعوا معاينة الخطوات. ويتوقف الوصول إلى السجل على صلاحيات مؤسستكم.
مديرمطوّر
ينشئ المدير مفتاح التطبيق. ولا يُعرض السرّ إلا مرة واحدة: انسخوه إلى مدير الأسرار لديكم.
مدير
عبر البدء السريع أدناه أو رابط مستضاف، ثم افحصوا الجلسة — والمراجعة إن وُجدت — في لوحة التحكم.
مديرمطوّر
حين تُفتح بيئة Live لمؤسستكم، أنشئوا مفتاح Live واستبدلوا به مفتاح Sandbox على الخادم.
مدير
أنشئوا جلسة من خادمكم بمفتاح Sandbox الخاص بتطبيقكم.
// Next.js Server Actions (App Router) backed by @sekyro/node.
//
// Two jobs live on YOUR server, never in the browser:
// 1. startVerification — create a session BOUND to the user's NNI and hand
// the page nothing but the one-time client token.
// 2. decideVerification — read the authoritative session and decide.
//
// The API key is a server secret. The client token is the end user's flow
// credential: returned once by `sessions.create`, never logged, never stored
// in a cookie or URL.
"use server";
import { Sekyro, SekyroApiError, type Session } from "@sekyro/node";
const sekyro = new Sekyro({
apiKey: requireEnv("SEKYRO_API_KEY"),
// Production is the default. For the sandbox set
// SEKYRO_API_BASE_URL=https://api-staging.sekyro.ai/api/v2 with an sk_test_ key.
baseUrl: process.env.SEKYRO_API_BASE_URL ?? "https://api.sekyro.ai/api/v2",
});
export interface StartVerificationResult {
sessionId: string;
/** Hand this to the page (`createVerification({ clientToken })`). Returned ONCE. */
clientToken: string;
/** ISO 8601 — the capture must finish before this. */
expiresAt: string;
}
/**
* Create a session bound to `nni`. The browser never sends the NNI: the SDK
* submits `population_registry` with an empty input and the server uses the
* bound value (a browser that sends a DIFFERENT nni gets 409 `nni_locked`).
*
* `attempt` is your own counter per user: the idempotency key makes a network
* retry of the SAME attempt return the SAME session instead of minting two.
*/
export async function startVerification(
userId: string,
nni: string,
attempt: number,
): Promise<StartVerificationResult> {
try {
const created = await sekyro.sessions.create(
// No recipe here: the API key belongs to an app, and the app's eKYC
// profile decides the steps (e.g. population_registry → liveness →
// face_match for the "Mauritania: registry" profile).
{ locale: "ar", input: { nni } },
{ idempotencyKey: `kyc:${userId}:${attempt}` },
);
// Persist the id BEFORE returning: the webhook can arrive before the page
// reports back, and your handler must be able to find the user by session.
await saveSessionId(userId, created.id);
return {
sessionId: created.id,
clientToken: created.clientToken,
expiresAt: created.expiresAt,
};
} catch (err) {
if (err instanceof SekyroApiError) {
// `retryable` covers network, timeout, 429, 5xx and idempotency_in_progress:
// back off and call again with the SAME idempotencyKey.
throw new Error(
`Sekyro AI ${err.code} (request ${err.requestId ?? "n/a"}), retryable=${err.retryable}`,
);
}
throw err;
}
}
export type Decision = "approved" | "rejected" | "pending" | "expired";
export interface DecideResult {
decision: Decision;
session: Session;
}
/**
* The ONLY place a verdict is decided. Call it when the page reports
* `onResult`, and again from your webhook handler on async transitions.
*/
export async function decideVerification(sessionId: string): Promise<DecideResult> {
const session = await sekyro.sessions.get(sessionId);
return { decision: decide(session), session };
}
function decide(session: Session): Decision {
switch (session.status) {
case "completed":
// `outcome` is `approved | review | rejected`; `review` here means a
// human decided the session is complete but flagged — treat as pending
// until your own policy says otherwise.
if (session.outcome === "approved") return "approved";
if (session.outcome === "rejected") return "rejected";
return "pending";
case "failed":
return "rejected";
case "review_pending":
// Parked for human review (up to 48 h). Wait for `session.review_decided`.
return "pending";
case "expired":
// The user did not finish in time: offer a new session.
return "expired";
case "created":
case "in_progress":
return "pending";
}
}
function requireEnv(name: string): string {
const value = process.env[name];
if (value === undefined || value === "") throw new Error(`${name} is not set`);
return value;
}
// Replace with your database write.
async function saveSessionId(_userId: string, _sessionId: string): Promise<void> {}لديكم وصول بالفعل؟ سجّلوا الدخول عبر Ibtikar ID لإدارة تطبيقاتكم ومفاتيحكم وجلساتكم.
ليس لديكم وصول بعد؟ تحصل عليه مؤسستكم من فريق Sekyro AI، ثم يدعوكم أحد المديرين.
صِفوا خدمتكم والأشخاص الذين تحتاجون إلى التحقق من هوياتهم. يساعدكم فريق Sekyro AI على اختيار طريقة التكامل ويفتح لكم الوصول إلى Sandbox.