店舗内メディアディスプレイAPI(Display API)の使い方

API Display メディア Android サイネージ ペアリング リテールメディア インプレッション

このガイドの目的

レシートローラーの店舗内メディアディスプレイAPI(Display API)を使って、店頭の Android 端末でメディアループ(画像・動画)を再生するアプリを実装する手順をまとめます。POS 端末併用のキオスクや、専用のサイネージ機(壁掛けタブレットなど)でレシートローラーのリテールメディアやキャンペーン動画を配信するためのガイドです。

前提

  • Android 端末側でブラウザを立ち上げる OAuth フローは想定していません。各端末は長期有効なデバイストークンで認証します
  • デバイストークンは「ペアリング」プロセスで取得します。店舗オーナーが管理画面で生成した6桁のコードを Android アプリに入力すると、端末ごとのトークンが発行されます
  • ベースURL: https://receiptroller.io

エンドポイント一覧

エンドポイント用途認証
POST /api/v1/displays/pair6桁のペアリングコードをデバイストークンに交換不要(コードが認証)
POST /api/v1/displays/{displayId}/heartbeat端末の生存通知(オンライン/オフライン判定用)デバイストークン
GET /api/v1/displays/{displayId}/playlist再生すべきメディアの順序付きリストを取得デバイストークン
POST /api/v1/displays/{displayId}/impressions再生実績(プレイ)をバッチで報告デバイストークン

ペアリングフロー

各端末は1回だけペアリングを行います。トークンは Android 側で永続化し、以後のすべてのリクエストで Authorization: Bearer {token} ヘッダーに付けます。

  1. 店舗オーナーが管理画面で新しいディスプレイを登録すると、6桁のペアリングコードが表示されます(有効期間10分・1回限り)
  2. Android アプリの初回起動画面でコードを入力
  3. Android が POST /api/v1/displays/pair を呼び出し、トークンと displayId を受け取ります
  4. Android はトークンと displayId をローカルに永続化(暗号化推奨)

リクエスト例:

POST /api/v1/displays/pair
Content-Type: application/json
User-Agent: RRDisplay/1.0 (Android 14; Pixel Tablet)

{
  "code": "123456",
  "screenWidth": 1920,
  "screenHeight": 1080,
  "supportsVideo": true,
  "supportsAudio": false
}

レスポンス例(200):

{
  "displayId": "a1b2c3d4-...",
  "organizationId": "7d325d1d-...",
  "deviceToken": "dvc_3f9c1a8b7e2d6f4a1b8c5d2e9f7a3b6c"
}

エラーレスポンス:

  • 400 missing_code — code フィールド未指定
  • 401 invalid_or_expired_code — コードが間違っているか、10分の有効期限を過ぎているか、既に使用済み

重要:

  • トークンはレスポンスでしか取得できません。サーバー側にはハッシュしか保存していないため、紛失した場合は管理画面でディスプレイを削除し、再度ペアリングしてください
  • 1つのペアリングコードは1回しか使えません。再ペアリング時は新しいコードを発行してください

ハートビート

端末がオンラインであることをサーバーに伝えます。管理画面の「オンライン/オフライン」表示はこのタイムスタンプを基準にしています(最後のハートビートから5分以内ならオンライン扱い)。

POST /api/v1/displays/{displayId}/heartbeat
Authorization: Bearer dvc_3f9c1a8b...
User-Agent: RRDisplay/1.0 (Android 14; Pixel Tablet)

レスポンス例(200):

{
  "ok": true,
  "serverTimeUtc": "2026-06-03T05:21:30Z",
  "nextHeartbeatSeconds": 60
}

推奨呼び出し間隔: レスポンスの nextHeartbeatSeconds に従ってください(現在60秒)。サーバー側がポーリング負荷に応じて値を変更する可能性があるため、ハードコードせずレスポンスを参照することを推奨します。

プレイリスト取得

端末が再生すべきメディアの順序付きリストを取得します。各アイテムには SAS 署名済みの直接アクセスURL(1時間有効)が含まれます。

GET /api/v1/displays/{displayId}/playlist
Authorization: Bearer dvc_3f9c1a8b...

レスポンス例(200):

{
  "displayId": "a1b2c3d4-...",
  "generatedAt": "2026-06-03T05:21:30Z",
  "pollIntervalSeconds": 60,
  "items": [
    {
      "creativeId": "creative-001",
      "name": "夏の新商品キャンペーン",
      "type": "Image",
      "mediaUrl": "https://strprdomnicon.blob.core.windows.net/creatives/...?sv=...&sig=...",
      "durationSeconds": 10,
      "hashHint": "creative-001_638549812340000000"
    },
    {
      "creativeId": "creative-002",
      "name": "新メニュー紹介動画",
      "type": "Video",
      "mediaUrl": "https://strprdomnicon.blob.core.windows.net/creatives/...?sv=...&sig=...",
      "durationSeconds": 30,
      "hashHint": "creative-002_638549812350000000"
    }
  ]
}

