# Cloud Events

## Introduction

Zapp offers a way to integrate with 3rd party provider using the `cloud Events` protocol - https://cloudevents.io/.
Using `Cloud Events` you can integrate your custom implementation of actions that your app users are making,
without the need to write any client side code.

Once you implement cloud events and configured it in Zapp, any time a user action is triggered on the app, your cloud events server will be notified.

> Note: To integrate your cloud events server in the app go to the relevant plugin configuration and set your server URL.

## List of available cloud events

:::note

If the event doesn't define an explicit response, please use the following:

```json
{
  "specversion": "1.0",
  "type": "com.applicaster.event.received.v1",
  "source": "<SERVER_URL>",
  "subject": "Event  <EVENT_UUID> was successfully received",
  "id": "<NEW_EVENT_UUID>",
  "time": "<TIME_IN_RFC3339_FORMAT>"
}
```

:::

### Video Started (`com.applicaster.video.started.v1`)

**Description:** Triggered any time a user starts video playback.

**Plugin:** `Continue Watching (Quick Brick)`

**Request Payload**:

```json
{
  "specversion": "1.0",
  "type": "com.applicaster.video.started.v1",
  "source": "<APP_URLSCHEME>://<BUNDLE_ID>/versions/<VERSION>",
  "subject": "Watched video segment <TIME> progress:0 <VIDEO_ID>",
  "id": "<EVENT_UUID>",
  "time": "<TIME_IN_RFC3339_FORMAT>",
  "datacontenttype": "applicastion/json",
  "data": {
    "userIdentifier": "<ACCESS_TOKEN>",
    "videoId": "<UNIQUE_VIDEO_ID>",
    "secondsFromStart": "0",
    "progress": 0,
    "status": "VIDEO_STARTED"
  }
}
```

#### `data.userIdentifier`

A string that represents the user. It's best to make use of the user's `access_token` and not the actual user id, to be able to make sure the request is authorized.

#### `data.videoId`

The unique video id. The video id is taken from the entry id set on the [Pipes2 JSON Feed](/integrations/feed-json-protocol).

:::

---

### Video Stopped (`com.applicaster.video.stopped.v1`)

**Description:** Triggered any time a user stops a videos.

**Plugin:** `Continue Watching (Quick Brick)`

**Request Payload**:

```json
{
      "specversion": "1.0",
      "type": "com.applicaster.video.stopped.v1",
      "source": "<APP_URLSCHEME>://<BUNDLE_ID>/versions/<VERSION>",
      "subject": "Watched video segment <TIME> progress:<PROGRESS> <VIDEO_ID>",
      "id": "<EVENT_UUID>",
      "time": "<TIME_IN_RFC3339_FORMAT>",
      "datacontenttype": "applicastion/json",
      "data": {
        "userIdentifier": "<ACCESS_TOKEN>",
        "videoId": "<UNIQUE_VIDEO_ID>",
        "secondsFromStart": <SECONDS_FROM_START>,
        "progress": <NUMBER_BETWEEN_0_TO_1>,
        "status": "<EVENT_STATUS>"
      }
    }

```

#### `data.userIdentifier`

A string that represents the user. It's best to make use of the user's `access_token` and not the actual user id, to be able to make sure the request is authorized.

#### `data.videoId`

The unique video id. The video id is taken from the entry id set on the [Pipes2 JSON Feed](/integrations/feed-json-protocol).

#### `data.secondsFromStart`

Indicates the second the user triggered the event.

#### `data.progress`

A number between 0 - 1 indicating the movie progress (where 0 indicates the beginning of the video and 1 indicates its end).

#### `data.status`

Can be one of the following values:

- `COMPLETED`: The video is considered as completed.
- `VIDEO_STOPPED`: The video was stopped without reaching the end of the video

