Hash-chained log segment, in answer to an audit_export command
POST /d/v1/logs
https://devices.tanba-ooh.kz/d/v1/logs
Анықтамалықтың техникалық мәтіні ағылшын тілінде жазылған.
Body (signed like telemetry): {command_id, entries: [{seq, at, kind, data, prev, hash}]}. The platform checks the chain, that it continues from the last export and that it contains the head hash last reported in a heartbeat. The answer holds the verdict; a segment that does not verify raises the city event device_log_tampered. One answer per command; a repeat returns the stored one.
Аутентификация
Құрылғының клиенттік сертификаты (өзара 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
| Атауы | Түрі | Міндетті | Сипаттама | Мысал |
|---|---|---|---|---|
command_id | string (uuid) | иә | The audit_export command this segment answers. | "0b1c3a52-6f0e-4c2e-9d3a-2f5d1e7a9c10" |
entries | array of object | иә | The log lines since the last export, in order. | |
entries[].seq | integer | иә | Number of the line. | 4021 |
entries[].at | string (date-time) | иә | When the line was written. | "2026-10-14T09:00:00Z" |
entries[].kind | string | иә | Kind of the line. | "play" |
entries[].data | object | Content of the line. | ||
entries[].prev | string | иә | Hash of the line before. | "9f2b6a41c7d83e05a1b4c6d7e8f90123456789abcdef0123456789abcdef0123" |
entries[].hash | string | иә | Hash of this line. | "4d7a1c0e9b3f58a26c4e1d07b9a3f5c8e2d60b14a7c93e5f8d1b2a6c0e4f7391" |
Мысал
{
"command_id": "0b1c3a52-6f0e-4c2e-9d3a-2f5d1e7a9c10",
"entries": [
{
"seq": 4021,
"at": "2026-10-14T09:00:00Z",
"kind": "play",
"data": {
"approval_number": "TNB-2026-000123"
},
"prev": "9f2b6a41c7d83e05a1b4c6d7e8f90123456789abcdef0123456789abcdef0123",
"hash": "4d7a1c0e9b3f58a26c4e1d07b9a3f5c8e2d60b14a7c93e5f8d1b2a6c0e4f7391"
}
]
}
Жауаптар
200 The verdict.
application/json
| Атауы | Түрі | Міндетті | Сипаттама | Мысал |
|---|---|---|---|---|
result | string | иә | What the checks found. ok, chain_broken, continuity_broken, head_mismatch | |
ok | boolean | иә | True when every check passed. | true |
entries | integer | иә | Number of lines in the segment. | 120 |
head_hash | string | иә | Hash of the last line of the segment. | "4d7a1c0e9b3f58a26c4e1d07b9a3f5c8e2d60b14a7c93e5f8d1b2a6c0e4f7391" |
duplicate | boolean | иә | True when this is a repeat and the stored verdict is returned. | false |
Мысал
{
"result": "ok",
"ok": true,
"entries": 120,
"head_hash": "4d7a1c0e9b3f58a26c4e1d07b9a3f5c8e2d60b14a7c93e5f8d1b2a6c0e4f7391",
"duplicate": false
}
Қателер
Қателер problem құжаты (application/problem+json) түрінде, тұрақты code өрісімен келеді.
| Мәртебесі | Мағынасы |
|---|---|
| 401 | The signature does not verify, or the device is unknown or revoked. |
| 413 | The segment is over 4 MB. |
| default | An error as a problem document. |
Код мысалдары
curl
curl -X POST 'https://devices.tanba-ooh.kz/d/v1/logs' \
-H 'X-Tanba-Signature: q3J2y0uO8e1m4n7pXc5Zk9d2Wv6Tg0hLsB-aRfN1YjE6uHxPzD3oVtKiC8wMbQe2nSgA4rUl7yFhJ9XcT0dZaw' \
-H 'Content-Type: application/json' \
-d '{
"command_id": "0b1c3a52-6f0e-4c2e-9d3a-2f5d1e7a9c10",
"entries": [
{
"seq": 4021,
"at": "2026-10-14T09:00:00Z",
"kind": "play",
"data": {
"approval_number": "TNB-2026-000123"
},
"prev": "9f2b6a41c7d83e05a1b4c6d7e8f90123456789abcdef0123456789abcdef0123",
"hash": "4d7a1c0e9b3f58a26c4e1d07b9a3f5c8e2d60b14a7c93e5f8d1b2a6c0e4f7391"
}
]
}'
Python
import requests
response = requests.post(
"https://devices.tanba-ooh.kz/d/v1/logs",
headers={
"X-Tanba-Signature": "q3J2y0uO8e1m4n7pXc5Zk9d2Wv6Tg0hLsB-aRfN1YjE6uHxPzD3oVtKiC8wMbQe2nSgA4rUl7yFhJ9XcT0dZaw",
"Content-Type": "application/json",
},
json={
"command_id": "0b1c3a52-6f0e-4c2e-9d3a-2f5d1e7a9c10",
"entries": [
{
"seq": 4021,
"at": "2026-10-14T09:00:00Z",
"kind": "play",
"data": {
"approval_number": "TNB-2026-000123",
},
"prev": "9f2b6a41c7d83e05a1b4c6d7e8f90123456789abcdef0123456789abcdef0123",
"hash": "4d7a1c0e9b3f58a26c4e1d07b9a3f5c8e2d60b14a7c93e5f8d1b2a6c0e4f7391",
},
],
},
)
print(response.status_code, response.text)
Go
package main
import (
"fmt"
"io"
"net/http"
"strings"
)
func main() {
body := strings.NewReader(`{
"command_id": "0b1c3a52-6f0e-4c2e-9d3a-2f5d1e7a9c10",
"entries": [
{
"seq": 4021,
"at": "2026-10-14T09:00:00Z",
"kind": "play",
"data": {
"approval_number": "TNB-2026-000123"
},
"prev": "9f2b6a41c7d83e05a1b4c6d7e8f90123456789abcdef0123456789abcdef0123",
"hash": "4d7a1c0e9b3f58a26c4e1d07b9a3f5c8e2d60b14a7c93e5f8d1b2a6c0e4f7391"
}
]
}`)
req, err := http.NewRequest("POST", "https://devices.tanba-ooh.kz/d/v1/logs", 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))
}