Nové

Hlídač zákaziek

Automatické doručovanie nových zákaziek podľa vašich filtrov — kraj, industry tagy, kľúčové slová, rozsah hodnôt. Pull API + email digest + webhook.

Koncept

Štyri pojmy, s ktorými sa stretnete vo všetkých endpointoch:

  • Tenderjedna konkrétna zákazka z portálu (NEN, VVZ, E-ZAK, TenderArena, atď.). Identifikovaná číselným ID.
  • Filteruložená sada kritérií (región, odbor, kľúčové slová, hodnota). Keď nová zákazka spĺňa kritériá, vznikne match.
  • Matchnová zákazka, ktorá spadla do vášho filtra. Reprezentuje väzbu (tender × filter) + timestamp + váš stav (sledované / skryté / prečítané).
  • Taxonomyčíselníky regiónov (NUTS), odborov (industry tags) a CPV kódov, podľa ktorých klasifikujeme zákazky.

Rýchly štart

Filtre možno nastaviť dvoma spôsobmi — výsledok je rovnaký, filter možno kedykoľvek upraviť ľubovoľnou cestou.

curl -H "X-Api-Key: mrw_live_…" \
  "https://veritra.io/api/v2/leads/tenders/search?qText=rekonstrukce%20mostu&limit=5"

Hore je ad-hoc vyhľadávanie. Ak chcete kontinuálny monitoring (nové zákazky zodpovedajúce vašim kritériám), použite filtre + webhook/email digest — pozri ďalej.

Odporúčanie: Pre filtrovanie vždy preferujte industryTags pred categories (CPV). Naše industryTags sú zjednotením LLM klasifikácie tendru a CPV mapovania — odolné voči chybnému CPV. Zadávatelia v CZ často priraďujú príliš všeobecné alebo nesúvisiace CPV kódy (typicky generické '45000000-0' pri stavebných zákazkách namiesto konkrétneho prefixu), takže čisto CPV filter časť relevantných zákaziek minie.

Vyhľadávanie zákaziek (ad-hoc)

Na jednorazové vyhľadanie zákaziek naprieč všetkými aktívnymi tendrami. Nepoužíva uložený filter, parametre idú priamo v query stringu.

GET/api/v2/leads/tenders/search

Query parametre

ParameterTypPopis
qText*stringHľadaný text (full-text v title + description).
regionsstringNUTS leaf codes CSV (CZ010,CZ020).
cpvPrefixesstringCPV prefixy CSV (45,452).
industryTagsstringIndustry tag IDs CSV (con_buildings,it_development).
minValuenumberMin. odhadovaná hodnota (Kč). Tendre bez hodnoty prechádzajú.
maxValuenumberMax. odhadovaná hodnota (Kč). Tendre bez hodnoty prechádzajú.
deadlineFromstring (YYYY-MM-DD)Lehota podania >= YYYY-MM-DD.
deadlineTostring (YYYY-MM-DD)Lehota podania <= YYYY-MM-DD.
sort"newest" | "deadline" | "value"Zoradenie: newest (predvolené), deadline (rastúce), value (klesajúce).
limitnumberPočet výsledkov, max 1000 (predvolené: 50). Pre >1k použite nextCursor pagination alebo /matches/export.
cursorstringKeyset cursor z predchádzajúcej stránky (pagination.nextCursor).

Príklad

curl -H "X-Api-Key: mrw_live_…" \
  "https://veritra.io/api/v2/leads/tenders/search?qText=rekonstrukce&regions=CZ010,CZ020&minValue=500000&limit=10"
GET/api/v2/leads/tenders/:id

Response: objekt data s poľami id, title, description, estimatedValue, deadlineAt, contractingAuthority, documents[], starred, excluded.

POST/api/v2/leads/tenders/:id/email

Body môže obsahovať recipientEmail pre forward na iný email. Predvolene user.email.

GET/api/v2/leads/documents/preview

Query: ?url=:original&kind=docx|xlsx. Allowlist hostov: NEN, E-ZAK, Tender Arena, Gemin, ProfilZadavatele.

