スクリーンタイム
このガイドでは、スクリーンタイムの統合について説明します。プレイヤーがプロダクトを利用していることの報告、判定の読み取り、7種類のスクリーンタイムWebhookイベントの処理、そしてプレイヤーが保護者に延長をリクエストできるようにする方法を扱います。
スクリーンタイムとは何か、k-IDとプロダクトの責任の境界がどこにあるかについては、先にスクリーンタイムの概念を読んでください。
始める前に
- 組織でスクリーンタイムが利用可能か確認する。 この機能を組織で有効にするには、k-IDにお問い合わせください。
- プロダクトで機能を有効にする。 スクリーンタイムは既定では無効で、Compliance Studioの2つのスイッチをこの順序で操作する必要があります。まずScreentimeを有効にし、次にScreentime Controlsを有効にして、変更を公開してください。ControlsはScreentimeに依存しているため、Screentimeが有効になるまで操作できません。エンドポイントが必要とするスイッチが有効になるまで、そのエンドポイントは
FEATURE_DISABLEDで失敗します。2つのエンドポイントのグループが必要とするスイッチは異なります。機能フラグを参照してください。 - Webhookのエンドポイントを設定する。 7種類のスクリーンタイムのシグナルはすべてWebhookとして届きます。プロダクトのDeveloper SettingsでURLとシークレットを設定し、Webhooksの概要の説明に従って署名を検証してください。
- 処理するイベントを購読する。 エンドポイントは購読しているイベントタイプのみを受信し、k-IDはそれ以外を配信の試行もエラーもなく破棄します。Compliance StudioのプロダクトのDeveloper Settingsページで、エンドポイントに対して
Screentime.BreakReminder、Screentime.LimitWarning、Screentime.LimitReached、Screentime.ScheduleChanged、Screentime.QuietHoursWarning、Screentime.QuietHoursReached、Screentime.OverrideResultを選択してください。これらは組織でスクリーンタイムが有効になってから一覧に表示されます。 kuidを持つプレイヤーのセッションを用意する。 スクリーンタイムの各呼び出しはk-IDのsessionIdに宛てて行われますが、その背後にあるスケジュールはセッションではなくプレイヤーのkuidに紐づいて保存されます。kuidを持たないセッションはスケジュールを持つことができないため、保護者がどのように設定してもGET /screentime/get-stateは{ "enabled": false }を返します。プレイヤーは信頼できる大人が同意を完了した時点でkuidを得るため、先にそのフローを実行し、以降のセッションでは保存したkuidを再利用してください。セッションと権限およびチャレンジを参照してください。
保護者がスケジュールを設定していないプレイヤーはエラーではありません。GET /screentime/get-stateは{ "enabled": false }を返し、判定が何かをブロックすることはなく、イベントも発行されません。この状態を前提に構築してください。ほとんどのプレイヤーはこの状態です。
利用時間の報告
利用時間を報告する方法は2つあり、この選択は見た目の違いではありません。プロダクトがリアルタイムのシグナルを受け取れるかどうかがこれで決まります。
進行中のセッション:startとend | 事後報告:push | |
|---|---|---|
| エンドポイント | /screentime/start、/screentime/end | /screentime/push |
| 1日の合計への加算 | される | される |
Screentime.BreakReminder | 発行される | 発行されない |
Screentime.LimitWarning | 発行される | 発行されない |
Screentime.LimitReached | 発行される | 発行されない |
| 報告できる範囲 | リアルタイム、事象から5分以内 | 過去7日以内の任意の時点 |
| 適した用途 | プロダクトがプレイヤーの開始と停止を把握できる場合 | プロダクトがセッション終了後にしか利用時間を把握できない場合 |
startとendを選んでくださいこれはエンドポイントの一覧からは推測できないスクリーンタイムの重要な点です。/screentime/pushで報告された利用時間は1日の合計に加算され、保護者のFamily Connectのグラフにも表示されますが、休憩リマインダー、制限警告、制限到達のイベントはいずれも発行されません。
理由はタイミングです。k-IDは/screentime/startの呼び出し時にリマインダーと警告をスケジュールするため、それらはプレイヤーがまだセッション中に発行されます。90分のセッションを事後に報告した場合、60分時点の休憩リマインダーは30分遅れて発行されることになり、それは休憩リマインダーとして機能しません。プロダクトが休憩リマインダーの義務を負っている場合、/screentime/pushではそれを満たせません。
静かな時間のイベントとScreentime.ScheduleChangedは、セッションの報告方法ではなく保護者のカレンダーによって駆動されるため、どちらの方法でも発行されます。
セッションのステートマシン
sessionIdごとに同時に進行できるスクリーンタイムのセッションは1つだけであり、以下のルールはそこから導かれます。
idはプロダクト側で生成します。/screentime/startには新しいUUIDを送信し、/screentime/endには同じidを送信してください。進行中のセッションと一致しないidでのendはNOT_FOUNDを返します。- リトライは安全です。 同じ
idでstartを繰り返しても、二重に集計されることなくacceptedが返ります。すでに終了したセッションに対してendを繰り返した場合もacceptedが返ります。 - 異なる
idでの2回目のstartはstatus: "replaced"を返します。 k-IDは前のセッションを終了してその時間を集計し、新しいセッションを進行中にします。アプリが強制終了されたり端末が再起動されたりしてendが届かなかった場合の復旧手段がこれです。単にstartを呼び出し直し、replacedかどうかを確認してください。同じプレイヤーが2台目のデバイスでプレイを始めたときにも同じことが起こり、その場合は保護者から見える影響があります。1台目は集計されなくなり、リマインダーも届かなくなります。利用時間の集計方法を参照してください。 - k-IDは4時間が経過したセッションを自動的に終了します。 その時点までの時間は集計され、保留中のリマインダーはキャンセルされます。これは
endをまったく送信しなくなったプロダクトの影響範囲を限定するものです。endを呼び出す代わりになるものではなく、あくまで安全網です。 - タイムスタンプはリアルタイムである必要があります。
/screentime/startは5分より前のtimestampと未来のtimestampを拒否します。/screentime/endはstartのタイムスタンプ以降であることを要求し、1分程度の進み方向のクロックずれを許容します。
startとendはどちらもレスポンスにカウンターを返します。そのため、セッションの開始時や終了時にプレイヤーへ残り時間を表示するだけの目的でGET /screentime/get-stateを別途呼び出す必要はありません。
{
"sessionId": "b1a6482d-5242-4b4a-aa88-3fa52595a672",
"id": "3f8c1d24-6b5e-4a07-9c2f-8d1e7a4b6c90",
"status": "accepted",
"screentime": {
"counter": {
"timeUsedTodayMinutes": 45,
"timeLimitTodayMinutes": 120,
"timeRemainingTodayMinutes": 75,
"dayResetsAt": "2026-06-25T00:00:00Z"
}
}
}
事後の報告
/screentime/pushは1回の呼び出しで最大100件の完了済みセグメントを受け取ります。各セグメントは冪等性のためのid、利用が始まった時点のtimestamp、そしてdurationSecondsを持ちます。
セグメントは個別に処理されるため、バッチ全体が成功したと想定せず、イベントごとのステータスを読んでください。
{
"sessionId": "b1a6482d-5242-4b4a-aa88-3fa52595a672",
"accepted": 1,
"rejected": 1,
"events": [
{ "id": "9d4e2f81-7a3b-4c56-8e90-1f2a3b4c5d6e", "status": "accepted" },
{ "id": "c1b2a3d4-5e6f-4708-9a1b-2c3d4e5f6071", "status": "rejected", "reason": "invalid_input" }
]
}
reasonがinvalid_inputの場合、そのセグメントは検証ルールに違反しており、そのまま再送しても再び失敗します。セグメントはすでに完了している必要があり、timestampは過去7日以内、durationSecondsは1秒以上24時間以下である必要があります。reasonがinternal_errorの場合は一時的なもので、再送して問題ありません。すでに受理されたidを再送した場合は、二重に集計されることなく再びacceptedとなります。
状態の読み取り
GET /screentime/get-stateは、ルールと現在の判定に関する唯一の真実の源です。セッションの開始時に呼び出し、状況を変えるイベントを受信した後にも呼び出してください。
{
"enabled": true,
"access": {
"allowed": false,
"details": {
"reason": "quiet_hours",
"resumesAt": "2026-06-25T07:00:00+09:00"
}
},
"state": {
"timeUsedTodayMinutes": 75,
"continuousUsageMinutes": 30,
"timeLimitTodayMinutes": 120,
"timeRemainingTodayMinutes": 45,
"dayResetsAt": "2026-06-25T00:00:00Z"
},
"schedule": {
"timezone": "Asia/Tokyo",
"breakReminderIntervalMinutes": 45,
"dailyLimits": [{ "day": "mon", "limitMinutes": 120 }],
"quietHours": [
{
"name": "Bedtime",
"days": ["mon", "tue", "wed", "thu", "fri"],
"start": "21:00",
"end": "07:00"
}
]
}
}
次の順序で読んでください。
enabled。falseのときレスポンスには他に何も含まれません。理由は3つあります。保護者がスクリーンタイムを設定していない、保護者が設定したうえで制御を無効にした、またはセッションにkuidがなくスケジュールの帰属先となるプレイヤーが存在しない、のいずれかです。テストで最も遭遇しやすいのは3番目です。いずれの場合も何も制限しないでください。access.allowed。これが判定であり、1日の制限とすべての静かな時間帯がすでに組み合わされています。scheduleから自分で再計算しないでください。access.details。allowedがfalseのときのみ含まれます。reasonはquiet_hoursまたはlimit_reachedで、resumesAtは制限が解除される時刻です。これがプレイヤーに伝えるべき情報です。2つの制限が同時に適用される場合は、後に終了する方が返されます。state。プロダクトのUIで使う本日の数値です。timeUsedTodayMinutesは進行中のセッションの時間を含み、continuousUsageMinutesはそのセッション単独の長さです。本日の曜日に制限が設定されていない場合、timeLimitTodayMinutesとtimeRemainingTodayMinutesは含まれません。schedule。表示用の保護者のルールです。時刻はschedule.timezoneにおけるHH:MMです。
イベントの処理
これらのイベントは、プロダクトのWebhookがそのイベントタイプを購読している場合にのみ届きます。ハンドラが一度も実行されない原因を調べる前に、始める前にを確認してください。
| イベント | 意味 | 対応 |
|---|---|---|
Screentime.BreakReminder | プレイヤーが設定された間隔にわたって1回のセッションを継続した | 休憩を促すメッセージを表示する |
Screentime.LimitWarning | 本日の残りが15分または5分 | セーブポイントに到達できるようプレイヤーに知らせる |
Screentime.LimitReached | 本日の制限が適用された | 制御を適用し、保護者へのリクエストを提示する |
Screentime.QuietHoursWarning | 15分後に時間帯が開始する | どの時間帯がいつ始まるかを伝える |
Screentime.QuietHoursReached | 時間帯が開始した | endsAtまで制御を適用する |
Screentime.ScheduleChanged | 保護者がルールを変更した | get-stateを再取得して保持している状態を置き換える |
Screentime.OverrideResult | 保護者が延長リクエストに回答した | ペイロードのstateを適用する |
配信について設計上考慮すべき性質が3つあります。
- イベントは
sessionIdに宛てて送られます。 7種類すべてに含まれるdata.sessionIdで振り分けてください。 - 配信は少なくとも1回です。 同じイベントが2回届くことがあります。Webhooksの概要の説明に従い、ハンドラを冪等にしてください。
- 静かな時間のイベントは進行中のセッションを必要としません。 プロダクトで有効なk-IDセッションを持つプレイヤーに対して、その時点でプレイしているかどうかに関係なく保護者のカレンダーに従って発行されます。割り込む対象のUIがあると想定しないでください。
保護者への延長リクエスト
access.allowedがfalseのとき、壁を示すだけでなくリクエストする手段をプレイヤーに提示してください。
sessionIdを指定して/screentime/request-overrideを呼び出します。レスポンスにはidとexpiresAtが含まれます。idを保存してください。- k-IDが保護者に通知し、保護者はFamily Connectで承認または拒否します。
- 結果は
data.idで対応付けられるScreentime.OverrideResultとして届き、statusはgranted、denied、expiredのいずれかです。 data.stateを読んでください。これは決定後に計算されたget-stateと同じ形の内容です。追加の取得は不要です。
このエンドポイントはプレイヤーごとに冪等です。リクエストが保留中の間に再度呼び出すと、2件目を作成せずに保留中のリクエストをそのまま返します。レスポンスの形はどちらの場合も同一なので、専用の処理を分ける必要はありません。回答がないリクエストは24時間後に有効期限切れとなり、status: "expired"でWebhookが発行されます。その後、プレイヤーは改めてリクエストできます。
承認された延長は本日の制限を引き上げます。スケジュールを無効にするものではなく、翌日には引き継がれません。
タイムゾーンと日付の境界
すべてのスケジュールはタイムゾーンを持ち、スクリーンタイムのすべてのルールはそのタイムゾーンで評価されます。タイムスタンプはオフセット付きのRFC 3339で送信し、変換はk-IDに任せてください。
- 1日の制限は、スケジュールのタイムゾーンにおける現地時間の深夜にリセットされます。状態レスポンスの
dayResetsAtがその時刻です。 - 現地時間の深夜をまたぐセッションは
/screentime/endで分割されるため、各日の合計はその日に実際に発生した利用時間を反映します。深夜をまたぐpushのセグメントも同様です。
機能フラグ
スクリーンタイムには2つの階層があり、プロダクトは最初の階層だけを有効にすることもできます。
| フラグ | 有効になる機能 | 対象のエンドポイント |
|---|---|---|
| Screentime | 受動的な利用時間の報告。保護者の設定は不要 | /screentime/push |
| Screentime Controls | 保護者が設定する制限、スケジュール、静かな時間、休憩リマインダー、延長 | /screentime/start、/screentime/end、/screentime/get-state、/screentime/request-override |
Screentime だけを有効にすると、プロダクトは利用時間を報告することだけができます。/screentime/pushに完了したセグメントを送ると、それが子どもの1日の合計に加算されます。これはアクティビティ取り込みと同じ形の統合であり、保護者が何かを設定する必要はありません。
Screentime Controls は第2の階層で、保護者が設定するスケジュールと、それに基づいて動作するすべてです。セッションのステートマシン(/screentime/start、/screentime/end)、現在の判定(/screentime/get-state)、保護者に依頼するフロー(/screentime/request-override)をゲートし、リアルタイムのイベントはそのセッションマシンから生成されます。
階層が無効なエンドポイントはFEATURE_DISABLEDを返します。Screentimeだけを有効にしたプロダクトは利用時間をpushできますが、セッションを開始したり状態を読み取ったりはできません。どちらも有効でないプロダクトはいずれのエンドポイントも呼び出せません。
公開前チェックリスト
- すべてのセッションの開始時に
GET /screentime/get-stateを呼び出しており、enabled: falseの場合はプレイヤーを制限しない。 scheduleから判定を再計算せず、access.allowedを読んでいる。- すべての
/screentime/startが同じidの/screentime/endと対になっており、statusがreplacedの場合をエラーとして扱わずに処理している。 - 各警告の組について、2番目のイベントだけでなく両方のイベントを処理している。
- プロダクトのWebhookが、処理する7種類の
Screentime.*イベントタイプのそれぞれを購読している。Webhookで選択されていないイベントタイプは配信されない。 - Webhookのハンドラが冪等であり、署名を検証している。
Screentime.ScheduleChangedでget-stateを再取得している。- 制限や静かな時間帯に達したプレイヤーに
/screentime/request-overrideを提示しており、expiredを含む3つの延長ステータスすべてを処理している。 - プロダクトが適用する制御が、
resumesAtまたはendsAtを使って解除される時刻をプレイヤーに伝えている。