はじめにクイックスタートガイド

カスタム年齢ゲート

このガイドでは、事前に構築されたウィジェットを使用せずに、k-ID API を直接使用してカスタム年齢ゲートを実装する方法を説明します。このアプローチにより、k-ID がコンプライアンスロジックを処理する間、ユーザーインターフェースを完全にコントロールできます。

必須権限によるシンプルな統合

このガイドでは、Compliance Studio ですべての権限を必須として設定するシンプルな統合パターンを紹介します。この設定では、セッションが作成されると(直接または保護者の同意後に)、すべての機能がすぐに利用可能になります。セッションアップグレードフローは必要ありません。

Automatic age assurance

プロダクトが対象管轄区域で Automatic age assurance を有効化している場合、すべての権限が必須として設定されていても、申告された年齢が保護者の同意をスキップできるほど高いプレイヤーに対して /age-gate/check は CHALLENGE_AGE_GATE_AGE_ASSURANCE チャレンジを返すことがあります。プレイヤーがセルフ検証を完了するまで、セッションの作成は遅延されます。この機能は、k-ID のみが付与できる組織レベルの設定によって制御されます。プロダクトで有効化されていない場合は、ステップ 4b をスキップできます。

AIコーディングエージェントと統合する

公式の Agent Skills では、カスタム年齢ゲート、Automatic age assurance、保護者の同意、webhook を扱います。

カスタム年齢ゲートとは?

カスタム年齢ゲートは、お客様が構築・管理する年齢確認インターフェースであり、k-ID の API がバックエンドでコンプライアンスロジックを処理します。このアプローチは以下のような場合に最適です:

  • 完全な UI コントロール:ブランドとユーザー体験に合わせた年齢ゲートをデザイン
  • プラットフォーム統合:モバイルアプリやゲームエンジン向けのネイティブ体験を構築
  • カスタムワークフロー:より大きなオンボーディングフローの一部として年齢確認を実装

前提条件

始める前に、以下が必要です:

  1. k-ID プロダクト:k-ID Compliance Studioでプロダクトを作成・設定
  2. 必須権限の設定:プロダクトの権限設定ですべての権限を「必須」として設定
  3. API キー:Compliance Studioのプロダクトの開発者設定ページから API キーを生成
  4. Webhook エンドポイント(推奨):チャレンジおよびセッションイベントを受信するためのセキュアな HTTPS エンドポイントを設定。詳細はWebhooksを参照。

ステップ 1:年齢ゲート要件を取得

年齢ゲートを表示する前に、/age-gate/get-requirementsを呼び出して、ユーザーの管轄区域に基づいて表示すべき内容を決定します。

重要

実装では、API キーがクライアント側のコードに公開されないよう、すべての API 呼び出しはサーバー間で行う必要があります。

リクエスト例

GET /api/v1/age-gate/get-requirements?jurisdiction=US-CA
Authorization: Bearer your-api-key

レスポンス例

{
  "shouldDisplay": true,
  "ageAssuranceRequired": false,
  "digitalConsentAge": 13,
  "civilAge": 18,
  "minimumAge": 0,
  "approvedAgeCollectionMethods": [
    "date-of-birth",
    "age-slider",
    "platform-account"
  ]
}

レスポンスフィールド

フィールド説明
shouldDisplay年齢ゲートを表示すべきかどうか
ageAssuranceRequiredこの管轄区域で年齢確認が必要かどうか
digitalConsentAgeデジタル同意の最低年齢(これ未満のユーザーは保護者の同意が必要)
civilAgeユーザーが法的に成人とみなされる年齢
minimumAgeプロダクトにアクセスするための最低年齢(Compliance Studio で設定)
approvedAgeCollectionMethodsこの管轄区域で許可されている年齢収集方法

年齢ゲートが不要な場合

shouldDisplayがfalseの場合、年齢ゲートをスキップし、/age-gate/get-default-permissionsを呼び出して、その管轄区域のデフォルトセッション権限を取得します。

プラットフォーム年齢シグナル

ゲームにプラットフォームから報告された年齢シグナル(Apple iOS、Google Play、Xbox、Meta Horizon、またはk-ID)がある場合、クエリパラメータ(platformName、platformAgeLow、platformAgeHigh、platformCategory、platformDeclarationType、platformVerificationId)として含めると、確認済みの成人シグナルが shouldDisplay を false に切り替えて、年齢ゲートを完全にスキップできます。プラットフォーム年齢シグナルを参照してください。

