API for connecting your system
Introduction and orientation
How to manage media, campaigns and reports directly from your own system without signing in to the administration.
What the API is for
The API connects the customer’s own system — for example an e-shop, an advertising campaign planner or an internal catalogue — directly to device content management. Your system can upload media, create and start campaigns and retrieve their results without anyone having to sign in to the administration and perform these operations manually.
The interface is the same one used by the web administration. The API therefore supports the same operations as the administration, within the permissions of the signed-in user.
When to use the API
For occasionally uploading a few media files and starting a campaign manually, the administration is the better choice: the campaign wizard shows a preview and checks media formats and device capacity. The API makes sense once manual work starts to slow you down.
| You need to | Recommended approach |
|---|---|
| Upload a few media files and start a campaign | Administration |
| Update content regularly, but manually | Administration |
| Upload media in bulk from your own system | API |
| Create campaigns automatically from your own scheduler | API |
| Pull campaign results into your own reporting | API |
The interface is not versioned and may change as the platform evolves. Notify your software provider before deploying an integration and verify that it still works after system updates.
Who this guide is for and who is responsible
- The platform provider operates the interface, provides the administration address and sets the Company’s playback mode.
- The customer decides on the scope of the integration and creates a dedicated user with the required permissions in their Company — see Create a user and first sign-in.
- The integration developer connects the customer’s system to the interface. The technical instructions and code examples in this guide are intended for that developer. If you develop the integration yourself, you also fulfil this role.
Technical reference
Technical guide for integration developers
Start with the shared calling conventions and sign-in. Then follow the workflow from uploading a media file to starting a campaign, and see the overview of available endpoints.
Shared calling conventions
- Address: the interface runs at the same address as your administration; all paths start with
/api/. In the examples the address is replaced withhttps://admin.example.org. If a test environment is available, your provider will give you its address. - Sign-in: every request carries the credentials using HTTP Basic Auth. No sessions or tokens are used.
- Data format: request bodies and responses are JSON. The exception is file upload, where the body consists of the raw binary data.
- Identifiers and values: identifiers are integers. Date and time use the format
YYYY-MM-DDTHH:MM:SS, display durations are in milliseconds (10000= 10 s). - Result: success is confirmed by status
200. It means the request was accepted, not that content is already playing on the devices.
Sign-in
The login is not an e-mail address but an eight-character code generated by the system when the user was created, for example A1B2C3D4. Create a dedicated user with the minimum necessary permissions for the integration; its access can then be revoked at any time without affecting other users.
curl -u "A1B2C3D4:your-password" https://admin.example.org/api/public/loginThe password is sent with every request. Store it as a secret, for example in an environment variable or a password vault — never in source code, a repository or logs.
The following examples assume these variables are set:
export API="https://admin.example.org"
export AUTH="A1B2C3D4:your-password"The examples use curl because it does not depend on a programming language. The interface is plain HTTP, so it can be called from any language or tool that supports Basic Auth, for example Python, JavaScript, PHP, Postman or Make.
Workflow: from a file to a running campaign
- Get the Company identifier. You will use it in all further calls.
- Upload a media file. The response returns its identifier.
- Load a campaign template. The server pre-fills it with the Company settings and the list of locations and devices.
- Edit the template — name, period, target devices and visual.
- Create the campaign. It is created as a draft and does not play yet.
- Activate the campaign. The system prepares a new playback plan and sends it to the devices.
- Monitor the results in the campaign report.
1. Company identifier
curl -u "$AUTH" "$API/api/public/login"Response (shortened):
{
"id": 1715714421868,
"name": "Integration – e-shop",
"login": "A1B2C3D4",
"company": { "id": 1698234512001, "name": "Advertising Agency Ltd." },
"permission": {
"resource": 12,
"device_layer_top": 12,
"report_layer_top": 4
}
}Store the value of company.id; the examples use it as the variable COMPANY. The permission section lists the user’s permissions; their meaning is described under Permissions.
2. Uploading a media file
The file is sent as the binary request body using PUT; its properties are given in headers.
curl -u "$AUTH" -X PUT \
"$API/api/resource/single/$COMPANY" \
-H "file-type: IMAGE" \
-H "file-mime: image/jpeg" \
-H "file-name: summer-sale.jpg" \
-H "file-suffix: jpg" \
--data-binary @summer-sale.jpg| Header | Content |
|---|---|
file-type | Media type according to the Media types table, for example IMAGE. |
file-mime | MIME type of the file, for example image/jpeg, video/mp4 or application/pdf. |
file-name | Media name shown in the administration; the server removes any extension. URL-encode names containing non-ASCII characters (encodeURIComponent). |
file-suffix | File extension without the dot. |
The same call in Python:
import requests
with open("summer-sale.jpg", "rb") as f:
r = requests.put(
f"{API}/api/resource/single/{COMPANY}",
auth=("A1B2C3D4", "your-password"),
headers={
"file-type": "IMAGE",
"file-mime": "image/jpeg",
"file-name": "summer-sale.jpg",
"file-suffix": "jpg",
},
data=f,
)
r.raise_for_status()
resource_id = r.json()["id"]The response contains the media identifier (id) and its type (type); you will use both values in the campaign visual.
{
"id": 1724832015443,
"type": 2,
"name": "summer-sale",
"width": 1920,
"height": 1080,
"duration": 0,
"aspectRatio": 32
}- Size: regular files up to approximately 1.1 GB, video with transparency up to approximately 2.1 GB. The server rejects larger files with
413. - Video preparation: video is converted in the background after upload. It is ready once the media detail (
GET /api/resource/single/{companyId}/{id}) contains a value indt_ready. For images, preparation is practically immediate. - Missing header: without
file-mime,file-nameorfile-suffixthe server returns204and does not create the media file. Always check that the response containsidbefore continuing.
3. Campaign template
Do not assemble the campaign by hand. Request a template that the server pre-fills with a default period (one month from today), the Company settings and the list of all locations and devices. Every device in it is marked isActive: false.
curl -u "$AUTH" "$API/api/campaign/company/$COMPANY" > campaign.jsonTemplate example (shortened):
{
"id": -1,
"groupId": -1,
"state": 2,
"name": "",
"start": "2026-11-01T10:00:00",
"end": "2026-12-01T10:00:00",
"isCommercial": true,
"isNonCommercial": false,
"settings": {
"type": 2,
"companyCount": 10,
"companyDuration": 10000,
"campaignCount": 1,
"campaignDuration": 10000
},
"locations": [
{
"id": 1698234512100,
"name": "City Mall",
"days": [ { "day": 1, "start": "09:00", "end": "21:00" } ],
"devices": [
{
"id": 1698234512201,
"name": "Entrance A",
"screenRatio": 32,
"isHorizontal": true,
"isActive": false
}
]
}
],
"visuals": []
}The value of settings.type determines the Company’s playback mode: 2 = SLOT, 4 = IMPRESSION. The mode is set by the provider and cannot be changed via the API.
4. Editing the template
name: campaign name shown in the administration and reports.start,end: start and end of the campaign.isActive: set totruefor the devices on which the campaign should play.visuals: add a visual with the uploaded media file.
"visuals": [
{
"id": 1,
"name": "Main visual",
"isPrimary": true,
"isCustom": true,
"res": [
{
"id": 1,
"conditionId": 0,
"resourceId": 1724832015443,
"name": "summer-sale",
"duration": 10000,
"type": 2
}
]
}
]| Field | Meaning |
|---|---|
id | Any number unique within the campaign; the server renumbers it on creation. |
isPrimary | true for the main visual, false for the others. |
isCustom | true – the visual belongs to this campaign. |
resourceId | Media identifier from step 2. |
duration | Display duration in milliseconds. |
type | Media type; the same value the upload returned in type. |
The template can be edited by a script, for example with jq:
jq --argjson res "$RESOURCE" '
.name = "Summer sale 2026"
| .start = "2026-11-02T00:00:00"
| .end = "2026-11-09T00:00:00"
| .locations[].devices[].isActive = true
| .visuals = [{
id: 1, name: "Main visual", isPrimary: true, isCustom: true,
res: [{ id: 1, conditionId: 0, resourceId: $res,
name: "summer-sale", duration: 10000, type: 2 }]
}]
' campaign.json > campaign-edited.jsonIf the campaign targets both landscape and portrait screens (isHorizontal), add a visual for each orientation. Otherwise the content will be cropped or scaled down on some screens. See Campaign visuals for details.
5. Creating the campaign
curl -u "$AUTH" -X POST \
"$API/api/campaign/company/$COMPANY" \
-H "Content-Type: application/json" \
--data @campaign-edited.json \
-o response.json
export CAMPAIGN=$(jq -r '.id' response.json)
export GROUP=$(jq -r '.groupId' response.json)The campaign is created as a draft and does not play anywhere. The response contains the saved campaign with the assigned id and groupId. The template already has state: 2 (draft); the server rejects any other value with 409.
6. Activating the campaign
curl -u "$AUTH" -X PATCH \
"$API/api/campaign/company/$COMPANY/$CAMPAIGN/+01:00/0"+01:00: time zone as an offset from UTC that the campaign follows. For Central Europe use+01:00in winter and+02:00in summer time.0: expected number of impressions for the whole campaign. It applies in IMPRESSION mode; in SLOT mode use0.
A campaign starting today or earlier becomes active and its start is set to the moment of activation. A campaign with a future start becomes published and starts on the scheduled day.
The system then recalculates the playback plan and sends it to the affected devices. For connected devices this usually takes tens of seconds; a disconnected device receives the plan once the connection is restored. Check device capacity in the administration, see Campaign capacity.
7. Campaign results
curl -u "$AUTH" "$API/api/report/$COMPANY/campaign/$GROUP"
curl -u "$AUTH" "$API/api/report/$COMPANY/campaign/$GROUP/impression"Reports use groupId, not the campaign id. The first endpoint returns the campaign summary, the second the numbers of recorded impressions. Devices send records continuously in batches, so the figures are not available in real time. See Campaign report for the meaning of the figures.
Pausing, stopping and deleting
# pause an active campaign; calling it again resumes it
curl -u "$AUTH" -X PATCH \
"$API/api/campaign/company/$COMPANY/$CAMPAIGN/+01:00/0"
# stop permanently
curl -u "$AUTH" -X PATCH \
"$API/api/campaign/company/$COMPANY/$CAMPAIGN/+01:00"
# delete
curl -u "$AUTH" -X DELETE \
"$API/api/campaign/company/$COMPANY/$CAMPAIGN"The endpoint from step 6 works as a toggle: it pauses an active or published campaign and resumes a paused one. Check the current campaign state (state in the detail) before calling it. A stopped campaign cannot be started again, but it remains in the list together with its reports. A deleted campaign disappears from the list.
Endpoint overview
Paths are relative to the administration address. {c} stands for the Company identifier.
Account
| Method | Path | Purpose |
|---|---|---|
GET | /api/public/login | Verifies the credentials and returns the user profile including company.id. |
GET | /api/user/info | Information about the signed-in user. |
GET | /api/user/check | Quick check that the credentials are valid. |
Media
| Method | Path | Purpose |
|---|---|---|
PUT | /api/resource/single/{c} | Uploads a file (file-* headers, body = binary data). |
GET | /api/resource/listing/{c}/1 | List of media; 1 = no type restriction. |
GET | /api/resource/single/{c}/{id} | Media detail. |
DELETE | /api/resource/single/{c}/{id} | Deletes a media file. |
Campaigns
| Method | Path | Purpose |
|---|---|---|
GET | /api/campaign/company/{c} | Template of a new campaign. |
POST | /api/campaign/company/{c} | Creates a campaign as a draft. |
PATCH | /api/campaign/company/{c} | Saves changes to an existing campaign. |
POST | /api/campaign/listing/company/{c} | List of campaigns; the request body contains a filter. |
GET | /api/campaign/company/{c}/{id} | Campaign detail. |
GET | /api/campaign/company/{c}/group/{groupId} | Campaign detail by groupId. |
PATCH | /api/campaign/company/{c}/{id}/{tz}/{impressions} | Activates, pauses or resumes a campaign. |
PATCH | /api/campaign/company/{c}/{id}/{tz} | Stops a campaign permanently. |
DELETE | /api/campaign/company/{c}/{id} | Deletes a campaign. |
Locations and devices
| Method | Path | Purpose |
|---|---|---|
GET | /api/location/listing/{c} | List of locations. |
GET | /api/location/single/{id} | Location detail including opening hours. |
GET | /api/device/listing/{c}/{locationId} | List of devices in a location. |
GET | /api/device/single/{id}/{lang} | Device detail; lang is a language code, for example en. |
Reports
| Method | Path | Purpose |
|---|---|---|
GET | /api/report/{c}/campaign/{groupId} | Campaign summary. |
GET | /api/report/{c}/campaign/{groupId}/impression | Numbers of recorded impressions. |
Code lists
Code list values are written as numbers in JSON.
Campaign state (state)
| Value | State | Meaning |
|---|---|---|
2 | Draft | Saved campaign that does not play. |
4 | Published | Activated campaign waiting for its start day. |
8 | Active | The campaign is playing. |
16 | Finished | The campaign end has passed. |
32 | Stopped | The campaign was stopped permanently. |
64 | Deleted | The campaign was deleted. |
128 | Paused | Playback is temporarily interrupted. |
Media types
type value | file-type header | Media |
|---|---|---|
2 | IMAGE | Image (JPG, PNG, WebP) |
4 | VIDEO | Video |
8 | PDF | PDF document |
16 | WWW_APP | Web application |
32 | TEXT | Long text |
128 | VIDEO_ALPHA | Video with transparent background |
256 | AUDIO | Audio |
For documents, the file-type header uses the value PDF, not DOCUMENT.
Permissions
The interface applies the same permissions as the administration and checks them on every request. In the sign-in response each area is expressed as a number that is the sum of the individual rights:
| Value | Right within your own Company |
|---|---|
4 | View |
8 | Add |
16 | Edit |
32 | Delete |
For example, resource: 28 (4 + 8 + 16) means the user may view, add and edit media, but not delete them. Higher values (128 to 1024) represent the same rights outside the user’s own Company; an integration does not need them.
| Area | Minimum for the described workflow |
|---|---|
resource – media | 12 (view, add) |
device_layer_top – campaigns | 12 (view, add) |
report_layer_top – campaign reports | 4 (view) |
location – locations | 4 (view) |
device_info – devices | 4 (view) |
Activating, pausing and stopping a campaign require the Add right. Deleting an activated campaign or a media file requires the Delete right; a campaign draft can be deleted without it. A good starting point is the Company Ad Manager role with unnecessary rights removed.
Error codes
| Code | Meaning | What to do |
|---|---|---|
400 | Bad request | Check the identifiers in the path, the time zone and the required data. |
401 | Missing permission | The user lacks the required right or access to the Company. For campaigns this is most often the missing Add right. |
404 | Not found | The record does not exist or does not belong to the given Company. |
406 | Not acceptable | Invalid credentials, or the campaign is in a state from which the requested transition is not allowed (e.g. stopped). |
409 | State conflict | The campaign being created is not a draft. |
413 | File too large | Reduce the file size; see step 2 for the limits. |
500 | Server error | Retry later; if it persists, contact your provider. |
Test the integration first in a test environment or on a device not visible to the public — an activated campaign really plays on production devices. No content plays outside the location’s opening hours, even for a campaign scheduled around the clock.
Store media and campaign identifiers (id, groupId) in your own system; the interface does not offer searching campaigns by name. Upload media one at a time and do not request reports more often than once every few minutes. Rely only on the endpoints listed in this guide.
Complete example
The script performs the whole workflow from uploading an image to activating a campaign on all devices of the Company. It requires curl and jq.
#!/usr/bin/env bash
set -euo pipefail
API="https://admin.example.org"
AUTH="${INTEGRATION_AUTH:?set INTEGRATION_AUTH=login:password}"
FILE="summer-sale.jpg"
NAME="Summer sale 2026"
FROM="2026-11-02T00:00:00"
TO="2026-11-09T00:00:00"
TZ_OFFSET="+01:00"
# 1) Company identifier
COMPANY=$(curl -sf -u "$AUTH" "$API/api/public/login" | jq -r '.company.id')
# 2) upload media
RESOURCE=$(curl -sf -u "$AUTH" -X PUT \
"$API/api/resource/single/$COMPANY" \
-H "file-type: IMAGE" \
-H "file-mime: image/jpeg" \
-H "file-name: $FILE" \
-H "file-suffix: jpg" \
--data-binary "@$FILE" | jq -r '.id')
# 3) campaign template
curl -sf -u "$AUTH" "$API/api/campaign/company/$COMPANY" > template.json
# 4) edit the template
jq --argjson res "$RESOURCE" --arg n "$NAME" --arg from "$FROM" --arg to "$TO" '
.name = $n
| .start = $from
| .end = $to
| .locations[].devices[].isActive = true
| .visuals = [{
id: 1, name: "Main visual", isPrimary: true, isCustom: true,
res: [{ id: 1, conditionId: 0, resourceId: $res,
name: $n, duration: 10000, type: 2 }]
}]
' template.json > campaign.json
# 5) create a draft
RESPONSE=$(curl -sf -u "$AUTH" -X POST \
"$API/api/campaign/company/$COMPANY" \
-H "Content-Type: application/json" \
--data @campaign.json)
CAMPAIGN=$(echo "$RESPONSE" | jq -r '.id')
GROUP=$(echo "$RESPONSE" | jq -r '.groupId')
# 6) activate
curl -sf -u "$AUTH" -X PATCH \
"$API/api/campaign/company/$COMPANY/$CAMPAIGN/$TZ_OFFSET/0" > /dev/null
echo "Campaign $CAMPAIGN activated, report: $API/api/report/$COMPANY/campaign/$GROUP"Related topics and next steps
← Back to overview – Integrations & content creation
Contact your software provider.