GET /api/v2/leads/documents/preview?url=https://nen.nipez.cz/…/Vyzva.docx&kind=docx
→ 302 Location: https://rwx-storage…/Tendero/doc-cache-v8/<hash>.html (signed, TTL 10 min)

Catalog & autocomplete (public, žiadny kľúč)

Pre UI dropdowny a tvorbu filtrov. Cache 1h server-side. Verejné, žiadna auth nie je potrebná.

GET/api/v2/leads/zadavatele?q=praha
GET/api/v2/leads/countries
GET/api/v2/leads/regions?country=CZ
GET/api/v2/leads/regions/catalog?country=CZ&locale=cs
GET/api/v2/leads/taxonomy/industry
GET/api/v2/leads/taxonomy/cpv
Search vs. Filter: Search je jednorazový dotaz, vráti aktuálny snapshot zákaziek. Filter (nižšie) predstavuje uložené kritériá — server vás priebežne notifikuje o nových zákazkách webhookom alebo email digestom.

Čo je možné filtrovať a povolené hodnoty

Filter sa skladá z polí uvedených nižšie. Všetky sú voliteľné okrem `name`. Prázdne pole = bez obmedzenia v danej dimenzii.

regions string[]

NUTS regióny pre danú krajinu (?country=CZ) s labels per locale.

Filter je pan-EU: Hodnoty `regions` sú NUTS kódy ľubovoľnej krajiny EÚ z nižšie uvedeného zoznamu. Filter vráti len tie regióny, na ktoré máte aktívne LEADS predplatné (subscription scope). Ak máte predplatené len CZ + SK, NUTS kódy DE budú filtrom ignorované.
Podporované krajiny (NUTS-0)
CZČeská republika
SKSlovensko
PLPolsko
DENěmecko
ATRakousko
FRFrancie
ESŠpanělsko
ITItálie
NLNizozemsko
BEBelgie
PTPortugalsko
SEŠvédsko
FIFinsko
DKDánsko
NONorsko
IEIrsko
GRŘecko
RORumunsko
BGBulharsko
HUMaďarsko
HRChorvatsko
SISlovinsko
LTLitva
LVLotyšsko
EEEstonsko
LULucembursko
CYKypr
MTMalta
CHŠvýcarsko
ISIsland
MKSeverní Makedonie

Pre získanie úplného NUTS stromu konkrétnej krajiny použite:

GET /api/v2/leads/regions/catalog?country=CZ&locale=cs
GET /api/v2/leads/regions/catalog?country=DE&locale=de
GET /api/v2/leads/regions/catalog?country=FR&locale=en
Príklad: NUTS-3 kódy pre CZ (kraje) — rozbaliť ▸
CZ010Hlavní město Praha
CZ020Středočeský kraj
CZ031Jihočeský kraj
CZ032Plzeňský kraj
CZ041Karlovarský kraj
CZ042Ústecký kraj
CZ051Liberecký kraj
CZ052Královéhradecký kraj
CZ053Pardubický kraj
CZ063Kraj Vysočina
CZ064Jihomoravský kraj
CZ071Olomoucký kraj
CZ072Zlínský kraj
CZ080Moravskoslezský kraj

industryTags string[] Odporúčané

Multi-tag odborová klasifikácia. Tender môže mať viac tagov (napr. 'stav_pozemni' + 'stav_remesla' pri rekonštrukcii). Filter zodpovedá tendru, ak má aspoň jeden zo zadaných tagov (JSON_OVERLAPS).

Prečo preferovať industryTags pred CPV: Naše industryTags vznikajú kombináciou LLM klasifikácie názvu/popisu tendru + CPV mapovania + regex hintov — zachytia relevantné zákazky aj u zadávateľov, ktorí priraďujú CPV nedbalo. CPV filter je presný, ale závisí od disciplíny zadávateľa, ktorá je v ČR slabá.

