コンテンツにスキップ

セッション

セッションは、利用者 1 人の本人確認 1 回分です。進行(status)と判定(outcome.verdict)は別の値で、判定は outcome にだけ入ります。

POST /v1/verification-sessions に、次の項目を必要なものだけ入れて送ります(すべて省略できます)。

項目内容
callbackUrl手続きを終えた利用者を戻す URL。管理画面の許可リストに登録した URL だけが使える
state戻り先にそのまま付けて返す値。利用者ごとに推測できない値を入れ、戻ってきたときに照合する
metadata自社側の相関データ(自社のユーザー ID など)。webhook と取得 API にそのまま返る。個人情報を入れない
preRegisteredInfo自社が把握している本人の情報(氏名・生年月日・住所・性別)。提出された内容との照合に使われる

callbackUrlstate を付けた URL の長さは 8000 バイトまでです(callbackUrl を省くときは state 単体で同じ上限)。metadata にも大きさの上限があり、超えると 400 が返ります。

// セッションを作り、利用者に渡す検証 URL を受け取る(Node 20 以降・サーバー側で実行する)。
import { randomUUID } from "node:crypto";
const requireEnv = (name: string): string => {
const value = process.env[name];
if (!value) throw new Error(`環境変数 ${name} を設定してください`);
return value;
};
const API_BASE = requireEnv("SUPATRUST_API_BASE");
const API_KEY = requireEnv("SUPATRUST_API_KEY");
export interface CreatedSession {
id: string;
status: string;
/** 1 回だけ使える秘密の URL。この応答でしか受け取れない */
verificationUrl: string;
verificationUrlExpiresAt: string;
expiresAt: string;
createdAt: string;
}
export async function createSession(input: {
callbackUrl?: string;
state?: string;
metadata?: Record<string, unknown>;
}): Promise<CreatedSession> {
const response = await fetch(`${API_BASE}/v1/verification-sessions`, {
method: "POST",
headers: { authorization: `Bearer ${API_KEY}`, "content-type": "application/json" },
body: JSON.stringify(input),
});
if (response.status !== 201) {
// エラーの本文は { "_tag": "..." , ... }。status と _tag で分岐する(ガイド「エラー」)
const body = (await response.json().catch(() => null)) as { _tag?: string } | null;
throw new Error(`セッションを作成できませんでした: ${response.status} ${body?._tag ?? ""}`);
}
return (await response.json()) as CreatedSession;
}
// 使い方
const state = randomUUID(); // 自社のサーバー側セッションに保存し、戻ってきたときに照合する
const session = await createSession({
callbackUrl: "https://example.com/verification/return",
state,
metadata: { userId: "user_0001" }, // 自社の相関 ID。個人情報は入れない
});
// session.id を自社のユーザーに結びつけて保存し、利用者を session.verificationUrl へ送る。
// verificationUrl はログに出さない
console.info("session created", { sessionId: session.id, expiresAt: session.expiresAt });

応答(201)の verificationUrl が、利用者に開いてもらう URL です。

  • この応答でしか受け取れません(取得 API には出ません)。受け取ったらすぐ利用者に渡し、ログに出さないでください
  • 1 回だけ使えます。有効期限は verificationUrlExpiresAt です
  • 利用者はスマートフォンのブラウザで開きます
  • セッション全体の期限は expiresAt です。それまでに手続きが終わらないと expired になります

status は次のいずれかです。

  • created
  • collecting
  • deciding
  • reviewing
  • completed
  • expired
  • failed
status意味
created作成直後。利用者はまだ手続きを始めていない
collecting利用者が手続き中(撮影・入力の途中)
deciding提出がそろい、判定中
reviewing審査待ち。判定が needs_review になり、人による確認を待っている
completed判定が確定した。結果は outcome.verdict
expired期限までに手続きが終わらなかった。新しいセッションを作り直す
failed回復できないエラーで終わった
  • 期限切れになるのは createdcollecting のときだけです。decidingreviewing は期限で終わりません
  • completedfailedreviewing再処理collecting に戻せます

