Nowe

Monitor przetargów

Automatyczne dostarczanie nowych przetargów zgodnych z Twoimi filtrami — region, tagi branżowe, słowa kluczowe, zakres wartości. Pull API + digest e-mail + webhook.

Pojęcia

Cztery terminy, które napotkasz we wszystkich punktach końcowych:

  • Tenderpojedyncze zamówienie z portalu (NEN, VVZ, E-ZAK, TenderArena itp.). Identyfikowane za pomocą numerycznego ID.
  • Filterzapisany zestaw kryteriów (region, branża, słowa kluczowe, wartość). Gdy nowe zamówienie spełnia kryteria, generowane jest dopasowanie.
  • Matchnowe zamówienie, które spełniło warunki Twojego filtra. Reprezentuje powiązanie (zamówienie × filtr) + znacznik czasu + Twój stan (oznaczone gwiazdką / wykluczone / wyświetlone).
  • Taxonomykontrolowane słowniki dla regionów (NUTS), branż (tagi branżowe) i kodów CPV używanych do klasyfikacji zamówień.

Szybki start

Filtry można skonfigurować na dwa sposoby — efekt jest identyczny, a filtr można edytować dowolną metodą w dowolnym momencie.

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

Powyższe to wyszukiwanie ad hoc. Do ciągłego monitorowania (nowe zamówienia spełniające Twoje kryteria) użyj filtrów + webhook/email digest — patrz poniżej.

Zalecenie: Zawsze preferuj industryTags zamiast categories (CPV) do filtrowania. Nasze industryTags łączą klasyfikację przetargów przez LLM z mapowaniem CPV — odporne na nieprawidłowe kody CPV. Czeskie instytucje zamawiające często przypisują zbyt ogólne lub niezwiązane kody CPV (zazwyczaj generyczny kod '45000000-0' dla budownictwa zamiast konkretnego prefiksu), dlatego filtr oparty wyłącznie na CPV pominie znaczną część istotnych przetargów.

Wyszukiwanie zamówień (ad hoc)

Do jednorazowych wyszukiwań wśród wszystkich aktywnych zamówień. Nie używa zapisanego filtra — parametry są przekazywane bezpośrednio w ciągu zapytania.

GET/api/v2/leads/tenders/search

Parametry zapytania

ParametrTypOpis
qText*stringTekst wyszukiwania (pełnotekstowe przeszukiwanie tytułu i opisu).
regionsstringKody liściowe NUTS w formacie CSV (CZ010,CZ020).
cpvPrefixesstringPrefiksy CPV w formacie CSV (45,452).
industryTagsstringIdentyfikatory tagów branżowych w formacie CSV (con_buildings,it_development).
minValuenumberMinimalna szacowana wartość (CZK). Zamówienia bez wartości przechodzą przez filtr.
maxValuenumberMaksymalna szacowana wartość (CZK). Zamówienia bez wartości przechodzą przez filtr.
deadlineFromstring (YYYY-MM-DD)Termin składania ofert >= RRRR-MM-DD.
deadlineTostring (YYYY-MM-DD)Termin składania ofert <= RRRR-MM-DD.
sort"newest" | "deadline" | "value"Sortowanie: najnowsze (domyślnie), termin (rosnąco), wartość (malejąco).
limitnumberLiczba wyników, maks. 1000 (domyślnie: 50). Dla >1 tys. wyników użyj paginacji nextCursor lub /matches/export.
cursorstringKursor zestawu kluczy z poprzedniej strony (pagination.nextCursor).

Przykład

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

Odpowiedź: { data: { id, title, description, estimatedValue, deadlineAt, contractingAuthority, documents[], starred, excluded } }.

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

Body może zawierać recipientEmail w celu przekazania wiadomości na inny adres e-mail. Domyślnie użytkownik user.email.

GET/api/v2/leads/documents/preview

Zapytanie: ?url=:original&kind=docx|xlsx. Dozwolone hosty: 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)

Katalog i autouzupełnianie (publiczne, bez klucza)

Do list rozwijanych interfejsu i tworzenia filtrów. Pamięć podręczna po stronie serwera: 1 godz. Publiczne, bez wymaganego uwierzytelnienia.

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
Wyszukiwanie a filtr: Wyszukiwanie to jednorazowe zapytanie zwracające bieżącą migawkę. Filtr (poniżej) to zapisane kryteria — serwer ciągle powiadamia Cię o nowych zamówieniach za pośrednictwem webhooka lub email digest.

