TAŇBA-OOH Әзірлеушілерге

Signed batch of play events

POST /d/v1/telemetry

https://devices.tanba-ooh.kz/d/v1/telemetry

Анықтамалықтың техникалық мәтіні ағылшын тілінде жазылған.

A tanba.pop/1 batch on the certificate channel. The body is JSON signed with the device key in the X-Tanba-Signature header (ES256, r||s, base64url, over the exact bytes of the body); the call presents the client certificate. The batch carries batch_seq, prev_sha256 (SHA-256 of the previous batch body, empty for the first), sent_at and events; each event may carry seq, a number that runs on by one per record across batches. The platform keeps the body exactly as received. The same batch_seq with the same bytes is answered duplicate: true; with other bytes it is refused 409 batch_conflict. Records at or below the highest number already taken are not counted again; a jump in the numbers and a prev_sha256 that does not match are accepted and recorded.

A batch whose signature fails is never counted: it is kept apart as evidence and answered 400 bad_signature. An unknown schema is 400 schema_unknown. A body without schema is the earlier format (play_start and play_end events), accepted as before; once a tanba.pop/1 batch of a device has been accepted, the device can no longer send the earlier format (409 legacy_after_pop). In a tanba.pop/1 batch a bad record is refused and listed in the answer; the rest of the batch counts. The records, flags and refusal reasons are in the guide "Send proof of play from your players or CMS".

Аутентификация

Құрылғының клиенттік сертификаты (өзара TLS) және құрылғы кілтімен жасалған дене қолтаңбасы.

Параметрлер

АтауыОрныТүріМіндеттіСипаттамаМысал
X-Tanba-SignatureтақырыпstringиәES256 signature of the exact bytes of the body by the device key, raw r||s (64 bytes), base64url without padding.q3J2y0uO8e1m4n7pXc5Zk9d2Wv6Tg0hLsB-aRfN1YjE6uHxPzD3oVtKiC8wMbQe2nSgA4rUl7yFhJ9XcT0dZaw

Сұраудың денесі

application/json

PopDeviceBatch

АтауыТүріМіндеттіСипаттамаМысал
schemastringиәFormat of the batch. tanba.pop/1
device_idstring (uuid)Signed channel only: the device that sends the batch; on the certificate channel the device is the one in the certificate."5c0e8f3a-71d2-4b6e-9a14-d8e2f7b30c59"
tsstring (date-time)Signed channel only: the clock of the agent, within 300 seconds of the clock of the platform."2026-10-14T09:05:00.120Z"
batch_seqintegerиәNumber of the batch for this device; taken once.1042
prev_sha256stringHex SHA-256 of the body of the previous batch; empty for the first."9f2b6a41c7d83e05a1b4c6d7e8f90123456789abcdef0123456789abcdef0123"
sent_atstring (date-time)When the player cut the batch."2026-10-14T09:05:00.120Z"
eventsarray of objectиәThe records of the batch in order; at most 1000.
events[].kindstringиәKind of the record. A finished play is play; reject is a file the player refused to show, mode_change a change of state and tamper a broken local log. play, reject, mode_change, tamper
events[].seqintegerNumber of the record for this player, +1 for every record across batches, from 1; required on play.18
events[].play_idstringIdentifier of the play, unique for the player; characters A-Za-z0-9_-."7c1e9a40b2d35f68"
events[].approval_numberstringNumber of the approval that was played; required on a play unless slot is set."TNB-2026-000123"
events[].slotstringAuthority content: no approval number, never billed, counted apart. gov, emergency
events[].media_sha256stringSHA-256 of the file played, 64 lowercase hex characters; required on a play."9f2b6a41c7d83e05a1b4c6d7e8f90123456789abcdef0123456789abcdef0123"
events[].startstring (date-time)When the play began, RFC 3339 with milliseconds."2026-10-14T09:04:50.000Z"
events[].endstring (date-time)When the play ended; start is not after it."2026-10-14T09:05:00.000Z"
events[].duration_msintegerTime on screen in milliseconds; a play counts from 1000.10000
events[].structurestringCode of the structure the player believes it stands at; a different one is flagged."ALM-0012"
events[].screenobjectState of the display during the play.
events[].screen.powerstringWhether the display was on. on, off, unknown
events[].atstring (date-time)When the event happened; on reject, mode_change and tamper."2026-10-14T09:00:00.000Z"
events[].codestringWhy the file was refused; on reject. NO_SIGNATURE, BAD_SIGNATURE, HASH_MISMATCH, EXPIRED_APPROVAL, REVOKED
events[].detailobjectFurther facts of a reject, mode_change or tamper record. On a play it may carry src, the attribution tag of a programmatic play: {channel, seller_id, deal_id, imp_id} with channel a lowercase word, seller_id the id of the selling platform's organisation (seller_id of authorised_sellers), deal_id and imp_id optional (A-Za-z0-9._:-, up to 64). A tag of any other shape is ignored; the play counts all the same. A tag is attribution, not proof, and never changes billing.

