- Reference
- Authentication
- Display statistics
- Feeds
- Creating a media
- Sharing a Media (with External Storage)
- Transform a media (cropping)
- How to list campaigns
- Creating a simple campaign
- Creating a multimedia campaign
- Updating a campaign
- Creating a solo or event campaign
- Creating an HTML campaign
- Creating a share of voice campaign
- Deleting a campaign
- Campaign advanced parameters
- Listing and editing screens
- Creating a campaign with screens loop indexes
- Using the remote
This document presents our Management API, its features and how to use them and their specifications.
This API enables developers to:
- create, read, update and delete campaigns,
- read and update screens information.
The examples will use the requests Python library.
Reference
See the reference for detailed information about all the API views.
Authentication
The authentication mechanisms are the same across all our APIs.
There are 2 possible types of authentication for our APIs : JWT (JSON Web Token) and OAuth 2.0.
JWT
Creating a token
Requesting a token requires executing a POST
request on https://manage.cenareo.com/api/get_jwt_token/
with username
and password
parameters.
Example with curl:
curl --request POST \
--url https://manage.cenareo.com/api/get_jwt_token/ \
--header 'content-type: application/json' \
--data '{
"username": "username",
"password": "password"
}'
The response is a JSON containing the token.
{
"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImphbWVzIiwidXNlcl9pZCI6MSwiZW1haWwiOiJqYW1lc0BjaXR5bWVvLmZyIiwiZXhwIjoxNTMyNzEwMDQ1fQ.1iqpwY1U-zdRMKdnmMa3P-zJknxxgVVtoxrQUV4olDw"
}
Using the token
This token must then be sent in the Authorization
header of subsequent requests.
If it expires, you can simply ask for a new one.
Example to get the list of your screens:
curl --request GET \
--url https://manage.cenareo.com/api/management/v2/screens \
--header 'Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImphbWVzIiwidXNlcl9pZCI6MSwiZW1haWwiOiJqYW1lc0BjaXR5bWVvLmZyIiwiZXhwIjoxNTMyNzEwMjgzfQ.swAJp-9OWXO2EFL1zKqbXenwhAVYoa56OyFMRw5GK90'
OAuth 2.0
Creating an application
To authenticate via OAuth 2.0 you must first create an application linked to your account. To create one go to this page https://manage.cenareo.com/api/oauth/applications/:
- Click on New Application
- Enter an application name
- The fields Client id and Client secret are pre-filled with random values, but it is possible to manually set them.
- Choose Confidential for the Client type
- Choose Client credentials for the Authorization grant type
- Save
Creating a token
Once you have created the application, you can ask for an authentication token. Our code examples will use the requests Python library.
import requests
data = {'grant_type': 'client_credentials'}
credentials = ('foo', 'bar') # ('<client_id>', '<client_secret>')
r = requests.post('https://manage.cenareo.com/api/oauth/token/', data=data, auth=credentials)
token_data = r.json()
token = token_data['access_token']
With the CLI:
# Via HTTPie (https://httpie.org/)
http --form 'https://manage.cenareo.com/api/oauth/token/' \
grant_type=client_credentials \
--auth client_id:client_secret
# via cURL
curl -X POST -d "grant_type=client_credentials" \
-u"client_id:client_secret" https://manage.cenareo.com/api/oauth/token/
The complete response from the server is in this format:
{
"access_token": "OknIg4rskTEDnmvXSPizcjNwMIFjWx",
"token_type": "Bearer",
"expires_in": 36000,
"scope": "read write groups"
}
The field expires_in indicates how long - in seconds - the token will be valid once emitted. You can however get a new token with the same method.
Using the token
From now on we will consider the token is stored in a variable named token
. Here is an example to get the list of your screens.
Example to get the list of your screens:
# ...
headers = {'authorization': 'JWT {}'.format(token), 'content-type': 'application/json'}
r = requests.get('https://manage.cenareo.com/api/management/v2/screens/', headers=headers)
Or with the CLI:
# via HTTPie
http 'https://manage.cenareo.com/api/management/v2/screens/' "Authorization:JWT $token"
# via cURL
curl -X GET --header "Authorization: JWT $token" \
'https://manage.cenareo.com/api/management/v2/screens/'
Display statistics
To know what was broadcasted on which screen and when, you can access our statistics API.
It provides raw detailed display data as well as elaborate aggregates over screens, campaigns and dates in real time.
Feeds
To manage feeds, you can access our Feeds API.
The feeds service allows you to create dynamic templates based on external data such as APIs.
Creating a media
All your different types of media are accessible on a single endpoint: /medias/
. When creating a media of another type
than video you have to provide its display_time
that describes for how long it will be displayed on screens.
Uploading a file
Warning, for file uploads only, the headers must now use the content-type application/json.
headers = {'authorization': 'JWT %s' % TOKEN}
# Send a picture
with open('campagne-vacances.jpg', 'rb') as f:
file_data = {'file': f}
payload = {'display_time': 15}
r = requests.post(
"https://manage.cenareo.com/api/management/v2/medias/",
headers=headers, files=file_data, data=payload
)
# Send a video
with open('pub-soda.mp4', 'rb') as f:
file_data = {'file': f}
r = requests.post(
"https://manage.cenareo.com/api/management/v2/medias/",
headers=headers, files=file_data
)
Details of a media will always contain a type
field with a value in picture
, video
or pdf
. HTML pages are shown as picture
.
Media generated with the content creation module and added to campaigns are accessible on /medias/
but it is not possible to generate them from the API.
Providing a URL
Instead of directly uploading a file, it is possible to create a media from an existing URL.
The content type is then deduced from the Content-Type
header if present. For application/octet-stream
content types, the first 1024 bytes of the file are analyzed.
If the Content-Type
is text/html
, the targeted media won't be downloaded, a screenshot will be taken instead, and the media type will be seen as picture
.
headers = {'authorization': 'JWT %s' % TOKEN}
# Send a picture
payload = {'display_time': 15, 'url_origin': "https://url.to/myimage"}
r = requests.post(
"https://manage.cenareo.com/api/management/v2/medias/",
headers=headers, data=payload
)
# Send a video
payload = {'url_origin': "https://url.to/myvideo"}
r = requests.post(
"https://manage.cenareo.com/api/management/v2/medias/",
headers=headers, data=payload
)
Automatic updates
Our API offers the possibility to update media provided through a URL at regular intervals. The file type (image, video, pdf) returned by this URL must not change over time.
To use this feature you must provide the refresh_time
field (in minutes). Our platform will then check every refresh_time
for changes, if a change is detected the media is updated.
The ETag
and Last-Modified
headers are taken into account when checking for changes.
It is also possible to provide a validity duration using the validity_time
field (in minutes). This value is used to determine how long a player continues to display a media after its last successful update. This means that if and an update fails - for example with an error 403, 404 or 502 - the media will not longer be displayed after the date of the last successful update + the validity duration.
headers = {'authorization': 'JWT %s' % TOKEN}
payload = {
'display_time': 15,
'url': "https://url.to/myfile",
# Check for update every 2 hours
'refresh_time': 120,
# Stop displaying the media on players disconnected
# for more than 4 hours
'validity_time': 240,
}
r = requests.post(
"https://manage.cenareo.com/api/management/v2/medias/",
headers=headers, data=payload
)
Sharing a Media (with External Storage)
In case your account has a linked external storage (Digital Asset Management: option available, contact us if you are interested), it is possible to upload a medium which will be available to all your users to create campaigns.
You can optionally specify the destination path where the media should be stored with folder
. This path can only contain letters (lowercase, uppercase, no accents) and numbers, as well as the special characters: - _
and space, and the folders are separated by /
, i.e : main-folder 2/medias/my_images
headers = {'authorization': 'JWT %s' % TOKEN}
# Share a picture with external storage path
with open('campagne-vacances.jpg', 'rb') as f:
file_data = {'file': f}
payload = {"folder": "my_folder/my-pictures"}
r = requests.post(
"https://manage.cenareo.com/api/management/v2/medias/file_to_external_storage/",
headers=headers, files=file_data, data=payload
)
sharing an existing media
When a media already exists on our platform, you can make it available in your external storage by adding to its url /to_external_storage/
and making a request on the url thus obtained.
You can also specify a folder.
headers = {'authorization': 'JWT %s' % TOKEN}
# Share a picture with external storage path
payload = {"folder": "my_folder/my-pictures"}
r = requests.post(
"https://manage.cenareo.com/api/management/v2/medias/<media_uid>/to_external_storage/",
headers=headers, data=payload
)
Transform a media (cropping)
You can crop an existing media by adding to its url /transform/
and making a request on the url thus obtained. In the parameters of your request, specify the position of the resizing frame with the x and y positions ("north west" position, top left corner of the frame), as well as the dimensions of the frame.
The image will be cropped to the size of the frame according to the angle chosen.
headers = {'authorization': 'JWT %s' % TOKEN}
# Share a picture with external storage path
payload = {
"width": 100,
"height": 100,
"x": 20,
"y": 20,
"rotation": 45,
}
r = requests.post(
"https://manage.cenareo.com/api/management/v2/medias/<media_uid>/transform/",
headers=headers, data=payload
)
How to list campaigns
In order to list your campaigns, you can perform a get request on :
headers = {'authorization': 'JWT %s' % TOKEN}
r = requests.get(
"https://manage.cenareo.com/api/management/v2/ads/",
headers=headers
)
Also be aware that you can perform a search on:
- the campaign's id
- the campaign's name
- a campaign's screen name
- a campaign's screen slug
- a campaign's screen group name
by using the query parameter "search" like below :
headers = {'authorization': 'JWT %s' % TOKEN}
r = requests.get(
"https://manage.cenareo.com/api/management/v2/ads/?search=<your search pattern>",
headers=headers
)
The search pattern must be an exact match
Creating a simple campaign
To link a campaign to its content and screens we use objects called broadcasts. These objects allow you to precisely associate a content and its screens. A multimedia campaign is simply a campaign with multiple broadcasts, each associated with a single content.
This is how broadcasts are displayed in the API :
{
"url":
"https://manage.cenareo.com/api/management/v2/broadcasts/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/",
"id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"ad": "https://manage.cenareo.com/api/management/v2/ads/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/",
"screens": [
"https://manage.cenareo.com/api/management/v2/screens/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/"
],
"order": 0,
"display_periods": {},
"media": "https://manage.cenareo.com/api/management/v2/medias/video-508/"
}
This is a complete Broadcast
, as displayed on the /broadcasts
endpoint. When creating a broadcast the only required fields are screens
, media
and ad
, except if you create a broadcast at the same time as a campaign. When creating a campaign you can provide a list of broadcasts.
Here is the example of a POST
request on the /ads
endpoint that will create a campaign with only one content displayed on two screens:
{
"name": "Single content campaign",
"campaign_startdate": "2017-08-01",
"broadcasts": [
{
"media": "<url media>",
"screens": ["<url screen1>", "<url screen2>"]
}
]
}
It is possible to not specify any broadcast when creating a campaign and add them later using a PATCH
request. Another method it to create broadcasts independently using POST
requests on /broadcasts
.
NB 1: Changing the broadcasts
of a campaign can cause broadcasts to be deleted and created, which in turn can trigger downloads on the players.
NB 2: Once the campaign has been created or updated, its attribute screens
will contain all the screens for its broadcasts. This attribute is not meant to be modified. To change the screens a campaign is displayed on, you should change its broadcasts (see below).
Creating a multimedia campaign
To create a multimedia campaign, you need to create a campaign with multiple broadcasts, each with its own media.
Here's an example of payload to POST
on /ads
:
{
"name": "Campaign with 2 media",
"campaign_startdate": "2017-08-01",
"multimedia_type": "cloned",
"broadcasts": [
{
"media": "<url media1>",
"screens": ["<url screen1>", "<url screen2>"]
},
{
"media": "<url media2>",
"screens": ["<url screen1>", "<url screen2>"]
}
]
}
You can choose between 3 types of multimedia campaigns using the multimedia_type
field:
playlist
means that every media file will be read in the given order on every screen.
With this type, all broadcasts should have the same screens.cloned
means that each media file will be considered like a separate campaign.
With this type, all broadcasts should have the same screens.stretched
means each media file must have different screens. The default value iscloned
Updating a campaign
To update a campaign's screens or media files you can PATCH
the broadcasts
fields of the campaign.
Here's the campaign we will be updating (we consider it has already been created):
{
"name": "Example name",
"multimedia_type": "cloned",
"url": "https://manage.cenareo.com/api/management/v2/ads/xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/",
"broadcasts": [
{
"screens": [
"<url screen1>",
"<url screen2>"
],
"media": "<url media1>"
},
{
"screens": [
"<url screen3>"
],
"media": "<url media2>"
}
]
}
What we want to change:
- screen2 should broadcast media2 and no longer broadcast media1
- screen4, a new screen for this campaign, should broadcast media2 and media1
We will do a PATCH
on the campaign's URL with this payload:
{
"broadcasts": [
{
"screens": [
"<url screen1>",
"<url screen4>"
],
"media": "<url media1>"
},
{
"screens": [
"<url screen2>",
"<url screen3>",
"<url screen4>"
],
"media": "<url media2>"
}
]
}
NB: When changing the broadcasts of a campaign, the new list replaces the old one. Please make sure to provide all broadcasts when changing the broadcasts of a campaign.
For a minor change to a broadcast you can patch the broadcast directly.
Creating a solo or event campaign
To create Event or Solo campaign, use the campaign_type
field:
- For a solo campaign:
{
"name": "Campagne média unique",
"campaign_startdate": "2017-08-01",
"campaign_type": "solo",
"..."
}
- For an event campaign:
{
"name": "Campagne média unique",
"campaign_startdate": "2017-08-01",
"campaign_type": "event",
"..."
}
See the reference for more detailed information.
Creating an HTML campaign
To create an HTML campaign using screenshots, you must provide the additional information in the adupdater
field of the campaign.
To specify the screens on which the screenshot should be broadcast, you must provide a single broadcast with no media
.
Here's an example payload to POST
on /ads
:
{
"name": "Mon site",
"broadcasts": [
{
"screens": ["<url screen1>", "<url screen2>"]
}
],
"adupdater": {
"category": "screenshot",
"refresh_time": 3600,
"parameters": {
"url": "https://example.com",
"geometry": [1024, 768],
"duration": 20
}
}
}
This will create a campaign showing a screenshot of [https://example.com] for 20 seconds each time. The screenshot will be updated every 3600 seconds and will have a resolution of 1024x768
.
See the reference for more information.
Creating a share of voice campaign
Share of voice campaign are created as any other campaign. You must provide an author_origin
value, so the platform knows on which quota it should be counted.
However, the platform will check that there is sufficient screen time quota available on the concerned screens.
It is also possible to create "sliding" campaigns. These campaigns will be displayed on a different screen every day, only one screen at a time and always in the same order. They must have at least 2 screens and last at least 2 days. This type of campaign allows for more campaigns with the same quota.
If the screen time quota is insufficient, an error will be returned to the related field.
Global errors and errors relative to multiple fields are returned to the __all__
field.
The response will contain 3 fields:
- code: the error code (see below)
- details: details about the error
- message: an HTML formatted error
The existing error codes are:
duration_error
: the duration is too highquota_exceeded
: the campaign would exceed the screen time quotas
The details
field is a dictionary associating fields to the corresponding errors.
In the case of quota_exceeded
error the details will have an additional quota
key with a specific format:
{
"__all__": {
"message": [
"..."
],
"code": "quota_exceeded",
"details": {
"quota": {
"49040363-5108-4563-a29b-42138922438c": {
"duration": 50.0,
"date": "2018-02-06T14:19:34.635242+00:00",
"date_fmt": "6 february 2018 15:19:34",
"quota": 40.0,
"screen_name": "screen_1"
},
"c1547016-0226-42d4-b3f3-74fde1931674": {
"duration": 40.0,
"date": "2018-02-06T14:19:34.539759+00:00",
"date_fmt": "6 february 2018 15:19:34",
"quota": 30.0,
"screen_name": "screen_2"
}
}
}
}
}
In quota
, each key is a screen id for which the quota is insufficient:
duration
: how long the new loop would be with this campaign,quota
: allocated time on that screen for the author's share of voice,date
: date when the quota overflow would start,date_fmt
: same asdate
but localized and formatted,screen_name
: the screen's slug
Another error for the screens_not_enough
error, triggered by a sliding campaign with 1 screen:
{
"screens": {
"message": "...",
"code": "screens_not_enough",
"details": {
"screens": "sliding campaigns must have at least 2 screens"
}
}
}
The screen time quotas are available on the screens API endpoint, under the field allocated_times
: https://manage.cenareo.com/api/management/v2/screens/.
NB : Share of voice campaigns can not have multiple contents.
Giving the campaign to another user
When creating a share of voice campaign, you can "give" it to another user after creation. The new author must have access to the campaign's share of voice.
NB: Giving a campaign is an irreversible operation.
Deleting a campaign
Send a DELETE
request on that ads url:
# Url of the ad we want to delete
ad_url = "https://manage.cenareo.com/api/management/v2/ads/xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/"
# Build headers, payload and send the request
headers = {'content-type': 'application/json', 'authorization': 'JWT %s' % TOKEN}
r = requests.delete(ad_url, headers=headers)
Campaign advanced parameters
A campaign's advanced parameters can be edited via the API:
# Url of the ad we want to change
ad_url = "https://manage.cenareo.com/api/management/v2/ads/xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/"
# Build headers, payload and send the request
headers = {"content-type": "application/json", "authorization": "JWT %s" % TOKEN}
# Here we want the ad to be displayed on-demand only:
payload = {"main_loop": False, "on_demand": True}
r = requests.patch(ad_url, headers=headers, data=json.dumps(payload))
Listing and editing screens
See the screens reference for detailed information about screens.
Here's an example of request to update the opening hours of a screen:
# Url of the screen you want to update
screen_url = "https://manage.cenareo.com/api/management/v2/screens/xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/"
# Headers, with authentication token
headers = {"content-type": "application/json", "authorization": "JWT %s" % TOKEN}
# Example of opening hours. The screen will be ON:
# - 9:00 to 18:00 on Monday, Tuesday and Wednesday
# - 9:00 to 12:00, 14:00 to 18:00 on Thursday and Friday
# - Whole day on Saturday and Sunday
payload = {
"opening_hours": {
"1": [["09:00", "18:00"]],
"2": [["09:00", "18:00"]],
"3": [["09:00", "18:00"]],
"4": [["09:00", "12:00"],["14:00","18:00"]],
"5": [["09:00", "12:00"],["14:00","18:00"]],
"6": [],
"7": [],
}
}
# PATCH request
r = requests.patch(screen_url, headers=headers, data=json.dumps(payload))
To reset a screen's opening hours, simply provide an empty object:
{
"opening_hours": {}
}
NB: The download_hours
and opening_hours
of a screen use the same format.
Creating a campaign with screens loop indexes
In the following example, the broadcast will be shown in the positions 0, 4 and 8 of the loop of screen xxx and in the positions 1, 5 and 10 for screen yyy. Loop indexes with no associated broadcasts for a screen are skipped.
{
"name": "My campaign",
"broadcasts": [
{
"screens_loop_indexes": {
"https://manager.cenareo.com/api/management/screens/xxx": [1, 5, 10],
"https://manager.cenareo.com/api/management/screens/yyy": [0, 4, 8],
},
"screens": [
"https://manager.cenareo.com/api/management/screens/xxx",
"https://manager.cenareo.com/api/management/screens/yyy"
],
"media": "https://manager.cenareo.com/api/management/media/xxx"
}
]
}
NB : If a loop index is missing for a screen, is interpreted as [+∞]
.
If multiple broadcasts have the same loop index, an order not configurable and inherent to the medias is chosen.
Using the remote
There are two ways to access remote URLs through the API: