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

Device heartbeat (client certificate, signed body)

POST /d/v1/heartbeat

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

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

The player reports its state and receives commands. The body is signed with the device key (header X-Tanba-Signature, ES256, r||s, base64url) and carries a rising seq and the nonce given in the last answer. With wait (up to 25 seconds) the platform holds the answer until a command is ready, so a cut-off or a restore reaches a connected player within seconds. A player that makes presence tokens for its QR code sends qr_key: false until it holds its key, true after; while it is false the answer carries qr_secret and returns at once.

A clock difference above five minutes is recorded as a clock skew. A change of edid_sha256 is recorded as a changed screen. A display size (screen_width_px and screen_height_px) that differs from the size on file for the structure in every heartbeat for 24 hours is recorded as a screen size mismatch; a heartbeat without a size changes nothing, and a change of the network prefix of the caller (/24 for IPv4, /48 for IPv6) as a changed network. A silence longer than the grace of the class (10 minutes for class A and B, 60 for C) is stored as an offline interval when the player is heard again.

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

Құрылғының клиенттік сертификаты (өзара 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
waitсұрауintegerжоқSeconds to hold the answer until a command is ready; 0 to 25.25

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

application/json

АтауыТүріМіндеттіСипаттамаМысал
seqintegerиәNumber of the heartbeat; it must be above the last one taken.1840
statestringиәState of the player. normal, compliance, cutoff
noncestringиәThe nonce or next_nonce of the last answer."3b9f0c6e2a7d41f85e0b9a3c7d2e1f60"
firmwarestringFirmware version."4.2.1"
spec_versionstringVersion of the conformance specification."1.1"
log_head_hashstringHex SHA-256 of the newest line of the hash-chained local log."9f2b6a41c7d83e05a1b4c6d7e8f90123456789abcdef0123456789abcdef0123"
tsstring (date-time)The clock of the player."2026-10-14T09:05:00Z"
current_approvalstringNumber of the approval on screen now."TNB-2026-000123"
revocation_list_versionintegerVersion of the revocation list in use.42
edid_sha256stringSHA-256 of the EDID block of the display, in hex."4d7a1c0e9b3f58a26c4e1d07b9a3f5c8e2d60b14a7c93e5f8d1b2a6c0e4f7391"
screen_width_pxintegerWidth in pixels of the display as the player sees it. Send with screen_height_px or not at all; a pair that differs from the size on file for the structure for 24 hours is recorded as a screen size mismatch.1920
screen_height_pxintegerHeight in pixels of the display as the player sees it; see screen_width_px.1080
qr_keybooleanFalse until the player holds its presence key, true after.true

Мысал

{
  "seq": 1840,
  "state": "normal",
  "nonce": "3b9f0c6e2a7d41f85e0b9a3c7d2e1f60",
  "firmware": "4.2.1",
  "spec_version": "1.1",
  "ts": "2026-10-14T09:05:00Z",
  "current_approval": "TNB-2026-000123",
  "revocation_list_version": 42,
  "qr_key": true
}

Жауаптар

200 Accepted.

application/json

АтауыТүріМіндеттіСипаттамаМысал
server_timestring (date-time)иәThe clock of the platform."2026-10-14T09:05:00Z"
next_noncestringиәThe nonce for the next heartbeat."c4e7a19b0d2f63858a1e9c7b3d05f246"
commandsarray of stringиәSigned command envelopes addressed to this player, base64 of the DER form; at most the oldest few not yet acknowledged.
revocation_listobject | nullThe newest revocation list: its version and address.
revocation_list.versionintegerVersion of the list.42
revocation_list.urlstringAddress of the list."https://devices.tanba-ooh.kz/d/v1/revocations/42"
telemetry_cut_sintegerиәSeconds after which the player cuts a telemetry batch; it also cuts at 1000 events.300
qr_secretstring64 hex characters, present only while the player reports qr_key false."7b1d4e90a2c65f38e0b7a91d3c5f8264b0e9d1a7c3f5826e4b0d9a1c7e3f5824"

Мысал

{
  "server_time": "2026-10-14T09:05:00Z",
  "next_nonce": "c4e7a19b0d2f63858a1e9c7b3d05f246",
  "commands": [
    "MIIC0QYJKoZIhvcNAQcCoIICwjCCAr4CAQExDzANBglghkgBZQMEAgEFADA"
  ],
  "revocation_list": {
    "version": 42,
    "url": "https://devices.tanba-ooh.kz/d/v1/revocations/42"
  },
  "telemetry_cut_s": 300,
  "qr_secret": "7b1d4e90a2c65f38e0b7a91d3c5f8264b0e9d1a7c3f5826e4b0d9a1c7e3f5824"
}

Қателер

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

МәртебесіМағынасы
401The signature does not verify (bad_signature), or the device is unknown, revoked or expired (device_unauthorized).
409The seq is not above the last one (replay) or the nonce is not the one issued (nonce_mismatch; the problem carries next_nonce).
422Field errors (validation_failed).
defaultAn error as a problem document.

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

curl

curl -X POST 'https://devices.tanba-ooh.kz/d/v1/heartbeat' \
  -H 'X-Tanba-Signature: q3J2y0uO8e1m4n7pXc5Zk9d2Wv6Tg0hLsB-aRfN1YjE6uHxPzD3oVtKiC8wMbQe2nSgA4rUl7yFhJ9XcT0dZaw' \
  -H 'Content-Type: application/json' \
  -d '{
  "seq": 1840,
  "state": "normal",
  "nonce": "3b9f0c6e2a7d41f85e0b9a3c7d2e1f60",
  "firmware": "4.2.1",
  "spec_version": "1.1",
  "ts": "2026-10-14T09:05:00Z",
  "current_approval": "TNB-2026-000123",
  "revocation_list_version": 42,
  "qr_key": true
}'
Python
import requests

response = requests.post(
    "https://devices.tanba-ooh.kz/d/v1/heartbeat",
    headers={
        "X-Tanba-Signature": "q3J2y0uO8e1m4n7pXc5Zk9d2Wv6Tg0hLsB-aRfN1YjE6uHxPzD3oVtKiC8wMbQe2nSgA4rUl7yFhJ9XcT0dZaw",
        "Content-Type": "application/json",
    },
    json={
        "seq": 1840,
        "state": "normal",
        "nonce": "3b9f0c6e2a7d41f85e0b9a3c7d2e1f60",
        "firmware": "4.2.1",
        "spec_version": "1.1",
        "ts": "2026-10-14T09:05:00Z",
        "current_approval": "TNB-2026-000123",
        "revocation_list_version": 42,
        "qr_key": True,
    },
)
print(response.status_code, response.text)
Go
package main

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

func main() {
	body := strings.NewReader(`{
  "seq": 1840,
  "state": "normal",
  "nonce": "3b9f0c6e2a7d41f85e0b9a3c7d2e1f60",
  "firmware": "4.2.1",
  "spec_version": "1.1",
  "ts": "2026-10-14T09:05:00Z",
  "current_approval": "TNB-2026-000123",
  "revocation_list_version": 42,
  "qr_key": true
}`)
	req, err := http.NewRequest("POST", "https://devices.tanba-ooh.kz/d/v1/heartbeat", 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))
}