Мысал

{
  "schema": "tanba.pop/1",
  "batch_seq": 1042,
  "prev_sha256": "9f2b6a41c7d83e05a1b4c6d7e8f90123456789abcdef0123456789abcdef0123",
  "sent_at": "2026-10-14T09:05:00.120Z",
  "events": [
    {
      "kind": "play",
      "seq": 18,
      "play_id": "7c1e9a40b2d35f68",
      "approval_number": "TNB-2026-000123",
      "media_sha256": "9f2b6a41c7d83e05a1b4c6d7e8f90123456789abcdef0123456789abcdef0123",
      "start": "2026-10-14T09:04:50.000Z",
      "end": "2026-10-14T09:05:00.000Z",
      "duration_ms": 10000,
      "screen": {
        "power": "on"
      }
    }
  ]
}

Жауаптар

200 Recorded; see `items` for the records that were not plainly accepted.

application/json

PopAnswer

АтауыТүріМіндеттіСипаттамаМысал
batch_seqintegerиәNumber of the batch.1042
receivedintegerиәNumber of records read from the body.12
acceptedintegerиәNumber of records counted, flagged ones included.10
duplicatesintegerиәNumber of records at or below the highest number already taken for their player.0
refusedintegerиәNumber of records refused.2
flaggedintegerиәNumber of accepted records that carry a flag.1
duplicatebooleanиәTrue when this very batch was received before; nothing was counted again.false
itemsarray of objectиәOnly the records that are not plainly accepted; a clean batch gets an empty list.
items[].indexintegerиәPosition in events, from 0.3
items[].statusstringиәWhat became of the record. duplicate, refused, flagged
items[].reasonstringPresent on refused records, for example time_in_future."time_in_future"
items[].flagsarray of stringPresent on flagged records; the flags are listed in the guide on sending proof of play.

Мысал

{
  "batch_seq": 1042,
  "received": 12,
  "accepted": 10,
  "duplicates": 0,
  "refused": 2,
  "flagged": 1,
  "duplicate": false,
  "items": [
    {
      "index": 3,
      "status": "flagged",
      "flags": [
        "incomplete"
      ]
    },
    {
      "index": 7,
      "status": "refused",
      "reason": "time_in_future"
    }
  ]
}

Қателер

Қателер problem құжаты (application/problem+json) түрінде, тұрақты code өрісімен келеді.

МәртебесіМағынасы
400The signature does not verify (bad_signature), the body is not JSON (invalid_json) or the schema is unknown (schema_unknown).
401The device is unknown, revoked or expired (device_unauthorized), or no certificate was presented (device_cert_required).
409The batch_seq was taken by a batch with other content (batch_conflict), or the batch is in the earlier format from a device that has had a tanba.pop/1 batch accepted (legacy_after_pop; do not retry it).
413More than 4 MB or 1000 events (body_too_large, batch_too_large).
422Field errors (validation_failed).
429More than one batch every 10 seconds for the device (a burst of 3 is allowed). Retry-After gives the seconds to wait; resend the same bytes (rate_limited).
defaultAn error as a problem document.

