Report plays from the operator's own CMS
POST /api/v1/plays/import
https://app.tanba-ooh.kz/api/v1/plays/import
Анықтамалықтың техникалық мәтіні ағылшын тілінде жазылған.
Scope write:plays. Operator organisations only; a console session of an operator administrator works too. The body is JSON (application/json, an object with a rows array) or CSV (text/csv, UTF-8, comma or semicolon, a header row) and at most 5 MB or 50 000 rows.
Every row names a structure of your organisation (structure_id or structure_code, not both), an approval number, a whole hour and the number of plays in that hour. source_ref is optional and names the feed (for example the CMS); it is part of the identity of a row. Hours are ISO 8601; one without an offset is read as Asia/Almaty time.
A row is stored under (organisation, structure, approval, hour, source). Sending it again replaces the earlier value and never adds to it; rows with different source_ref values are separate and add up. Rows are judged one by one: the answer lists every row as accepted or refused with a reason code, and the accepted rows are stored even when others are refused. Reasons:
| Reason | Meaning |
|---|---|
structure_missing, structure_ambiguous | Neither or both of structure_id and structure_code were given. |
structure_not_found | No such structure in your organisation. |
approval_missing, approval_not_found | No approval number, or no signed approval with that number. |
approval_not_for_structure | The approval does not cover that structure. |
approval_not_valid_at_hour | The hour is outside the approval's period, before it was signed, or after it was revoked. |
hour_invalid, hour_not_whole | Not an ISO 8601 hour, or not on the hour. |
hour_in_future, hour_too_old | The hour has not begun, or began more than 62 days ago. |
plays_invalid | Not a whole number of at least 0. |
plays_over_cap | More than 3600 plays an hour for each screen of the structure (at least one). |
source_ref_invalid | Longer than 100 characters. |
duplicate_row | The same row appeared earlier in this batch. |
Imported plays are not evidence. They are kept apart from the plays the players report, they are never billed, and they never decide a compliance event. Each night they are compared with the reported plays (see reconciliation in /api/v1/stats/reports); a structure whose players reported and whose import differs by more than 10 % raises an import_mismatch city event. Making imported plays billable would need an explicit decision of the platform and is not part of this interface.
Аутентификация
Authorization тақырыбындағы API кілті немесе консоль сеансы.
Рұқсат: write:plays
Сұраудың денесі
application/json
| Атауы | Түрі | Міндетті | Сипаттама | Мысал |
|---|---|---|---|---|
rows | array of object | иә | The rows to store, one for each structure, approval and hour. | |
rows[].structure_id | string (uuid) | Identifier of the structure; give this or structure_code, not both. | "b7e4a2c9-0d1f-4a68-8e35-91c7f2d40b16" | |
rows[].structure_code | string | Code of the structure; give this or structure_id, not both. | "ALM-0012" | |
rows[].approval_number | string | Number of the approval that was in force in that hour. | "TNB-2026-000123" | |
rows[].hour | string | ISO 8601, on the hour. | "2026-10-06T14:00:00+05:00" | |
rows[].plays | integer | Number of plays in that hour, a whole number of 0 or more. | 118 | |
rows[].source_ref | string | Name of the feed the row comes from, up to 100 characters; part of the identity of the row. | "cms-main" |
Мысал
{
"rows": [
{
"structure_code": "ALM-0012",
"approval_number": "TNB-2026-000123",
"hour": "2026-10-06T14:00:00+05:00",
"plays": 118,
"source_ref": "cms-main"
},
{
"structure_code": "ALM-0012",
"approval_number": "TNB-2026-000123",
"hour": "2026-10-06T15:00:00+05:00",
"plays": 120,
"source_ref": "cms-main"
}
]
}
text/csv
Мысал
structure_code,approval_number,hour,plays,source_ref
ALM-0012,TNB-2026-000123,2026-10-06T14:00:00+05:00,118,cms-main
ALM-0012,TNB-2026-000123,2026-10-06T15:00:00+05:00,120,cms-main
Жауаптар
200 The batch was read; see the rows for what was stored.
application/json
| Атауы | Түрі | Міндетті | Сипаттама | Мысал |
|---|---|---|---|---|
batch_id | string (uuid) | иә | Identifier of the batch. | "0b1c3a52-6f0e-4c2e-9d3a-2f5d1e7a9c10" |
received | integer | иә | Number of rows read from the body. | 2 |
accepted | integer | иә | Number of rows stored. | 1 |
refused | integer | иә | Number of rows refused. | 1 |
rows | array of object | иә | One entry for each row, in the order of the body. | |
rows[].row | integer | иә | Position in the body, from 1. | 1 |
rows[].status | string | иә | Whether the row was stored. accepted, refused | |
rows[].reason | string | Present on refused rows. | "hour_in_future" |
Мысал
{
"batch_id": "0b1c3a52-6f0e-4c2e-9d3a-2f5d1e7a9c10",
"received": 2,
"accepted": 1,
"refused": 1,
"rows": [
{
"row": 1,
"status": "accepted"
},
{
"row": 2,
"status": "refused",
"reason": "hour_in_future"
}
]
}
Қателер
Қателер problem құжаты (application/problem+json) түрінде, тұрақты code өрісімен келеді.
| Мәртебесі | Мағынасы |
|---|---|
| 400 | An error as a problem document. |
| 401 | No valid session or API key (unauthenticated, session_expired, invalid_api_key). |
| 403 | The role or the key's scope does not allow this (forbidden, key_scope_denied, key_route_denied). |
| 413 | An error as a problem document. |
| 415 | An error as a problem document. |
| 422 | Field errors (validation_failed). |
| default | An error as a problem document. |
Код мысалдары
curl
curl -X POST 'https://app.tanba-ooh.kz/api/v1/plays/import' \
-H 'Authorization: Bearer YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"rows": [
{
"structure_code": "ALM-0012",
"approval_number": "TNB-2026-000123",
"hour": "2026-10-06T14:00:00+05:00",
"plays": 118,
"source_ref": "cms-main"
},
{
"structure_code": "ALM-0012",
"approval_number": "TNB-2026-000123",
"hour": "2026-10-06T15:00:00+05:00",
"plays": 120,
"source_ref": "cms-main"
}
]
}'
Python
import requests
response = requests.post(
"https://app.tanba-ooh.kz/api/v1/plays/import",
headers={
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
json={
"rows": [
{
"structure_code": "ALM-0012",
"approval_number": "TNB-2026-000123",
"hour": "2026-10-06T14:00:00+05:00",
"plays": 118,
"source_ref": "cms-main",
},
{
"structure_code": "ALM-0012",
"approval_number": "TNB-2026-000123",
"hour": "2026-10-06T15:00:00+05:00",
"plays": 120,
"source_ref": "cms-main",
},
],
},
)
print(response.status_code, response.text)
Go
package main
import (
"fmt"
"io"
"net/http"
"strings"
)
func main() {
body := strings.NewReader(`{
"rows": [
{
"structure_code": "ALM-0012",
"approval_number": "TNB-2026-000123",
"hour": "2026-10-06T14:00:00+05:00",
"plays": 118,
"source_ref": "cms-main"
},
{
"structure_code": "ALM-0012",
"approval_number": "TNB-2026-000123",
"hour": "2026-10-06T15:00:00+05:00",
"plays": 120,
"source_ref": "cms-main"
}
]
}`)
req, err := http.NewRequest("POST", "https://app.tanba-ooh.kz/api/v1/plays/import", body)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer YOUR_API_KEY")
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
data, _ := io.ReadAll(resp.Body)
fmt.Println(resp.Status, string(data))
}