48 tagov v 13 oblastiach
🏗️Stavebnictví
con_buildingsPozemní stavby a rekonstrukce budov
con_civilInženýrské stavby (silnice, mosty, voda)
con_tradesStavební řemesla a dílčí práce
con_energy_efficiencyEnergetické úspory a OZE
con_materialsStavební materiál
📐Projektování a dozor
des_documentationProjektová dokumentace a studie
des_supervision_ohsTechnický dozor a BOZP
des_surveyingGeodézie a pozemkové úpravy
💻IT a software
it_developmentVývoj SW a integrace
it_licensingSW licence a předplatné
it_hardwareHW a infrastruktura
it_cybersecurityKybernetická bezpečnost
it_data_aiData, analytika, AI/ML
📡Telekomunikace
telecom_internetTelekomunikace, internet, mobilní
🧑‍💼Profesionální služby
prof_marketingMarketing, PR, reklama
prof_legalPrávní služby
prof_accountingÚčetnictví, dotace, audit
prof_hrHR, nábor
prof_translationPřeklady a tlumočení
prof_insurancePojištění a finanční
🛡️Provoz a údržba
ops_cleaningÚklidové služby
ops_securityOstraha a recepční
ops_maintenanceÚdržba a servis zařízení
ops_wasteOdpadové hospodářství
ops_facilitySpráva nemovitostí
🍽️Stravování a ubytování
cat_cateringStravování, catering
cat_accommodationUbytování a konference
cat_foodPotraviny a nápoje
🚚Doprava a vozidla
trans_transportPřeprava cestujících a nákladu
trans_vehiclesVozidla, díly, leasing
🏥Zdravotnictví
health_pharmaLéčiva a farma
health_devicesZdravotnické přístroje a materiál
health_careZdravotní a sociální péče
📦Zboží a vybavení
goods_furnitureNábytek a vybavení interiérů
goods_clothingOděvy, OOPP, uniformy
goods_electricalElektromateriál
goods_machineryPrůmyslové stroje
Energetika a vodárenství
energy_fuelsPohonné hmoty a paliva
energy_power_heatElektřina a teplo
energy_waterVodárenství
🌳Příroda, lesy, bezpečnost
nat_forestryLesní hospodářství
nat_greeneryÚdržba zeleně a zahradnictví
nat_agricultureZemědělství
defense_safetyHasiči, vojsko, obrana
🔬Věda a vzdělávání
sci_labLaboratorní a měřicí vybavení
sci_researchVýzkum a vývoj
edu_trainingVzdělávání a školení
culture_mediaKultura, knihy, média
Chýba váš tag? Ak nenájdete tag pre svoje odvetvie, napíšte nám na michal@veritra.io — pridáme ho.

categories string[] Menej presné

CPV (Common Procurement Vocabulary) prefixy ľubovoľnej dĺžky — zhoda cez `LIKE 'prefix%'`. Tj. '45' zachytí všetky stavebné oddiely, '4523' len inžinierske stavby, '45316110' len pouličné osvetlenie.

35 najčastejších CPV oddielov (2-miestnych)
03Poľnohospodárstvo, lesníctvo, rybárstvo
09Palivá a energia
15Potraviny a nápoje
18Odevy, obuv, OOPP
22Tlačoviny, knihy
30Kancelárske stroje, IT vybavenie
31Elektrické stroje, káble, svietidlá
32Rádio, TV, telekomunikácie
33Zdravotnícke vybavenie, liečivá
34Vozidlá, doprava
35Bezpečnosť, hasičská a vojenská technika
38Laboratórne a meracie prístroje
39Nábytok, vybavenie interiérov
42Priemyselné stroje
44Stavebné materiály a konštrukcie
45Stavebné práce
48Softvér a licencie
50Opravy a údržba
55Stravovanie, ubytovanie
60Doprava (preprava)
64Pošta, telekomunikačné služby
65Verejné služby (elektrina, voda)
66Finančné a poisťovacie služby
70Realitné služby a správa budov
71Architektonické, projekčné, inžinierske služby
72IT služby (vývoj, integrácia, podpora)
73Výskum a vývoj
75Verejná správa, obrana
77Poľnohospodárske, lesnícke, záhradnícke služby
79Obchodné služby (právne, účtovníctvo, HR, marketing)
80Vzdelávanie, školenia
85Zdravotná a sociálna starostlivosť
90Odpady, životné prostredie, upratovanie
92Kultúra, šport, rekreácia
98Ostatné služby pre obyvateľstvo