運用ガイド:

  • 再生順序items 配列の順番に再生し、最後まで再生したら最初に戻ってループ再生してください
  • キャッシュhashHint が変わらない限り、メディアファイルは再ダウンロード不要です。ローカルキャッシュを推奨します。hashHint が変わったら再取得してください
  • SAS URL の有効期限mediaUrl は1時間有効です。期限切れの URL でダウンロードを試みると 403 が返るので、pollIntervalSeconds 間隔でプレイリストを再取得し、新しい URL を取得してください
  • durationSeconds — 画像は10秒(既定)、動画は実尺。アプリは指定秒数で次のアイテムに切り替えてください
  • 空のプレイリスト — オーナー側でまだクリエイティブが承認されていない場合、items は空配列です。空のときは黒画面または「準備中」プレースホルダーを表示することを推奨します

再生実績の報告(インプレッション)

再生した1スロットごとに「いつ・どのクリエイティブを・どれだけ再生したか」を Android からサーバーへバッチ送信します。Phase 3 / t-e9a2f501。

POST /api/v1/displays/{displayId}/impressions
Authorization: Bearer dvc_3f9c1a8b...
Content-Type: application/json

{
  "events": [
    {
      "eventId": "a1b2c3d4-e5f6-7890-1234-567890abcdef",
      "creativeId": "creative-001",
      "campaignId": "camp-001",
      "placementId": "place-001",
      "playedAtUtc": "2026-06-03T05:20:00Z",
      "durationPlayedMs": 10000,
      "completed": true
    },
    {
      "eventId": "b2c3d4e5-f678-9012-3456-7890abcdef01",
      "creativeId": "creative-002",
      "campaignId": "camp-001",
      "placementId": "place-001",
      "playedAtUtc": "2026-06-03T05:20:10Z",
      "durationPlayedMs": 30000,
      "completed": true
    }
  ]
}

レスポンス例(200):

{
  "accepted": 2,
  "duplicates": 0,
  "rejected": 0,
  "errors": []
}

運用ガイド:

  • 送信頻度 — 60秒ごとに送信することを推奨します。1スロット再生ごとに送ると過剰なネットワーク負荷になります
  • バッチ上限 — 1回のリクエストで最大500件まで。超過すると 400 batch_too_large が返ります
  • eventId はクライアントが生成 — UUID v4 を推奨。サーバー側でこのIDを使って冪等性を保証しています。オフライン後に同じバッチを再送しても、サーバーは2回目をサイレントに duplicates としてカウントします
  • completed フラグ — スロットを最後まで再生したら true、途中で次に切り替えた場合は false。完了率の分析に使われます
  • オフライン対応 — ネットワーク失敗時はローカルキューに保持し、復旧後に再送してください。冪等性により重複送信は安全です
  • placementId / campaignId は任意 — プレイリストアイテムには直接含まれていませんが、Android アプリが Phase 2 のプレイリスト構造を理解している場合は付与すると分析の粒度が上がります。空でも accepted されます

レスポンスフィールド:

  • accepted — 新規に記録された件数
  • duplicates — 既に処理済みだった件数(再送による重複)
  • rejected — 必須フィールド欠落などで処理できなかった件数
  • errors — 拒否理由の配列(デバッグ用、内容はバージョンで変わる可能性あり)

典型的な実装フロー(Android)

  1. 初回起動 — トークンがローカルに存在しなければペアリング画面を表示。コード入力 → POST /pair → トークン取得 → 保存
  2. 常時実行ループ
    1. 起動時およびその後 60秒ごとに GET /playlist を呼び出し
    2. hashHint が前回と異なるアイテムは再ダウンロード、同じアイテムはキャッシュから読み込み
    3. 取得した順に画像/動画を全画面再生、durationSeconds ごとに次へ
    4. 最後のアイテムまで再生したら先頭に戻る
  3. 並行 — 60秒ごとに POST /heartbeat を呼び出してサーバーに生存を伝える
  4. 並行 — 再生したスロットごとにローカルキューに記録し、60秒ごとに POST /impressions でバッチ送信
  5. エラー処理
    • 401 — トークン無効。再ペアリング画面に戻す
    • 403 — トークンとパス上の displayId が一致しない(バグ)。ログを取って再ペアリング
    • ネットワーク失敗 — 最後に取得したプレイリストをそのまま再生し続け、インプレッションキューを保持してバックグラウンドで再試行

セキュリティ

  • デバイストークンは長期有効です。Android Keystore など端末の暗号化ストレージに保存してください
  • サーバー側にはトークンの SHA-256 ハッシュしか保存していません。紛失したトークンは管理画面でディスプレイを削除して再ペアリングすることで失効します
  • 1つのトークン = 1つのディスプレイ。複数端末で同じトークンを使い回すと、管理画面の「最後のハートビート」「現在の解像度」が端末ごとに上書きされ混乱します

このAPIの今後

現在のプレイリストは「キャンペーン × プレイスメントによる時間帯・店舗ターゲティングと重み付けループ」に対応しています。今後のフェーズで以下を追加します:

  • 店舗オーナー向け再生実績ダッシュボード(インプレッション数、完了率、ディスプレイ別ファンアウト)
  • 頻度上限の実効化(プレイスメントの frequencyCapPerHour は現在設定可能ですが、Phase 5 の実装まではノーオプ)

API スキーマは前方互換を保ちます — 既存の4つのエンドポイントのレスポンス形式は将来も変更しません(追加フィールドはあり得ます)。

関連情報

公開日: 2026-06-03 更新日: 2026-06-03
このトピックについて
開発者API
機能の詳細を見る
タグ
API (17) OAuth (11) Android (8) Webhook (8) iOS (7) api (6) oauth (5) トラブル (5) POS連携 (4) getting-started (4)
関連記事