Analytics API
Analytics data is composed of atomic events stored in a form similar to:
{
"store_id": [ "-MeZJMTPL6CJndeZshjd" ],
"controller": [ "SHOWROOM (id:CONTROLLER-I9 ip:192.168.1.100)" ],
"year": [ "2022" ],
"weekday": [ "4" ],
"installation_id": [ "-MiXCxXPdTBwYxgmthGN" ],
"audience_id": [ "-NFSTjOj33xXTDW0WSQV" ],
"ev_action": [ "in" ],
"client_id": [ "-MV1AcMcnZE7_9Knly4F" ],
"ev_category": [ "media" ],
"campaign_name": [ "LIFT Intel" ],
"@timestamp": [ "2022-11-24T14:58:03.034713Z" ],
"month": [ "11" ],
"hour": [ "15" ],
"installation_name": [ "Lift&Learn Installation" ],
"media_idle": [ false ],
"media_id": [ "-MpabW2QVoLFBRIbViun" ],
"store_name": [ "Showroom Store" ],
"audience_name": [ "Axe Gold Temptation" ],
"media_name": [ "Leather&Cookies.jpg" ],
"day": [ "24" ],
"client_name": [ "Broox" ],
"campaign_id": [ "-MpazcBg5z25dCaEYiXW" ]
}
See Event Schema for the current schema.
Broox Studio provides an endpoint to request processed analytics data.
- The URL PATH format is composed of:
- A
clientId
, obtained from Broox Studio (e.g.-MV1BcMxnZE7_9Kn3y4F
). - A
reportName
, a simple keyword to select the report. - Several query parameters to filter the data.
- A
GET /analytics/<clientId>/<reportName>?query_args...`
- The Endpoint URL is:
https://<your-cms-name>.web.app/analytics/
- All requests return Content-type:
application/json
- The requests are authenticated via a
Authentication: Bearer <api key>
header.
Authentication
-
Go to Broox Studio
-
Edit your client record
-
Copy your
client id
andapi key
. -
In your requests, add the header:
GET /analytics/<clientId>/<reportName>?query_args...`
Authentication:Bearer API_KEY
Common query parameters
Most common filters is by date range.
bt
andet
. Both should be present.- ISO8601 format:
2020-12-31T00:00:00.000
GET /analytics/-MV1AcMcnZE7_9Knly4F/eventcount?bt=2021-05-01T00:00:00&et=2022-10-01T00:00:00
{
"count": 100
}
IDentifiers and Names
Several filters can be paired as filter_id
+ filter_name
.
While _id
fields are unique, _name
fields are not necessarily so.
For example campaign_id
+campaign_name
: You could have two campaigns named "Christmas campaign" with different ids (think christmas campaign of different years, where the user entered the same name).
Also, keep in mind that values present in Broox Studio might not match those present in the events, as the former might have been deleted, altered, etc. The event database is a historical record, not a live one.
See Event Schema for the current schema.
"Keyword" (strict) Filters:
This filters have a limited range of values, and should always use an exact match.
-
Studio Object Identifiers:
store_id
:installation_id
campaign_id
playlist_id
audience_id
-
Split dates:
day
: 1-31month
: 1-12weekday
: 0-6hour
: 0-24year
: 4-digit YYYY (e.g.2022)
-
Event data:
ev_category
:media
,sensor
,profile
...ev_action
:in
,out
...
-
Event target (optional, when referred to a profile):
target_type
:face
,person
,blob
target_action
:enter
,exit
,look
...target_gender
:M
orF
target_dominant_emotion
:happy
,sad
,neutral
...
Boolean filters
Those can use a true/false value.
- For
event_category
== "media"media_idle
- For
event_category
== "profile"is_view
(someone is been looking at the screen long enough).
"Match" (text) filters:
This filters are textual strings, unindexed and matched by substring. In general those should not be used for querying.
-
Studio object names:
client_name
store_name
installation_name
controller
(name)campaign_name
playlist_name
audience_name
media_name
canvas_name
trigger_name
-
Vision Node Profile type values:
target_id
(a random id given to a face/person/blob)target_age
: integer 0-99.target_dwell
: Seconds the target has been present.target_distance
: (optional, distance to camera)target_attention
: (optional, % of dwell_time looking straight to screen)area
: (optional, name of an area of detection)
Reports
Reports might contain extra filters/aggregations. Those parameter names are prepended with an underscore character (e.g. _average_field
).
Metadata reports
This reports offer the structure and list of available Studio Objects
- campaigns/playlists/installations/audiences
GET /analytics/-MV1AcMcnZE7_9Knly4F/campaigns
{
"-YYYYyyyyyy": "San ValentÃn",
"-XXXX122332": "Navidad"
}
- Collapsed store+campaign
GET /analytics/-MV1AcMcnZE7_9Knly4F/collapsed
{
"-MaXuJ5jZFIwEyLjyfKp": {
"store_name": "Main Store 1",
"installations": {
"-MqzBq3Gz2VseORluDe2": {
"installation_name": "LED Wall",
"campaigns": {
"-YYYYyyyyyy": "San ValentÃn"
},
"audiences": {
"-XXYTYszxz": "Audience 1"
}
},
"-MqzBq3Gz2VseORaaaaa": {
"installation_name": "Kiosk",
"campaigns": {
"-XXXX122332": "Navidad"
},
"audiences": {
"-XXYTYszxz": "Audience 2"
}
}
}
}
}
eventcount
Number of events given a filter range.
GET /analytics/-MV1AcMcnZE7_9Knly4F/eventcount?bt=2021-05-01T00:00:00&et=2022-10-01T00:00:00
{
"count": 100
}
average
- Average of a numeric field values.
- Numeric field to average:
_avgfield=field_name
GET /analytics/-MV1AcMcnZE7_9Knly4F/average?_avgfield=target_dwell&ev_action=out
{ average: 9.890901652255442 }
split
- Count of events split by a keyword field.
- Use the
_split
parameter for the field to split with. - Use the
_split_missing
parameter to set a default value when the_split
field has no value set.
GET /analytics/-MV1AcMcnZE7_9Knly4F/split?bt=2021-05-01T00:00:00&et=2022-10-01T00:00:00&_split=target_gender&_missing=unknown
{
"count": 33040,
"split": [
{
"key": "F",
"doc_count": 8265
},
{
"key": "M",
"doc_count": 8255
}
]
}
Averages over splits
- Use the
_aggregate
and_aggregate_field
parameters. _aggregate
can beavg
orsum
_aggregate_field
can be any numerical field.
GET /analytics/-MV1AcMcnZE7_9Knly4F/split?bt=2021-05-01T00:00:00&et=2022-10-01T00:00:00&_split=weekday&_aggregate=avg&_aggregate_field=target_dwell
{
"count": 33040,
"split": [
{
"key": 4,
"doc_count": 8265,
"average": { "value": 10.23 }
},
{
"key": 5,
"doc_count": 2251,
"average": { "value": 20.12 }
},...
]
}
Multisplit
Split ranges by two fields.
- User
_split0
and_split1
as split fields. - Optionally, use the
_aggregate
and_aggregate_field
parameters. _aggregate
can beavg
orsum
_aggregate_field
can be any numerical field.- Optionally use the
_split0_missing
and_split1_missing
parameters to set values in case the field is not populated.
GET /analytics/-MV1AcMcnZE7_9Knly4F/multisplit?_split0=weekday&_split1=target_gender&_split1_missing=Unknown
{
count: 40000,
split: [
{
key: [4, 'M'],
key_as_string: '4|M',
doc_count: 1584,
},
}
Multisplit with ranges
- Report:
multisplit_range
- Use
_field_term
for the field to split via values. (e.g. day) - Use
_field_ranges
for the field to split via ranges (e.g. target_age) - Use
_rangeN=min-max
for the ranges. - Optionally,
_rangeN_key
to set a key for the returned dictionary.
GET /analytics/-MV1AcMcnZE7_9Knly4F/multisplit_range?_field_term=day&_field_ranges=target_age&_range1=0-20&_range1_key=Children (0-20)&_range2=21-40...
{ count: 40000,
split: [
{
"key": 25,
"doc_count": 1640,
"split": {
"buckets": {
"Children (0-20)": {
"from": 0,
"to": 20,
"doc_count": 163
},
"Young (21-40)": {
"from": 21,
"to": 40,
"doc_count": 205
},
"Middle Age (41-60)": {
"from": 41,
"to": 60,
"doc_count": 203
},
"Elder (61-99)": {
"from": 61,
"to": 99,
"doc_count": 212
}
}
}
},
ranges split
- Use the
_split
field for the target field and_split_missing
optionally for missing values. - Use
_rangeN=min-max
for the ranges. - Optionally,
_rangeN_key
to set a key for the returned dictionary.
GET /analytics/-MV1AcMcnZE7_9Knly4F/ranges?_split=target_age&_range1=0-20&_range1_key=Children (0-20)&_range2=21-40...
{
count: 40000,
split: {
'Children (0-20)': { from: 0, to: 20, doc_count: 3925 },
'Young (21-40)': { from: 21, to: 40, doc_count: 4974 },
'Middle Age (41-60)': { from: 41, to: 60, doc_count: 4955 },
'Elder (61-99)': { from: 61, to: 99, doc_count: 5317 }
}
}
Date Histogram
GET /analytics/-MV1AcMcnZE7_9Knly4F/datehistogram?_interval=1M&_format=yyyy-MM-dd&ev_action=out
[
{ key_as_string: '2021-02-01', key: 1612137600000, doc_count: 43 },
{ key_as_string: '2021-03-01', key: 1614556800000, doc_count: 1879 },
{ key_as_string: '2021-04-01', key: 1617235200000, doc_count: 1622 },
{ key_as_string: '2021-05-01', key: 1619827200000, doc_count: 1658 },
{ key_as_string: '2021-06-01', key: 1622505600000, doc_count: 1813 },
{ key_as_string: '2021-07-01', key: 1625097600000, doc_count: 1871 },
{ key_as_string: '2021-08-01', key: 1627776000000, doc_count: 1784 },
{ key_as_string: '2021-09-01', key: 1630454400000, doc_count: 1849 },
{ key_as_string: '2021-10-01', key: 1633046400000, doc_count: 1906 },
{ key_as_string: '2021-11-01', key: 1635724800000, doc_count: 1446 },
{ key_as_string: '2021-12-01', key: 1638316800000, doc_count: 1784 },
{ key_as_string: '2022-01-01', key: 1640995200000, doc_count: 1674 },
{ key_as_string: '2022-02-01', key: 1643673600000, doc_count: 693 }
]
Product Interaction
Product interactions are obtained by filtering by ev_category=sensor
(Sensor Node events).
In case of different trigger sets (different products), you have to split by audience_id
. You'll have to get the audience_id:audience_name relation from the collapsed
query or audiences
query.
Some examples:
- Interaction count
GET /analytics/-MV1AcMcnZE7_9Knly4F/eventcount?bt=2021-05-01T00:00:00&et=2022-10-01T00:00:00&ev_category=sensor
- Interaction avg. time
GET /analytics/-MV1AcMcnZE7_9Knly4F/split?bt=2022-08-01T00:00:00&et=2022-12-12T00:00:00&ev_action=out&ev_category=sensor&_split=audience_id&_aggregate=avg&_aggregate_field=target_attention_time