Kompletný katalóg (9 454 kódov) nájdete na /docs/leads/cpv — prehľadávateľný a zoskupený po oddieloch.

keywords string[]

LIKE zhoda v názve aj popise zákazky. Case-insensitive. Medzi položkami OR. Užitočné ako poistka, ak industryTags/CPV nezachytia všetko (napr. špecifický typ zákazky v texte).

"keywords": ["rekonstrukce", "VO", "osvětlení", "MŠ"]

minValue / maxValue number | null

Rozsah odhadovanej hodnoty zákazky v Kč. Možno zadať len minValue, len maxValue, alebo oboje.

Dôležité: Tendre bez vyplnenej odhadovanej hodnoty (estimatedValue je null alebo 0) prechádzajú filtrom v oboch smeroch. Mnoho zadávateľov hodnotu neuvádza — keby sme ich odrezali, prišli by ste o ne.

emailDigest boolean

Ak je true, dostávate 1× denne (5:00 UTC) e-mail so zhrnutím nových zhôd z tohto filtra. Predvolená hodnota je true. Vypnete ho úpravou filtra. Webhook sa nastavuje zvlášť, na úrovni účtu (pozri sekciu nižšie).

name string · active boolean

`name` — max 120 znakov, povinné. `active` — ak false, filter nebeží v crone (žiadne nové zhody, digest, webhook). Možno pozastaviť bez vymazania.

Logika filtrovania

Polia sa kombinujú takto:

MATCH = (zadávateľ.NUTS3 ∈ expand(regions))
     AND (minValue ≤ estimatedValue ≤ maxValue  ALEBO  estimatedValue je null alebo 0)
     AND (industry_or_cpv  ALEBO  keyword_match)

# expand(regions): NUTS kódy sa expandujú na NUTS 3 leaves
# (CZ → 14 krajov, CZ01 → CZ010, CZ010 → CZ010).

industry_or_cpv:
   ak  industryTags zadané  →  JSON_OVERLAPS(industryTags, tender.industryTags)
   inak categories zadané   →  tender.cpvCode LIKE ANY (categories + "%")
   inak                     →  false   (žiadny industry filter sa neaplikuje)

keyword_match:
   ak  keywords zadané      →  tender.title alebo description LIKE ANY (%kw%)
   inak                     →  false

# Keď nezadáte žiadne industryTags / categories / keywords,
# vrátia sa všetky tendre spĺňajúce regions a value range.

industryTags má prednosť pred categories — ak zadáte oboje, použije sa len industryTags (categories sa ignoruje). keywords fungujú nezávisle (sú vo vetve OR spolu s industry_or_cpv).

Endpointy pre správu filtrov

Pre správu filtrov použite management kľúč (mrw_live_…). Má samostatné rate limity a nespotrebúva LEADS kredity, takže správa filtrov neovplyvňuje vaše denné vyzdvihávanie zákaziek.

GET/api/v2/leads/filters
POST/api/v2/leads/filters
GET/api/v2/leads/filters/:id
PATCH/api/v2/leads/filters/:id
DELETE/api/v2/leads/filters/:id
GET/api/v2/leads/filters/:id/matches/export?format=csv|xlsx|json&view=all|starred|excluded

Body parametre (POST / PUT) — spoločné

ParameterTypPopis
name*stringNázov filtra (max 120 znakov)
regionsstring[]NUTS kódy (napr. `CZ010`). Mixovať možno ľubovoľnú úroveň. Prázdne pole = všetky regióny.
industryTagsstring[]Odporúčaná cesta. Tag IDs z našej taxonómie (napr. 'stav_pozemni', 'it_vyvoj'). Multi-tag — OR medzi položkami.
categoriesstring[]CPV prefixy ľubovoľnej dĺžky (napr. '45', '4523', '45316110'). Menej presné ako industryTags.
keywordsstring[]Kľúčové slová — LIKE zhoda v názve aj popise zákazky (OR medzi položkami).
minValuenumber | nullMin. odhadovaná hodnota (Kč). Tendre bez hodnoty prechádzajú.
maxValuenumber | nullMax. odhadovaná hodnota (Kč). Tendre bez hodnoty prechádzajú.
emailDigestbooleanDenný e-mail digest (predvolené true)
activebooleanAktívny filter (predvolené true)

