Collector API
Introduction
The Collector receives data sent from web plugins, native sdks or custom integrations and forwards it to our backend processing systems.
The following sections describe how you can create a custom integration script to communicate with the Watching That Collector.
Authentication
Requests to the Collector endpoint must all be sent with an authorization header that looks like:
X-Api-Key: <apiKey>
You can find your api key in your Watching That account.
Failing to set this header or setting it to an invalid value will make the API respond with a 401 Unauthorized
http code and the payload of the message will be discarded.
Endpoints
Depending on where you're sending the data from (a server environment for SSAI for example or browser/native), you may use one of the following endpoints:
POST https://collector.watchingthat.com/v5/in
POST https://c.p2r14.com/v5/in
We will change the latter endpoint domain periodically to prevent it being blocked by ad blockers. For server environments we suggest you use the former one as it will always run from the same domain.
These endpoints are expecting data in JSON format as an array of objects. Each object should represent an event that happens during the lifecycle of a video (including its ads).
The list of fields from each object can be found in the Event Fields section below.
The data must be sent with a application/json
content type: Content-Type: application/json
.
The general JSON payload of a POST call:
[
{ <event 1 field1>: <value>, <event 1 field2>: <value>, ... },
{ <event 2 field1>: <value>, <event 2 field2>: <value>, ... }
...
]
Examples
- Curl
- Javascript
- Python
curl -X POST \
'https://collector.watchingthat.com/v5/in' \
-H 'Content-Type: application/json' \
-H 'X-Api-Key: 12345' \
-d '[ { "type": "init", "rid": "123-abc", "cst": 0 }, { "type": "play", "rid": "123-abc", "cst": 1000 } ]'
const data = JSON.stringify([
{ type: 'init', rid: '123-abc', cst: 0 },
{ type: 'play', rid: '123-abc', cst: 1000 },
]);
const xhr = new XMLHttpRequest();
xhr.withCredentials = true;
xhr.addEventListener('readystatechange', function () {
if (this.readyState === 4) {
console.log(this.responseText);
}
});
xhr.open('POST', 'https://collector.watchingthat.com/v5/in');
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.setRequestHeader('X-Api-Key', '12345');
xhr.send(data);
import requests
url = "https://collector.watchingthat.com/v5/in"
payload = [
{ "type": "init", "rid": "123-abc", "cst": 0 },
{ "type": "play", "rid": "123-abc", "cst": 1000 }
]
headers = {
'Content-Type': "application/json",
'X-Api-Key': "12345",
}
response = requests.request("POST", url, data=payload, headers=headers)
print(response.text)
Concepts
Before showing you the list of fields you can send to our endpoints, we should explain a few concepts that make our platform tick:
Content Sessions
To be able to group related events together and have a clear picture of what happens during the lifecycle of a video, we have the concept of "content sessions". A content session represents the interval from when the video player initialises the video, goes through playing the video (including any potential preroll/midroll ads), until the video ends (including any postroll ad).
In case your video player is set up to go through a playlist, once a session has ended, another one should be started for the next video.
A session is identified by the rid
field: all the events that belong to the same session should have the same rid
.
It is your responsibility to create a unique rid
at the beginning of each session and send it to our endpoint along with the rest of the data.
Note that
rid
is a required field.
[
{ "rid": "abc123", "type": "init" },
{ "rid": "abc123", "type": "play" },
{ "rid": "foobar-qqq", "type": "imp" }
]
In the example above the first 2 events belong to a session while the third one belongs to another session.
The actual content of the rid
field is not important, as long as it is reasonably unique in time.
In our plugins we create rid
from the current ms timestamp plus a random short string but you can use any uuid-like string.
if you are sending data from both server side (SSAI for example) and client side (browser/phone/tv), make sure you're using the same rid
in both contexts so we can group all the events of a session together.
Content Session Time
Once a content session has started, events will start occurring at various times during the life of that session. It is important to know the order the events occurred in as well as the time they occurred at as this can give us a lot of hints about things that might go wrong (or well) with a session. The actual timestamp of the event is unimportant and unreliable because of potentially misconfigured clocks on user devices. That's why we use "milliseconds since session start".
The content session time is identified by the cst
field.
Note that
cst
is a required field
You will have to initialise a variable to the current timestamp for the init event and then, for each event, you should send the diff between the now-current time and the time of the init in the cst
field:
// during init
const startTime = Date.now();
// for each event, including init:
const eventPayload = { cst: Date.now() - startTime }; // plus any other fields for the event
Ad Sessions
Ad sessions are called "Ad slots" in app
Just like we group all events of a content session together by using the rid
field, we also group all events of an ad together by using the adGid
field.
We call this group the "ad session", and it should contain all ad lifecycle events: the ad request for that particular ad, ad load, ad start, impression, quartiles, ad complete and the potential errors.
Obviously, some of these events might not be present - in case of an error there might not be an impression, for example.
It is your responsibility to create a unique adGid
for each new ad and send it to our endpoint along with the rest of the data.
The actual content of the adGid
field is not important, as long as it is reasonably unique in time.
One suggestion for creating the adGid
field is to use rid
concatenated with the ad count but any guid string will do.
If the same ad plays twice in the same content session or even in different content sessions, the adGid
field must be different between the two plays.
Don't mistake this for, say, the creative id which can repeat between two ad plays.
[
{ rid: 'abc123', type: 'init' }, // these events are not related to the ad session
{ rid: 'abc123', type: 'play' }, // so they don't have an adGid
{ rid: 'abc123', adGid: 'abc123-0', type: 'serve' }, // these two events belong to the same
{ rid: 'abc123', adGid: 'abc123-0', type: 'imp' }, // ad so they have the same adGid
{ rid: 'abc123', adGid: 'abc123-1', type: 'serve' }, // these three events belong to the same
{ rid: 'abc123', adGid: 'abc123-1', type: 'sa' }, // ad so they have the same adGid
{ rid: 'abc123', adGid: 'abc123-1', type: 'imp' }, // but different from the previous one
];
Type
The type
field identifies what sort of event is being sent. See the Event Types table below for a list of the types we support.
If the event is an init
, send type: 'init'
, if the event is an impression, send type: 'imp'
, etc.
Note that
type
is a required field.
Event fields
This is the complete list of fields an event might have in alphabetical order. In the list below we marked with an asterisk (*) the fields that you should try and send with every event (if you have access to those fields).
Field | Type | Required | Possible Values | Description |
---|---|---|---|---|
aBl | int | -1, 0, 1 | Whether an Ad Blocker was detected or not. Use -1 for undetermined. | |
adAN | string | Ad: Advertiser Name. | ||
adBreak | int | yes, for ad events | -1, 0, 1, ... | Ad: The index of the break: 0 for PREROLL, 1/2/3/... for MIDROLL and -1 for POSTROLL |
adBreakPos | int | yes, for ad events | Ad: The position of the ad in the break, starting from 0 | |
adCId | string | Ad: Creative Id. | ||
adDID | string | Ad: The first deal ID present in the wrapper chain for the current ad, starting from the top. Omit field if unavailable. | ||
adGid | string | yes, for ad events | Ad: A unique identifier generated by you that is used to link all events of an ad during a session (ad request, ad load, ad start, impression, errors, quartiles, etc). A different ad during the same session should get a different adGid . | |
adId | string | Ad: Ad ID. | ||
adMgV | string | Ad Manager SDK Version. This is the version of IMA / Freewheel Ad Manager SDK. | ||
adMU | string | Ad: Media Url - the url of the video file for the ad. | ||
adSys | string | Ad: Ad System. | ||
adType | string | PREROLL, MIDROLL, POSTROLL | Ad: The type of the ad break. | |
adURL | string | Ad: Tag Url after macro replacements. | ||
adWCId | string[] | Ad: Selected creative IDs used for wrapper ads. The array must start with the inline ad (innermost) and continue to the outermost wrapper ad. Omit the field or send an empty array if there are no wrapper ads. | ||
adWId | string[] | Ad: IDs used for wrapper ads. The array must start with the inline ad (innermost) and continue to the outermost wrapper ad. Omit the field or send an empty array if there are no wrapper ads. | ||
adWSys | string[] | Ad: Names of the ad systems used for wrapper ads. The array must start with the inline ad (innermost) and continue to the outermost wrapper ad. Omit the field or send an empty array if there are no wrapper ads. | ||
autoplay | boolean | Is this player autoplaying? | ||
browser | string | * | The name of the browser the user is using. If you are sending the event from the browser, from the page where the event happened, you may skip this as we'll infer it from the server logs. | |
country | string | * | UK, US, DE, ... | 2 letter country code for the country of the user watching the ad/video. If you are sending the event from the browser, from the page where the event happened, you may skip this as we'll infer it from the server logs. |
cst | int | yes | Relative time of the event from init (init should have cst=0). | |
custom | object | * | An object with custom data you might want to send to Watching That for analysis. See the custom data docs for more details. | |
debug | string | Extra data you might want to send with the event that should help you find clusters of data in our app. | ||
device | string | The name of the device the user is using. If you are sending the event from the browser, from the page where the event happened, you may skip this as we'll infer it from the server logs. | ||
deviceType | string | * | desktop, phone, tablet, tv | The type of device the user is using. |
err | string | yes if type is error | Outer error code. For example 900 . | |
err2 | string | Inner Error Code. Sometimes err is too generic and might represent a group of errors. In this case err2 can be used for more context. We use it for the error code coming from ima3. | ||
fif | int | 0, 1 | Friendly Iframe - whether the iframe of the player allows communication with the rest of the page. | |
h | int | Player height. | ||
idx | int | * | If the video is part of a playlist, this is the index of the video in that playlist. Starts at 1. | |
ip | string | The IP address of the user watching the ad/video. This is only used to resolve the country of the user and then removed from our servers. If you're sending the country already, you don't need to send this. If you are sending the event from the browser, from the page where the event happened, you may skip this as we'll infer it from the server logs. | ||
isLive | int | * | 0, 1 | Whether the media is a live stream (1) or vod (0). |
mediaId | string | * | The unique code/name you are using for the video of the current session. Useful to group data by video. | |
originalAdURL | string | Ad Tag Url before macro replacements. | ||
os | string | * | The name of the operating system the user is using. If you are sending the event from the browser, from the page where the event happened, you may skip this as we'll infer it from the server logs. | |
pageTags | string | A list of comma delimited tags relevant for this video. | ||
per | int | * | How much of the ad area is visible at the time of event. Since the user might scroll the page where the video is embedded, an ad might not be 100% visible when playing (or not at all). | |
playerId | string | * | The unique code/name you are using for this player. Useful to group data by player. | |
plGid | string | Playlist ID - a unique identifier generated by you that is used to link all content sessions in a playlist. Should be created once when the player initialises on a page and never be changed during the lifetime of that player. | ||
plV | string | * | The version of the video player. This may be different from the v field, depending on your setup. | |
pu | string | Page Url - the page where this event happened. | ||
pVisible | int | * | 0, 1 | Page Visible - indicates whether the page is visible or not. Some errors might occur when the browser is minimised or the tab is not the active one. |
rid | string | yes | Request ID - unique video session ID that starts from the first event in a video (init) and lasts until the last POSTROLL finishes. | |
src | string | A short code to identify the app or plugin sending events. We are currently using bcp for "Brightcove plugin" / jwp for "JW plugin" / etc. | ||
st | string | The event subtype. Sometimes the type might be too general. Use a subtype to better segment this event. | ||
streamId | string | * | The ID for the video stream supplied by the IMA DAI SDK. | |
type | string | yes | init, play, ... | The event type. See below for the meaning of each of these values. |
v | string | The version of the app/plugin defined by the src field. Also see the plV field for the version of the video player. | ||
w | int | Player width. | ||
wh | int | Window/Screen Height. | ||
ww | int | Window/Screen Width. |
Event Types
During a content session, one or more of the following event types might be sent. Note that some events, like the errors, for example, might appear multiple times in a session.
Value | Description |
---|---|
init | This marks the start of a session. Use this event to send various data that doesn't need to be repeated for every event. pu , src , v , idx and others are values that don't change during a session and can be safely sent just once here |
play | Video Play event. Sent either when the player starts autoplaying or when the user clicks on the play button. |
serve | Send this when an ad is requested. You should know the ad tag url by now so that should be sent here. |
adBreak | An ad break has started |
adClick | Ad Click event |
al | Ad Loaded event |
sa | Ad Started event |
imp | Impression event |
efq | Ad First Quartile event |
esq | Ad Second Quartile (midpoint) event |
etq | Ad Third Quartile event |
ecp | Ad Complete event |
skip | Skip ad event |
vready | Video Ready, loaded in the player and ready to be interacted with. In a Brightcove player, this is the video_impression event. |
vview | Video View event, recorded when a video is considered to be viewed. It might be the same as c0 but it doesn't have to be. In a Brightcove player, this is the video_view event. |
error | Error event |
c0 | Content started playing event |
c25 | Content First Quartile event |
c50 | Content Second Quartile (midpoint) event |
c75 | Content Third Quartile event |
c95 | Content reached 95% playthrough |
mute | User muted the audio (or changed the volume down to 0) |
unmute | User unmuted the audio (or changed the volume up from 0) |
Apart from these types you can also send other event types you might want us to track. See the docs on custom events for more details.