Skip to content

0011: Targeted API Resource Access Pattern

STATUS

Approved

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