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

Signed agent telemetry batch

POST /s/v1/telemetry

https://agents.tanba-ooh.kz/s/v1/telemetry

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

A tanba.pop/1 batch on the signed channel, for agents that have no client certificate. The body is JSON with device_id and ts (the clock of the agent, within 300 seconds of the clock of the platform), signed with the key registered at enrolment in the X-Tanba-Signature header (ES256, r||s, base64url, over the exact bytes of the body). The rules after that are those of the certificate channel: batch_seq, prev_sha256, sent_at, events, the record numbers seq, the body kept as received, repeats and conflicts.

The body is at most 1 MB, and a device may send 10 batches a minute. A signature that does not verify is 401 bad_signature and nothing is stored.

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

Тіркеу кезінде жазылған кілтпен жасалған дене қолтаңбасы. Сертификат қажет емес.

Параметрлер

АтауыОрныТүріМіндеттіСипаттамаМысал
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 body is not JSON (invalid_json) or the schema is unknown (schema_unknown).
401The signature does not verify (bad_signature), the device is unknown or revoked (device_unauthorized), or ts is out of range (stale_request).
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 1 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://agents.tanba-ooh.kz/s/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://agents.tanba-ooh.kz/s/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://agents.tanba-ooh.kz/s/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))
}