Skip to content

POP Magic Row Technical Introduction

POP provides the backend integration layer for Magic Row recommendations. Magic Row aims to make the homepage feel intelligent, dynamic, and tailored to individual preferences, while laying the foundation for a scalable smart lobby that continuously adapts to user intent and behaviour.

For client integration, GFE calls POP to retrieve Magic Row recommendation decisions for a customer. POP validates and routes the request, retrieves recommendation data from CDS when needed, caches recommendation results in AWS ElastiCache Valkey, and returns a CloudEvents response to GFE.

This page introduces the POP architecture and documents the current /v1/decisions client integration contract. It intentionally does not cover running the services locally.

POP services

POP currently contains two services for the Magic Row recommendation flow.

Service Responsibility
des-router POP entry endpoint. It accepts CloudEvents HTTP requests on /v1/decisions, validates the required headers and body, and routes the request to the downstream recommendation service by brand and event attributes.
des-recommendations Recommendation orchestration service. It checks and refreshes Magic Row recommendation data in AWS ElastiCache Valkey, retrieves recommendations from CDS when required, and returns the decision response to des-router.

High-level architecture

GFE integrates with POP as the recommendation decision API. POP integrates with CDS as the recommendation source and uses AWS ElastiCache Valkey as the recommendation cache.

---
config:
  themeVariables:
    fontSize: 24px
title: High-level architecture
---
flowchart LR
  customer["Customer"]
  gfe["GFE client"]
  cds["CDS Magic Row recommendations"]

  subgraph pop_service["POP service"]
    direction TB
    router["des-router"]
    recommendations["des-recommendations"]
    valkey["AWS ElastiCache Valkey recommendation cache"]
  end

  customer --> gfe
  gfe -->|"CloudEvents request"| router
  router -->|"Route by CloudEvent headers"| recommendations
  recommendations -->|"Read / write cache"| valkey
  recommendations -->|"Retrieve recommendations when needed"| cds
  cds --> recommendations
  recommendations --> router
  router -->|"CloudEvents response"| gfe

Response path: CDS or Valkey returns data to des-recommendations, which returns the response to des-router, and des-router returns the CloudEvents response to GFE.

Magic Row recommendation scenarios

The response source depends on the customer state and whether questionnaire data exists.

Scenario Expected recommendation source
Customer completes the Magic Row questionnaire Questionnaire-driven Magic Row recommendations from CDS.
New customer on day 0 does not complete the Magic Row questionnaire DAY 0 recommendations.
Existing customer does not complete the Magic Row questionnaire TAG recommendations, returned as "we think you like" recommendations.

POP request flows

Request without refreshCache, cache miss

When refreshCache is omitted and no cached recommendation exists, des-recommendations retrieves Magic Row recommendations from CDS, saves the result in Valkey, and returns the response through des-router.

---
config:
  themeVariables:
    fontSize: 24px
title: POP request without refreshCache cache miss
---
sequenceDiagram
  autonumber
  participant GFE
  participant Router as POP des-router
  participant Recs as POP des-recommendations
  participant Cache as AWS ElastiCache Valkey
  participant CDS

  GFE->>Router: POST /v1/decisions without refreshCache
  Router->>Router: Validate CloudEvents headers and body
  Router->>Recs: Route recommendation request
  Recs->>Cache: Get cached recommendations by customer/product/brand
  Cache-->>Recs: Cache miss
  Recs->>CDS: Retrieve Magic Row recommendations
  CDS-->>Recs: Recommendation result
  Recs->>Cache: Save recommendation result
  Recs-->>Router: Return recommendation response
  Router-->>GFE: Return CloudEvents response

Request without refreshCache, cache hit

When refreshCache is omitted and cached recommendations are available, des-recommendations returns the cached result without calling CDS.

---
config:
  themeVariables:
    fontSize: 24px
title: POP request without refreshCache cache hit    
---
sequenceDiagram
  autonumber
  participant GFE
  participant Router as POP des-router
  participant Recs as POP des-recommendations
  participant Cache as AWS ElastiCache Valkey

  GFE->>Router: POST /v1/decisions without refreshCache
  Router->>Router: Validate CloudEvents headers and body
  Router->>Recs: Route recommendation request
  Recs->>Cache: Get cached recommendations by customer/product/brand
  Cache-->>Recs: Cached recommendation result
  Recs-->>Router: Return cached recommendation response
  Router-->>GFE: Return CloudEvents response

Request with refreshCache: true

When refreshCache is true, des-recommendations bypasses the cache read path for the decision result, retrieves the latest Magic Row recommendations from CDS, refreshes the Valkey cache, and returns the refreshed response through des-router.

---
config:
  themeVariables:
    fontSize: 24px
title: POP request with refreshCache true    
---
sequenceDiagram
  autonumber
  participant GFE
  participant Router as POP des-router
  participant Recs as POP des-recommendations
  participant Cache as AWS ElastiCache Valkey
  participant CDS

  GFE->>Router: POST /v1/decisions with refreshCache true
  Router->>Router: Validate CloudEvents headers and body
  Router->>Recs: Route recommendation refresh request
  Recs->>CDS: Retrieve latest Magic Row recommendations
  CDS-->>Recs: Recommendation result
  Recs->>Cache: Refresh cached recommendation result
  Recs-->>Router: Return refreshed recommendation response
  Router-->>GFE: Return CloudEvents response

Service access

Send requests from inside the platform network to the environment host for the environment you are integrating with.

