Renew the client certificate (current certificate, signed body with a new certificate request)
POST /d/v1/renew
https://devices.tanba-ooh.kz/d/v1/renew
Authenticated like a heartbeat: the client certificate and a signature of the body by the device key. The key in the request must be new; the request's own signature proves possession of it. The platform refuses a revoked device, a certificate that is not the newest of the device, a bad or unchanged key, and a certificate with more than 60 days left. The answer carries the new certificate (same device id, one year) and old_valid_until; the old certificate stays accepted until then, 24 hours. A player renews on its own when less than 30 days are left.
Authentication
Client certificate of the device (mutual TLS) and a signature of the body by the device key.
Parameters
| Name | In | Type | Required | Description | Example |
|---|---|---|---|---|---|
X-Tanba-Signature | header | string | yes | ES256 signature of the exact bytes of the body by the device key, raw r||s (64 bytes), base64url without padding. | q3J2y0uO8e1m4n7pXc5Zk9d2Wv6Tg0hLsB-aRfN1YjE6uHxPzD3oVtKiC8wMbQe2nSgA4rUl7yFhJ9XcT0dZaw |
Request body
application/json
| Name | Type | Required | Description | Example |
|---|---|---|---|---|
csr | string | yes | Certificate request for the new key, PEM or base64 of the DER form; ECDSA P-256. | "MIIBEjCBuAIBADAZMRcwFQYDVQQDEw5leGFtcGxlLXBsYXllcjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABA" |
force | boolean | Skip the 60-day rule; only after an administrator has allowed it for 24 hours. | false |
Example
{
"csr": "MIIBEjCBuAIBADAZMRcwFQYDVQQDEw5leGFtcGxlLXBsYXllcjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABA",
"force": false
}
Responses
200 New certificate issued; the old one stays accepted for 24 hours.
application/json
| Name | Type | Required | Description | Example |
|---|---|---|---|---|
device_id | string (uuid) | yes | The same device identifier. | "5c0e8f3a-71d2-4b6e-9a14-d8e2f7b30c59" |
certificate_pem | string | yes | The new certificate in PEM form. | "-----BEGIN CERTIFICATE-----\nMIIB...\n-----END CERTIFICATE-----\n" |
chain_pem | string | yes | The chain up to the device certificate authority. | "-----BEGIN CERTIFICATE-----\nMIIB...\n-----END CERTIFICATE-----\n" |
not_after | string (date-time) | yes | When the new certificate expires. | "2028-10-14T09:00:00Z" |
old_valid_until | string (date-time) | yes | Until when the old certificate is still accepted. | "2026-10-15T09:00:00Z" |
Example
{
"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"
}
Errors
Errors are problem documents (application/problem+json) with a stable code.
| Status | Meaning |
|---|---|
| 401 | The signature does not verify, or the device is unknown or revoked. |
| 409 | Renewal refused (renewal_refused): not due, not the newest certificate, or the key is already held. |
| 422 | Field errors (validation_failed). |
| default | An error as a problem document. |
Code samples
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))
}