ステップ 2:年齢ゲート UI を構築

レスポンスに基づいて、承認された収集方法を使用して年齢ゲートインターフェースを構築します:

  • date-of-birth:完全な生年月日入力(YYYY-MM-DD)
  • age-slider:年齢範囲またはスライダー選択
  • platform-account:既存のプラットフォームアカウントの年齢データを使用

UXガイドライン

年齢スライダーの動作、日付ピッカーの要件、アクセシビリティに関する考慮事項などの詳細な設計推奨事項については、UXガイドラインを参照してください。

年齢ゲート UI の例

<div id="age-gate">
  <h2>生年月日を入力してください</h2>
  <form id="age-gate-form">
    <label for="dob">生年月日</label>
    <input type="date" id="dob" name="dob" required />
    <button type="submit">続行</button>
  </form>
</div>

ステップ 3:API で年齢を確認

ユーザーが年齢を送信したら、収集した年齢情報と管轄区域を指定して/age-gate/checkを呼び出します。使用した年齢収集方法に応じて、dateOfBirthまたはageのいずれかを渡すことができます。

生年月日を使用したリクエスト例

日付ピッカーを使用して完全な生年月日を収集した場合:

POST /api/v1/age-gate/check
Content-Type: application/json
Authorization: Bearer your-api-key

{
  "jurisdiction": "US-CA",
  "dateOfBirth": "2015-04-15"
}

年齢を使用したリクエスト例

年齢スライダーを使用してユーザーの年齢を収集した場合:

POST /api/v1/age-gate/check
Content-Type: application/json
Authorization: Bearer your-api-key

{
  "jurisdiction": "US-CA",
  "age": 9
}

プラットフォーム年齢シグナルを使用したリクエスト例

platformAgeSignal を単独で、または dateOfBirth/age と一緒に含めることもできます。確認済みシグナルは別の確認手順なしに確認済み年齢権限を満たし、確認されていないシグナルでも年齢矛盾検出に活用されます。プラットフォーム年齢シグナルを参照してください。

POST /api/v1/age-gate/check
Content-Type: application/json
Authorization: Bearer your-api-key

{
  "jurisdiction": "US-CA",
  "dateOfBirth": "2005-04-15",
  "platformAgeSignal": {
    "name": "apple-ios",
    "ageLow": 18,
    "ageHigh": 25,
    "declarationType": "governmentIDChecked"
  }
}

可能なレスポンス

API は 3 つのステータスのいずれかを返します:PASS、PROHIBITED、または CHALLENGE。PASS の場合は即座にセッションが作成されます。CHALLENGE レスポンスはセッションが存在する前に解決される必要があり、challenge.type で分岐して提示方法を決定する必要があります:

  • CHALLENGE_PARENTAL_CONSENT:申告された年齢が低すぎて保護者の同意なしには続行できません。信頼できる大人が承認する必要があります(ステップ 4)。
  • CHALLENGE_AGE_GATE_AGE_ASSURANCE:申告された年齢は保護者の同意をスキップできるほど高いものの、プロダクトでこの管轄区域に対して Automatic age assurance が有効化されているため、プレイヤーがその申告を証明する必要があります(ステップ 4b)。

PASS: ユーザーは続行可能

ユーザーの年齢により即座にアクセスが許可されます。すべての権限を持つセッションが作成されます。

{
  "status": "PASS",
  "session": {
    "sessionId": "608616da-4fd2-4742-82bf-ec1d4ffd8187",
    "ageStatus": "LEGAL_ADULT",
    "dateOfBirth": "2005-04-15",
    "jurisdiction": "US-CA",
    "permissions": [...],
    "status": "ACTIVE"
  }
}

アクション:sessionIdを保存し、ユーザーが続行できるようにします。

PROHIBITED: ユーザーはブロック

ユーザーの年齢がプロダクトに設定された最低年齢を下回っています。

{
  "status": "PROHIBITED"
}

アクション:年齢に適したメッセージを表示し、アクセスを防止します。

CHALLENGE と CHALLENGE_PARENTAL_CONSENT:保護者の同意が必要

ユーザーの年齢により検証可能な保護者の同意(VPC)が必要です。信頼できる大人が承認するためのチャレンジが作成されます。