Pola filtrów i dozwolone wartości

Filtr składa się z poniższych pól. Wszystkie są opcjonalne z wyjątkiem `name`. Puste pole oznacza brak ograniczenia dla danego wymiaru.

regions string[]

Regiony NUTS dla wybranego kraju (?country=CZ) z etykietami uwzględniającymi język.

Filtr obejmuje całą UE: Wartości `regions` to kody NUTS z dowolnego kraju UE wymienionego poniżej. Filtr zwraca tylko regiony, dla których posiadasz aktywną subskrypcję LEADS (zakres subskrypcji). Jeśli masz subskrypcję tylko dla CZ + SK, kody NUTS dla Niemiec będą po cichu ignorowane.
Obsługiwane kraje (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
GBVelká Británie
JPJaponsko

Aby uzyskać pełne drzewo NUTS dla konkretnego kraju, użyj:

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
Przykład: kody NUTS-3 dla CZ (regiony) — rozwiń ▸
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[] Zalecane

Wielotagowa klasyfikacja branżowa. Przetarg może mieć wiele tagów (np. 'stav_pozemni' + 'stav_remesla' dla remontu). Filtr dopasowuje przetarg, jeśli ustawiony jest co najmniej jeden z żądanych tagów (JSON_OVERLAPS).

Dlaczego preferować industryTags zamiast CPV: Nasze industryTags łączą klasyfikację LLM tytułu/opisu przetargu z mapowaniem CPV i wskazówkami regex — wychwytują istotne przetargi nawet wtedy, gdy instytucja zamawiająca przypisała CPV niestarannie. Czysty filtr CPV jest precyzyjny, ale zależy od dyscypliny instytucji zamawiającej, która w CZ jest niska.

48 tagów w 13 obszarach
🏗️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
Brak tagu? Jeśli nie znajdziesz tagu dla swojej branży, napisz do michal@veritra.io — dodamy go.

categories string[] Mniej dokładne

Prefiksy CPV (Wspólny Słownik Zamówień) o dowolnej długości — dopasowywane przez `LIKE 'prefix%'`. Tak więc '45' obejmuje wszystkie działy budowlane, '4523' tylko inżynierię lądową, '45316110' tylko oświetlenie ulic.

35 najpopularniejszych działów CPV (2-cyfrowych)
03Rolnictwo, leśnictwo, rybołówstwo
09Paliwa i energia
15Żywność i napoje
18Odzież, obuwie, środki ochrony indywidualnej
22Materiały drukowane, książki
30Maszyny biurowe, sprzęt IT
31Maszyny elektryczne, kable, oświetlenie
32Radio, TV, telekomunikacja
33Sprzęt medyczny, farmaceutyki
34Pojazdy, transport
35Sprzęt ochrony, przeciwpożarowy i wojskowy
38Przyrządy laboratoryjne i pomiarowe
39Meble, wyposażenie wnętrz
42Maszyny przemysłowe
44Materiały budowlane i konstrukcje
45Roboty budowlane
48Oprogramowanie i licencje
50Naprawa i konserwacja
55Catering, zakwaterowanie
60Transport (przewóz)
64Usługi pocztowe i telekomunikacyjne
65Media (energia elektryczna, woda)
66Usługi finansowe i ubezpieczeniowe
70Zarządzanie nieruchomościami i obiektami
71Usługi architektoniczne, projektowe i inżynieryjne
72Usługi IT (rozwój, integracja, wsparcie)
73Badania i rozwój
75Administracja publiczna, obronność
77Usługi rolnicze, leśne i ogrodnicze
79Usługi biznesowe (prawne, księgowe, HR, marketing)
80Edukacja i szkolenia
85Opieka zdrowotna i społeczna
90Gospodarka odpadami, ochrona środowiska, sprzątanie
92Kultura, sport, rekreacja
98Pozostałe usługi publiczne

Pełny katalog (9 454 kodów) jest dostępny pod adresem /docs/leads/cpv — z możliwością wyszukiwania i grupowania według działów.

keywords string[]

Dopasowanie LIKE w tytule i opisie zamówienia. Bez rozróżniania wielkości liter. Operator OR między elementami. Przydatne jako siatka bezpieczeństwa, gdy industryTags/CPV nie wychwytują wszystkiego (np. określony typ zamówienia widoczny tylko w treści).

"keywords": ["rekonstrukcja", "oświetlenie", "przedszkole"]

minValue / maxValue number | null

Zakres szacowanej wartości zamówienia w CZK. Można ustawić tylko minValue, tylko maxValue lub oba.

Ważne: Zamówienia bez ustawionej wartości szacowanej (estimatedValue jest null lub 0) przechodzą przez filtr w obu kierunkach. Wiele instytucji zamawiających nie ujawnia wartości — ich wykluczenie spowodowałoby utratę zamówień.

emailDigest boolean

Jeśli true, otrzymujesz jeden codzienny e-mail (5:00 UTC) z podsumowaniem nowych dopasowań dla tego filtra. Domyślnie true. Można wyłączyć, edytując filtr. Webhooki konfiguruje się oddzielnie, na poziomie konta (patrz poniżej).

name string · active boolean

`name` — maks. 120 znaków, wymagane. `active` — jeśli false, filtr jest wykluczony z harmonogramu cron (brak nowych dopasowań, brak podsumowania, brak webhooka). Używaj do wstrzymania bez usuwania.

Logika filtra

Pola łączą się w następujący sposób:

MATCH = (authority.NUTS3 ∈ expand(regions))
     AND (minValue ≤ estimatedValue ≤ maxValue  OR  estimatedValue is null or 0)
     AND (industry_or_cpv  OR  keyword_match)

# expand(regions): kody NUTS są rozwijane do liści NUTS 3
# (CZ → 14 regionów, CZ01 → CZ010, CZ010 → CZ010).

industry_or_cpv:
   if    industryTags set  →  JSON_OVERLAPS(industryTags, tender.industryTags)
   elif  categories set    →  tender.cpvCode LIKE ANY (categories + "%")
   else                    →  false   (brak filtra branżowego)

keyword_match:
   if    keywords set      →  tender.title or description LIKE ANY (%kw%)
   else                    →  false

# Jeśli nie ustawiono industryTags / categories / keywords,
# zwracane są wszystkie zamówienia pasujące do regions i zakresu wartości.

industryTags ma pierwszeństwo przed categories — jeśli ustawiono oba, używany jest tylko industryTags (categories jest ignorowane). keywords działają niezależnie (są połączone operatorem OR z industry_or_cpv).

Endpointy zarządzania filtrami

Użyj klucza zarządzającego (mrw_live_…) do zarządzania filtrami. Posiada osobne limity żądań i nie zużywa kredytów LEADS, dzięki czemu zarządzanie filtrami nie wpływa na dzienny limit pobierania leadów.

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

Parametry body (POST / PUT) — wspólne

ParametrTypOpis
name*stringNazwa filtra (maks. 120 znaków)
regionsstring[]Kody NUTS (np. `CZ010`). Można mieszać dowolne poziomy. Pusta tablica = wszystkie regiony.
industryTagsstring[]Zalecana ścieżka. Identyfikatory tagów z naszej taksonomii (np. 'stav_pozemni', 'it_vyvoj'). Wieloznaczne — operator OR między elementami.
categoriesstring[]Prefiksy CPV dowolnej długości (np. '45', '4523', '45316110'). Mniej precyzyjne niż industryTags.
keywordsstring[]Słowa kluczowe — dopasowanie LIKE w tytule i opisie zamówienia (OR między elementami).
minValuenumber | nullMinimalna szacowana wartość (CZK). Zamówienia bez wartości przechodzą przez filtr.
maxValuenumber | nullMaksymalna szacowana wartość (CZK). Zamówienia bez wartości przechodzą przez filtr.
emailDigestbooleanCodzienne podsumowanie e-mail (domyślnie true)
activebooleanAktywny filtr (domyślnie true)

Webhooki nie są już konfigurowane per filtr — patrz sekcja Webhook poniżej (endpointy na poziomie konta). Pole webhookUrl jest odrzucane z kodem HTTP 410 ze względów bezpieczeństwa.

Przykłady

Poniższe przykłady są przedstawione po angielsku dla czytelności. W środowisku produkcyjnym słowa kluczowe są dopasowywane (LIKE %kw%) do tytułu i opisu ogłoszenia w języku wydawcy — obecnie zawsze po czesku, dlatego filtr należy tworzyć z użyciem czeskich terminów (np. "osvětlení", "veřejné osvětlení").

Budownictwo w Pradze powyżej 500 tys. CZK

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

Rozwój IT i licencje SW (wszystkie regiony)

curl -X POST -H "X-Api-Key: mrw_live_…" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Rozwój IT + licencje SW",
    "industryTags": ["it_development", "it_licensing", "it_data_ai"],
    "keywords": ["system informatyczny", "moduł"]
  }' \
  https://veritra.io/api/v2/leads/filters

