Základní URL
https://politran.cz/api/v1
Všechny požadavky posílejte s hlavičkami Authorization: Bearer <token> a Accept: application/json. API odpovídá vždy JSONem (i na chyby). Datum YYYY-MM-DD, čas ISO 8601, částky jako řetězec "12345.67".
Instrukce pro AI a integrátory (strojově čitelné)
Kanonická specifikace pro generování klientského kódu (Cursor, Copilot, ChatGPT, Claude …):
→ politran-api-ai-instructions.md — kontrakt API (endpointy, chyby, povinné chování klienta)
→ politran-api-agent-brief-advertiser.md — zadání pro AI agenta: napojení nástroje agentury / zadavatele
→ politran-api-agent-brief-publisher.md — zadání pro AI agenta: napojení interního systému vydavatele
→ openapi-v1.yaml — OpenAPI 3.1 pro generátory klientů · → příkladové skripty (curl, PHP, Python, Node, přijímače webhooků)
Postup: asistentovi vložte kontrakt + příslušný brief a řekněte: „Naprogramuj napojení podle těchto dvou dokumentů.“
Obsah
1. Přístup a tokeny
- Žádost o přístup: hlavní osoba organizace klikne v aplikaci na Nastavení → API tokeny → „Požádat o přístup k API“. SPIR přístup schválí a nastaví limit počtu požadavků.
- Token: po schválení si člen organizace vytvoří token tamtéž. Token je vázaný na jednu organizaci, jednu stranu (
advertiser/publisher), zvolené rozsahy a platnost. Zobrazí se jen jednou. - Viditelnost: uživatelský token vidí to, co jeho tvůrce (člen zadavatele jen svá oznámení, hlavní osoba všechna). Organizační token (jen hlavní osoba) vidí celou organizaci a přežije odchod tvůrce — použijte ho pro trvalé integrace.
Rozsahy tokenu
| Rozsah | Umožňuje |
|---|---|
notices:read | čtení oznámení, seznam vydavatelů, metadata polí, historie, poznámky, QR |
notices:write | založení, změny, smazání konceptu, odeslání vydavateli, přidání poznámky |
creatives:read / creatives:write | stažení / nahrání a mazání kreativ |
exports | hromadné exporty (xlsx / csv / json, ZIP kreativ) |
webhooks:manage | registrace a správa webhooků |
Ověření tokenu
GET /v1/me — vrátí uživatele, organizaci, stranu, rozsahy, expiraci a limity.
2. Chyby a limity
{
"error": {
"code": "VALIDATION_FAILED",
"message": "The request contains invalid fields.",
"details": { "publisher_org_id": ["…"] },
"request_id": "8f0c4d5e-…"
}
}
| HTTP | code | Kdy |
|---|---|---|
| 401 | UNAUTHENTICATED, TOKEN_LEGACY | chybí / neplatný / expirovaný token; token starého formátu |
| 403 | INSUFFICIENT_SCOPE, TOKEN_SIDE_MISMATCH, API_ACCESS_NOT_APPROVED, ORGANISATION_FROZEN, … | kontext tokenu / organizace |
| 404 | NOT_FOUND | neexistuje nebo není v rozsahu tokenu (cizí organizace = vždy 404) |
| 409 | INVALID_STATE | akce není z aktuálního stavu možná (details.current_status) |
| 409 | IDEMPOTENCY_CONFLICT | stejný Idempotency-Key, jiný payload |
| 412 | — | If-Match nesedí (oznámení se mezitím změnilo) |
| 422 | VALIDATION_FAILED, CREATIVE_REJECTED | chybná pole, chybějící povinná pole před odesláním, nepodporovaný soubor |
| 429 | RATE_LIMITED | limit organizace nebo tokenu; viz Retry-After |
Limity a hlavičky
- Limit per organizace (nastavuje SPIR, výchozí 60/min a 5 000/den) a per token 120/min. Hlavičky
X-RateLimit-Limit,X-RateLimit-Remaining, při 429Retry-After. - Požadavky bez Bearer tokenu: 20/min/IP. Po 20 odpovědích
401z jedné IP během 10 minut je IP na 15 minut blokovaná (429 IP_BLOCKED,Retry-After) — ověřte token dřív, než klienta pustíte do smyčky. X-Request-Idv každé odpovědi — uveďte ho při hlášení problému.Idempotency-Key(8–128 znaků) uPOST: bezpečné opakování požadavku bez duplicit; opakovaná odpověď máIdempotency-Replayed: true.If-Match: "<updated_at>"uPATCH: ochrana proti přepsání cizí změny.
3. Endpointy — strana zadavatele
Cesty relativně k https://politran.cz/api/v1. Token se stranou advertiser.
GET /publishers
Aktivní vydavatelské organizace (id, name, ico, can_publish). Filtry q, ico. Hodnota id jde do publisher_org_id.
GET /notice-fields
Metadata polí oznámení: label, tooltip, type, enum, required_before_send, condition, group. Povinnost polí nastavuje SPIR v administraci — načítejte ji odsud, ne z dokumentace.
GET /notices
Seznam oznámení organizace. Filtry: status[], publisher_org_id, short_code, bulk_group_id, campaign_from, campaign_to, updated_since (synchronizace), q, sort (-created_at, updated_at, …), page, per_page (≤ 100).
GET /notices/{id} · /notices/by-code/{short_code}
Detail včetně subjektů (parties), polí (fields), kreativ, public_url, editable a allowed_actions.
POST /notices
Založí koncept a vrátí detail (201). S "send": true ho ve stejné transakci odešle vydavateli — při chybějících povinných polích vrátí 422 a nic neuloží. Doporučeno posílat Idempotency-Key.
PATCH /notices/{id}
Částečná změna (jen zaslané klíče). Jen ve stavech, kdy zadavatel edituje; jinak 409 INVALID_STATE.
DELETE /notices/{id}
Smaže koncept (DRAFT) → 204.
POST /notices/{id}/send-to-publisher
Ověří povinná pole (422 s details) a přepne do SENT_TO_PUBLISHER. Z PUBLISHED_AND_RETURNED_FOR_REWORK se vrací rovnou do PUBLISHED.
Kreativy
GET /notices/{id}/creatives · POST /notices/{id}/creatives (multipart/form-data, pole file, max 10 MB; JPEG/PNG/GIF/WebP, MP3/WAV/OGG, MP4/WebM, PDF) · GET /notices/{id}/creatives/{cid}/download · DELETE /notices/{id}/creatives/{cid}. Kreativy se nikdy nezobrazují veřejně.
Ostatní
GET /notices/{id}/qr.svg (QR kód veřejné stránky) · GET /notices/{id}/history (historie změn od Publikováno) · GET / POST /notices/{id}/messages (poznámky k předání zadavatel ↔ vydavatel).
4. Tělo oznámení
- Klíče = názvy polí z
GET /notice-fields(publisher_org_id,campaign_start_date,funding_source,is_personalised,election_relation_type,controlling_entity,payer_info, …). - Subjekt Zadavatel (
advertiser_*) se při založení bere z organizace; zaslané hodnoty se ignorují (meta.warnings). - Zkratky:
controlling_same_as_advertiser: true,payer_source: "advertiser" | "controlling" | "other". election_relation_type:none|elections_referendum|legislative_process— pole druhé větve se vyprázdní.- Pole cílení (
targeting_*,distribution_*,views_count, …) jsou povinná jen přiis_personalised: true.
documentation/API.md).
5. Stavy oznámení
status | status_label | Zadavatel edituje | Veřejná stránka |
|---|---|---|---|
DRAFT | Koncept | ano | ne |
SENT_TO_PUBLISHER | Odesláno vydavateli | ne | ano (s upozorněním) |
RETURNED_FOR_REWORK | Vráceno k přepracování | ano | ne |
PUBLISHED | Publikováno | ne | ano |
PUBLISHED_AND_RETURNED_FOR_REWORK | Publikováno a vráceno k přepracování | ano (změny hned veřejně) | ano |
SENT_TO_EU | Odesláno EU | ne | ano |
Klient nemusí znát stavový automat: detail vrací editable a allowed_actions.
6. Příklady (curl)
TOKEN="…"; BASE="https://politran.cz/api/v1"
# ověření tokenu
curl -s -H "Authorization: Bearer $TOKEN" -H "Accept: application/json" "$BASE/me"
# nový koncept (idempotentně)
curl -s -H "Authorization: Bearer $TOKEN" -H "Accept: application/json" \
-H "Content-Type: application/json" -H "Idempotency-Key: crm-campaign-889-v1" \
-d '{"publisher_org_id": 2, "campaign_start_date": "2026-10-01", "campaign_end_date": "2026-10-31",
"controlling_same_as_advertiser": true, "payer_source": "advertiser",
"funding_source": "Dary členů", "complaint_mechanism": "https://strana.cz/stiznosti"}' \
"$BASE/notices"
# kreativa
curl -s -H "Authorization: Bearer $TOKEN" -H "Accept: application/json" \
-F "file=@banner.png" "$BASE/notices/42/creatives"
# odeslat vydavateli
curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "Accept: application/json" \
"$BASE/notices/42/send-to-publisher"
7. Endpointy — strana vydavatele
Prefix https://politran.cz/api/v1/publisher, token se stranou publisher. Uživatelský token vidí oznámení podle stejného směrování jako web (přiřazení zadavatelů, catch-all, Cc, eskalace; hlavní osoba vše), organizační token vidí všechna přijatá oznámení. Vydavatel vidí stavy SENT_TO_PUBLISHER, PUBLISHED, PUBLISHED_AND_RETURNED_FOR_REWORK, SENT_TO_EU.
Úroveň 1 — MVP
GET /publisher/advertisers — zadavatelé, od kterých máte oznámení (pro filtr advertiser_org_id).
GET /publisher/notices — filtry status[], advertiser_org_id, advertiser_ico, short_code, campaign_from/to, sent_from/to, updated_since, q, sort (-sent_to_publisher_at výchozí, updated_at, published_at, …).
GET /publisher/notices/{id} · /by-code/{short_code} — detail s allowed_actions (publish, reject, return_for_rework, update, creatives_write, add_message).
POST /publisher/notices/{id}/publish — SENT_TO_PUBLISHER → PUBLISHED. Bez vyplněné „URL pro hlášení nesprávností“ u vydavatele vrací 409 PUBLISHER_REPORT_URL_MISSING.
POST /publisher/notices/{id}/reject — odmítnutí, oznámení se vrátí zadavateli do konceptu (a z vašeho seznamu zmizí).
POST /publisher/notices/{id}/return-for-rework — tělo { "message": "…" } (povinné). Publikované oznámení zůstává veřejné (PUBLISHED_AND_RETURNED_FOR_REWORK).
Úroveň 2 — rozšíření
PATCH /publisher/notices/{id} — jen ve stavech SENT_TO_PUBLISHER / PUBLISHED. Měnit lze termíny, ceny, dosah, distribuci, cílení, pozastavení; subjekty a přiřazení vydavatele jsou zamčené (422 s výčtem). Změna pošle zadavateli e-mail „upraveno vydavatelem“.
Kreativy: GET / POST /publisher/notices/{id}/creatives, GET …/{cid}/download, DELETE …/{cid}.
Exporty: viz sekce 8.
Úroveň 3 — pokročilé
GET /publisher/notices/{id}/history · GET / POST /publisher/notices/{id}/messages · webhooky (sekce 9). Založení oznámení vydavatelem zatím není k dispozici (čeká na produktové rozhodnutí SPIR).
8. Exporty (obě strany, rozsah exports)
POST /exports (zadavatel) nebo /publisher/exports (vydavatel) s tělem { "file_format": "xlsx|csv|json", "include_creatives": bool, "campaign_from"?, "campaign_to"? } vrátí 202 a id. Export se generuje na pozadí: ptejte se GET /exports/{id}, po status: "completed" stáhněte tabular_url a případně creatives_url (ZIP). Soubory expirují po 60 dnech. Posílejte Idempotency-Key, ať se export nespustí dvakrát.
9. Webhooky (obě strany, rozsah webhooks:manage)
Registrace: POST /webhooks (zadavatel) / /publisher/webhooks (vydavatel) s { "name", "url" (https), "events": ["notice.published", …] } nebo ["*"]. Odpověď obsahuje secret — zobrazí se jen jednou. Dále GET/PATCH/DELETE /webhooks/{id}, POST …/rotate-secret, POST …/test (synchronní ping), GET …/deliveries. Přehled a znovuzapnutí také v aplikaci: Nastavení → API webhooky (hlavní osoba).
Události
| Událost | Kdy |
|---|---|
notice.sent_to_publisher / notice.resubmitted | zadavatel odeslal / znovu zaslal upravené publikované oznámení |
notice.updated | uložení polí (web i API, ne autosave); data.changed_fields, data.edited_by_publisher |
notice.published / notice.returned_for_rework / notice.rejected / notice.sent_to_eu | rozhodnutí vydavatele (u vrácení data.message) |
notice.creative_added / notice.creative_removed / notice.message_added / notice.deleted | kreativy, poznámky, smazání |
Doručení a podpis
POST {vaše URL}
Content-Type: application/json
X-Politran-Event: notice.published
X-Politran-Event-Id: 0c1f… # dedup
X-Politran-Signature: t=1758800000,v1=<hex> # v1 = HMAC-SHA256(secret, t + "." + tělo)
{ "id": "0c1f…", "event": "notice.published", "created_at": "…", "api_version": "v1",
"actor": { "channel": "api", "side": "publisher", "user_id": 5 },
"data": { "notice": { "id": 42, "short_code": "26-…", "status": "PUBLISHED", "public_url": "…", "updated_at": "…" }, "previous_status": "SENT_TO_PUBLISHER" } }
- Ověřte podpis konstantním porovnáním a odmítněte
tstarší než 5 minut. Podpis počítejte nad syrovým tělem. - Odpovězte
2xxdo 5 s; zpracování odložte. Bez přesměrování. - Neúspěch se opakuje po 1, 5, 30, 120 a 720 minutách; stejná událost může přijít vícekrát — deduplikujte podle
id. - Po 30 neúspěšných pokusech v řadě se endpoint vypne a hlavní osoba dostane e-mail. Po opravě zapněte (
PATCH is_active: truenebo v účtu) a dorovnejte přesupdated_since. - Jen
https://na veřejný host (privátní adresy jsou odmítnuty).
Hotové přijímače v PHP a Node: příkladové skripty.
10. Jak testovat
Pro vývoj integrace požádejte SPIR o testovací organizaci (zadavatel i vydavatel) přímo na produkci se schváleným přístupem k API — samostatný sandbox SPIR neprovozuje. Testovací oznámení posílejte jen testovacímu vydavateli SPIR, používejte zjevně testovací texty a po testu je smažte (koncept maže zadavatel, odeslané oznámení vydavatel odmítne). Pozor: oznámení má od odeslání vydavateli veřejnou stránku /n/{code}, proto nikdy neposílejte testovací oznámení reálným vydavatelům.