Environment Host
qa pop-service-qa.platformservices-dev.aws.private
nxt pop-service-nxt.platformservices-dev.aws.private
drk pop-service-drk.platformservices-prod.aws.private
prd pop-service-prd.platformservices-prod.aws.private

Endpoint:

POST /v1/decisions

Example target for nxt:

http://pop-service-nxt.platformservices-dev.aws.private/v1/decisions

Request format

Requests are CloudEvents in HTTP binary content mode. The CloudEvent attributes are sent as HTTP headers and the event data is sent as a JSON request body.

The router currently routes gaming recommendation requests where:

  • ce-source is urn:uki:pop:gaming:mobile:ios
  • ce-type is uki.pop.gaming.recommendation.request
  • ce-subject is gaming-magic-row
  • ce-brand is one of skybet, paddypower, or betfair

Required headers

All of these headers must be present and non-empty.

Header Required value
ce-source urn:uki:pop:gaming:mobile:ios
ce-type uki.pop.gaming.recommendation.request
ce-specversion 1.0
ce-id Unique request/event id from the caller
ce-subject gaming-magic-row
ce-brand skybet, paddypower, or betfair
ce-datacontenttype application/json
Content-Type application/json

ce-time is commonly sent by clients and is accepted by the router, but it is not part of the router's required-header list.

Request body

The JSON body contains the recommendation decision input.

Field Type Required Description
accountId string Yes Customer account id.
product string Yes Product to request recommendations for. The tested value is vegas.
refreshCache boolean No When true, asks the downstream recommendation service to refresh cached data.

Example body:

{
  "accountId": "702221011",
  "product": "vegas",
  "refreshCache": true
}

Happy path example

curl --location -i \
  --header 'ce-id: req-qa-200' \
  --header 'ce-source: urn:uki:pop:gaming:mobile:ios' \
  --header 'ce-type: uki.pop.gaming.recommendation.request' \
  --header 'ce-brand: skybet' \
  --header 'ce-subject: gaming-magic-row' \
  --header 'ce-specversion: 1.0' \
  --header 'ce-datacontenttype: application/json' \
  --header 'Content-Type: application/json' \
  --data '{
    "accountId": "999",
    "product": "vegas"
  }' \
  'http://pop-service-nxt.platformservices-dev.aws.private/v1/decisions'

Successful responses are also CloudEvents in HTTP binary content mode. The response body contains the decision result.

Example response headers:

HTTP/1.1 200 OK
ce-brand: skybet
ce-datacontenttype: application/json
ce-id: req-qa-200
ce-source: urn:uki:pop:recommendations
ce-specversion: 1.0
ce-subject: gaming-magic-row
ce-type: gaming.recommendation.response
content-type: application/json
ce-time: 2026-05-14T14:44:24.639411Z

Example response body:

{
  "accountId": "999",
  "source": "DAY 0",
  "recommendations": [
    {
      "rank": 1,
      "score": 123.0,
      "gamecode": "the-goonies-hyg-vbp"
    },
    {
      "rank": 2,
      "score": 124.0,
      "gamecode": "king-kong-cash-jk-vbp"
    },
    {
      "rank": 3,
      "score": 125.0,
      "gamecode": "jungle-jackpots-vbp"
    },
    {
      "rank": 4,
      "score": 126.0,
      "gamecode": "congo-cash-vpr"
    }
  ],
  "errors": []
}

Response contract

For a successful decision response, clients should expect:

Field Type Description
accountId string Account id from the request.
source string Recommendation source, for example TAG or DAY 0.
recommendations array Ranked recommendation list.
recommendations[].rank number Rank assigned by the recommendation service.
recommendations[].score number Score assigned by the recommendation service.
recommendations[].gamecode string Recommended game code.
errors array Empty on a successful response.

Non-200 responses

Non-200 responses are returned when request validation fails, no route matches the CloudEvent attributes, the media type is unsupported, or the downstream service times out.

Observed statuses:

Status Typical cause Client expectation
400 Bad Request Missing or empty required header, unsupported ce-brand, or invalid JSON/body field. Fix the request. The response usually contains an errors attribute with validation details.
404 Not Found No route matched the request attributes, for example unsupported ce-type or ce-subject. Check route-driving CloudEvent headers. The response contains an errors attribute.
415 Unsupported Media Type Content-Type is not application/json. Send Content-Type: application/json. The current observed response body may be empty.
504 Gateway Timeout Downstream recommendation service did not respond before the router timeout. Retry according to the caller's retry policy. The response contains an errors attribute.

Router-generated error responses use these CloudEvent headers:

Header Value
ce-type uki.pop.router.error
ce-source urn:uki:pop:router
ce-specversion 1.0 when the router needs to generate it
ce-id Caller ce-id when available, otherwise a generated id
ce-time Response timestamp

Example missing header response:

HTTP/1.1 400 Bad Request
ce-specversion: 1.0
ce-subject: gaming-magic-row
ce-brand: skybet
ce-datacontenttype: application/json
Content-Type: application/json
ce-time: 2026-05-14T14:44:33.398560Z
ce-type: uki.pop.router.error
ce-source: urn:uki:pop:router
ce-id: 92cb17ff-6cfc-4370-ba9c-c528c76b51bb
{
  "errors": [
    "Invalid header list: ce-id"
  ]
}

Example body validation response:

{
  "accountId": "",
  "source": null,
  "recommendations": [],
  "errors": [
    {
      "code": "VALIDATION_ERROR",
      "message": "Missing required creator property 'accountId' (index 0)"
    }
  ]
}

Example timeout response:

{
  "errors": [
    "Response took longer than timeout: PT0.3S"
  ]
}