{
  "status": "CHALLENGE",
  "challenge": {
    "challengeId": "683409f1-2930-4132-89ad-827462eed9af",
    "oneTimePassword": "PP5BUS",
    "type": "CHALLENGE_PARENTAL_CONSENT",
    "url": "https://family.k-id.com/authorize?otp=PP5BUS"
  }
}

アクション:challengeIdを保存し、信頼できる大人のチャレンジ画面を表示します(ステップ 4 を参照)。

CHALLENGE と CHALLENGE_AGE_GATE_AGE_ASSURANCE:Automatic age assurance

プレイヤーが保護者の同意をスキップできるほど高い年齢を申告し、プロダクトがその管轄区域で Automatic age assurance を有効化している場合に返されます。セッションが作成される前に、プレイヤーは申告内容を証明する必要があります(顔年齢推定またはID書類)。信頼できる大人は関与せず、OTP も発行されません。

{
  "status": "CHALLENGE",
  "challenge": {
    "challengeId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "type": "CHALLENGE_AGE_GATE_AGE_ASSURANCE",
    "url": "https://family.k-id.com/age-gate/verify?token=..."
  }
}

アクション:challengeId を保存し、challenge.url を iframe に埋め込みます(ステップ 4b を参照)。

ステップ 4:信頼できる大人のチャレンジ画面を構築

CHALLENGEレスポンスを受け取ったら、ユーザーが同意を求めて信頼できる大人に連絡できる画面を表示します。チャレンジレスポンスには必要なものがすべて含まれています。

UXガイドライン

信頼できる大人の同意フローの詳細な設計推奨事項(レイアウト例や実装のヒントを含む)については、UXガイドラインの保護者同意を参照してください。

チャレンジ画面のオプション

チャレンジ画面では、信頼できる大人が同意を完了するための 3 つのオプションを提供する必要があります:

オプション 1:メール通知

ユーザーが信頼できる大人のメールアドレスを入力できるようにします。送信されたら、/challenge/send-emailを呼び出して同意リクエストメールを送信します。

POST /api/v1/challenge/send-email
Content-Type: application/json
Authorization: Bearer your-api-key

{
  "challengeId": "683409f1-2930-4132-89ad-827462eed9af",
  "email": "parent@example.com"
}

オプション 2:QR コード

challenge.urlフィールドから生成された QR コードを表示します。信頼できる大人は携帯電話でこれをスキャンして、同意ポータルに直接アクセスできます。

// チャレンジURLからQRコードを生成
const qrCodeUrl = challenge.url;
// QRコードライブラリを使用してレンダリング: "https://family.k-id.com/authorize?otp=PP5BUS"

オプション 3:手動コード入力

challenge.oneTimePasswordを表示し、信頼できる大人にasktoplay.comにアクセスしてコードを入力するよう指示します。

チャレンジ画面の実装例

<div id="challenge-screen">
  <h2>信頼できる大人に助けを求めてください</h2>
  <p>
    以下のいずれかの方法で、信頼できる大人にセットアップを完了してもらってください。
  </p>

  <div class="options-container">
    <!-- オプション1:メール -->
    <div class="option">
      <h3>オプション1</h3>
      <p>信頼できる大人のメールアドレスを入力</p>
      <form id="email-form">
        <label for="email">メール</label>
        <input type="email" id="email" placeholder="メールアドレス" required />
        <button type="submit">送信</button>
      </form>
    </div>

    <!-- オプション2:QRコード -->
    <div class="option">
      <h3>オプション2</h3>
      <p>
        信頼できる大人にQRコードをスキャンまたはクリックしてもらってください。
      </p>
      <div id="qr-code">
        <!-- challenge.urlからQRコードをレンダリング -->
      </div>
    </div>

    <!-- オプション3:手動コード -->
    <div class="option">
      <h3>オプション3</h3>
      <p>
        <a href="https://asktoplay.com">asktoplay.com</a
        >にアクセスし、以下のコードを入力してください。
      </p>
      <div class="code-display">
        <span id="otp-code">PP5BUS</span>
        <button onclick="copyCode()">コピー</button>
      </div>
    </div>
  </div>

  <button id="do-later">後で行う</button>
</div>

チャレンジの保存