:::note
Items can be considered as completed without reaching `progress=1`. To find out more check [`entry.extensions.auto_resume_completion_threshold`](feed-json-protocol#entryextensionsauto_resume_completion_threshold).
:::

---

### Video Progress (`com.applicaster.video.progress.v1`)

**Description:** Triggered on player progress change (based on the interval parameter configured in the _Continue Watching_ plugin).

**Plugin:** `Continue Watching (Quick Brick)`

Supported Version:

- iOS, Android, Apple TV, Android TV, Fire TV, Samsung TV, LG TV: **6.1.6**
- Roku: SDK **0.1.0**

Make sure to enable the **Enable Heartbeat** switch on the plugin configuration.

**Request Payload**:

```json
{
      "specversion": "1.0",
      "type": "com.applicaster.video.progress.v1",
      "source": "<APP_URLSCHEME>://<BUNDLE_ID>/versions/<VERSION>",
      "subject": "Watched video segment <TIME> progress:<PROGRESS> <VIDEO_ID>",
      "id": "<EVENT_UUID>",
      "time": "<TIME_IN_RFC3339_FORMAT>",
      "datacontenttype": "applicastion/json",
      "data": {
        "userIdentifier": "<ACCESS_TOKEN>",
        "videoId": "<UNIQUE_VIDEO_ID>",
        "secondsFromStart": <SECONDS_FROM_START>,
        "progress": <NUMBER_BETWEEN_0_TO_1>,
        "status": "WATCHING",
        "sessionStartAt": <NUMBER IN SECONDS>,
      }
    }

```

#### `data.userIdentifier`

A string that represents the user. It's best to make use of the user's `access_token` and not the actual user id, to be able to make sure the request is authorized.

#### `data.videoId`

The unique video id. The video id is taken from the entry id set on the [Pipes2 JSON Feed](/integrations/feed-json-protocol).

#### `data.secondsFromStart`

Indicates the second the user triggered the event.

#### `data.progress`

A number between 0 - 1 indicating the movie progress (where 0 indicates the beginning of the video and 1 indicates its end).

#### `data.sessionStartAt`

Indicates the number of seconds the current session started at. This will be bigger then 0 in case the user already watched this video in the past.

---

### Video Paused (`com.applicaster.video.paused.v1`)

**Description:** Triggered on player pause.

**Plugin:** `Continue Watching (Quick Brick)`

Supported Version:

- iOS, Android, Apple TV, Android TV, Fire TV, Samsung TV, LG TV: **6.1.6**
- Roku: SDK **0.1.0**

The event could be configured to send heartbeat during pause.
To enable this, make sure to enable the **Send Heartbeat On Pause** switch on the plugin configuration.

**Request Payload**:

```json
{
      "specversion": "1.0",
      "type": "com.applicaster.video.paused.v1",
      "source": "<APP_URLSCHEME>://<BUNDLE_ID>/versions/<VERSION>",
      "subject": "Watched video segment <TIME> progress:<PROGRESS> <VIDEO_ID>",
      "id": "<EVENT_UUID>",
      "time": "<TIME_IN_RFC3339_FORMAT>",
      "datacontenttype": "applicastion/json",
      "data": {
        "userIdentifier": "<ACCESS_TOKEN>",
        "videoId": "<UNIQUE_VIDEO_ID>",
        "secondsFromStart": <SECONDS_FROM_START>,
        "progress": <NUMBER_BETWEEN_0_TO_1>,
        "status": "VIDEO_PAUSED",
        "sessionStartAt": <NUMBER IN SECONDS>,
      }
    }

```

#### `data.userIdentifier`

A string that represents the user. It's best to make use of the user's `access_token` and not the actual user id, to be able to make sure the request is authorized.

#### `data.videoId`

The unique video id. The video id is taken from the entry id set on the [Pipes2 JSON Feed](/integrations/feed-json-protocol).

#### `data.secondsFromStart`

Indicates the second the user triggered the event.

#### `data.progress`

A number between 0 - 1 indicating the movie progress (where 0 indicates the beginning of the video and 1 indicates its end).

#### `data.sessionStartAt`

Indicates the number of seconds the current session started at. This will be bigger then 0 in case the user already watched this video in the past.

---

### Video Interval Completed (`com.applicaster.video.interval.v1`)

**Description:** The event is triggered when a player finishes a predetermined segment. A segment is deemed completed when a user successfully views the specified duration (interval), regardless of ads, fast forward, rewind, or seek actions.

**Plugin:** `Continue Watching (Quick Brick)`

Supported Version:

- iOS, Android, Apple TV, Android TV, Fire TV, Samsung TV, LG TV: **9.0.0**
- Roku: SDK **5.8.0**

To enable this, make sure to enable the **Enable Video Interval** switch on the plugin configuration.

**Request Payload**:

```json
{
  "specversion": "1.0",
  "type": "com.applicaster.video.interval.v1",
  "source": "<APP_URLSCHEME>://<BUNDLE_ID>/versions/<VERSION>",
  "subject": "Watched video interval <INTERVAL_DURATION> of video <VIDEO_ID>",
  "id": "<EVENT_UUID>",
  "time": "<TIME_IN_RFC3339_FORMAT>",
  "datacontenttype": "application/json",
  "data": {
    "userIdentifier": "<ACCESS_TOKEN>",
    "videoId": "<UNIQUE_VIDEO_ID>",
    "interval": <INTERVAL_DURATION>,
    "status": "INTERVAL_COMPLETED"
  }
}

```

#### `data.userIdentifier`

A string that represents the user. It's best to make use of the user's `access_token` and not the actual user id, to be able to make sure the request is authorized.

#### `data.videoId`

The unique video id. The video id is taken from the entry id set on the [Pipes2 JSON Feed](/integrations/feed-json-protocol).

#### `data.interval`

The interval of video watched in seconds.

#### `data.status`

Indicates the status of the event. In this case, "INTERVAL_COMPLETED" signifies that the user has watched the defined interval.

---

:::info
Additional data could be added to the event data object. In order to do so, the origin feed entry (e.g. the entry for the current playable video) should include the required data in "extensions.continue-watching".

for example, for the origin entry:

```json
{
  "id": "12345",
  "title": "Some Video",
  "extensions": {
    "continue-watching": {
      "key1": "some info1",
      "key2": "some info2"
    }
  }
}
```

The "pause" event payload will be (the additional data will be added to all type of events):

```json
{
  "specversion": "1.0",
  "type": "com.applicaster.video.paused.v1",
  "source": "<APP_URLSCHEME>://<BUNDLE_ID>/versions/<VERSION>",
  "subject": "Watched video segment <TIME> progress:<PROGRESS> <VIDEO_ID>",
  "id": "<EVENT_UUID>",
  "time": "<TIME_IN_RFC3339_FORMAT>",
  "datacontenttype": "applicastion/json",
  "data": {
    "userIdentifier": "<ACCESS_TOKEN>",
    "videoId": "12345",
    "secondsFromStart": <SECONDS_FROM_START>,
    "progress": <NUMBER_BETWEEN_0_TO_1>,
    "status": "VIDEO_PAUSED",
    "sessionStartAt": <NUMBER IN SECONDS>,
    "key1": "some info1", // data added
    "key2": "some info2" // data added
  }
}

```

:::

### Add to favorites (`com.applicaster.favorites.added.v1`)

**Description:** Triggered any time a user adds an item to her favorites list

**Plugin:** `Favorites`

**Request Payload**:

```json
{
  "specversion": "1.0",
  "type": "com.applicaster.favorites.added.v1",
  "source": "<APP_URLSCHEME>://<BUNDLE_ID>/versions/<VERSION>",
  "subject": "<ENTRY_ID> added to favorites",
  "id": "<EVENT_UUID>",
  "time": "<TIME_IN_RFC3339_FORMAT>",
  "datacontenttype": "applicastion/json",
  "data": {
    "userIdentifier": "<ACCESS_TOKEN>",
    "entryId": "<UNIQUE_ENTRYs_ID>"
  }
}
```

#### `data.userIdentifier`

A string that represents the user. It's best to make use of the user's `access_token`/`id_token` and not the actual user id, to be able to make sure the request is authorized.

#### `data.entryId`

The unique entry id. The entry id is taken from the entry id set on the Pipes2 JSON Feed.

---

### Remove from favorites (`com.applicaster.favorites.removed.v1`)

**Description:**: Triggered any time a user remove an item from favorites

**Plugin:** `Favorites`

**Request Payload**:

```json
{
  "specversion": "1.0",
  "type": "com.applicaster.favorites.removed.v1",
  "source": "<APP_URLSCHEME>://<BUNDLE_ID>/versions/<VERSION>",
  "subject": "<ENTRY_ID> wast removed from favorites",
  "id": "<EVENT_UUID>",
  "time": "<TIME_IN_RFC3339_FORMAT>",
  "datacontenttype": "applicastion/json",
  "data": {
    "userIdentifier": "<ACCESS_TOKEN>",
    "entryId": "<UNIQUE_ENTRYs_ID>"
  }
}
```

#### `data.userIdentifier`

A string that represents the user. It's best to make use of the user's `access_token`/`id_token` and not the actual user id, to be able to make sure the request is authorized.

#### `data.entryId`

The unique entry id. The entry id is taken from the entry id set on the Pipes2 JSON Feed.

:::info
Additional data could be added to the event data object. In order to do so, the origin feed entry should include the required data in `extensions.local_storage_favourites_action`.

For example, for the origin entry:

```json
{
  "id": "12345",
  "title": "Some Video",
  "extensions": {
    "local_storage_favourites_action": {
      "custom_key_1": "some info 1",
      "custom_key_2": "some info 2"
    }
  }
}
```

The "Add to favorites" event payload will be (the additional data will be added to both add and remove events):

```json
{
  "specversion": "1.0",
  "type": "com.applicaster.favorites.added.v1",
  "source": "<APP_URLSCHEME>://<BUNDLE_ID>/versions/<VERSION>",
  "subject": "12345 added to favorites",
  "id": "<EVENT_UUID>",
  "time": "<TIME_IN_RFC3339_FORMAT>",
  "datacontenttype": "application/json",
  "data": {
    "userIdentifier": "<ACCESS_TOKEN>",
    "entryId": "12345",
    "custom_key_1": "some info 1", // data added
    "custom_key_2": "some info 2" // data added
  }
}
```
:::

---

### PIN Code (`com.applicaster.pin.v1`)

**Description:** Triggered when a user submits a PIN on a parental lock screen. Used to validate the PIN server-side before granting access to age-restricted content.

**Plugin:** `Parent Lock`

:::tip
See [PIN Code](/integrations/pin-code) for the full integration guide, including the PIN reset flow.
:::

**Request Payload**:

```json
{
  "specversion": "1.0",
  "type": "com.applicaster.pin.v1",
  "source": "<APP_URLSCHEME>://<BUNDLE_ID>/versions/<VERSION>",
  "subject": "Pin Code Validation",
  "id": "<EVENT_UUID>",
  "time": "<TIME_IN_RFC3339_FORMAT>",
  "datacontenttype": "application/json",
  "data": {
    "pin_code": "1234"
  }
}
```

**Response**:

```json
{
  "specversion": "1.0",
  "type": "com.applicaster.event.received.v1",
  "source": "<SERVER_URL>",
  "subject": "Valid Pin Code",
  "id": "<submitted-pin-value>",
  "time": "<TIME_IN_RFC3339_FORMAT>"
}
```

Return HTTP `400` when the PIN is invalid.

---

### PIN Code Recovery (`com.applicaster.pin.recovery.requested.v1`)

**Description:** Triggered when a user requests a PIN reset. The server sends a reset email to the account's registered email address.

**Plugin:** `Parent Lock`

:::tip
See [PIN Code](/integrations/pin-code) for the full integration guide, including how to serve the recovery feed.
:::

**Request Payload**:

```json
{
  "specversion": "1.0",
  "type": "com.applicaster.pin.recovery.requested.v1",
  "source": "<APP_URLSCHEME>://<BUNDLE_ID>/versions/<VERSION>",
  "subject": "Pin Code Recovery Request",
  "id": "pin_code_recovery_requested",
  "time": "<TIME_IN_RFC3339_FORMAT>"
}
```

**Response**:

```json
{
  "specversion": "1.0",
  "type": "com.applicaster.event.received.v1",
  "source": "<SERVER_URL>",
  "subject": "Pin Code Recovery Requested",
  "id": "pin_code_recovery_requested",
  "time": "<TIME_IN_RFC3339_FORMAT>"
}
```

---

## Server implementations guidelines

- Server should return a `201` status on success.
- According to the `Cloud Event` specs the `time` should be in `rfc3339` format e.g. `2021-04-12T23:20:50.52Z`.
  Please make sure you implement your server accordingly.