Webhook sa na filtri už nenastavuje — pozri sekciu Webhook nižšie (account-level endpointy). Pole webhookUrl je z bezpečnostných dôvodov odmietnuté s HTTP 410.

Príklady

Kľúčové slová vyhľadávajú v názve a popise zákazky (LIKE %kw%) v jazyku, v ktorom zadávateľ publikuje (zatiaľ vždy po česky).

Stavby v Prahe nad 500 tis. Kč

curl -X POST -H "X-Api-Key: mrw_live_…" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Stavby Praha 500k+",
    "regions": ["CZ010"],
    "industryTags": ["con_buildings", "con_trades"],
    "minValue": 500000
  }' \
  https://veritra.io/api/v2/leads/filters

IT vývoj a SW licencie (všetky kraje)

curl -X POST -H "X-Api-Key: mrw_live_…" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "IT vývoj + SW licencie",
    "industryTags": ["it_development", "it_licensing", "it_data_ai"],
    "keywords": ["informačný systém", "modul"]
  }' \
  https://veritra.io/api/v2/leads/filters

Verejné osvetlenie (CZ) — kombinácia tagov + keywords

curl -X POST -H "X-Api-Key: mrw_live_…" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Verejné osvetlenie (CZ)",
    "industryTags": ["goods_electrical", "con_civil"],
    "keywords": ["osvetlenie", "VO ", "verejné osvetlenie", "lampy"],
    "minValue": 200000
  }' \
  https://veritra.io/api/v2/leads/filters

Deaktivovať filter

curl -X PATCH -H "X-Api-Key: mrw_live_…" \
  -H "Content-Type: application/json" \
  -d '{"active": false}' \
  https://veritra.io/api/v2/leads/filters/<id>

Doručovanie — webhook vs polling

Dva spôsoby, ako dostať nové zákazky do svojho ERP. Väčšina integrátorov kombinuje oba.

Webhook (push)

Veritra pošle POST na tvoj endpoint do ~2 sekúnd od matchu. Event leads.match.created s plnou payload zákazky.

Plus: real-time, žiadny polling overhead, server-side gating.

Mínus: vyžaduje verejne dostupný endpoint (HTTPS), HMAC verifikácia, idempotency handling.

Polling (pull)

Váš ERP volá GET /api/v2/leads/matches?since=… periodicky (napr. každých 5 min). Vráti matches od daného timestampu.

Plus: žiadny verejný endpoint, jednoduchá implementácia, restart-friendly.

Mínus: latencia 5–10 min, rate-limit constraint (60 req/min mgmt API), zbytočné prázdne odpovede.

Odporúčame: primárne webhook + denný polling (since=yesterday) ako safety net pre prípad, že doručenie webhooku zlyhalo po vyčerpaní retry budget.

Získanie matchov

GET/api/v2/leads/matches

Query parametre

ParameterTypPopis
filterIdstringFiltrovať podľa konkrétneho filtra
qTextstringHľadaný text (full-text v title + description).
sincestring (ISO 8601 datetime)Iba matche od dátumu (ISO datetime)
deliveredbooleanfalse = len nedoručené, true = len doručené
view"starred" | "excluded"Špeciálne zobrazenie: starred (obľúbené) | excluded (skryté).
sort"newest" | "deadline" | "value"Zoradenie: newest (predvolené), deadline (rastúce), value (klesajúce).
limitnumberPočet výsledkov, max 1000 (predvolené: 50). Pre >1k použite nextCursor pagination alebo /matches/export.
cursorstringKeyset cursor z predchádzajúcej stránky (pagination.nextCursor).

Vrátené matche sú automaticky označené ako delivered=true. Každý request spotrebuje 1 kredit.

Formáty matchId