同意を待っている間、challengeIdを保存します。ローカルストレージ、ユーザーのアカウントに関連付けられたデータベース、またはプラットフォームに適した他の永続ストレージに保存できます。ユーザーが同意が許可される前にアプリに戻った場合、/challenge/getを使用してチャレンジを取得し、チャレンジ画面を復元します。

// チャレンジ作成時に保存(localStorageを使用した例)
localStorage.setItem("pendingChallenge", challengeId);

// アプリ再起動時に保留中のチャレンジを確認
const pendingChallenge = localStorage.getItem("pendingChallenge");
if (pendingChallenge) {
  // チャレンジの詳細を取得してチャレンジ画面を表示
  const challenge = await fetchChallenge(pendingChallenge);
  showChallengeScreen(challenge);
}

ステップ 4b:自動年齢保証チャレンジの処理

プロダクトで Automatic age assurance が有効化されていない場合は、このステップをスキップしてください。challenge.type が CHALLENGE_AGE_GATE_AGE_ASSURANCE の場合、プレイヤー自身が検証します。ステップ 4 の信頼できる大人の画面を表示しないでください。

検証 iframe の埋め込み

challenge.url を iframe に直接レンダリングします。iframe は管轄区域とプロダクト設定に応じて、顔年齢推定またはID書類検証を処理します。

<iframe
  id="age-assurance-widget"
  src="CHALLENGE_URL"
  width="100%"
  height="600"
  frameborder="0"
  allow="camera;payment;publickey-credentials-get;publickey-credentials-create">
</iframe>

allow 属性は必須です:

  • camera:顔年齢推定
  • payment:クレジットカードベースの検証
  • publickey-credentials-get / publickey-credentials-create:WebAuthn / AgeKey

Verification.Result DOM イベントの監視

プレイヤーが完了すると、iframe は Verification.Result メッセージを送信します。これはレスポンシブな UI 更新にのみ使用してください。データの整合性のためには、ステップ 5 の Webhook を待つか、/challenge/get-status をポーリングしてください。

window.addEventListener("message", (event) => {
  if (!event.origin.endsWith(".k-id.com")) {
    return;
  }

  const message = event.data;
  if (message?.eventType === "Verification.Result") {
    if (message.data.status === "PASS") {
      // プレイヤーが正常に検証されました。
      // セッションが作成中です - sessionId を取得するには
      // Challenge.StateChange Webhook を待つか /challenge/get-status をポーリングします。
      closeAgeAssuranceIframe();
    } else if (message.data.status === "FAIL") {
      // プレイヤーが年齢基準を満たしていません。セッションは作成されません。
      closeAgeAssuranceIframe();
      showVerificationFailedMessage();
    }
  }
});

詳細なイベント構造については、Verification.Result を参照してください。

項目CHALLENGE_PARENTAL_CONSENT(ステップ 4)CHALLENGE_AGE_GATE_AGE_ASSURANCE(ステップ 4b)
検証する人信頼できる大人プレイヤー本人
oneTimePassword存在する存在しない
challenge.urlhttps://family.k-id.com/authorize?otp=...https://family.k-id.com/age-gate/verify?token=...
UIメール / QR コード / OTP 入力カメラアクセスを持つ iframe
/challenge/send-email適用可能適用不可
セッション作成のタイミング大人が承認したときに作成プレイヤーが検証を通過した後に作成

ステップ 5:Webhook イベントを処理

チャレンジのステータスが変更されたとき、またはセッションが削除されたときにイベントを受信するように Webhook エンドポイントを設定します。

Challenge.StateChangeイベント

このイベントは、保護者の同意チャレンジでも自動年齢保証チャレンジでも、チャレンジが解決されたときに発行されます。チャレンジが作成されたときにのみ(つまり /age-gate/check レスポンスが status: "CHALLENGE" だったとき)送信されます。

{
  "eventType": "Challenge.StateChange",
  "data": {
    "id": "683409f1-2930-4132-89ad-827462eed9af",
    "productId": 42,
    "status": "PASS",
    "dob": "2015-04-15",
    "sessionId": "0ad1641f-c154-4c2-8bb2-74dbd0de7723",
    "approverEmail": "parent@example.com",
    "kuid": "7a1f2c3d-4e5f-6789-abcd-ef0123456789"
  }
}

