TAŇBA-OOH Разработчикам

Продление клиентского сертификата (текущий сертификат, подписанное тело с новым запросом сертификата)

POST /d/v1/renew

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

Вызов проходит как heartbeat: клиентский сертификат и подпись тела ключом устройства. Ключ в запросе должен быть новым; собственная подпись запроса сертификата доказывает владение им. Платформа отказывает отозванному устройству, сертификату, который не самый новый у устройства, плохому или прежнему ключу и сертификату, у которого осталось больше 60 дней. Ответ несет новый сертификат (тот же идентификатор устройства, один год) и old_valid_until. Прежний сертификат принимается еще 24 часа. Плеер продлевает сертификат сам, когда остается меньше 30 дней.

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

Клиентский сертификат устройства (взаимный TLS) и подпись тела ключом устройства.

Параметры

ИмяГдеТипОбязательныйОписаниеПример
X-Tanba-SignatureзаголовокstringдаПодпись ES256 точных байтов тела ключом устройства, сырые r||s (64 байта), base64url без заполнения.q3J2y0uO8e1m4n7pXc5Zk9d2Wv6Tg0hLsB-aRfN1YjE6uHxPzD3oVtKiC8wMbQe2nSgA4rUl7yFhJ9XcT0dZaw

Тело запроса

application/json

ИмяТипОбязательныйОписаниеПример
csrstringдаЗапрос сертификата для нового ключа, PEM или base64 от DER; ECDSA P-256."MIIBEjCBuAIBADAZMRcwFQYDVQQDEw5leGFtcGxlLXBsYXllcjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABA"
forcebooleanНе применять правило 60 дней; только после разрешения администратора на 24 часа.false

Пример

{
  "csr": "MIIBEjCBuAIBADAZMRcwFQYDVQQDEw5leGFtcGxlLXBsYXllcjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABA",
  "force": false
}

Ответы

200 Новый сертификат выдан; прежний принимается еще 24 часа.

application/json

ИмяТипОбязательныйОписаниеПример
device_idstring (uuid)даТот же идентификатор устройства."5c0e8f3a-71d2-4b6e-9a14-d8e2f7b30c59"
certificate_pemstringдаНовый сертификат в формате PEM."-----BEGIN CERTIFICATE-----\nMIIB...\n-----END CERTIFICATE-----\n"
chain_pemstringдаЦепочка до центра сертификации устройств."-----BEGIN CERTIFICATE-----\nMIIB...\n-----END CERTIFICATE-----\n"
not_afterstring (date-time)даКогда истекает новый сертификат."2028-10-14T09:00:00Z"
old_valid_untilstring (date-time)даДо какого момента еще принимается прежний сертификат."2026-10-15T09:00:00Z"

Пример

{
  "device_id": "5c0e8f3a-71d2-4b6e-9a14-d8e2f7b30c59",
  "certificate_pem": "-----BEGIN CERTIFICATE-----\nMIIB...\n-----END CERTIFICATE-----\n",
  "chain_pem": "-----BEGIN CERTIFICATE-----\nMIIB...\n-----END CERTIFICATE-----\n",
  "not_after": "2028-10-14T09:00:00Z",
  "old_valid_until": "2026-10-15T09:00:00Z"
}

Ошибки

Ошибки приходят документом problem (application/problem+json) со стабильным полем code.

СтатусЗначение
401Подпись не проходит либо устройство неизвестно или отозвано.
409В продлении отказано (renewal_refused): срок не пришел, сертификат не самый новый или ключ уже занят.
422Ошибки полей (validation_failed).
defaultОшибка в виде документа problem.

Примеры кода

curl

curl -X POST 'https://devices.tanba-ooh.kz/d/v1/renew' \
  -H 'X-Tanba-Signature: q3J2y0uO8e1m4n7pXc5Zk9d2Wv6Tg0hLsB-aRfN1YjE6uHxPzD3oVtKiC8wMbQe2nSgA4rUl7yFhJ9XcT0dZaw' \
  -H 'Content-Type: application/json' \
  -d '{
  "csr": "MIIBEjCBuAIBADAZMRcwFQYDVQQDEw5leGFtcGxlLXBsYXllcjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABA",
  "force": false
}'
Python
import requests

response = requests.post(
    "https://devices.tanba-ooh.kz/d/v1/renew",
    headers={
        "X-Tanba-Signature": "q3J2y0uO8e1m4n7pXc5Zk9d2Wv6Tg0hLsB-aRfN1YjE6uHxPzD3oVtKiC8wMbQe2nSgA4rUl7yFhJ9XcT0dZaw",
        "Content-Type": "application/json",
    },
    json={
        "csr": "MIIBEjCBuAIBADAZMRcwFQYDVQQDEw5leGFtcGxlLXBsYXllcjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABA",
        "force": False,
    },
)
print(response.status_code, response.text)
Go
package main

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

func main() {
	body := strings.NewReader(`{
  "csr": "MIIBEjCBuAIBADAZMRcwFQYDVQQDEw5leGFtcGxlLXBsYXllcjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABA",
  "force": false
}`)
	req, err := http.NewRequest("POST", "https://devices.tanba-ooh.kz/d/v1/renew", 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))
}