0031: Player API - MVP Phase
STATUS
Approved
CONTEXT
As defined in the player-api-poc-phase document
The AdGem system currently manages player progress through an internal PHP package (
ad-tracking) that has accumulated significant technical debt. This package handles tracking of player progress through offers, and will eventually also handle validation of incoming postbacks (such as enforcement of Max Completion Time and Conditional Goals) and management of player data (targeting info - demographics, affinities for certain campaign verticals, etc). As the system has evolved, this package has become increasingly difficult to maintain and extend.
With the necessary infrastructure in place and the vertical slice of the POST /players endpoint completed we can move on to the MVP-phase of the Player API.
DECISION
The MVP phase will focus on exposing all existing functionality of the ad-tracking php package as REST Endpoints (deprecating methods that are no longer needed).
MVP Scope
POC Endpoint(s):
ad-tracking Function |
Endpoint | HTTP Verb |
|---|---|---|
createPlayer(string $adgem_uid, int $app_id, int $pub_id, ?string $external_player_id) |
/v1/apps/{app_id}/players/ |
POST |
MVP Endpoint(s) to add:
ad-tracking Function |
Endpoint | HTTP Verb | Response | Note(s) |
|---|---|---|---|---|
getPlayer(string $adgem_uid, int $app_id) |
/v1/apps/{app_id}/players/{adgem_uid} |
GET | Player | |
getPlayerByPlayerId(string $player_id, int $app_id) |
/v1/apps/{app_id}/players/by-external/{external_player_id} |
GET | Player | Can't be used for creating transactions since it's not adgem_uid. |
PlayerContract::addReward() |
/v1/apps/{app_id}/players/{adgem_uid}/rewards |
POST | N/A | |
PlayerContract::getReward(string $reward_id) |
/v1/apps{app_id}/players/{adgem_uid}/rewards/{reward_id} |
GET | Reward | |
PlayerContract::redeemReward(string $reward_id, ?DateTimeInterface $redeemed_at = null) |
/v1/apps{app_id}/players/{adgem_uid}/rewards/{reward_id} |
PATCH | ||
PlayerContract::getRewards() |
/v1/apps/{app_id}/players/{adgem_uid}/rewards |
GET | Rewards Collection | |
queryPlayerTransactionsWithAvailableRewards(PlayerContract $player) |
/v1/apps/{app_id}/players/{adgem_uid}/rewards?available=true |
GET | Rewards Collection | Notably moving away from transaction based rewards and instead tracking them on Player. |
getPlayersByAppGroup(int $publisher_id, string $player_id) |
/v1/publishers/{publisher_id}/players/by-external/{external_player_id} |
GET | Player Collection | Being kept for legacy support but something we're considering deprecating in the future. It uses an external_player_id context so it can't be used to create transactions. |
createTransaction(PlayerContract $player, CampaignContract $campaign, string $tx_id, $payment_details_array, string $fingerprint): Transaction |
/v1/apps/{app_id}/players/{adgem_uid}/transactions |
POST | N/A | |
getPlayerTransactions(PlayerContract $player, ?int $campaign_id) |
/v1/apps/{app_id}/players/{adgem_uid}/transactions |
GET | Transaction Collection | Though the ad-tracking function supports a nullable $campaign_id parameter, in practice it was only ever directly called without that argument. |
getPlayerInProgressTransaction(PlayerContract $player, int $campaign_id) |
/v1/apps/{app_id}/players/{adgem_uid}/transactions?campaign_id={campaign_id}&status=in-progress |
GET | Transaction Collection | Since this is the /transactions endpoint, it will return a Transaction Collection rather than a single Transaction. This is an intentional deviation from the ad-tracking function (which returned a single Transaction). It is the responsibility of the caller to determine if the given filters (such as campaign_id and status) are sufficient to narrow down to a single Transaction. |
getTransaction(string $tx_id) |
/v1/transactions/{tx_id} |
GET | Transaction | |
completeGoalById(int $goal_id) |
/v1/transactions/{tx_id}/goals/{goal_id} |
POST |
Tree Diagram Breakdown of Endpoint Structure
Deprecate Feature(s):
Clicks & Conversions
Clicks and conversions were originally ported from ClickDB, a slow and painful legacy system and created scaling problems for early AdGem. The functionality was added to ad-tracking without clear design intent - it was more of a temporary solution while the team lacked the data infrastructure expertise they have today. The team now has a robust analytical infrastructure, with much more understanding and expertise. With this, data like conversion counts and click tracking is better suited for analytical systems rather than transactional systems.
While transitioning to the Player API if anything breaks without this functionality, the team can evaluate adding it back or finding alternative solutions. The benefit of this is so that the team can focus on delivering a more narrow scoped MVP. Additionally, preventing us from carrying forward technical debt from legacy design decisions.
Clicks:
- createClick
- getClick
- getPlayerClicks
- getPlayerCampaignClicks
Conversions:
- createConversion
- getConversions
- getPlayerConversions
- getConversionsByCampaign
- getConversionsByFingerprint
- getConversionsByCampaigAndDateRange
Campaign
Removing overly opinionated campaign setup.
- getCampaign
- getPreviouslyConvertedCampaigns - Sorting logic has since evolved, no longer needed in original uses.
Transaction
getTransactionsByFingerprint- Reason: "unused".getPlayerTransactionsWithAvailableRewards- Moving away from tracking rewards on Transactions in favor of tracking Player rewards.DAO::putTransaction(Transaction $transaction, bool $isNew = false)- Used to either create a new transaction which we cover or redeem a given transaction. But no longer needed since we're managing rewards on Player.
Impressions
Unnecessary functionality, since been deprecated and removed (Issue).
- getRecentImpressions
- addImpressions
- removeImpressions
API Design
Redeem Player Reward
PATCH /v1/apps{app_id}/players/{adgem_uid}/rewards/{reward_id}/redeem
Content-Type: application/json
{
"redeemed_at": datetime
}
Create Player Reward Endpoint
POST /v1/apps/{app_id}/players/{adgem_uid}/rewards
Content-Type: application/json
{
"adgem_uid": string,
"app_id": integer,
"campaign_id": integer,
"goal_id": integer,
"transaction_id": integer,
"conversion_id": integer, // we may not need this with deprecation of conversion
"case_id": integer,
"reward_origin": string,
"amount": float,
"is_redeemed": bool,
"redeem_at": datetime,
"is_available": bool,
}
Create Transaction Endpoint
POST /v1/apps/{app_id}/players/{adgem_uid}/transactions
Content-Type: application/json
{
"adgem_uid": string,
"app_id": integer,
"campaign_id": integer,
"transaction_id": string,
"payment_details": {
"revenue": double,
"payout": double,
"margin": double,
"multiplier": double,
},
"fingerprint": string,
"sub_site_id": string|optional
}
NOTES
References
- PR #65: docs: Player API MVP
- PR #75: docs: Update player api directory and metadata
- PR #127: docs: backfill PR reference links for existing ADRs
Original Author
Ben Giese
Approval date
09/02/2025
Approved by
Dakota Washok, Dylan Kreth