approverEmail、dob、kuid などのフィールドはオプションです。受信するフィールドのセットは、そのチャレンジを生成したフローに依存します。challengeId を使って、イベントを発行したチャレンジに関連付けてください。完全なフィールド契約については、Challenge.StateChange を参照してください。

ステータス説明アクション
PASS同意が付与されたsessionIdを保存してアクセスを許可
FAIL同意が拒否された適切なメッセージを表示、ユーザーは続行不可
IN_PROGRESSチャレンジはまだ保留中待機を続ける

同意成功時の処理

PASSステータスを受け取ったら(Webhook またはポーリング経由)、レスポンスにはsessionIdが含まれます。以下を行う必要があります:

  1. セッション権限を取得: sessionIdを使用して/session/getを呼び出し、権限を含む完全なセッション詳細を取得します。
GET /api/v1/session/get?id=0ad1641f-c154-4c2-8bb2-74dbd0de7723
Authorization: Bearer your-api-key

レスポンス例:

{
  "session": {
    "ageStatus": "DIGITAL_MINOR",
    "dateOfBirth": "2015-04-15",
    "etag": "6d9d24fccd428f845b355122799948dd0a52fc5d",
    "jurisdiction": "US-CA",
    "kuid": "7a1f2c3d-4e5f-6789-abcd-ef0123456789",
    "permissions": [
      {
        "enabled": true,
        "managedBy": "GUARDIAN",
        "name": "text-chat-private"
      },
      {
        "enabled": false,
        "managedBy": "GUARDIAN",
        "name": "voice-chat"
      }
    ],
    "sessionId": "0ad1641f-c154-4c2-8bb2-74dbd0de7723",
    "status": "ACTIVE"
  },
  "status": "PASS"
}
  1. 保存されたチャレンジをセッションに置き換え: challengeIdをクリアし、代わりにsessionIdを保存します。これにより、ユーザーが権限を付与されたアクティブなセッションを持っていることが示されます。
// 保留中のチャレンジをクリアしてアクティブなセッションを保存
user.challengeId = null;
user.sessionId = data.sessionId;
  1. 権限を適用: セッションレスポンスの権限を使用して、アプリケーションの機能を有効または無効にします。

Session.Deleteイベント

このイベントは、セッションが削除されたとき(例えば、保護者が Family Connect を通じてアクセスを取り消したとき)に発生します。

{
  "eventType": "Session.Delete",
  "data": {
    "id": "0ad1641f-c154-4c2-8bb2-74dbd0de7723",
    "productId": 42
  }
}

アクション:保存されたセッションを削除し、ユーザーに年齢ゲートフローを再度完了させます。

Webhook ハンドラーの例

app.post("/webhook/k-id", (req, res) => {
  const { eventType, data } = req.body;

  switch (eventType) {
    case "Challenge.StateChange":
      if (data.status === "PASS") {
        // アクセスを許可 - ユーザーのsessionIdを保存
        grantAccess(data.sessionId, data.kuid);
      } else if (data.status === "FAIL") {
        // アクセスを拒否
        denyAccess(data.id);
      }
      break;

    case "Session.Delete":
      // アクセスを取り消し - ユーザーは再度年齢ゲートを完了する必要がある
      revokeSession(data.id);
      break;
  }

  res.status(200).send("OK");
});

フォールバックとしてのポーリング

Webhook が利用できない場合、/challenge/get-statusをポーリングしてチャレンジステータスを確認できます:

GET /api/v1/challenge/get-status?id=683409f1-2930-4132-89ad-827462eed9af
Authorization: Bearer your-api-key

ポーリング制限

ポーリング時は、リクエスト間で少なくとも 5 秒待ってください。ポーリング頻度が高すぎる場合、API は HTTP 429 を返す可能性があります。

Webhook 設定

カスタム年齢ゲートフローでは、Webhook エンドポイントが以下を受信するように設定されていることを確認してください:

  • Challenge.StateChange:保護者の同意が承認または拒否されたときに通知
  • Session.Delete:保護者によってセッションが削除されたときに通知

Webhook 署名の検証については、Webhooksを参照してください。

次のステップ

カスタム年齢ゲートを実装したら、以下のリソースを参照して統合を強化してください:

k-ID のカスタム年齢ゲート統合により、k-ID が複雑な管轄区域ロジックと保護者同意フローを処理する間、完全にブランド化されたコンプライアンス体験を構築できます。

On this page