Authenticating...
Skip to main content

0011: Targeted API Resource Access Pattern

STATUS

Accepted

CONTEXT

AdAction wants to offer a product that serves single- and multi-reward targeted offers to app users. This API should allow apps to filter offers using player-level data.

Considered Options

  • REST
  • GraphQL - DECISION

Option 1: REST

Description

The REST API architectural style "enables client applications to exchange data with a server using HTTP verbs, which is the standard communication protocol of the internet" (What's the Difference Between GraphQL and REST?).

Example Request

GET /v1/offers/targeted?player_id=testplayer
Host: https://offer-api.adgem.com
Accept: application/json
Authorization: Bearer 0|123456789abcdefghi // authentication approach to be determined

Example Response

// response schema to be determined
{
"status": "success",
"data": {
"offers": [
{
"id": "61918292528467968",
"campaign_id": 20391,
"name": "Prison Escape",
"store_id": "1525116042",
"tracking_type": "CPE",
"total_payout_usd": 0.3,
"total_amount": 0.3,
"start_datetime": "2023-03-22 17:04:30",
"is_multi_reward": false,
"completion_difficulty": 6,
"is_featured_campaign": false,
"campaign_vertical": "games_casual_general",
"updated_at": "2024-11-19T06:41:09.000000Z",
"creatives": {
"name": "Prison Escape",
"icon_url": "https://adgem-dashboard-production.s3.us-east-2.amazonaws.com/campaigns/20391/campaign-offerwall-creatives/icons/OrOJ6ksQZqJzQxh.png",
"description": "Pin the pulls in the correct order to help Andy overcome obstacles and get free from prison!",
"short_description": "Collect 10k Coins to Earn!",
"instructions": [
"Play Prison Escape",
"Collect 10k coins within 5 days after installing"
],
"disclaimer": "New Users Only!, Offer must be completed within 5 days!",
"currency_name": "Coin",
"currency_name_plural": "Coins",
"categories": [
"app",
"free"
],
"sort_order_setting": null,
"stickers": [],
"hero_image_url": "https://adgem-dashboard-production.s3.us-east-2.amazonaws.com/campaigns/20391/campaign-offerwall-creatives/hero_images/jkgkzGXMrgCXZsj"
},
"goals": [
{
"id": "61918292553633792",
"name": "Collect 10k coins",
"description": "Collect 10k coins",
"amount": 0.3,
"payout_usd": 0.3,
"order": 1
}
],
"links": {
"click_url": "https://api.adgem.com/v1/click?all=1&integration=offer-api&appid=29522&cid=20391&playerid={playerid}&offer_id=61918292528467968",
"support_url": "https://api.adgem.com/support/29522/20391/{playerid}"
},
"stats": {
"network_epc": "0.0052"
},
},
...
]
},
"links": {
"support_portal": "https://api.adgem.com/support/player?appid=29522&playerid={playerid}"
}
}

Pros

  • Minimal infrastructure (follow established pattern)
  • Publisher engineering teams likely have more experience with REST
  • Publisher Integrations team has more experience with REST

Cons

  • Overfetching data
  • Backward compatibility not required, but can be alleviated through versioning
  • Non-encoded filters (assuming GET request)
  • Weakly typed
  • Possibly slower performance

Option 2: GraphQL

Description

"GraphQL is an API query language that defines specifications of how a client application should request data from a remote server. You can use GraphQL in your API calls without relying on the server-side application to define the request" (What's the Difference Between GraphQL and REST?).

Example Request (No Specified Fields)

POST /v1/offers/targeted?player_id=testplayer
Host: https://offer-api.adgem.com
Accept: application/json
Authorization: Bearer 0|123456789abcdefghi // authentication approach to be determined
// query schema to be deteremined
Body:
{
query: query {
player(id:"testplayer")
offers {
id
campaign_id
name
store_id
tracking_type
total_payout_usd
total_amount
start_datetime
is_multi_reward
completion_difficulty
is_featured_campaign
campaign_vertical
updated_at
creatives {
name
icon_url
description
short_description
instructions
disclaimer
currency_name
currency_name_plural
categories
sort_order_setting
stickers
hero_image_url
}
goals {
id
name
description
amount
payout_usd
order
}
links {
click_url
support_url
}
stats {
network_epc
}
}
}
}

Example Response (No Specified Fields)

// response schema to be determined
{
"status": "success",
"data": {
"offers": [
{
"id": "61918292528467968",
"campaign_id": 20391,
"name": "Prison Escape",
"store_id": "1525116042",
"tracking_type": "CPE",
"total_payout_usd": 0.3,
"total_amount": 0.3,
"start_datetime": "2023-03-22 17:04:30",
"is_multi_reward": false,
"completion_difficulty": 6,
"is_featured_campaign": false,
"campaign_vertical": "games_casual_general",
"updated_at": "2024-11-19T06:41:09.000000Z",
"creatives": {
"name": "Prison Escape",
"icon_url": "https://adgem-dashboard-production.s3.us-east-2.amazonaws.com/campaigns/20391/campaign-offerwall-creatives/icons/OrOJ6ksQZqJzQxh.png",
"description": "Pin the pulls in the correct order to help Andy overcome obstacles and get free from prison!",
"short_description": "Collect 10k Coins to Earn!",
"instructions": [
"Play Prison Escape",
"Collect 10k coins within 5 days after installing"
],
"disclaimer": "New Users Only!, Offer must be completed within 5 days!",
"currency_name": "Coin",
"currency_name_plural": "Coins",
"categories": [
"app",
"free"
],
"sort_order_setting": null,
"stickers": [],
"hero_image_url": "https://adgem-dashboard-production.s3.us-east-2.amazonaws.com/campaigns/20391/campaign-offerwall-creatives/hero_images/jkgkzGXMrgCXZsj"
},
"goals": [
{
"id": "61918292553633792",
"name": "Collect 10k coins",
"description": "Collect 10k coins",
"amount": 0.3,
"payout_usd": 0.3,
"order": 1
}
],
"links": {
"click_url": "https://api.adgem.com/v1/click?all=1&integration=offer-api&appid=29522&cid=20391&playerid={playerid}&offer_id=61918292528467968",
"support_url": "https://api.adgem.com/support/29522/20391/{playerid}"
},
"stats": {
"network_epc": "0.0052"
},
},
...
]
},
"links": {
"support_portal": "https://api.adgem.com/support/player?appid=29522&playerid={playerid}"
}
}

Example Request (With Specified Fields)

POST /v1/offers/targeted?player_id=testplayer
Host: https://offer-api.adgem.com
Accept: application/json
Authorization: Bearer 0|123456789abcdefghi // authentication approach to be determined
// query schema to be deteremined
Body:
{
query: query {
player(id:"testplayer")
offers {
id
name
total_amount
campaign_vertical
creatives {
name
icon_url
description
instructions
disclaimer
currency_name
currency_name_plural
hero_image_url
}
goals {
id
name
description
amount
order
}
links {
click_url
support_url
}
}
}
}

Example Response (With Specified Fields)

// response schema to be determined
{
"status": "success",
"data": {
"offers": [
{
"id": "61918292528467968",
"name": "Prison Escape",
"total_amount": 0.3,
"campaign_vertical": "games_casual_general",
"creatives": {
"name": "Prison Escape",
"icon_url": "https://adgem-dashboard-production.s3.us-east-2.amazonaws.com/campaigns/20391/campaign-offerwall-creatives/icons/OrOJ6ksQZqJzQxh.png",
"description": "Pin the pulls in the correct order to help Andy overcome obstacles and get free from prison!",
"instructions": [
"Play Prison Escape",
"Collect 10k coins within 5 days after installing"
],
"disclaimer": "New Users Only!, Offer must be completed within 5 days!",
"currency_name": "Coin",
"currency_name_plural": "Coins",
"hero_image_url": "https://adgem-dashboard-production.s3.us-east-2.amazonaws.com/campaigns/20391/campaign-offerwall-creatives/hero_images/jkgkzGXMrgCXZsj"
},
"goals": [
{
"id": "61918292553633792",
"name": "Collect 10k coins",
"description": "Collect 10k coins",
"amount": 0.3,
"order": 1
}
],
"links": {
"click_url": "https://api.adgem.com/v1/click?all=1&integration=offer-api&appid=29522&cid=20391&playerid={playerid}&offer_id=61918292528467968",
"support_url": "https://api.adgem.com/support/29522/20391/{playerid}"
},
},
...
]
},
"links": {
"support_portal": "https://api.adgem.com/support/player?appid=29522&playerid={playerid}"
}
}

Pros

  • Flexibility
  • Event subscription
  • Encoded payload
  • Requires backward compatibility
  • Built-in strongly-typed architecture
  • Built-in error handling

Cons

  • Publisher engineering teams likely have less experience with GraphQL (see "Popularity" in GraphQL vs. REST)
  • Publisher Integrations team has less experience with GraphQL

Laravel GraphQL Package Options

DECISION

Choosing option 2 (GraphQL) would give publishers more flexibility to request only the data they want to meet the needs of their apps and users.

CONSEQUENCES

Choosing option 2 (GraphQL) will require installing new libraries, as well as expecting a longer integration time due to the fact that the team lacks experience with this technology.

Risks

Because the team lacks experience with GraphQL, there's a risk of less-than-ideal integration. We will want to take extra time to investigate best practices, especially with comparable APIs.

NOTES

References

Original Author

Micah Wierenga

Approval date

Approved by

Appendix