Get into the dashboard
Your organization is opened by the Sekyro AI team. An admin invites you with a role; accept with the invited address.
All roles
A no-code hosted link, or the API from your server: pick the integration that suits your team, test it in Sandbox, then go Live.
The four ways to integrate Sekyro AI, side by side. Start with the simplest: you can add another later on the same app.
| Criterion | Hosted links | Server API | Web and React | iOS and Android |
|---|---|---|---|---|
| Availability | Available | Available | To be confirmed | Planned |
| Code to write | None | Your server | Your server and your site | Your server and your app |
| Where the person verifies | A Sekyro AI hosted page, opened from a link | The session’s hosted page | Inside your site | Inside your app |
| Platform to declare | None — an app without platforms serves hosted links | None | Web: allowed origins | iOS: Team ID and bundle ID · Android: package and SHA-256 fingerprints |
| API key | No: links are created in the dashboard | Yes, server-side only | Yes, server-side only | Yes, server-side only |
Scroll the table sideways
An integration, in the order the dashboard proposes it. Each step says who can do it.
Your organization is opened by the Sekyro AI team. An admin invites you with a role; accept with the invited address.
All roles
Name it and pick its platforms. Web needs allowed origins, iOS and Android need identifiers. With no platform, the app serves the API and hosted links.
AdminDeveloper
Start from a preset and check the step preview. Registry access depends on your organization’s entitlement.
AdminDeveloper
An admin creates the app key. The secret is shown once: copy it into your secrets manager.
Admin
With the quickstart below or a hosted link, then inspect the session — and any review — in the dashboard.
AdminDeveloper
Once Live is open for your organization, create a Live key and replace the Sandbox key on your server.
Admin
Create a session from your server with one of your app’s Sandbox keys.
// 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> {}Already have access? Sign in with Ibtikar ID to manage your apps, keys and sessions.
No access yet? Your organization gets it from the Sekyro AI team, then an admin invites you.
Describe your service and the people you need to verify. The Sekyro AI team helps you choose an integration and opens your Sandbox access.