Oświetlenie publiczne (CZ) — kombinacja tagów i słów kluczowych

curl -X POST -H "X-Api-Key: mrw_live_…" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Oświetlenie publiczne (CZ)",
    "industryTags": ["goods_electrical", "con_civil"],
    "keywords": ["oświetlenie", "latarnia uliczna", "oświetlenie publiczne", "lampy"],
    "minValue": 200000
  }' \
  https://veritra.io/api/v2/leads/filters

Dezaktywuj 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>

Dostarczanie — webhook a polling

Dwa sposoby pobierania nowych przetargów do systemu ERP. Większość integratorów łączy oba.

Webhook (push)

Veritra wysyła żądanie POST do Twojego punktu końcowego w ciągu ~2 sekund od dopasowania. Zdarzenie leads.match.created z pełnym ładunkiem danych przetargu.

Zalety: działanie w czasie rzeczywistym, brak narzutu pollingu, filtrowanie po stronie serwera.

Wady: wymaga publicznie dostępnego punktu końcowego (HTTPS), weryfikacji HMAC oraz obsługi idempotentności.

Polling (pull)

Twój ERP cyklicznie wywołuje GET /api/v2/leads/matches?since=… (np. co 5 min). Zwraca dopasowania od podanego znacznika czasu.

Zalety: brak publicznego punktu końcowego, prosta implementacja, odporność na restarty.

