DocsIDarsa.technology

Docs / AI Box And Video Analytics Software / API Reference

DOOH Audience Meter API

The DOOH Audience Meter API: the audience of digital screens, measured by a camera beside each screen. It gives opportunities to see (OTS), impressions, attention and engagement, the audience's age bands, gender and mood, the view log, play logs from the screens' CMS or players, a basic campaign manager, the audience per campaign and creative with its cost per thousand (CPM), and an audience feed for ad platforms.

Personal data. Views are anonymous: no identity and no image is kept.

Calling the API

The same as the platform API: an API account's token as Authorization: Bearer <API_KEY>. Site and camera ids come from the platform API (GET /api/v1/sites, GET /api/v1/cameras). Each operation says who can call it and which scopes it needs. Errors, times, paging and the rate limit follow the platform API.

Feeds With Their Own Credentials

Two operations are for other systems and use a credential of their own, made by an admin of the site on the dashboard: the signed play-log push from a CMS or player (the site's play-log key, Standard Webhooks signatures) and the audience feed for ad servers (a feed token).

1.0.0

Audience

Headline figures, trend and audience insights.

OTS, Impressions, Attention And Engagement, The Audience Now, And The Impressions' Demographics And Moods

GET/sites/{id}/overview

Who can call it: An account with a role (viewer or admin) on the site. Token scopes: read.

Parameters

FieldTypeDescription
idrequired pathinteger
fromrequired querystringRFC 3339
torequired querystringRFC 3339

Responses

StatusMeaning
200OK
401RFC 9457 problem details
403RFC 9457 problem details
404RFC 9457 problem details
409RFC 9457 problem details
422RFC 9457 problem details
502RFC 9457 problem details

Request

curl -s -X GET "https://<BOX_HOST>/api/dooh/v1/sites/1/overview?from=2026-11-01T00%3A00%3A00%2B07%3A00&to=2026-11-02T00%3A00%3A00%2B07%3A00" \
  -H "Authorization: Bearer <API_KEY>"
import requests

r = requests.get(
    "https://<BOX_HOST>/api/dooh/v1/sites/1/overview?from=2026-11-01T00%3A00%3A00%2B07%3A00&to=2026-11-02T00%3A00%3A00%2B07%3A00",
    headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)
const r = await fetch("https://<BOX_HOST>/api/dooh/v1/sites/1/overview?from=2026-11-01T00%3A00%3A00%2B07%3A00&to=2026-11-02T00%3A00%3A00%2B07%3A00", {
  method: "GET",
  headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());

Response 200

{
  "ots": 9620,
  "impressions": 3105,
  "engaged": 684,
  "attention_rate": 32.3,
  "engagement_rate": 7.1,
  "avg_attention_s": 3.1,
  "avg_dwell_s": 6.4,
  "audience_now": {
    "in_view": 7,
    "watching": 2
  },
  "demographics": {
    "cells": [
      {
        "age_band": "18-24",
        "gender": "male",
        "impressions": 512,
        "share": 19.8
      }
    ],
    "unknown": 514
  },
  "emotions": {
    "neutral": 2210,
    "happy": 604,
    "sad": 41,
    "surprised": 88,
    "angry": 12,
    "unknown": 150
  }
}

OTS And Impressions Per Hour Or Day

GET/sites/{id}/trend

Who can call it: An account with a role (viewer or admin) on the site. Token scopes: read.

Parameters

FieldTypeDescription
idrequired pathinteger
fromrequired querystringRFC 3339
torequired querystringRFC 3339
bucket querystringhour or day

Responses

StatusMeaning
200OK
401RFC 9457 problem details
403RFC 9457 problem details
404RFC 9457 problem details
409RFC 9457 problem details
422RFC 9457 problem details
502RFC 9457 problem details

Request

curl -s -X GET "https://<BOX_HOST>/api/dooh/v1/sites/1/trend?from=2026-11-01T00%3A00%3A00%2B07%3A00&to=2026-11-02T00%3A00%3A00%2B07%3A00" \
  -H "Authorization: Bearer <API_KEY>"
import requests

r = requests.get(
    "https://<BOX_HOST>/api/dooh/v1/sites/1/trend?from=2026-11-01T00%3A00%3A00%2B07%3A00&to=2026-11-02T00%3A00%3A00%2B07%3A00",
    headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)
const r = await fetch("https://<BOX_HOST>/api/dooh/v1/sites/1/trend?from=2026-11-01T00%3A00%3A00%2B07%3A00&to=2026-11-02T00%3A00%3A00%2B07%3A00", {
  method: "GET",
  headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());

Response 200

{
  "bucket": "hour",
  "buckets": [
    {
      "start": "<start>",
      "ots": 12,
      "impressions": 12
    }
  ]
}

Audience Insights: The Peak Hour, Dayparts By Age Band, The Attention Histogram, The Status Per Day, Each Screen

GET/sites/{id}/analytics

Who can call it: An account with a role (viewer or admin) on the site. Token scopes: read.

Parameters

FieldTypeDescription
idrequired pathinteger
fromrequired querystringRFC 3339
torequired querystringRFC 3339

Responses

StatusMeaning
200OK
401RFC 9457 problem details
403RFC 9457 problem details
404RFC 9457 problem details
409RFC 9457 problem details
422RFC 9457 problem details
502RFC 9457 problem details

Request

curl -s -X GET "https://<BOX_HOST>/api/dooh/v1/sites/1/analytics?from=2026-11-01T00%3A00%3A00%2B07%3A00&to=2026-11-02T00%3A00%3A00%2B07%3A00" \
  -H "Authorization: Bearer <API_KEY>"
import requests

r = requests.get(
    "https://<BOX_HOST>/api/dooh/v1/sites/1/analytics?from=2026-11-01T00%3A00%3A00%2B07%3A00&to=2026-11-02T00%3A00%3A00%2B07%3A00",
    headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)
const r = await fetch("https://<BOX_HOST>/api/dooh/v1/sites/1/analytics?from=2026-11-01T00%3A00%3A00%2B07%3A00&to=2026-11-02T00%3A00%3A00%2B07%3A00", {
  method: "GET",
  headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());

Response 200

{
  "summary": {
    "ots": 12,
    "impressions": 12,
    "engaged": 12,
    "attention_rate": 12,
    "engagement_rate": 12,
    "avg_attention_s": 60,
    "avg_dwell_s": 60,
    "peak_hour": {
      "start": "<start>",
      "impressions": 12
    }
  },
  "dayparts": {
    "dayparts": [
      "<daypart>"
    ],
    "age_bands": [
      "<age_band>"
    ],
    "impressions": [
      [
        12
      ]
    ]
  },
  "attention_histogram": {
    "edges_s": [
      12
    ],
    "counts": [
      12
    ]
  },
  "status_per_day": [
    {
      "day": "<day>",
      "walk_by": 12,
      "noticed": 12,
      "engaged": 12
    }
  ],
  "screens": [
    {
      "ots": 12,
      "impressions": 12,
      "engaged": 12,
      "attention_rate": 12,
      "engagement_rate": 12,
      "avg_attention_s": 60,
      "avg_dwell_s": 60,
      "camera_id": 1,
      "screen": "Screen 1"
    }
  ]
}

The Campaign Manager's Records Of The Site (Campaigns With Their Creatives), By Name

GET/sites/{id}/campaign-records

Who can call it: An account with a role (viewer or admin) on the site. Token scopes: read.

Parameters

FieldTypeDescription
idrequired pathinteger

Responses

StatusMeaning
200OK
401RFC 9457 problem details
403RFC 9457 problem details
404RFC 9457 problem details
409RFC 9457 problem details
422RFC 9457 problem details
502RFC 9457 problem details

Request

curl -s -X GET "https://<BOX_HOST>/api/dooh/v1/sites/1/campaign-records" \
  -H "Authorization: Bearer <API_KEY>"
import requests

r = requests.get(
    "https://<BOX_HOST>/api/dooh/v1/sites/1/campaign-records",
    headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)
const r = await fetch("https://<BOX_HOST>/api/dooh/v1/sites/1/campaign-records", {
  method: "GET",
  headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());

Response 200

{
  "items": [
    {
      "id": 1,
      "site_id": 1,
      "name": "Main gate",
      "advertiser": "Advertiser A",
      "flight_start": "<flight_start>",
      "flight_end": "<flight_end>",
      "screens": [
        12
      ],
      "external_id": "<external_id>",
      "creatives": [
        {
          "id": 1,
          "name": "Main gate",
          "length_s": 60,
          "external_id": "<external_id>"
        }
      ],
      "version": 3,
      "updated": "<updated>"
    }
  ]
}

Add A Campaign Record With Its Creatives (Recorded In The Audit Log)

POST/sites/{id}/campaign-records

Names (trimmed, any case) and CMS ids are unique on the site, creatives' within the campaign.

Who can call it: An account that is an admin of the site. Token scopes: write.

Parameters

FieldTypeDescription
idrequired pathinteger

Body

FieldTypeDescription
namerequiredstringunique on the site (trimmed, any case): plays are matched by it
advertiserstring
flight_startrequireddateYYYY-MM-DD, the site's local day, inclusive
flight_endrequireddateon or after flight_start
screensrequiredinteger[]camera ids of the site's screens
external_idstringthe CMS's campaign id, unique on the site; null or empty: none
creativesobject[]
creatives[].namerequiredstring
creatives[].length_srequiredinteger
creatives[].external_idstringthe CMS's creative id; null or empty: none
versionintegerupdates: the version read

Responses

StatusMeaning
201OK
401RFC 9457 problem details
403RFC 9457 problem details
404RFC 9457 problem details
409RFC 9457 problem details
422RFC 9457 problem details
502RFC 9457 problem details

Request

curl -s -X POST "https://<BOX_HOST>/api/dooh/v1/sites/1/campaign-records" \
  -H "Authorization: Bearer <API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{"name":"Main gate","advertiser":"Advertiser A","flight_start":"2026-11-02","flight_end":"2026-11-02","screens":[12],"external_id":"<external_id>","creatives":[{"name":"Main gate","length_s":60,"external_id":"<external_id>"}],"version":3}'
import requests

r = requests.post(
    "https://<BOX_HOST>/api/dooh/v1/sites/1/campaign-records",
    headers={"Authorization": "Bearer <API_KEY>"},
    json={
      "name": "Main gate",
      "advertiser": "Advertiser A",
      "flight_start": "2026-11-02",
      "flight_end": "2026-11-02",
      "screens": [
        12
      ],
      "external_id": "<external_id>",
      "creatives": [
        {
          "name": "Main gate",
          "length_s": 60,
          "external_id": "<external_id>"
        }
      ],
      "version": 3
    },
)
print(r.status_code, r.text)
const r = await fetch("https://<BOX_HOST>/api/dooh/v1/sites/1/campaign-records", {
  method: "POST",
  headers: { "Authorization": "Bearer <API_KEY>", "Content-Type": "application/json" },
  body: JSON.stringify({
    "name": "Main gate",
    "advertiser": "Advertiser A",
    "flight_start": "2026-11-02",
    "flight_end": "2026-11-02",
    "screens": [
      12
    ],
    "external_id": "<external_id>",
    "creatives": [
      {
        "name": "Main gate",
        "length_s": 60,
        "external_id": "<external_id>"
      }
    ],
    "version": 3
  }),
});
console.log(r.status, await r.text());

Response 201

{
  "id": 1,
  "site_id": 1,
  "name": "Main gate",
  "advertiser": "Advertiser A",
  "flight_start": "<flight_start>",
  "flight_end": "<flight_end>",
  "screens": [
    12
  ],
  "external_id": "<external_id>",
  "creatives": [
    {
      "id": 1,
      "name": "Main gate",
      "length_s": 60,
      "external_id": "<external_id>"
    }
  ],
  "version": 3,
  "updated": "<updated>"
}

One Campaign Record

GET/sites/{id}/campaign-records/{record}

Who can call it: An account with a role (viewer or admin) on the site. Token scopes: read.

Parameters

FieldTypeDescription
idrequired pathinteger
recordrequired pathinteger

Responses

StatusMeaning
200OK
401RFC 9457 problem details
403RFC 9457 problem details
404RFC 9457 problem details
409RFC 9457 problem details
422RFC 9457 problem details
502RFC 9457 problem details

Request

curl -s -X GET "https://<BOX_HOST>/api/dooh/v1/sites/1/campaign-records/1" \
  -H "Authorization: Bearer <API_KEY>"
import requests

r = requests.get(
    "https://<BOX_HOST>/api/dooh/v1/sites/1/campaign-records/1",
    headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)
const r = await fetch("https://<BOX_HOST>/api/dooh/v1/sites/1/campaign-records/1", {
  method: "GET",
  headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());

Response 200

{
  "id": 1,
  "site_id": 1,
  "name": "Main gate",
  "advertiser": "Advertiser A",
  "flight_start": "<flight_start>",
  "flight_end": "<flight_end>",
  "screens": [
    12
  ],
  "external_id": "<external_id>",
  "creatives": [
    {
      "id": 1,
      "name": "Main gate",
      "length_s": 60,
      "external_id": "<external_id>"
    }
  ],
  "version": 3,
  "updated": "<updated>"
}

Replace A Campaign Record And Its Creatives (Pass The Version Read: A Stale One Is Refused With 409); Audited

PUT/sites/{id}/campaign-records/{record}

Who can call it: An account that is an admin of the site. Token scopes: write.

Parameters

FieldTypeDescription
idrequired pathinteger
recordrequired pathinteger

Body

FieldTypeDescription
namerequiredstringunique on the site (trimmed, any case): plays are matched by it
advertiserstring
flight_startrequireddateYYYY-MM-DD, the site's local day, inclusive
flight_endrequireddateon or after flight_start
screensrequiredinteger[]camera ids of the site's screens
external_idstringthe CMS's campaign id, unique on the site; null or empty: none
creativesobject[]
creatives[].namerequiredstring
creatives[].length_srequiredinteger
creatives[].external_idstringthe CMS's creative id; null or empty: none
versionintegerupdates: the version read

Responses

StatusMeaning
200OK
401RFC 9457 problem details
403RFC 9457 problem details
404RFC 9457 problem details
409RFC 9457 problem details
422RFC 9457 problem details
502RFC 9457 problem details

Request

curl -s -X PUT "https://<BOX_HOST>/api/dooh/v1/sites/1/campaign-records/1" \
  -H "Authorization: Bearer <API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{"name":"Main gate","advertiser":"Advertiser A","flight_start":"2026-11-02","flight_end":"2026-11-02","screens":[12],"external_id":"<external_id>","creatives":[{"name":"Main gate","length_s":60,"external_id":"<external_id>"}],"version":3}'
import requests

r = requests.put(
    "https://<BOX_HOST>/api/dooh/v1/sites/1/campaign-records/1",
    headers={"Authorization": "Bearer <API_KEY>"},
    json={
      "name": "Main gate",
      "advertiser": "Advertiser A",
      "flight_start": "2026-11-02",
      "flight_end": "2026-11-02",
      "screens": [
        12
      ],
      "external_id": "<external_id>",
      "creatives": [
        {
          "name": "Main gate",
          "length_s": 60,
          "external_id": "<external_id>"
        }
      ],
      "version": 3
    },
)
print(r.status_code, r.text)
const r = await fetch("https://<BOX_HOST>/api/dooh/v1/sites/1/campaign-records/1", {
  method: "PUT",
  headers: { "Authorization": "Bearer <API_KEY>", "Content-Type": "application/json" },
  body: JSON.stringify({
    "name": "Main gate",
    "advertiser": "Advertiser A",
    "flight_start": "2026-11-02",
    "flight_end": "2026-11-02",
    "screens": [
      12
    ],
    "external_id": "<external_id>",
    "creatives": [
      {
        "name": "Main gate",
        "length_s": 60,
        "external_id": "<external_id>"
      }
    ],
    "version": 3
  }),
});
console.log(r.status, await r.text());

Response 200

{
  "id": 1,
  "site_id": 1,
  "name": "Main gate",
  "advertiser": "Advertiser A",
  "flight_start": "<flight_start>",
  "flight_end": "<flight_end>",
  "screens": [
    12
  ],
  "external_id": "<external_id>",
  "creatives": [
    {
      "id": 1,
      "name": "Main gate",
      "length_s": 60,
      "external_id": "<external_id>"
    }
  ],
  "version": 3,
  "updated": "<updated>"
}

Delete A Campaign Record (Its Plays Stay, Reported Under Their Names Again); Audited

DELETE/sites/{id}/campaign-records/{record}

Who can call it: An account that is an admin of the site. Token scopes: write.

Parameters

FieldTypeDescription
idrequired pathinteger
recordrequired pathinteger

Responses

StatusMeaning
204OK
401RFC 9457 problem details
403RFC 9457 problem details
404RFC 9457 problem details
409RFC 9457 problem details
422RFC 9457 problem details
502RFC 9457 problem details

Request

curl -s -X DELETE "https://<BOX_HOST>/api/dooh/v1/sites/1/campaign-records/1" \
  -H "Authorization: Bearer <API_KEY>"
import requests

r = requests.delete(
    "https://<BOX_HOST>/api/dooh/v1/sites/1/campaign-records/1",
    headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)
const r = await fetch("https://<BOX_HOST>/api/dooh/v1/sites/1/campaign-records/1", {
  method: "DELETE",
  headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());

The Campaigns Named In The Play Logs That Match No Record

GET/sites/{id}/campaign-records/unmatched

The most played first, with what their plays say: the first and last play, the screens, the CMS id seen most, and the creatives with their mean length.

Who can call it: An account with a role (viewer or admin) on the site. Token scopes: read.

Parameters

FieldTypeDescription
idrequired pathinteger

Responses

StatusMeaning
200OK
401RFC 9457 problem details
403RFC 9457 problem details
404RFC 9457 problem details
409RFC 9457 problem details
422RFC 9457 problem details
502RFC 9457 problem details

Request

curl -s -X GET "https://<BOX_HOST>/api/dooh/v1/sites/1/campaign-records/unmatched" \
  -H "Authorization: Bearer <API_KEY>"
import requests

r = requests.get(
    "https://<BOX_HOST>/api/dooh/v1/sites/1/campaign-records/unmatched",
    headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)
const r = await fetch("https://<BOX_HOST>/api/dooh/v1/sites/1/campaign-records/unmatched", {
  method: "GET",
  headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());

Response 200

{
  "items": [
    {
      "name": "Main gate",
      "external_id": "<external_id>",
      "plays": 12,
      "first": "2026-11-02T08:00:00Z",
      "last": "2026-11-02T08:00:00Z",
      "screens": [
        12
      ],
      "creatives": [
        {
          "name": "Main gate",
          "external_id": "<external_id>",
          "plays": 12,
          "length_s": 60
        }
      ]
    }
  ]
}

Add An Unmatched Campaign To The Manager, Prefilled From Its Plays

POST/sites/{id}/campaign-records/adopt

The flight runs from the first to the last play's local day, with its screens, CMS ids and creatives with their mean length; no advertiser. Recorded in the audit log.

Who can call it: An account that is an admin of the site. Token scopes: write.

Parameters

FieldTypeDescription
idrequired pathinteger

Body

FieldTypeDescription
namerequiredstringan unmatched campaign's name (any case)

Responses

StatusMeaning
201OK
401RFC 9457 problem details
403RFC 9457 problem details
404RFC 9457 problem details
409RFC 9457 problem details
422RFC 9457 problem details
502RFC 9457 problem details

Request

curl -s -X POST "https://<BOX_HOST>/api/dooh/v1/sites/1/campaign-records/adopt" \
  -H "Authorization: Bearer <API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{"name":"Main gate"}'
import requests

r = requests.post(
    "https://<BOX_HOST>/api/dooh/v1/sites/1/campaign-records/adopt",
    headers={"Authorization": "Bearer <API_KEY>"},
    json={
      "name": "Main gate"
    },
)
print(r.status_code, r.text)
const r = await fetch("https://<BOX_HOST>/api/dooh/v1/sites/1/campaign-records/adopt", {
  method: "POST",
  headers: { "Authorization": "Bearer <API_KEY>", "Content-Type": "application/json" },
  body: JSON.stringify({
    "name": "Main gate"
  }),
});
console.log(r.status, await r.text());

Response 201

{
  "id": 1,
  "site_id": 1,
  "name": "Main gate",
  "advertiser": "Advertiser A",
  "flight_start": "<flight_start>",
  "flight_end": "<flight_end>",
  "screens": [
    12
  ],
  "external_id": "<external_id>",
  "creatives": [
    {
      "id": 1,
      "name": "Main gate",
      "length_s": 60,
      "external_id": "<external_id>"
    }
  ],
  "version": 3,
  "updated": "<updated>"
}

View Log

Every viewer, newest first, and its export.

The View Log, Newest First, Paged

GET/sites/{id}/logs

Who can call it: An account with a role (viewer or admin) on the site. Token scopes: read.

Parameters

FieldTypeDescription
idrequired pathinteger
fromrequired querystringRFC 3339
torequired querystringRFC 3339
camera queryinteger
status querystringwalk_by, noticed or engaged
age_band querystring
gender querystring
limit queryinteger
before querystringnext_before of the previous page

Responses

StatusMeaning
200OK
401RFC 9457 problem details
403RFC 9457 problem details
404RFC 9457 problem details
409RFC 9457 problem details
422RFC 9457 problem details
502RFC 9457 problem details

Request

curl -s -X GET "https://<BOX_HOST>/api/dooh/v1/sites/1/logs?from=2026-11-01T00%3A00%3A00%2B07%3A00&to=2026-11-02T00%3A00%3A00%2B07%3A00&camera=1&limit=50&before=2026-11-02T08%3A00%3A00Z" \
  -H "Authorization: Bearer <API_KEY>"
import requests

r = requests.get(
    "https://<BOX_HOST>/api/dooh/v1/sites/1/logs?from=2026-11-01T00%3A00%3A00%2B07%3A00&to=2026-11-02T00%3A00%3A00%2B07%3A00&camera=1&limit=50&before=2026-11-02T08%3A00%3A00Z",
    headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)
const r = await fetch("https://<BOX_HOST>/api/dooh/v1/sites/1/logs?from=2026-11-01T00%3A00%3A00%2B07%3A00&to=2026-11-02T00%3A00%3A00%2B07%3A00&camera=1&limit=50&before=2026-11-02T08%3A00%3A00Z", {
  method: "GET",
  headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());

Response 200

{
  "items": [
    {
      "id": "<id>",
      "camera_id": 1,
      "screen": "Screen 1",
      "time": "2026-11-02T08:00:00Z",
      "dwell_s": 60,
      "attention_s": 60,
      "first_glance_s": 60,
      "status": "walk_by",
      "age_band": "<age_band>",
      "gender": "female",
      "emotion": "<emotion>"
    }
  ],
  "next_before": "<next_before>"
}

The View Log As CSV Or XLSX (EN/ID, Local Times, At Most 20 000 Rows; Audited)

GET/sites/{id}/logs/export

Who can call it: An account with a role (viewer or admin) on the site. Token scopes: read, export.

Parameters

FieldTypeDescription
idrequired pathinteger
fromrequired querystringRFC 3339
torequired querystringRFC 3339
format querystringcsv or xlsx
language querystringen or id

Responses

StatusMeaning
200OK
401RFC 9457 problem details
403RFC 9457 problem details
404RFC 9457 problem details
409RFC 9457 problem details
422RFC 9457 problem details
502RFC 9457 problem details

Request

curl -s -X GET "https://<BOX_HOST>/api/dooh/v1/sites/1/logs/export?from=2026-11-01T00%3A00%3A00%2B07%3A00&to=2026-11-02T00%3A00%3A00%2B07%3A00" \
  -H "Authorization: Bearer <API_KEY>"
import requests

r = requests.get(
    "https://<BOX_HOST>/api/dooh/v1/sites/1/logs/export?from=2026-11-01T00%3A00%3A00%2B07%3A00&to=2026-11-02T00%3A00%3A00%2B07%3A00",
    headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)
const r = await fetch("https://<BOX_HOST>/api/dooh/v1/sites/1/logs/export?from=2026-11-01T00%3A00%3A00%2B07%3A00&to=2026-11-02T00%3A00%3A00%2B07%3A00", {
  method: "GET",
  headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());

Campaigns

The campaign manager (advertiser, flight dates, screens, creatives; plays matched by the CMS's id, else by name), the names in the play logs not in it, and the audience per campaign and creative with its export.

The Audience Per Campaign And Creative, With The Estimated Cost And CPM

GET/sites/{id}/campaigns

A view counts for every creative on its screen while the person was in view. Plays are matched to the campaign manager's records by the CMS's id when the play carries one a record has, else by name (trimmed, any case). A record's campaign carries its advertiser and flight dates and counts the plays outside them; records in flight without a play are listed with zeros.

Who can call it: An account with a role (viewer or admin) on the site. Token scopes: read.

Parameters

FieldTypeDescription
idrequired pathinteger
fromrequired querystringRFC 3339
torequired querystringRFC 3339

Responses

StatusMeaning
200OK
401RFC 9457 problem details
403RFC 9457 problem details
404RFC 9457 problem details
409RFC 9457 problem details
422RFC 9457 problem details
502RFC 9457 problem details

Request

curl -s -X GET "https://<BOX_HOST>/api/dooh/v1/sites/1/campaigns?from=2026-11-01T00%3A00%3A00%2B07%3A00&to=2026-11-02T00%3A00%3A00%2B07%3A00" \
  -H "Authorization: Bearer <API_KEY>"
import requests

r = requests.get(
    "https://<BOX_HOST>/api/dooh/v1/sites/1/campaigns?from=2026-11-01T00%3A00%3A00%2B07%3A00&to=2026-11-02T00%3A00%3A00%2B07%3A00",
    headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)
const r = await fetch("https://<BOX_HOST>/api/dooh/v1/sites/1/campaigns?from=2026-11-01T00%3A00%3A00%2B07%3A00&to=2026-11-02T00%3A00%3A00%2B07%3A00", {
  method: "GET",
  headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());

Response 200

{
  "price_per_slot": null,
  "currency": "IDR",
  "campaigns": [
    {
      "campaign_id": "Morning Coffee",
      "plays": 320,
      "play_s": 4800,
      "ots": 2410,
      "impressions": 806,
      "engaged": 170,
      "attention_rate": 33.4,
      "engagement_rate": 7.1,
      "avg_attention_s": 2.9,
      "est_cost": null,
      "est_cpm": null,
      "out_of_flight": 0,
      "record": {
        "id": 3,
        "advertiser": "Advertiser A",
        "flight_start": "2026-11-01",
        "flight_end": "2026-11-30",
        "external_id": "CMP-1001",
        "screens": [
          41
        ]
      },
      "creatives": [
        {
          "creative_id": "Coffee 15s",
          "record_id": 4,
          "length_s": 15,
          "plays": 320,
          "play_s": 4800,
          "ots": 2410,
          "impressions": 806,
          "engaged": 170,
          "attention_rate": 33.4,
          "engagement_rate": 7.1,
          "avg_attention_s": 2.9,
          "est_cost": null,
          "est_cpm": null,
          "out_of_flight": 0
        }
      ]
    }
  ]
}

The Audience Per Campaign And Creative As CSV Or XLSX (EN/ID; Audited)

GET/sites/{id}/campaigns/export

Who can call it: An account with a role (viewer or admin) on the site. Token scopes: read, export.

Parameters

FieldTypeDescription
idrequired pathinteger
fromrequired querystringRFC 3339
torequired querystringRFC 3339
format querystringcsv or xlsx
language querystringen or id

Responses

StatusMeaning
200OK
401RFC 9457 problem details
403RFC 9457 problem details
404RFC 9457 problem details
409RFC 9457 problem details
422RFC 9457 problem details
502RFC 9457 problem details

Request

curl -s -X GET "https://<BOX_HOST>/api/dooh/v1/sites/1/campaigns/export?from=2026-11-01T00%3A00%3A00%2B07%3A00&to=2026-11-02T00%3A00%3A00%2B07%3A00" \
  -H "Authorization: Bearer <API_KEY>"
import requests

r = requests.get(
    "https://<BOX_HOST>/api/dooh/v1/sites/1/campaigns/export?from=2026-11-01T00%3A00%3A00%2B07%3A00&to=2026-11-02T00%3A00%3A00%2B07%3A00",
    headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)
const r = await fetch("https://<BOX_HOST>/api/dooh/v1/sites/1/campaigns/export?from=2026-11-01T00%3A00%3A00%2B07%3A00&to=2026-11-02T00%3A00%3A00%2B07%3A00", {
  method: "GET",
  headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());

Play Logs

What each screen played: the signed feed from a CMS or player, or a CSV.

Import Play Logs As CSV

POST/sites/{id}/plays/upload

Columns screen,creative,campaign,start,end, optionally followed by the CMS's campaign_id,creative_id. A play is stored once per screen, creative and start. Recorded in the audit log.

Who can call it: An account that is an admin of the site. Token scopes: write.

Parameters

FieldTypeDescription
idrequired pathinteger

Responses

StatusMeaning
200OK
401RFC 9457 problem details
403RFC 9457 problem details
404RFC 9457 problem details
409RFC 9457 problem details
422RFC 9457 problem details
502RFC 9457 problem details

Request

curl -s -X POST "https://<BOX_HOST>/api/dooh/v1/sites/1/plays/upload" \
  -H "Authorization: Bearer <API_KEY>"
import requests

r = requests.post(
    "https://<BOX_HOST>/api/dooh/v1/sites/1/plays/upload",
    headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)
const r = await fetch("https://<BOX_HOST>/api/dooh/v1/sites/1/plays/upload", {
  method: "POST",
  headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());

Response 200

{
  "stored": 12
}

The Latest Plays Of A Period And Whether A Key Is Set

GET/sites/{id}/plays

Who can call it: An account with a role (viewer or admin) on the site. Token scopes: read.

Parameters

FieldTypeDescription
idrequired pathinteger
fromrequired querystringRFC 3339
torequired querystringRFC 3339
limit queryinteger

Responses

StatusMeaning
200OK
401RFC 9457 problem details
403RFC 9457 problem details
404RFC 9457 problem details
409RFC 9457 problem details
422RFC 9457 problem details
502RFC 9457 problem details

Request

curl -s -X GET "https://<BOX_HOST>/api/dooh/v1/sites/1/plays?from=2026-11-01T00%3A00%3A00%2B07%3A00&to=2026-11-02T00%3A00%3A00%2B07%3A00&limit=50" \
  -H "Authorization: Bearer <API_KEY>"
import requests

r = requests.get(
    "https://<BOX_HOST>/api/dooh/v1/sites/1/plays?from=2026-11-01T00%3A00%3A00%2B07%3A00&to=2026-11-02T00%3A00%3A00%2B07%3A00&limit=50",
    headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)
const r = await fetch("https://<BOX_HOST>/api/dooh/v1/sites/1/plays?from=2026-11-01T00%3A00%3A00%2B07%3A00&to=2026-11-02T00%3A00%3A00%2B07%3A00&limit=50", {
  method: "GET",
  headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());

Response 200

{
  "key": true,
  "items": [
    {
      "camera_id": 1,
      "screen": "Screen 1",
      "creative_id": "<creative_id>",
      "campaign_id": "<campaign_id>",
      "start": "<start>",
      "end": "<end>",
      "source": "api",
      "cms_campaign_id": "<cms_campaign_id>",
      "cms_creative_id": "<cms_creative_id>"
    }
  ]
}

Signed Play Log From A CMS Or Player

POST/feeds/plays/{site}

Signed with the site's play-log key the Standard Webhooks way: webhook-id, webhook-timestamp (within 5 minutes) and webhook-signature: v1,<base64 HMAC-SHA256 of id.timestamp.body>; a repeated id is refused. Screens are named as on the dashboard; campaign_id and creative_id (optional) are the CMS's ids, which the campaign manager matches first.

Authentication: The site's play-log key (made by an admin of the site on the dashboard); no API token.

Parameters

FieldTypeDescription
siterequired pathinteger
webhook-idrequired headerstringA unique id for this message, at most 128 characters. A repeated id is refused.
webhook-timestamprequired headerstringThe time of signing in Unix seconds; it must be within 5 minutes of the box's clock.
webhook-signaturerequired headerstringv1, and the base64 HMAC-SHA256 of <webhook-id>.<webhook-timestamp>.<body> with the site's key (the base64 part after whsec_). Several signatures may be given, separated by spaces.

Body

FieldTypeDescription
playsrequiredobject[]
plays[].screenrequiredstring
plays[].creativerequiredstring
plays[].campaignrequiredstring
plays[].startrequiredstring
plays[].endrequiredstring
plays[].campaign_idstringthe CMS's campaign id (optional)
plays[].creative_idstringthe CMS's creative id (optional)

Responses

StatusMeaning
200OK
401RFC 9457 problem details
403RFC 9457 problem details
404RFC 9457 problem details
409RFC 9457 problem details
422RFC 9457 problem details
502RFC 9457 problem details

Request

curl -s -X POST "https://<BOX_HOST>/api/dooh/v1/feeds/plays/1" \
  -H "webhook-id: <WEBHOOK_ID>" \
  -H "webhook-timestamp: <WEBHOOK_TIMESTAMP>" \
  -H "webhook-signature: <WEBHOOK_SIGNATURE>" \
  -H "Content-Type: application/json" \
  -d '{"plays":[{"screen":"Lobby Screen","creative":"Coffee 15s","campaign":"Morning Coffee","start":"2026-11-02T07:00:00+07:00","end":"2026-11-02T07:00:15+07:00","campaign_id":"CMP-1001","creative_id":"CRV-2001"}]}'
import requests

r = requests.post(
    "https://<BOX_HOST>/api/dooh/v1/feeds/plays/1",
    headers={"webhook-id": "<WEBHOOK_ID>", "webhook-timestamp": "<WEBHOOK_TIMESTAMP>", "webhook-signature": "<WEBHOOK_SIGNATURE>"},
    json={
      "plays": [
        {
          "screen": "Lobby Screen",
          "creative": "Coffee 15s",
          "campaign": "Morning Coffee",
          "start": "2026-11-02T07:00:00+07:00",
          "end": "2026-11-02T07:00:15+07:00",
          "campaign_id": "CMP-1001",
          "creative_id": "CRV-2001"
        }
      ]
    },
)
print(r.status_code, r.text)
const r = await fetch("https://<BOX_HOST>/api/dooh/v1/feeds/plays/1", {
  method: "POST",
  headers: { "webhook-id": "<WEBHOOK_ID>", "webhook-timestamp": "<WEBHOOK_TIMESTAMP>", "webhook-signature": "<WEBHOOK_SIGNATURE>", "Content-Type": "application/json" },
  body: JSON.stringify({
    "plays": [
      {
        "screen": "Lobby Screen",
        "creative": "Coffee 15s",
        "campaign": "Morning Coffee",
        "start": "2026-11-02T07:00:00+07:00",
        "end": "2026-11-02T07:00:15+07:00",
        "campaign_id": "CMP-1001",
        "creative_id": "CRV-2001"
      }
    ]
  }),
});
console.log(r.status, await r.text());

Response 200

{
  "stored": 12
}

Audience Feed

The token-protected feed of the audience per screen and interval.

Whether The Site's Audience Feed Token Is Set, And The Feed's URL

GET/sites/{id}/audience-feed

Who can call it: An account that is an admin of the site. Token scopes: read.

Parameters

FieldTypeDescription
idrequired pathinteger

Responses

StatusMeaning
200OK
401RFC 9457 problem details
403RFC 9457 problem details
404RFC 9457 problem details
409RFC 9457 problem details
422RFC 9457 problem details
502RFC 9457 problem details

Request

curl -s -X GET "https://<BOX_HOST>/api/dooh/v1/sites/1/audience-feed" \
  -H "Authorization: Bearer <API_KEY>"
import requests

r = requests.get(
    "https://<BOX_HOST>/api/dooh/v1/sites/1/audience-feed",
    headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)
const r = await fetch("https://<BOX_HOST>/api/dooh/v1/sites/1/audience-feed", {
  method: "GET",
  headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());

Response 200

{
  "site": 12,
  "token": true,
  "url": "https://<RECEIVER_HOST>/hooks/arsa"
}

The Audience Per Screen And Interval, For Ad Servers

GET/feeds/audience/{site}

Intervals of 15 minutes, an hour or a day in the site's time zone, at most 31 days: OTS, impressions, engaged viewers and the impressions' attention. Send the site's audience feed token as Authorization: Bearer <FEED_TOKEN>.

Authentication: The site's audience feed token (made by an admin of the site on the dashboard); no API token.

Parameters

FieldTypeDescription
siterequired pathinteger
fromrequired querystringRFC 3339
torequired querystringRFC 3339
bucket querystring15m, hour (default) or day

Responses

StatusMeaning
200OK
401RFC 9457 problem details
403RFC 9457 problem details
404RFC 9457 problem details
409RFC 9457 problem details
422RFC 9457 problem details
502RFC 9457 problem details

Request

curl -s -X GET "https://<BOX_HOST>/api/dooh/v1/feeds/audience/1?from=2026-11-01T00%3A00%3A00%2B07%3A00&to=2026-11-02T00%3A00%3A00%2B07%3A00" \
  -H "Authorization: Bearer <FEED_TOKEN>"
import requests

r = requests.get(
    "https://<BOX_HOST>/api/dooh/v1/feeds/audience/1?from=2026-11-01T00%3A00%3A00%2B07%3A00&to=2026-11-02T00%3A00%3A00%2B07%3A00",
    headers={"Authorization": "Bearer <FEED_TOKEN>"},
)
print(r.status_code, r.text)
const r = await fetch("https://<BOX_HOST>/api/dooh/v1/feeds/audience/1?from=2026-11-01T00%3A00%3A00%2B07%3A00&to=2026-11-02T00%3A00%3A00%2B07%3A00", {
  method: "GET",
  headers: { "Authorization": "Bearer <FEED_TOKEN>" },
});
console.log(r.status, await r.text());

Response 200

{
  "site": 12,
  "timezone": "Asia/Jakarta",
  "from": "2026-11-02T08:00:00Z",
  "to": "2026-11-02T08:00:00Z",
  "bucket": "15m",
  "definitions": {
    "ots": "<ots>",
    "impressions": "<impressions>",
    "engaged": "<engaged>",
    "attention_s": "<attention_s>"
  },
  "screens": [
    {
      "camera_id": 1,
      "screen": "Screen 1",
      "intervals": [
        {
          "start": "<start>",
          "ots": 12,
          "impressions": 12,
          "engaged": 12,
          "attention_s": 60
        }
      ]
    }
  ]
}

Settings

When a viewer has noticed or engaged, the crowd threshold, the price per slot, retention, and each camera's screen.

The Site's DOOH Audience Meter Settings

GET/sites/{id}/settings

Who can call it: An account with a role (viewer or admin) on the site. Token scopes: read.

Parameters

FieldTypeDescription
idrequired pathinteger

Responses

StatusMeaning
200OK
401RFC 9457 problem details
403RFC 9457 problem details
404RFC 9457 problem details
409RFC 9457 problem details
422RFC 9457 problem details
502RFC 9457 problem details

Request

curl -s -X GET "https://<BOX_HOST>/api/dooh/v1/sites/1/settings" \
  -H "Authorization: Bearer <API_KEY>"
import requests

r = requests.get(
    "https://<BOX_HOST>/api/dooh/v1/sites/1/settings",
    headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)
const r = await fetch("https://<BOX_HOST>/api/dooh/v1/sites/1/settings", {
  method: "GET",
  headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());

Response 200

{
  "noticed_s": 60,
  "engaged_s": 60,
  "crowd_threshold": 12,
  "stats_days": 30,
  "price_per_slot": 12,
  "version": 3
}

Change The Site's DOOH Audience Meter Settings (Applied To Every Camera Of The Site; Recorded In The Audit Log)

PUT/sites/{id}/settings

Who can call it: An account that is an admin of the site. Token scopes: write.

Parameters

FieldTypeDescription
idrequired pathinteger

Body

FieldTypeDescription
noticed_srequirednumberattention that makes an impression
engaged_srequirednumberattention that makes an engaged viewer; at least noticed_s
crowd_thresholdintegernull: no crowd alert
stats_daysrequiredinteger
price_per_slotnumberIDR per play, for the estimated cost and CPM; null: no estimate
versioninteger

Responses

StatusMeaning
200OK
401RFC 9457 problem details
403RFC 9457 problem details
404RFC 9457 problem details
409RFC 9457 problem details
422RFC 9457 problem details
502RFC 9457 problem details

Request

curl -s -X PUT "https://<BOX_HOST>/api/dooh/v1/sites/1/settings" \
  -H "Authorization: Bearer <API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{"noticed_s":60,"engaged_s":60,"crowd_threshold":12,"stats_days":30,"price_per_slot":12,"version":3}'
import requests

r = requests.put(
    "https://<BOX_HOST>/api/dooh/v1/sites/1/settings",
    headers={"Authorization": "Bearer <API_KEY>"},
    json={
      "noticed_s": 60,
      "engaged_s": 60,
      "crowd_threshold": 12,
      "stats_days": 30,
      "price_per_slot": 12,
      "version": 3
    },
)
print(r.status_code, r.text)
const r = await fetch("https://<BOX_HOST>/api/dooh/v1/sites/1/settings", {
  method: "PUT",
  headers: { "Authorization": "Bearer <API_KEY>", "Content-Type": "application/json" },
  body: JSON.stringify({
    "noticed_s": 60,
    "engaged_s": 60,
    "crowd_threshold": 12,
    "stats_days": 30,
    "price_per_slot": 12,
    "version": 3
  }),
});
console.log(r.status, await r.text());

Response 200

{
  "noticed_s": 60,
  "engaged_s": 60,
  "crowd_threshold": 12,
  "stats_days": 30,
  "price_per_slot": 12,
  "version": 3
}

The Camera's Screen: Its Name And Where It Is As Seen From The Camera

GET/cameras/{id}/screen

Who can call it: An account with a role (viewer or admin) on the site. Token scopes: read.

Parameters

FieldTypeDescription
idrequired pathinteger

Responses

StatusMeaning
200OK
401RFC 9457 problem details
403RFC 9457 problem details
404RFC 9457 problem details
409RFC 9457 problem details
422RFC 9457 problem details
502RFC 9457 problem details

Request

curl -s -X GET "https://<BOX_HOST>/api/dooh/v1/cameras/1/screen" \
  -H "Authorization: Bearer <API_KEY>"
import requests

r = requests.get(
    "https://<BOX_HOST>/api/dooh/v1/cameras/1/screen",
    headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)
const r = await fetch("https://<BOX_HOST>/api/dooh/v1/cameras/1/screen", {
  method: "GET",
  headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());

Response 200

{
  "camera": 12,
  "name": "Main gate",
  "yaw_offset_deg": 90,
  "pitch_offset_deg": 90,
  "cone_yaw_deg": 90,
  "cone_pitch_deg": 90,
  "version": 3
}

Set The Camera's Screen (Recorded In The Audit Log)

PUT/cameras/{id}/screen

Who can call it: An account that is an admin of the site. Token scopes: write.

Parameters

FieldTypeDescription
idrequired pathinteger

Body

FieldTypeDescription
namerequiredstring
yaw_offset_degnumberthe head yaw of someone looking at the screen
pitch_offset_degnumber
cone_yaw_degnumber
cone_pitch_degnumber
versioninteger

Responses

StatusMeaning
200OK
401RFC 9457 problem details
403RFC 9457 problem details
404RFC 9457 problem details
409RFC 9457 problem details
422RFC 9457 problem details
502RFC 9457 problem details

Request

curl -s -X PUT "https://<BOX_HOST>/api/dooh/v1/cameras/1/screen" \
  -H "Authorization: Bearer <API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{"name":"Main gate","yaw_offset_deg":90,"pitch_offset_deg":90,"cone_yaw_deg":90,"cone_pitch_deg":90,"version":3}'
import requests

r = requests.put(
    "https://<BOX_HOST>/api/dooh/v1/cameras/1/screen",
    headers={"Authorization": "Bearer <API_KEY>"},
    json={
      "name": "Main gate",
      "yaw_offset_deg": 90,
      "pitch_offset_deg": 90,
      "cone_yaw_deg": 90,
      "cone_pitch_deg": 90,
      "version": 3
    },
)
print(r.status_code, r.text)
const r = await fetch("https://<BOX_HOST>/api/dooh/v1/cameras/1/screen", {
  method: "PUT",
  headers: { "Authorization": "Bearer <API_KEY>", "Content-Type": "application/json" },
  body: JSON.stringify({
    "name": "Main gate",
    "yaw_offset_deg": 90,
    "pitch_offset_deg": 90,
    "cone_yaw_deg": 90,
    "cone_pitch_deg": 90,
    "version": 3
  }),
});
console.log(r.status, await r.text());

Response 200

{
  "camera": 12,
  "name": "Main gate",
  "yaw_offset_deg": 90,
  "pitch_offset_deg": 90,
  "cone_yaw_deg": 90,
  "cone_pitch_deg": 90,
  "version": 3
}
Was this page helpful?