Nové

Hlídač zakázek

Automatické doručování nových zakázek dle vašich filtrů — kraj, industry tagy, klíčová slova, rozsah hodnot. Pull API + email digest + webhook.

Koncept

Čtyři pojmy, které potkáte ve všech endpointech:

  • Tenderjedna konkrétní zakázka z portálu (NEN, VVZ, E-ZAK, TenderArena, atd.). Identifikována číselným ID.
  • Filteruložená sada kritérií (region, obor, klíčová slova, hodnota). Když nová zakázka splní kritéria, vznikne match.
  • Matchnová zakázka, která padla do vašeho filtru. Reprezentuje vazbu (tender × filter) + timestamp + váš stav (sledováno / skryto / přečteno).
  • Taxonomyčíselníky regionů (NUTS), oborů (industry tags) a CPV kódů, podle kterých klasifikujeme zakázky.

Rychlý start

Filtry lze nastavit dvěma způsoby — výsledek je stejný, filtr lze kdykoliv editovat libovolnou cestou.

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

Nahoře je ad-hoc hledání. Pokud chcete kontinuální monitoring (nové zakázky odpovídající vašim kritériím), použijte filtry + webhook/email digest — viz dále.

Doporučení: Pro filtrování vždy preferujte industryTags nad categories (CPV). Naše industryTags jsou sjednocením LLM klasifikace tendru a CPV mapování — odolné vůči chybnému CPV. Zadavatelé v CZ často přiřazují příliš obecné nebo nesouvisející CPV kódy (typicky generické '45000000-0' u stavebních zakázek místo konkrétního prefixu), takže čistě CPV filtr kus relevantních zakázek mine.

Hledání zakázek (ad-hoc)

Pro jednorázové vyhledání zakázek napříč všemi aktivními tendry. Nepoužívá uložený filtr, parametry jdou přímo v query stringu.

GET/api/v2/leads/tenders/search

Query parametry

ParametrTypPopis
qText*stringHledaný 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č). Tendery bez hodnoty procházejí.
maxValuenumberMax. odhadovaná hodnota (Kč). Tendery bez hodnoty procházejí.
deadlineFromstring (YYYY-MM-DD)Lhůta podání >= YYYY-MM-DD.
deadlineTostring (YYYY-MM-DD)Lhůta podání <= YYYY-MM-DD.
sort"newest" | "deadline" | "value"Řazení: newest (default), deadline (rostoucí), value (klesající).
limitnumberPočet výsledků, max 1000 (výchozí: 50). Pro >1k použij nextCursor pagination nebo /matches/export.
cursorstringKeyset cursor z předchozí stránky (pagination.nextCursor).

Pří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: data objekt s fieldy id, title, description, estimatedValue, deadlineAt, contractingAuthority, documents[], starred, excluded.

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

Body může obsahovat recipientEmail pro forward na jiný email. Defaultně user.email.

GET/api/v2/leads/documents/preview

Query: ?url=:original&kind=docx|xlsx. Allowlist hostů: 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, žádný klíč)

Pro UI dropdowny a filter creation. Cache 1h server-side. Veřejné, žádná auth potřeba.

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 jednorázový dotaz, vrátí current snapshot zakázek. Filter (níže) je uložená kritéria — server vás průběžně notifikuje na nové zakázky webhookem nebo email digestem.

Co lze filtrovat a povolené hodnoty

Filtr se skládá z polí níže. Všechna jsou volitelná kromě `name`. Prázdné pole = bez omezení v dané dimenzi.

regions string[]

NUTS regiony pro danou zemi (?country=CZ) s labels per locale.