matchId má dva formáty podľa pôvodu:

  • cm… (cuid prefix) — vrátený z pre-computed LeadMatch tabuľky (cron job, ktorý páruje zákazky každý deň). Stabilný naprieč volaniami, použiteľný pre mark-as-viewed.
  • live-12345 synthetic prefix pre live-detekovaný match cez search / browse mode (qText param, view=starred/excluded). Nie je v LeadMatch tabuľke → mark-as-viewed je no-op. tenderId je suffix po pomlčke.

Príklad

curl -H "X-Api-Key: mrw_leads_…" \
  "https://veritra.io/api/v2/leads/matches?delivered=false&limit=10"

Odpoveď

{
  "data": [
    {
      "matchId": "cm…",
      "filterId": "cm…",
      "filterName": "Stavby Praha",
      "matchedAt": "2026-04-03T06:00:00.000Z",
      "viewedAt": null,
      "delivered": true,
      "tender": {
        "id": 12345,
        "title": "Rekonštrukcia mosta ev.č. 123",
        "estimatedValue": 12500000,
        "deadlineAt": "2026-05-15T22:00:00.000Z",
        "publishedAt": "2026-04-01T08:00:00.000Z",
        "firstSeenAt": "2026-04-01T08:30:00.000Z",
        "url": "https://nen.nipez.cz/...",
        "portalType": "NEN",
        "cpvCode": "45000000",
        "tenderType": "OFFERS",
        "contractingAuthority": {
          "ico": "12345678",
          "name": "Město Praha",
          "region": "Praha",
          "district": "Praha 1"
        },
        "documents": [
          { "name": "Výzva.pdf", "url": "https://nen.nipez.cz/…", "fileType": "pdf", "fileSizeBytes": 320000 }
        ],
        "starred": false,
        "excluded": false
      }
    }
  ],
  "pagination": { "nextCursor": "eyJmaXJzdFNlZW5BdC…", "totalCount": 42 }
}

Pre ďalšiu stránku pošlite pagination.nextCursor ako parameter ?cursor=.

Akcie na matche

Star (obľúbiť), exclude (skryť) a view (označiť ako prečítané) sú per-tender preferencie uložené v UserTenderPreference.

GET/api/v2/leads/matches/:matchId

Response: rovnaký tvar ako položka zoznamu matches.

POST/api/v2/leads/matches/:matchId/star
{ "starred": true }
POST/api/v2/leads/matches/:matchId/exclude
{ "excluded": true }
POST/api/v2/leads/matches/:matchId/view

Pre synthetic live-:id matches je no-op (žiadny riadok na označenie).

GET/api/v2/leads/preferences
{
  "data": {
    "starred": [12345, 67890],
    "excluded": [54321]
  }
}

Taxonomy

Statické referenčné katalógy pre hodnoty filtrov. Locale-aware labels.

GET/api/v2/leads/taxonomy/industry?locale=cs
{
  "data": {
    "locale": "cs",
    "areas": [{ "id": "construction", "icon": "🏗️", "label": "Stavebnictví" }, …],
    "tags": [
      { "id": "con_buildings", "area": "construction", "label": "Pozemní stavby", "cpvPrefixes": ["452"] },
      …
    ]
  }
}
GET/api/v2/leads/taxonomy/cpv?locale=cs

Response: stromová štruktúra oddiely → skupiny → triedy → kategórie → podkategórie.

Webhook

Webhook endpointy (CRUD, secret rotation) sú account-level — zdokumentované raz v Account API → Webhooky. Tu je popísaný len leads-specific event payload (leads.match.created) a HMAC verifikácia.

Doručovanie eventov

Pri novom matchi pošleme event typu leads.match.created s HMAC-SHA256 podpisom v hlavičke X-Signature-256, idempotency kľúčom v X-Idempotency-Key a typom v X-Event-Type. Pri zlyhaní retry s exponenciálnym backoffom do ~33 hodín.

Retry policy

Ak váš endpoint vráti non-2xx (alebo neodpovie do 10 s), Veritra opakuje pokus s exponential backoff: 1 min, 5 min, 30 min, 2 h, 12 h, 24 h. Po 6 zlyhaniach sa webhook označí ako failed a v dashboarde sa rozbliká. Idempotency-key zostáva pre každý pokus rovnaký — váš endpoint MUSÍ dedupolikovať (inak sa ten istý match spracuje 6×).