Wady: opóźnienie 5–10 min, ograniczenie liczby żądań (60 żądań/min dla API zarządzającego), marnowanie zasobów na puste odpowiedzi.

Zalecane: podstawowy webhook + codzienny polling (since=yesterday) jako siatka bezpieczeństwa na wypadek wyczerpania budżetu ponownych prób dostarczenia webhooka.

Pobieranie dopasowań

GET/api/v2/leads/matches

Parametry zapytania

ParametrTypOpis
filterIdstringFiltruj według określonego filtru
qTextstringTekst wyszukiwania (pełnotekstowe przeszukiwanie tytułu i opisu).
sincestring (ISO 8601 datetime)Tylko dopasowania od tej daty (data i czas w formacie ISO)
deliveredbooleanfalse = tylko niedostarczone, true = tylko dostarczone
view"starred" | "excluded"Widok specjalny: starred (ulubione) | excluded (ukryte).
sort"newest" | "deadline" | "value"Sortowanie: najnowsze (domyślnie), termin (rosnąco), wartość (malejąco).
limitnumberLiczba wyników, maks. 1000 (domyślnie: 50). Dla >1 tys. wyników użyj paginacji nextCursor lub /matches/export.
cursorstringKursor zestawu kluczy z poprzedniej strony (pagination.nextCursor).

Zwrócone dopasowania są automatycznie oznaczane delivered=true. Każde żądanie zużywa 1 kredyt.

Formaty matchId

matchId występuje w dwóch formatach zależnie od źródła:

  • cm… (prefiks cuid) — zwracany z wstępnie obliczonej tabeli LeadMatch (cron dzienny). Stabilny między wywołaniami, można używać do oznaczania jako wyświetlone.
  • live-12345 Prefiks syntetyczny dla dopasowań wykrytych na żywo przez wyszukiwanie / tryb przeglądania (parametr qText, view=starred/excluded). Nie znajduje się w tabeli LeadMatch → oznaczenie jako wyświetlone nie wywołuje żadnej akcji. tenderId to sufiks po myślniku.

Przykład

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

Odpowiedź