Filtr je pan-EU: Hodnoty `regions` jsou NUTS kódy libovolné EU země z níže uvedeného seznamu. Filtr vrátí jen ty regiony, na které máte aktivní LEADS předplatné (subscription scope). Pokud máte předplacené jen CZ + SK, NUTS kódy DE budou filterem ignorovány.
Podporované země (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

Pro získání plného NUTS stromu konkrétní země použijte:

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
Příklad: NUTS-3 kódy pro CZ (kraje) — rozbalit ▸
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[] Doporučeno

Multi-tag oborová klasifikace. Tender může mít víc tagů (např. 'stav_pozemni' + 'stav_remesla' u rekonstrukce). Filtr matchne tender, pokud má alespoň jeden ze zadaných tagů (JSON_OVERLAPS).

Proč preferovat industryTags před CPV: Naše industryTags vznikají kombinací LLM klasifikace názvu/popisu tendru + CPV mapování + regex hintů — odchytí relevantní zakázky i u zadavatelů co přiřazují CPV nedbale. CPV filtr je přesný, ale závisí na disciplíně zadavatele, která je v ČR slabá.

48 tagů ve 13 oblastech
🏗️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
Chybí váš tag? Pokud nenajdete tag pro své odvětví, napište nám na michal@veritra.io — přidáme jej.

categories string[] Méně přesné

CPV (Common Procurement Vocabulary) prefixy libovolné délky — match přes `LIKE 'prefix%'`. Tj. '45' chytne všechny stavební oddíly, '4523' jen inženýrské stavby, '45316110' jen pouliční osvětlení.

35 nejčastějších CPV oddílů (2-místných)
03Zemědělství, lesnictví, rybolov
09Paliva a energie
15Potraviny a nápoje
18Oděvy, obuv, OOPP
22Tiskoviny, knihy
30Kancelářské stroje, IT vybavení
31Elektrické stroje, kabely, svítidla
32Rádio, TV, telekomunikace
33Zdravotnické vybavení, léčiva
34Vozidla, doprava
35Bezpečnost, hasičská a vojenská technika
38Laboratorní a měřicí přístroje
39Nábytek, vybavení interiérů
42Průmyslové stroje
44Stavební materiály a konstrukce
45Stavební práce
48Software a licence
50Opravy a údržba
55Stravování, ubytování
60Doprava (přeprava)
64Pošta, telekomunikační služby
65Veřejné služby (elektřina, voda)
66Finanční a pojišťovací služby
70Realitní služby a správa budov
71Architektonické, projekční, inženýrské služby
72IT služby (vývoj, integrace, podpora)
73Výzkum a vývoj
75Veřejná správa, obrana
77Zemědělské, lesnické, zahradnické služby
79Obchodní služby (právní, účetnictví, HR, marketing)
80Vzdělávání, školení
85Zdravotní a sociální péče
90Odpady, životní prostředí, úklid
92Kultura, sport, rekreace
98Ostatní služby pro obyvatelstvo

Kompletní katalog (9 454 kódů) najdete na /docs/leads/cpv — searchovatelné a seskupené po oddílech.

keywords string[]

LIKE match v názvu i popisu zakázky. Case-insensitive. Mezi položkami OR. Užitečné jako pojistka, pokud industryTags/CPV ne všechno odchytí (např. specifický typ zakázky v textu).

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

minValue / maxValue number | null

Rozsah odhadované hodnoty zakázky v Kč. Lze zadat jen minValue, jen maxValue, nebo oba.

Důležité: Tendery bez vyplněné odhadované hodnoty (estimatedValue je null nebo 0) procházejí filtrem oběma směry. Mnoho zadavatelů hodnotu neuvádí — kdybychom je odřízli, přišli byste o ně.

emailDigest boolean

Pokud true, dostáváte 1× denně (5:00 UTC) email se shrnutím nových matchů z tohoto filtru. Výchozí true. Vypnete editací filtru. Webhook se nastavuje zvlášť, na úrovni účtu (viz sekce níže).

name string · active boolean

`name` — max 120 znaků, povinné. `active` — pokud false, filtr neběží v cronu (žádné nové matche, digest, webhook). Lze pozastavit bez mazání.

Logika filtrování

Pole se kombinují takto:

MATCH = (zadavatel.NUTS3 ∈ expand(regions))
     AND (minValue ≤ estimatedValue ≤ maxValue  NEBO  estimatedValue je null nebo 0)
     AND (industry_or_cpv  NEBO  keyword_match)

# expand(regions): NUTS kódy se expandují na NUTS 3 leaves
# (CZ → 14 krajů, CZ01 → CZ010, CZ010 → CZ010).

industry_or_cpv:
   pokud  industryTags zadané  →  JSON_OVERLAPS(industryTags, tender.industryTags)
   jinak categories zadané     →  tender.cpvCode LIKE ANY (categories + "%")
   jinak                       →  false   (žádný industry filtr neaplikován)

keyword_match:
   pokud  keywords zadané      →  tender.title nebo description LIKE ANY (%kw%)
   jinak                       →  false

# Když nezadáte žádné industryTags / categories / keywords,
# vrátí se všechny tendery splňující regions a value range.

industryTags má přednost před categories — pokud zadáte oboje, použije se jen industryTags (categories se ignoruje). keywords fungují nezávisle (jsou v OR větvi s industry_or_cpv).

Endpointy pro správu filtrů

Pro správu filtrů použijte management klíč (mrw_live_…). Má samostatné rate limity a nespotřebovává LEADS kredity, takže management filtrů neovlivňuje vaše denní vyzvedávání zakázek.

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 parametry (POST / PUT) — společné

ParametrTypPopis
name*stringNázev filtru (max 120 znaků)
regionsstring[]NUTS kódy (např. `CZ010`). Mixovat lze libovolný level. Prázdné pole = všechny regiony.
industryTagsstring[]Doporučená cesta. Tag IDs z naší taxonomie (např. 'stav_pozemni', 'it_vyvoj'). Multi-tag — OR mezi položkami.
categoriesstring[]CPV prefixy libovolné délky (např. '45', '4523', '45316110'). Méně přesné než industryTags.
keywordsstring[]Klíčová slova — LIKE match v názvu i popisu zakázky (OR mezi položkami).
minValuenumber | nullMin. odhadovaná hodnota (Kč). Tendery bez hodnoty procházejí.
maxValuenumber | nullMax. odhadovaná hodnota (Kč). Tendery bez hodnoty procházejí.
emailDigestbooleanDenní email digest (výchozí true)
activebooleanAktivní filtr (výchozí true)

Webhook se na filtru už nenastavuje — viz sekci Webhook níže (account-level endpointy). Pole webhookUrl je z bezpečnostních důvodů odmítnuto s HTTP 410.

Příklady

Klíčová slova hledají v názvu a popisu zakázky (LIKE %kw%) v jazyce, ve kterém zadavatel publikuje (zatím vždy česky).

Stavby v Praze 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 licence (všechny kraje)

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

Veřejné osvětlení (CZ) — kombinace tagů + keywords

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

Deaktivovat filtr

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čování — webhook vs polling

Dva způsoby, jak dostat nové zakázky do svého ERP. Většina integrátorů kombinuje oba.

Webhook (push)

Tendero pošle POST na tvůj endpoint do ~2 sekund od match. Event leads.match.created s plnou payload zakázky.

Plus: real-time, žádné polling overhead, server-side gating.

Mínus: vyžaduje veřejně dostupný endpoint (HTTPS), HMAC verifikace, idempotency handling.

Polling (pull)

Tvůj ERP volá GET /api/v2/leads/matches?since=… periodicky (např. každých 5 min). Vrátí matches od daného timestampu.

Plus: žádný veřejný endpoint, jednoduchá implementace, restart-friendly.

Mínus: latence 5-10 min, rate-limit constraint (60 req/min mgmt API), zbytečné prázdné odpovědi.

Doporučujeme: primárně webhook + denní polling (since=yesterday) jako safety net pro případ, že webhook delivery selhala přes retry budget.

Získání matchů

GET/api/v2/leads/matches

Query parametry

ParametrTypPopis
filterIdstringFiltrovat dle konkrétního filtru
qTextstringHledaný text (full-text v title + description).
sincestring (ISO 8601 datetime)Pouze matche od data (ISO datetime)
deliveredbooleanfalse = jen nedoručené, true = jen doručené
view"starred" | "excluded"Speciální view: starred (oblíbené) | excluded (skryté).
sort"newest" | "deadline" | "value"Řazení: newest (default), deadline (rostoucí), value (klesající).
limitnumberPočet výsledků, max 1000 (výchozí: 50). Pro >1k použij nextCursor pagination nebo /matches/export.
cursorstringKeyset cursor z předchozí stránky (pagination.nextCursor).

Vrácené matche jsou automaticky označeny jako delivered=true. Každý request spotřebuje 1 kredit.

Formáty matchId

matchId má dva formáty podle původu:

  • cm… (cuid prefix) — vrácený z pre-computed LeadMatch tabulky (cron job, který párované zakázky každý den). Stabilní napříč voláními, použitelný pro mark-as-viewed.
  • live-12345 synthetic prefix pro live-detekovaný match přes search / browse mode (qText param, view=starred/excluded). Není v LeadMatch tabulce → mark-as-viewed je no-op. tenderId je suffix po pomlčce.

Příklad

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

Odpověď

{
  "data": [
    {
      "matchId": "cm…",
      "filterId": "cm…",
      "filterName": "Stavby Praha",
      "matchedAt": "2026-04-03T06:00:00.000Z",
      "viewedAt": null,
      "delivered": true,
      "tender": {
        "id": 12345,
        "title": "Rekonstrukce mostu 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 }
}

Pro další stránku pošli pagination.nextCursor jako ?cursor= parametr.

Akce na matche

Star (oblíbit), exclude (skrýt) a view (mark as read) jsou per-tender preference uloženy v UserTenderPreference.

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

Response: stejný shape jako matches list item.

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

Pro synthetic live-:id matches je no-op (žádný řádek k označení).

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

Taxonomy

Statické referenční katalogy pro filter values. 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á struktura oddíly → skupiny → třídy → kategorie → podkategorie.

Webhook

Webhook endpointy (CRUD, secret rotation) jsou account-level — dokumentované jednou v Account API → Webhooky. Tady je popsán jen leads-specific event payload (leads.match.created) a HMAC verifikace.

Doručování eventů

Při novém matchi pošleme event typu leads.match.created s HMAC-SHA256 podpisem v hlavičce X-Signature-256, idempotency klíčem v X-Idempotency-Key a typem v X-Event-Type. Při selhání retry s exponenciálním backoffem do ~33 hodin.

Retry policy

Pokud tvůj endpoint vrátí non-2xx (nebo neodpoví do 10s), Tendero retryuje se exponential backoff: 1min, 5min, 30min, 2h, 12h, 24h. Po 6 selháních se webhook označí jako failed a v dashboardu blikne. Idempotency-key se pro každý pokus stejný — tvůj endpoint MUSÍ deduplikovat (jinak ten samý match zpracuje 6×).

Správa endpointů přes API

Můžeš spravovat webhook endpointy bez vstupu do dashboardu. Limit 5 endpointů na účet.

GET/api/v2/account/webhooks
POST/api/v2/account/webhooks
Webhook secret se zobrazí jen jednou! Při POST /webhooks dostaneš ve response field 'secret' — ulož ho hned do prostředí (např. MRICKWOOD_WEBHOOK_SECRET). Nelze ho získat zpět. Pokud ztratíš, použij /rotate-secret pro vygenerování nového (starý okamžitě 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 podepisuje payload HMAC-SHA256(secret, raw_body) a posílá v hlavičce X-Signature-256 ve formátu sha256=hex. Verifikuj v constant-time porovnáním, jinak je integrace zranitelná 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í email s přehledem nových zakázek. Digest se odesílá ráno (5:00 UTC) na email vašeho účtu. Lze vypnout editací filtru.

Limity

ParametrTypPopis
Trial500 req/měsíc1 den zdarma. Bez karty / fakturačního profilu. Default ApiKey.requestsLimit.
Placený plánneomezeněMěsíční limit je vypnut. Platí jen technický rate limit 100/h/klíč.
Filtry20Max počet aktivních filtrů per účet.
Webhook endpointy5Max počet aktivních webhook URL per účet.

Po překročení měsíčního limitu vrátí API 429 s hlavičkou Retry-After. Limit se resetuje 1. dne v měsíci.