Contact Associated Event State

Problem

Campaign stats information like last click and view as well as counter of both corresponding types are stored on the device with no connection to the identified in contact. When multiple contacts are sharing a device they are also sharing this information leading to messages not being shown to some contacts.

Requirements

  • Both identified and anonymous contacts needs to be supported.

  • If a campaign is shown when anonymous, it needs to be considered when identified.

  • Switching back and forth between identified contacts should be possible without loosing the contact associated state

Device Event State

The SDK looks for a property called deviceEventState in the response from any request against the device-event-service and if available it will store it locally on the device and include it in any further request to the Device Event Service. As long as the value of that property is valid JSON the SDK does not care about the actual content of it.

We are free to change the structure of the device-event-state without changing the SDK.

Proposed Solution

The deviceEventState structure is extended with a new sub-structure beneath the contactStats property. This structure is an object containing one or more campaignStats structures mapped by the contact-reference.

The current campaignStats property is kept and used when no identified contact is available. If the contact is identified the service will update the contactStats and not change the campaignStats.

When evaluating if a campaign is a match both the campaignStats and the current contactStats will need to be considered.
A campaign which has ended needs to be removed from all campaignStats as well as all contactStats.

Example

 "deviceEventState": {
    "campaignStats": {
      "123456789": {
        "views": 10,
        "lastViewAt": "2020-12-09T07:00:00Z",
        "clicks": 2,
        "lastClickAt": "2020-12-09T10:00:00Z"
      },
      "987654321": {
        "views": 4,
        "lastViewAt": "2020-12-09T07:00:00Z",
        "clicks": 0
      }
    },
    "contactReferences": {
      "<contact-reference-1>": {
        "campaignStats": {
          "123456789": {
            "views": 2,
            "lastViewAt": "2022-12-06T12:15:00Z",
            "lastClickAt": "2022-12-06T12:15:30Z"
          }
        }
      },
      "<contact-reference-2>": {
        "campaignStats": {
          "123456789": {
            "views": 5,
            "lastViewAt": "2022-12-06T12:15:00Z",
            "clicks": 2,
            "lastClickAt": "2022-12-06T12:15:30Z"
          }
        }
      }
    }
  }

Scenarios

Newly identified contact registers views
  1. /events-request is sent containing DeviceEventState with no contactStats.

  2. Service adds new contactStats to DeviceEventState with entry for contact-reference of request.

  3. Response is sent with with new contactStats and untouched campaignStats.

Identified contact sends triggering event
  1. /events-request is sent containing DeviceEventState with campaignStats and contactStats.

  2. Service combines campaignStats and contactStats for evaluation.

  3. If the combined stats still fulfil the campaign configuration the message is returned.

Identified contact registers views for campaign viewed when not logged in
  1. /events-request is sent containing DeviceEventState with campaignStats and contactStats.

  2. Service increments stats in contactStats

  3. Response is sent with with modified contactStats and untouched campaignStats.

Downsides

The payload will increase a bit since the stats for all identified contacts will be sent back and forth between the client and server.