REST API v1

Automatické zadávání oznámení politické reklamy z kampaňových systémů zadavatelů a agentur a jejich příjem, schvalování a synchronizace na straně vydavatelů — včetně exportů a webhooků.

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
  2. Chyby a limity
  3. Endpointy — strana zadavatele
  4. Tělo oznámení
  5. Stavy oznámení
  6. Příklady (curl)
  7. Endpointy — strana vydavatele
  8. Exporty
  9. Webhooky
  10. Jak testovat

1. Přístup a tokeny

  1. Žá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ů.
  2. 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.
  3. 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.
Token nelze získat heslem přes API. Neukládejte ho do gitu; použijte proměnnou prostředí nebo správce tajemství.

Rozsahy tokenu

RozsahUmožňuje
notices:readčtení oznámení, seznam vydavatelů, metadata polí, historie, poznámky, QR
notices:writezaložení, změny, smazání konceptu, odeslání vydavateli, přidání poznámky
creatives:read / creatives:writestažení / nahrání a mazání kreativ
exportshromadné exporty (xlsx / csv / json, ZIP kreativ)
webhooks:manageregistrace 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-…"
  }
}
HTTPcodeKdy
401UNAUTHENTICATED, TOKEN_LEGACYchybí / neplatný / expirovaný token; token starého formátu
403INSUFFICIENT_SCOPE, TOKEN_SIDE_MISMATCH, API_ACCESS_NOT_APPROVED, ORGANISATION_FROZEN, …kontext tokenu / organizace
404NOT_FOUNDneexistuje nebo není v rozsahu tokenu (cizí organizace = vždy 404)
409INVALID_STATEakce není z aktuálního stavu možná (details.current_status)
409IDEMPOTENCY_CONFLICTstejný Idempotency-Key, jiný payload
412—If-Match nesedí (oznámení se mezitím změnilo)
422VALIDATION_FAILED, CREATIVE_REJECTEDchybná pole, chybějící povinná pole před odesláním, nepodporovaný soubor
429RATE_LIMITEDlimit organizace nebo tokenu; viz Retry-After

Limity a hlavičky

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í

Kompletní popis odpovědi a příklady najdete ve strojově čitelných instrukcích výše a v repozitáři (documentation/API.md).

5. Stavy oznámení

statusstatus_labelZadavatel editujeVeřejná stránka
DRAFTKonceptanone
SENT_TO_PUBLISHEROdesláno vydavatelineano (s upozorněním)
RETURNED_FOR_REWORKVráceno k přepracováníanone
PUBLISHEDPublikovánoneano
PUBLISHED_AND_RETURNED_FOR_REWORKPublikováno a vráceno k přepracováníano (změny hned veřejně)ano
SENT_TO_EUOdesláno EUneano

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álostKdy
notice.sent_to_publisher / notice.resubmittedzadavatel odeslal / znovu zaslal upravené publikované oznámení
notice.updatedulož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_eurozhodnutí vydavatele (u vrácení data.message)
notice.creative_added / notice.creative_removed / notice.message_added / notice.deletedkreativy, 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" } }

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.