{
  "data": [
    {
      "matchId": "cm…",
      "filterId": "cm…",
      "filterName": "Budownictwo w Pradze",
      "matchedAt": "2026-04-03T06:00:00.000Z",
      "viewedAt": null,
      "delivered": true,
      "tender": {
        "id": 12345,
        "title": "Przebudowa mostu nr ref. 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 }
}

Aby przejść do następnej strony, przekaż pagination.nextCursor jako parametr ?cursor=.

Akcje dopasowań

Oznaczenie gwiazdką (ulubione), wykluczenie (ukrycie) i wyświetlenie (oznaczenie jako przeczytane) to preferencje per-przetarg przechowywane w UserTenderPreference.

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

Odpowiedź: taka sama struktura jak element listy dopasowań.

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

Brak operacji dla syntetycznych dopasowań live-:id (brak wiersza do oznaczenia).

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

Taksonomia

Statyczne katalogi referencyjne dla wartości filtrów. Etykiety uwzględniające ustawienia regionalne.

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

Odpowiedź: struktura drzewiasta: działy → grupy → klasy → kategorie → podkategorie.

Webhook

Punkty końcowe webhooków (CRUD, rotacja klucza tajnego) są na poziomie konta — udokumentowane raz w Account API → Webhooks. Ta sekcja dotyczy wyłącznie ładunku zdarzenia specyficznego dla leadów (leads.match.created) oraz weryfikacji HMAC.

Dostarczanie zdarzeń

Po nowym dopasowaniu wysyłamy zdarzenie typu leads.match.created z podpisem HMAC-SHA256 w nagłówku X-Signature-256 klucz idempotentności w X-Idempotency-Key, a typ w X-Event-Type. W przypadku błędu ponawiamy próbę z wykładniczym opóźnieniem przez maksymalnie ~33 godziny.

Polityka ponownych prób

Jeśli Twój punkt końcowy zwróci kod spoza zakresu 2xx (lub nie odpowie w ciągu 10 s), Veritra ponawia próbę z wykładniczym opóźnieniem: 1 min, 5 min, 30 min, 2 h, 12 h, 24 h. Po 6 nieudanych próbach webhook jest oznaczany jako niedziałający i sygnalizowany w panelu. Klucz idempotentności pozostaje taki sam dla każdej próby — Twój punkt końcowy MUSI deduplikować żądania (w przeciwnym razie to samo dopasowanie zostanie przetworzone 6×).

Zarządzanie punktami końcowymi przez API

Punktami końcowymi webhooka można zarządzać bez wchodzenia do panelu. Limit: 5 punktów końcowych na konto.

GET/api/v2/account/webhooks
POST/api/v2/account/webhooks
Sekret webhooka jest wyświetlany tylko raz! Gdy żądanie POST /webhooks zakończy się sukcesem, odpowiedź zawiera pole 'secret' — zapisz je natychmiast w swoim środowisku (np. VERITRA_WEBHOOK_SECRET). Nie można go pobrać ponownie. W przypadku utraty użyj /rotate-secret, aby wygenerować nowy (stary traci ważność natychmiast).
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

Weryfikacja podpisu HMAC

Serwer podpisuje ładunek jako HMAC-SHA256(secret, raw_body) i przesyła go w nagłówku X-Signature-256 w formacie sha256=hex. Weryfikuj w czasie stałym — w przeciwnym razie integracja jest podatna na ataki czasowe.

// 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": "Budownictwo w Pradze",
    "matchId": "cm…",
    "tender": { "id": "12345", "title": "…", "estimatedValue": 12500000 }
  }
}

Codzienny digest e-mail

Dzięki emailDigest: true otrzymujesz codzienny e-mail z podsumowaniem nowych przetargów. Digest jest wysyłany rano (5:00 UTC) na adres e-mail przypisany do konta. Możesz go wyłączyć, edytując filtr.

Limity

ParametrTypOpis
Wersja próbna500 żądań/miesiąc7 dni bezpłatnie. Nie wymaga karty ani profilu rozliczeniowego. Domyślne ApiKey.requestsLimit.
Plan płatnybez limituMiesięczny limit zostaje zniesiony. Obowiązuje wyłącznie techniczny limit 100 żądań/h/klucz.
Filtry20Maksymalna liczba aktywnych filtrów na konto.
Punkty końcowe webhooków5Maksymalna liczba aktywnych adresów URL webhooków na konto.

Po przekroczeniu miesięcznego limitu API zwraca kod 429 z nagłówkiem Retry-After. Limit jest resetowany 1. dnia miesiąca.