Správa endpointov cez API

Môžete spravovať webhook endpointy bez vstupu do dashboardu. Limit 5 endpointov na účet.

GET/api/v2/account/webhooks
POST/api/v2/account/webhooks
Webhook secret sa zobrazí iba raz! Pri POST /webhooks dostanete v response field 'secret' — uložte ho ihneď do prostredia (napr. VERITRA_WEBHOOK_SECRET). Nie je možné ho získať späť. Ak ho stratíte, použite /rotate-secret na vygenerovanie nového (starý je okamžite invalidný).
GET/api/v2/account/webhooks/:id
PATCH/api/v2/account/webhooks/:id
DELETE/api/v2/account/webhooks/:id
POST/api/v2/account/webhooks/:id/rotate-secret
POST/api/v2/account/webhooks/:id/test
GET/api/v2/account/webhooks/:id/deliveries
POST/api/v2/account/webhooks/:id/deliveries/:deliveryId/replay

HMAC signature verification

Server podpisuje payload HMAC-SHA256(secret, raw_body) a posiela v hlavičke X-Signature-256 vo formáte sha256=hex. Verifikujte konštantno-časovým porovnaním, inak je integrácia zraniteľná na timing attack.

// Node.js (Express)
import crypto from "node:crypto";

const WEBHOOK_SECRET = process.env.MRICKWOOD_WEBHOOK_SECRET!;

function verify(rawBody: string, sig: string | undefined): boolean {
  if (!sig) return false;
  const expected = "sha256=" + crypto
    .createHmac("sha256", WEBHOOK_SECRET)
    .update(rawBody)
    .digest("hex");
  // Constant-time comparison (timing-safe)
  const a = Buffer.from(sig);
  const b = Buffer.from(expected);
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

app.post("/webhooks/veritra",
  express.raw({ type: "application/json" }),
  async (req, res) => {
    const raw = req.body.toString("utf8");
    if (!verify(raw, req.header("X-Signature-256"))) {
      return res.status(401).send("Invalid signature");
    }
    const idempKey = req.header("X-Idempotency-Key")!;
    const evt = JSON.parse(raw);
    // Idempotency: store idempKey, skip if already processed
    if (await alreadyProcessed(idempKey)) return res.status(200).send("ok");
    await processEvent(evt);
    await markProcessed(idempKey);
    res.status(200).send("ok"); // Must be 2xx within 10s
  },
);
# Python (Flask)
import hmac, hashlib, os
from flask import Flask, request, abort

WEBHOOK_SECRET = os.environ["MRICKWOOD_WEBHOOK_SECRET"]

def verify(raw: bytes, sig: str | None) -> bool:
    if not sig: return False
    expected = "sha256=" + hmac.new(
        WEBHOOK_SECRET.encode(), raw, hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(sig, expected)

@app.post("/webhooks/mrickwood")
def webhook():
    raw = request.get_data()
    if not verify(raw, request.headers.get("X-Signature-256")):
        abort(401)
    # ... idempotency check + process
    return "ok", 200
{
  "id": "evt_…",
  "type": "leads.match.created",
  "createdAt": "2026-04-03T06:00:00.000Z",
  "data": {
    "filterId": "cm…",
    "filterName": "Stavby Praha",
    "matchId": "cm…",
    "tender": { "id": "12345", "title": "…", "estimatedValue": 12500000 }
  }
}

Email digest

S emailDigest: true dostanete denný e-mail s prehľadom nových zákaziek. Digest sa odosiela ráno (5:00 UTC) na e-mail vášho účtu. Možno vypnúť úpravou filtra.

Limity

ParameterTypPopis
Trial500 req/mesiac1 deň zadarmo. Bez karty / fakturačného profilu. Default ApiKey.requestsLimit.
Platený plánneobmedzeneMesačný limit je vypnutý. Platí len technický rate limit 100/h/kľúč.
Filtre20Max počet aktívnych filtrov per účet.
Webhook endpointy5Max počet aktívnych webhook URL per účet.

Po prekročení mesačného limitu vráti API 429 s hlavičkou Retry-After. Limit sa resetuje 1. deň v mesiaci.