判定があると outcome が入ります(statusreviewingcompleted のとき)。それ以外は outcomenull です。outcome.verdict は次のいずれかです。

  • approved
  • rejected
  • needs_review
verdict意味
approved本人確認できた
rejected本人確認できなかった
needs_review自動では決められず、人による審査に回った。審査が終わると approvedrejected の通知がもう一度届く

outcome.source は、判定がどこで決まったかを表します。

  • automatic
  • manual_review
  • automatic は自動の判定、manual_review は人による審査です。manual_review のときは outcome.review に審査日時とメモが入ります
  • outcome.reasons は、拒否や審査に回った理由のコードの配列です。値は今後増えます。知らない値が来てもエラーにせず、無視してください
  • outcome.checks には、実施した確認ごとの要約(書類・顔・入力内容との一致)が入ります。一致したかどうかだけで、値そのものは入りません

GET /v1/verification-sessions/{id} は、状態・判定に加えて、個人情報(preRegisteredInfo ・利用者の入力 userInputInfo ・券面から読み取った項目 documentOcr)を返します。

// セッションの状態と結果を取得する(Node 20 以降・サーバー側で実行する)。
// 応答には利用者の個人情報(入力内容・券面から読み取った項目)が含まれる。ログに出さない。
const requireEnv = (name: string): string => {
const value = process.env[name];
if (!value) throw new Error(`環境変数 ${name} を設定してください`);
return value;
};
const API_BASE = requireEnv("SUPATRUST_API_BASE");
const API_KEY = requireEnv("SUPATRUST_API_KEY");
/** 取得 API の応答のうち、この例で使う部分(全体は API リファレンス) */
export interface SessionDetail {
id: string;
status: string;
completedAt: string | null;
purgedAt: string | null;
documentOcr: {
documentType: string | null;
fields: { side: string; name: string; text: string | null; normalized: string | null }[];
} | null;
outcome: {
verdict: string;
source: string;
decidedAt: string;
reasons: string[];
} | null;
}
export async function getSession(sessionId: string): Promise<SessionDetail | null> {
const response = await fetch(
`${API_BASE}/v1/verification-sessions/${encodeURIComponent(sessionId)}`,
{ headers: { authorization: `Bearer ${API_KEY}` } },
);
if (response.status === 404) return null; // 存在しない・別のテナントのもの・ID の形式違い
if (!response.ok) throw new Error(`セッションを取得できませんでした: ${response.status}`);
return (await response.json()) as SessionDetail;
}
// 使い方: 状態と判定で分岐する
const session = await getSession(process.argv[2] ?? "");
if (session === null) {
console.info("セッションが見つかりません");
} else if (session.outcome === null) {
// 判定がまだ無い(created / collecting / deciding)か、判定なしで終わった(expired / failed)
console.info("判定なし", { status: session.status });
} else {
switch (session.outcome.verdict) {
case "approved":
// 本人確認済みとして扱う
break;
case "rejected":
// 本人確認できなかったとして扱う
break;
case "needs_review":
// 審査の結果を待つ。確定すると通知がもう一度届く
break;
}
// reasons は知らない値が来ても例外にしない。券面の項目(documentOcr)は丸ごとログに出さない
console.info("判定あり", {
status: session.status,
verdict: session.outcome.verdict,
source: session.outcome.source,
reasonCount: session.outcome.reasons.length,
});
}
if (session?.purgedAt) {
// 保存期間を過ぎて個人情報が消去済み。個人情報の項目は null で返る(判定結果の outcome は残る)
console.info("個人情報は消去済み", { purgedAt: session.purgedAt });
}
  • 応答の個人情報は、ログ・エラー通知・分析基盤にそのまま流さないでください
  • 個人情報の取得は SupaTrust 側で記録されます
  • 保存期間を過ぎると個人情報は消去されます。消去後も 404 にはならず、purgedAt に消去日時が入り、個人情報の値が null になります。判定結果(outcome)は残ります
  • 存在しない ID・別のテナントの ID・形式が違う ID は、どれも同じ 404 です