Skip to content

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

graph TD A[API Root] --> B[Version 1] B --> C[Apps] B --> D[Publishers] B --> E[Global Transactions] C --> F[Players by UID] C --> G[Players by External ID] D --> H[Players by External ID - Legacy] F --> I[Get Player Details] F --> J[Update Player ID] F --> K[Rewards Management] F --> L[Transactions Management] G --> M[Get Player by External ID] H --> N[Get Players by App Group<br/>Legacy - Consider Deprecating] K --> O[Get All Rewards] K --> P[Get Available Rewards Only] K --> Q[Add New Reward] K --> R[Specific Reward] L --> S[Get Player Transactions] L --> T[Get In-Progress Transactions] L --> U[Create New Transaction] R --> V[Get Reward Details] R --> W[Redeem Actions] W --> X[Redeem Reward] E --> Y[Get Transaction by ID] classDef getOp fill:#e1f5fe,stroke:#0277bd,color:#000 classDef postOp fill:#f3e5f5,stroke:#7b1fa2,color:#000 classDef patchOp fill:#fff3e0,stroke:#ef6c00,color:#000 classDef legacy fill:#ffebee,stroke:#c62828,color:#000 classDef resource fill:#f1f8e9,stroke:#33691e,color:#000 class I,M,N,O,P,S,T,V,Y getOp class Q,U,X postOp class J patchOp class N,H legacy class C,D,E,F,G,K,L,R,W resource

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.

Related Thread Comment

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

Original Author

Ben Giese

Approval date

09/02/2025

Approved by

Dakota Washok, Dylan Kreth

Appendix