TAŇBA-OOH Developers

Submit the plays an exchange says it served

POST /api/v1/plays/claims

https://app.tanba-ooh.kz/api/v1/plays/claims

Scope write:plays. Adtech organisations only (the administrator of an ad platform); a console session works too. The body is JSON, an object with a rows array, at most 5 MB or 50 000 rows.

A claim is what the platform says it served. The registry is not in the bid path and holds no order: it only checks the claim against the plays the players proved (see GET /api/v1/plays/claims/verification). Claims are kept apart from the plays of the players and are never billed.

Every row names a structure (structure_id or structure_code, not both), an approval (approval_number) or a creative file (creative_sha256, not both), a whole hour and the number of plays in that hour. deal_id and seller_account_id (your account on the exchange, as in source.schain) are optional. A row is accepted only when the operator of the structure has authorised your organisation, and that account when named, to sell it on that day. A row is stored under (organisation, structure, approval, hour, deal); sending it again replaces the earlier value. Rows are judged one by one and the accepted rows are stored even when others are refused. Reasons:

ReasonMeaning
structure_missing, structure_ambiguous, structure_not_foundNo structure, both forms given, or no such structure.
approval_missing, approval_ambiguousNeither an approval nor a file, or both.
approval_not_found, approval_not_for_structureNo signed approval with that number, or it does not cover the structure.
creative_not_found, creative_ambiguousNo approval with that file for the structure in that hour, or several (name the number).
seller_not_authorisedThe operator has not authorised your organisation (and account) to sell this structure on that day.
hour_invalid, hour_not_whole, hour_in_future, hour_too_oldNot an ISO 8601 hour, not on the hour, not begun, or begun more than 62 days ago.
plays_invalidNot a whole number of at least 0.
deal_id_invalid, seller_account_invalidCharacters or length not allowed.
duplicate_rowThe same row appeared earlier in this batch.

Authentication

API key in the Authorization header, or a console session.

Scope: write:plays

Request body

application/json

NameTypeRequiredDescriptionExample
rowsarray of objectyesThe claim lines, one for each structure, approval, hour and deal.
rows[].structure_idstring (uuid)Identifier of the structure; give this or structure_code, not both."b7e4a2c9-0d1f-4a68-8e35-91c7f2d40b16"
rows[].structure_codestringCode of the structure; give this or structure_id, not both."ALM-0012"
rows[].approval_numberstringNumber of the approval; give this or creative_sha256."TNB-2026-000123"
rows[].creative_sha256stringSHA-256 of the media file; the approval is the one that covers the structure in that hour."2419fe2d001c9185b3b903283894a5d955f45a57d1eaf1c6b787759650f00a17"
rows[].hourstringISO 8601, on the hour; one without an offset is Asia/Almaty time."2026-10-06T14:00:00+05:00"
rows[].playsintegerNumber of plays the platform says it served in that hour.118
rows[].deal_idstringDeal the plays ran under, characters A-Za-z0-9._:-; part of the identity of the line."deal-4411"
rows[].seller_account_idstringYour account on the exchange; it must be one the operator authorised."acc-77"

Example

{
  "rows": [
    {
      "structure_code": "ALM-0012",
      "approval_number": "TNB-2026-000123",
      "hour": "2026-10-06T14:00:00+05:00",
      "plays": 118,
      "deal_id": "deal-4411"
    },
    {
      "structure_code": "ALM-0012",
      "creative_sha256": "2419fe2d001c9185b3b903283894a5d955f45a57d1eaf1c6b787759650f00a17",
      "hour": "2026-10-06T15:00:00+05:00",
      "plays": 120
    }
  ]
}

Responses

200 The batch was read; see the rows for what was stored.

application/json

NameTypeRequiredDescriptionExample
batch_idstring (uuid)yesIdentifier of the batch."0b1c3a52-6f0e-4c2e-9d3a-2f5d1e7a9c10"
receivedintegeryesNumber of rows read from the body.2
acceptedintegeryesNumber of rows stored.1
refusedintegeryesNumber of rows refused.1
rowsarray of objectyesOne entry for each row, in the order of the body.
rows[].rowintegeryesPosition in the body, from 1.1
rows[].statusstringyesWhether the row was stored. accepted, refused
rows[].reasonstringPresent on refused rows."seller_not_authorised"

Example

{
  "batch_id": "0b1c3a52-6f0e-4c2e-9d3a-2f5d1e7a9c10",
  "received": 2,
  "accepted": 1,
  "refused": 1,
  "rows": [
    {
      "row": 1,
      "status": "accepted"
    },
    {
      "row": 2,
      "status": "refused",
      "reason": "seller_not_authorised"
    }
  ]
}

Errors

Errors are problem documents (application/problem+json) with a stable code.

StatusMeaning
400An error as a problem document.
401No valid session or API key (unauthenticated, session_expired, invalid_api_key).
403The role or the key's scope does not allow this (forbidden, key_scope_denied, key_route_denied).
413An error as a problem document.
422Field errors (validation_failed).
defaultAn error as a problem document.

Code samples

curl

curl -X POST 'https://app.tanba-ooh.kz/api/v1/plays/claims' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
  "rows": [
    {
      "structure_code": "ALM-0012",
      "approval_number": "TNB-2026-000123",
      "hour": "2026-10-06T14:00:00+05:00",
      "plays": 118,
      "deal_id": "deal-4411"
    },
    {
      "structure_code": "ALM-0012",
      "creative_sha256": "2419fe2d001c9185b3b903283894a5d955f45a57d1eaf1c6b787759650f00a17",
      "hour": "2026-10-06T15:00:00+05:00",
      "plays": 120
    }
  ]
}'
Python
import requests

response = requests.post(
    "https://app.tanba-ooh.kz/api/v1/plays/claims",
    headers={
        "Authorization": "Bearer YOUR_API_KEY",
        "Content-Type": "application/json",
    },
    json={
        "rows": [
            {
                "structure_code": "ALM-0012",
                "approval_number": "TNB-2026-000123",
                "hour": "2026-10-06T14:00:00+05:00",
                "plays": 118,
                "deal_id": "deal-4411",
            },
            {
                "structure_code": "ALM-0012",
                "creative_sha256": "2419fe2d001c9185b3b903283894a5d955f45a57d1eaf1c6b787759650f00a17",
                "hour": "2026-10-06T15:00:00+05:00",
                "plays": 120,
            },
        ],
    },
)
print(response.status_code, response.text)
Go
package main

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

func main() {
	body := strings.NewReader(`{
  "rows": [
    {
      "structure_code": "ALM-0012",
      "approval_number": "TNB-2026-000123",
      "hour": "2026-10-06T14:00:00+05:00",
      "plays": 118,
      "deal_id": "deal-4411"
    },
    {
      "structure_code": "ALM-0012",
      "creative_sha256": "2419fe2d001c9185b3b903283894a5d955f45a57d1eaf1c6b787759650f00a17",
      "hour": "2026-10-06T15:00:00+05:00",
      "plays": 120
    }
  ]
}`)
	req, err := http.NewRequest("POST", "https://app.tanba-ooh.kz/api/v1/plays/claims", body)
	if err != nil {
		panic(err)
	}
	req.Header.Set("Authorization", "Bearer YOUR_API_KEY")
	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))
}