--- title: "無料アイテム" description: "コードの入力や支払いなしで、プレイヤーがキャンペーン報酬を受け取れるようにします。サーバーが利用状況を確認して受け取りを申請し、redemption.completed Webhook を処理してゲーム内で報酬を付与します。" source: "https://docs.tokenz.one/ja/v2/free-items" api_version: "v2" locale: "ja" version_status: "current" docs_stage: "prod" --- # 無料アイテム コードの入力や支払いなしで、プレイヤーがキャンペーン報酬を受け取れるようにします。サーバーが利用状況を確認して受け取りを申請し、`redemption.completed` Webhook を処理してゲーム内で報酬を付与します。 リクエストとレスポンスの詳細は[無料アイテム API リファレンス](https://docs.tokenz.one/ja/v2/api/free-items)を参照してください。 ## 認証とプレイヤー識別子 API はサーバーからシークレット API キーで呼び出します。状況取得には `FreeItemStatus`、受け取りには `FreeItemClaim` スコープが必要です。キーはバックエンドに保管し、`playerId` はゲームで認証済みのプレイヤーから取得してください。 `playerId` はマーチャント側の識別子です。Tokenz はゲーム内のアカウントを検索せず、この文字列でプレイヤーを識別します。 - 前後の空白は除去されます。空白だけの値は使用できません。 - 最大 100 文字です。長すぎる値は切り詰められず、`request.decoding-failed` で拒否されます。 - 大文字と小文字は区別されます。`Player_123` と `player_123` は別のプレイヤーとして扱われます。 - すべての呼び出しで同じ安定した識別子を使用してください。小文字化などで別の識別子を統合しないでください。 - 同じ識別子なら、デバイス、セッション、ストアフロントが変わっても受け取り上限を共有します。 ## キャンペーンの設定とステータス Tokenz Dashboard で期間、報酬、キャンペーン全体とプレイヤーごとの上限、繰り返し頻度を設定し、一時停止・再開・終了を管理します。無料アイテムの設定が表示されない場合は、Tokenz サポートにアカウントでの提供状況を確認してください。 - `draft`:編集中。受け取り不可。すべての項目を編集できます。 - `scheduled`:公開済みで開始待ち。`startAt` に達すると受け取り可能になります。 - `active`:実施中。受け取り可能です。 - `paused`:一時停止中。受け取り不可。`active` に再開できます。 - `ended`:終了済み。受け取り不可。この状態は元に戻せません。 状況取得と受け取りは、無料アイテムのキャンペーンが `active`、または `scheduled` で `startAt` に達しており、かつ `endAt` より前の場合に成功します。保存済みステータスの更新前でも、開始時刻に達した `scheduled` キャンペーンは利用可能です。時刻は絶対時刻で比較され、プレイヤーの所在地によって終了時刻は変わりません。 ## 上限と繰り返し 各受け取りで、独立した 2 つの上限を確認します。 - **キャンペーン全体の上限**:全プレイヤーの合計です。上限に達すると `campaign.limit-reached` で拒否されます。 - **プレイヤーごとの上限**:同じ `playerId` の受け取り回数です。単発では `campaign.player-limit-reached`、繰り返しでは `campaign.player-period-limit-reached` で拒否されます。他のプレイヤーには影響しません。 単発キャンペーンでは、両方の上限がキャンペーン全期間に適用され、リセットされません。繰り返しキャンペーンでは、**両方の上限が各期間の開始時にリセットされます**。拒否された受け取りは、どちらの残り回数も消費しません。 | 頻度 | 期間の開始 | | --- | --- | | 単発 | リセットなし | | 日次 | 現地時刻の午前 0 時 | | 週次 | 月曜日の現地時刻の午前 0 時 | | 月次 | 毎月 1 日の現地時刻の午前 0 時 | 使用するのはプレイヤーではなくキャンペーンのタイムゾーンです。このタイムゾーンは API で返されないため、境界を自前で計算せず `nextResetAt` をカウントダウンに使用し、その時刻を過ぎたら状況を再取得してください。その時刻以降の受け取りは次の期間に属します。リセットしても `endAt` は延長されません。 例えば日次で全体 100 回、1 人 1 回なら、毎日最大 100 人が 1 回ずつ受け取れます。翌日のリセットで全体の残り回数が 100 に戻り、前日に受け取った人も再び受け取れます。 ## 受け取りフロー 1. 自社システムに `campaignId` を保存します。 2. サーバーから `GET /v2/free-items/{campaignId}?playerId=...` を呼び出し、報酬と残り回数を表示します。 3. プレイヤーの操作に応じて `POST /v2/free-items/claim` を呼び出します。 4. Tokenz が受け取り記録を作成し、`201` と `redemption.completed` Webhook を返します。 5. バックエンドで署名を検証し、`redemptionId` を使って重複付与を防ぎ、報酬を付与します。 ## 状況の取得 `campaignId` は必須のパスパラメーター、`playerId` は任意のクエリパラメーターです。状況確認は受け取り枠を予約しないため、その後に利用状況が変わる可能性があります。上限に達していても、状況取得では上限エラーではなく残り回数 `0` が返されます。 ```bash curl --request GET \ --url 'https://api.tokenz.one/v2/free-items/campaign_1p4LPTRKB5Z_t?playerId=player_98765' \ --header 'Authorization: Bearer secret_test_YOUR_KEY_HERE' ``` ### 成功レスポンス(200) ```json { "campaign": { "id": "campaign_1p4LPTRKB5Z_t", "name": "Weekly Free Pack" }, "rewardPreview": { "name": "Marathon Energy Gift", "imageUrl": "https://images.example.com/rewards/energy.png", "quantity": 1 }, "campaignRemaining": 842, "remainingForPlayer": 1, "nextResetAt": "2026-09-14T00:00:00Z" } ``` - `campaignRemaining`:全プレイヤーの残り回数。繰り返しでは現在の期間、単発では全期間に対する値です。全体上限がなければ省略されます。 - `remainingForPlayer`:このプレイヤーの残り回数。`playerId` がない場合、またはプレイヤー上限がない場合は省略されます。 - `nextResetAt`:両方の上限が次にリセットされる絶対時刻。単発では省略されます。 ## 無料アイテムの受け取り 本文の `campaignId` と `playerId` は必須です。成功には利用可能なキャンペーンと、両方の上限に空きが必要です。API は記録を作成し、実際の報酬付与はバックエンドが Webhook を処理して行います。 ```bash curl --request POST \ --url https://api.tokenz.one/v2/free-items/claim \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer secret_test_YOUR_KEY_HERE' \ --data '{ "campaignId": "campaign_1p4LPTRKB5Z_t", "playerId": "player_98765" }' ``` ### 成功レスポンス(201) ```json { "redemption": { "redemptionId": "redemption_1p4LPTRKB5Z_t", "playerId": "player_98765", "campaign": { "id": "campaign_1p4LPTRKB5Z_t", "name": "Weekly Free Pack" }, "reward": { "skuRedemptionReward": { "type": "EXTERNAL_SKU", "sku": "ITEM_ENERGY_GIFT", "name": "Marathon Energy Gift", "quantity": 1 } }, "redeemedAt": "2026-09-10T08:10:00Z", "kind": "FREE_ITEM_CLAIM", "source": "FREE_ITEM_CLAIM", "nextPeriodStart": "2026-09-14T00:00:00Z" }, "remainingForPlayer": 0, "nextResetAt": "2026-09-14T00:00:00Z" } ``` `remainingForPlayer` は今回の受け取り後の残り回数で、プレイヤー上限がなければ省略されます。`nextResetAt` は単発では省略されます。`redemption` 内のフィールドは次の Webhook と共通です。 ## Webhook と報酬付与 このイベントを購読しているエンドポイントに `redemption.completed` が送信されます。ギフトコード(旧称:引き換えコード)と同じイベントです。 **報酬付与前に `Tokenz-Signature` を検証してください。** 生のリクエストボディで検証してから解析します。署名なしで信用すると、偽造リクエストで報酬を付与してしまいます。[Webhook エンドポイントを保護する](https://docs.tokenz.one/ja/v2/checkout/webhooks-get-started#3%29-%E3%82%A6%E3%82%A7%E3%83%96%E3%83%95%E3%83%83%E3%82%AF%E3%82%A8%E3%83%B3%E3%83%89%E3%83%9D%E3%82%A4%E3%83%B3%E3%83%88%E3%82%92%E4%BF%9D%E8%AD%B7%E3%81%99%E3%82%8B)を参照してください。 ```json { "id": "f75ff6f0-bdf9-4b25-bb96-968f8733a7e3", "object": "redemption.completed", "createdAt": "2026-09-10T08:10:00Z", "test": true, "eventData": { "type": "redemption", "version": "v2", "data": { "redemption": { "redemptionId": "redemption_1p4LPTRKB5Z_t", "campaign": { "id": "campaign_1p4LPTRKB5Z_t", "name": "Weekly Free Pack" }, "playerId": "player_98765", "reward": { "skuRedemptionReward": { "type": "EXTERNAL_SKU", "sku": "ITEM_ENERGY_GIFT", "name": "Marathon Energy Gift", "quantity": 1 } }, "redeemedAt": "2026-09-10T08:10:00Z", "kind": "FREE_ITEM_CLAIM", "source": "FREE_ITEM_CLAIM", "nextPeriodStart": "2026-09-14T00:00:00Z" } } } } ``` - `kind`:無料アイテムでは `FREE_ITEM_CLAIM`、ギフトコードでは `CODE_REDEMPTION`。新規イベントには設定されますが、過去にキューに入ったイベントでは省略される場合があります。 - `source`:互換性維持のための非推奨フィールドです。新規連携では `kind` を使用してください。 - `code`:ギフトコード引き換え時のみ存在し、無料アイテムでは省略されます。既存ハンドラーが必須とみなしていないか確認してください。 - `nextPeriodStart`:繰り返しの無料アイテムでのみ存在します。配信時刻ではなく、`redeemedAt` 時点の繰り返し設定から計算した次の期間の開始時刻です。単発とギフトコードでは省略されます。 通常は `redemptionId`、`playerId`、`reward` を使う共通の付与処理で十分です。`kind` が欠けている、または未知の値でも種類を推測せず、この 3 項目で付与し、照合対象として記録してください。イベントのトップレベル `id` は配信追跡、`redemptionId` は重複付与の防止に使います。 ## 再試行と応答の喪失 受け取り API に冪等性キーはありません。同じリクエストを繰り返すと、上限に余裕があれば別の受け取りになります。応答を失っても、無条件に再試行しないでください。 Webhook で成功を確認できますが、キャンペーンとプレイヤーを識別するだけなので、特定のタイムアウトしたリクエストと一意に対応しない場合があります。自社のリクエストログと照合してください。応答も Webhook もない場合、公開 API では受け取り記録を再取得できません。再試行前に Tokenz サポートへ連絡してください。Webhook の重複排除だけでは、受け取りリクエストの重複を防げません。 Webhook 配信が受け付けられない場合、間隔を広げ、ランダムな変動を加えて再試行します。初回試行から最大 72 時間の期間内に再試行が予定されますが、その期間内の成功を保証するものではなく、実際の実行が遅れることもあります。固定の回数や時刻に依存しないでください。配信は数日後に届く場合があるため、処理済み `redemptionId` は短命なキャッシュではなく永続ストレージに保存し、同じ ID への付与を冪等にしてください。 ## エラー - `400` / `request.decoding-failed`:不正な ID、空白または長すぎる `playerId`、不正なリクエスト。 - `401`:API キーがない、または無効。 - `403`:必要なスコープがない。 - `404` / `entity.not-found`:キャンペーンがない、他のマーチャントに属する、またはキーとテストモードが異なる。 - `422` / `campaign.kind-invalid`:無料アイテムのキャンペーンではない。 - `422` / `campaign.status-invalid`:下書き、一時停止、終了、または開始前の予約済み状態。 - `422` / `campaign.expired`:終了時刻に到達。終了済みステータスでは `campaign.status-invalid` の場合もあります。 - `422` / `campaign.limit-reached`:全体上限に到達。繰り返しでは現在の期間、単発では全期間の上限です。 - `422` / `campaign.player-limit-reached`:プレイヤー上限に到達。 - `422` / `campaign.player-period-limit-reached`:現在の期間のプレイヤー上限に到達。`nextResetAt` 以降に再試行できます。 - `429`:レート制限。両エンドポイントは API キーごと、および指定された場合はプレイヤーごとに制限されます。 - `500`:予期しないサーバーエラー。 ## テスト `secret_test_` で始まるキーを使い、Dashboard のテストモードでキャンペーンを作成します。テストと本番は分離され、キーのモードと一致するキャンペーンのみ利用できます。テスト ID は `_t` で終わり、テスト Webhook はテスト用エンドポイントだけに `"test": true` で届きます。 成功と報酬付与、両方の上限到達、期間リセット、一時停止と期限切れ、重複配信をテストしてください。空白・長すぎる `playerId`、レート制限、テストと本番の分離も確認し、`redemptionId` ごとの付与が 1 回だけであることを検証します。