Код мысалдары

curl

curl -X POST 'https://devices.tanba-ooh.kz/d/v1/telemetry' \
  -H 'X-Tanba-Signature: q3J2y0uO8e1m4n7pXc5Zk9d2Wv6Tg0hLsB-aRfN1YjE6uHxPzD3oVtKiC8wMbQe2nSgA4rUl7yFhJ9XcT0dZaw' \
  -H 'Content-Type: application/json' \
  -d '{
  "schema": "tanba.pop/1",
  "batch_seq": 1042,
  "prev_sha256": "9f2b6a41c7d83e05a1b4c6d7e8f90123456789abcdef0123456789abcdef0123",
  "sent_at": "2026-10-14T09:05:00.120Z",
  "events": [
    {
      "kind": "play",
      "seq": 18,
      "play_id": "7c1e9a40b2d35f68",
      "approval_number": "TNB-2026-000123",
      "media_sha256": "9f2b6a41c7d83e05a1b4c6d7e8f90123456789abcdef0123456789abcdef0123",
      "start": "2026-10-14T09:04:50.000Z",
      "end": "2026-10-14T09:05:00.000Z",
      "duration_ms": 10000,
      "screen": {
        "power": "on"
      }
    }
  ]
}'
Python
import requests

response = requests.post(
    "https://devices.tanba-ooh.kz/d/v1/telemetry",
    headers={
        "X-Tanba-Signature": "q3J2y0uO8e1m4n7pXc5Zk9d2Wv6Tg0hLsB-aRfN1YjE6uHxPzD3oVtKiC8wMbQe2nSgA4rUl7yFhJ9XcT0dZaw",
        "Content-Type": "application/json",
    },
    json={
        "schema": "tanba.pop/1",
        "batch_seq": 1042,
        "prev_sha256": "9f2b6a41c7d83e05a1b4c6d7e8f90123456789abcdef0123456789abcdef0123",
        "sent_at": "2026-10-14T09:05:00.120Z",
        "events": [
            {
                "kind": "play",
                "seq": 18,
                "play_id": "7c1e9a40b2d35f68",
                "approval_number": "TNB-2026-000123",
                "media_sha256": "9f2b6a41c7d83e05a1b4c6d7e8f90123456789abcdef0123456789abcdef0123",
                "start": "2026-10-14T09:04:50.000Z",
                "end": "2026-10-14T09:05:00.000Z",
                "duration_ms": 10000,
                "screen": {
                    "power": "on",
                },
            },
        ],
    },
)
print(response.status_code, response.text)
Go
package main

import (
	"fmt"
	"io"
	"net/http"
	"strings"
)

func main() {
	body := strings.NewReader(`{
  "schema": "tanba.pop/1",
  "batch_seq": 1042,
  "prev_sha256": "9f2b6a41c7d83e05a1b4c6d7e8f90123456789abcdef0123456789abcdef0123",
  "sent_at": "2026-10-14T09:05:00.120Z",
  "events": [
    {
      "kind": "play",
      "seq": 18,
      "play_id": "7c1e9a40b2d35f68",
      "approval_number": "TNB-2026-000123",
      "media_sha256": "9f2b6a41c7d83e05a1b4c6d7e8f90123456789abcdef0123456789abcdef0123",
      "start": "2026-10-14T09:04:50.000Z",
      "end": "2026-10-14T09:05:00.000Z",
      "duration_ms": 10000,
      "screen": {
        "power": "on"
      }
    }
  ]
}`)
	req, err := http.NewRequest("POST", "https://devices.tanba-ooh.kz/d/v1/telemetry", body)
	if err != nil {
		panic(err)
	}
	req.Header.Set("X-Tanba-Signature", "q3J2y0uO8e1m4n7pXc5Zk9d2Wv6Tg0hLsB-aRfN1YjE6uHxPzD3oVtKiC8wMbQe2nSgA4rUl7yFhJ9XcT0dZaw")
	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))
}