Nieuw

Aanbestedingsmonitor

Automatische levering van nieuwe aanbestedingen die overeenkomen met uw filters — regio, branchetags, trefwoorden, waardebereik. Pull API + e-maildigest + webhook.

Concepten

Vier begrippen die u in alle eindpunten tegenkomt:

  • Tendereen enkele aanbesteding van een portaal (NEN, VVZ, E-ZAK, TenderArena, enz.). Geïdentificeerd door een numeriek ID.
  • Filtereen opgeslagen set criteria (regio, sector, trefwoorden, waarde). Wanneer een nieuwe aanbesteding aan de criteria voldoet, wordt een treffer gegenereerd.
  • Matcheen nieuwe aanbesteding die binnen uw filter valt. Vertegenwoordigt de koppeling (aanbesteding × filter) + tijdstempel + uw status (gemarkeerd / uitgesloten / bekeken).
  • Taxonomygecontroleerde vocabulaires voor regio's (NUTS), sectoren (sectortags) en CPV-codes die worden gebruikt om aanbestedingen te classificeren.

Snel starten

Filters kunnen op twee manieren worden ingesteld — het resultaat is identiek en een filter kan op elk moment via beide wegen worden bewerkt.

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

Bovenstaande is een ad-hoczoekactie. Voor continue monitoring (nieuwe aanbestedingen die aan uw criteria voldoen) gebruikt u filters + webhook/e-maildigest — zie hieronder.

Aanbeveling: Geef altijd de voorkeur aan industryTags boven categories (CPV) voor filtering. Onze industryTags zijn een combinatie van LLM-aanbestedingsclassificatie en CPV-koppeling — robuust tegen onjuiste CPV-codes. Tsjechische aanbestedende diensten kennen vaak te generieke of niet-gerelateerde CPV-codes toe (doorgaans de generieke '45000000-0' voor bouw in plaats van een specifiek prefix), waardoor een filter op alleen CPV een groot deel van de relevante aanbestedingen mist.

Aanbesteding zoeken (ad hoc)

Voor eenmalige zoekopdrachten door alle actieve aanbestedingen. Maakt geen gebruik van een opgeslagen filter — parameters worden rechtstreeks in de querystring meegegeven.

GET/api/v2/leads/tenders/search

Queryparameters

ParameterTypeBeschrijving
qText*stringZoektekst (volledige tekst in titel + beschrijving).
regionsstringNUTS-bladcodes CSV (CZ010,CZ020).
cpvPrefixesstringCPV-prefixen CSV (45,452).
industryTagsstringIndustrietag-ID's CSV (con_buildings,it_development).
minValuenumberMin. geschatte waarde (CZK). Tenders zonder waarde passeren het filter.
maxValuenumberMax. geschatte waarde (CZK). Tenders zonder waarde passeren het filter.
deadlineFromstring (YYYY-MM-DD)Indieningstermijn >= YYYY-MM-DD.
deadlineTostring (YYYY-MM-DD)Indieningstermijn <= YYYY-MM-DD.
sort"newest" | "deadline" | "value"Sorteren: nieuwste (standaard), deadline (oplopend), waarde (aflopend).
limitnumberAantal resultaten, max. 1000 (standaard: 50). Voor >1k resultaten gebruikt u nextCursor-paginering of /matches/export.
cursorstringKeyset-cursor van vorige pagina (pagination.nextCursor).

Voorbeeld

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

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

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

De body mag recipientEmail bevatten om door te sturen naar een ander e-mailadres. Standaard is user.email.

GET/api/v2/leads/documents/preview

Query: ?url=:original&kind=docx|xlsx. Toegestane hosts: 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)

Catalogus & automatisch aanvullen (openbaar, geen sleutel vereist)

Voor UI-dropdowns en het aanmaken van filters. Servercache van 1 uur. Openbaar, geen authenticatie vereist.

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
Zoeken versus filter: Zoeken is een eenmalige query die de huidige momentopname retourneert. Een filter (zie hieronder) bestaat uit opgeslagen criteria — de server informeert u doorlopend over nieuwe aanbestedingen via webhook of e-maildigest.

Filtervelden en toegestane waarden

Een filter bestaat uit de onderstaande velden. Alle zijn optioneel, behalve `name`. Een leeg veld betekent geen beperking op die dimensie.

regions string[]

NUTS-regio's voor het opgegeven land (?country=CZ) met gelokaliseerde labels.

Filter is pan-EU: De waarden van `regions` zijn NUTS-codes van elk EU-land dat hieronder is vermeld. Het filter retourneert alleen regio's waarvoor u een actief LEADS-abonnement heeft (abonnementsbereik). Als u alleen CZ + SK heeft geabonneerd, worden Duitse NUTS-codes stilzwijgend genegeerd.
Ondersteunde landen (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

Gebruik het volgende om de volledige NUTS-boom voor een specifiek land op te halen:

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
Voorbeeld: NUTS-3-codes voor CZ (regio's) — uitvouwen ▸
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[] Aanbevolen

Brancheclassificatie met meerdere tags. Een aanbesteding kan meerdere tags hebben (bijv. 'stav_pozemni' + 'stav_remesla' voor een renovatie). Het filter matcht een aanbesteding als ten minste één van de gevraagde tags is ingesteld (JSON_OVERLAPS).

Waarom de voorkeur geven aan industryTags boven CPV: Onze industryTags combineren LLM-classificatie van de aanbestedingstitel/-beschrijving met CPV-koppeling en regex-hints — ze vangen relevante aanbestedingen ook wanneer de aanbestedende dienst CPV-codes onzorgvuldig heeft toegewezen. Een puur CPV-filter is exact maar afhankelijk van de discipline van de aanbestedende dienst, die in CZ beperkt is.

48 tags verdeeld over 13 gebieden
🏗️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
Tag ontbreekt? Als u geen tag voor uw branche kunt vinden, schrijf dan naar michal@veritra.io — wij voegen deze toe.

categories string[] Minder nauwkeurig

CPV-prefixen (Common Procurement Vocabulary) van willekeurige lengte — gematcht via `LIKE 'prefix%'`. '45' dekt alle bouwdivisies, '4523' alleen civiele techniek, '45316110' alleen straatverlichting.

35 meest voorkomende CPV-divisies (2-cijferig)
03Landbouw, bosbouw, visserij
09Brandstoffen en energie
15Voedsel en dranken
18Kleding, schoeisel, PBM
22Drukwerk, boeken
30Kantoormachines, IT-apparatuur
31Elektrische machines, kabels, verlichting
32Radio, tv, telecommunicatie
33Medische apparatuur, farmaceutica
34Voertuigen, transport
35Beveiliging, brandweer- en militaire uitrusting
38Laboratorium- en meetinstrumenten
39Meubilair, interieurinrichting
42Industriële machines
44Bouwmaterialen en -constructies
45Bouwwerken
48Software en licenties
50Reparatie en onderhoud
55Catering, accommodatie
60Transport (vervoer)
64Post- en telecomdiensten
65Openbare nutsvoorzieningen (elektriciteit, water)
66Financiële diensten en verzekeringen
70Vastgoed en facilitair beheer
71Architectuur-, ontwerp- en ingenieursdiensten
72IT-diensten (ontwikkeling, integratie, ondersteuning)
73Onderzoek en ontwikkeling
75Openbaar bestuur, defensie
77Landbouw-, bosbouw- en tuinbouwdiensten
79Zakelijke diensten (juridisch, boekhouding, HR, marketing)
80Onderwijs en opleiding
85Gezondheidszorg en maatschappelijke dienstverlening
90Afval, milieu, reiniging
92Cultuur, sport, recreatie
98Overige diensten voor het publiek

De volledige catalogus (9.454 codes) is beschikbaar op /docs/leads/cpv — doorzoekbaar en gegroepeerd per divisie.

keywords string[]

LIKE-overeenkomst in tendertitel en -beschrijving. Hoofdletterongevoelig. OR tussen items. Handig als vangnet als industryTags/CPV niet alles detecteren (bijv. een specifiek tendertype dat alleen in de tekst verschijnt).

"keywords": ["reconstructie", "verlichting", "kleuterschool"]

minValue / maxValue number | null

Geschatte tenderwaardebereik in CZK. U kunt alleen minValue, alleen maxValue of beide instellen.

Belangrijk: Tenders zonder een geschatte waarde (estimatedValue is null of 0) passeren het filter in beide richtingen. Veel aanbestedende diensten maken de waarde niet bekend — het uitsluiten hiervan zou u tenders doen mislopen.

emailDigest boolean

Indien ingeschakeld, ontvangt u dagelijks één e-mail (05:00 UTC) met een samenvatting van nieuwe overeenkomsten voor dit filter. Standaard ingeschakeld. Uitschakelen via filterbewerking. Webhooks worden afzonderlijk geconfigureerd op accountniveau (zie hieronder).

name string · active boolean

`name` — max. 120 tekens, verplicht. `active` — indien false wordt het filter uitgesloten van de cron (geen nieuwe overeenkomsten, geen digest, geen webhook). Gebruik dit om te pauzeren zonder te verwijderen.

Filterlogica

Velden worden als volgt gecombineerd:

OVEREENKOMST = (authority.NUTS3 ∈ expand(regions))
     AND (minValue ≤ estimatedValue ≤ maxValue  OF  estimatedValue is null of 0)
     AND (industry_or_cpv  OF  keyword_match)

# expand(regions): NUTS-codes worden uitgebreid naar NUTS 3-bladeren
# (CZ → 14 regio's, CZ01 → CZ010, CZ010 → CZ010).

industry_or_cpv:
   if    industryTags ingesteld  →  JSON_OVERLAPS(industryTags, tender.industryTags)
   elif  categories ingesteld    →  tender.cpvCode LIKE ANY (categories + "%")
   else                          →  false   (geen industriefilter toegepast)

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

# Als u geen industryTags / categories / keywords instelt,
# worden alle tenders geretourneerd die overeenkomen met regio's en waardebereik.

industryTags heeft voorrang op categories — als u beide instelt, wordt alleen industryTags gebruikt (categories wordt genegeerd). keywords werken onafhankelijk (ze worden met OR gecombineerd met industry_or_cpv).

Filterbeheer-endpoints

Gebruik de beheersleutel (mrw_live_…) om filters te beheren. Dit heeft afzonderlijke snelheidslimieten en verbruikt geen LEADS-credits, zodat filterbeheer geen invloed heeft op het dagelijks ophalen van leads.

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

Bodyparameters (POST / PUT) — gedeeld

ParameterTypeBeschrijving
name*stringFilternaam (max. 120 tekens)
regionsstring[]NUTS-codes (bijv. `CZ010`). Combineer elk niveau. Lege array = alle regio's.
industryTagsstring[]Aanbevolen aanpak. Tag-ID's uit onze taxonomie (bijv. 'stav_pozemni', 'it_vyvoj'). Meerdere tags — OR tussen items.
categoriesstring[]CPV-prefixen van willekeurige lengte (bijv. '45', '4523', '45316110'). Minder nauwkeurig dan industryTags.
keywordsstring[]Trefwoorden — LIKE-overeenkomst in tendertitel en -beschrijving (OR tussen items).
minValuenumber | nullMin. geschatte waarde (CZK). Tenders zonder waarde passeren het filter.
maxValuenumber | nullMax. geschatte waarde (CZK). Tenders zonder waarde passeren het filter.
emailDigestbooleanDagelijkse e-maildigest (standaard ingeschakeld)
activebooleanActief filter (standaard ingeschakeld)

Webhooks worden niet langer per filter geconfigureerd — zie de Webhook sectie hieronder (endpoints op accountniveau). Het webhookUrl veld wordt geweigerd met HTTP 410 om veiligheidsredenen.

Voorbeelden

De onderstaande voorbeelden zijn ter wille van de leesbaarheid in het Engels weergegeven. In productie worden trefwoorden (LIKE %kw%) vergeleken met de aanbestedingstitel en -omschrijving in de taal van de aanbestedende dienst — momenteel altijd Tsjechisch. Stel uw filter dan ook in met Tsjechische termen (bijv. "osvětlení", "veřejné osvětlení").

Bouw in Praag boven 500k CZK

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

IT-ontwikkeling en SW-licenties (alle regio's)

curl -X POST -H "X-Api-Key: mrw_live_…" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "IT-ontwikkeling + SW-licenties",
    "industryTags": ["it_development", "it_licensing", "it_data_ai"],
    "keywords": ["informatiesysteem", "module"]
  }' \
  https://veritra.io/api/v2/leads/filters

Openbare verlichting (CZ) — combinatie van tags en trefwoorden

curl -X POST -H "X-Api-Key: mrw_live_…" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Openbare verlichting (CZ)",
    "industryTags": ["goods_electrical", "con_civil"],
    "keywords": ["verlichting", "straatlantaarn", "openbare verlichting", "lampen"],
    "minValue": 200000
  }' \
  https://veritra.io/api/v2/leads/filters

Een filter deactiveren

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>

Levering — webhook vs. polling

Twee manieren om nieuwe aanbestedingen in uw ERP te ontvangen. De meeste integrators combineren beide.

Webhook (push)

Veritra stuurt binnen ~2 seconden na een overeenkomst een POST naar uw eindpunt. Gebeurtenis leads.match.created met volledige aanbestedingspayload.

Voordelen: realtime, geen polling-overhead, servergestuurde filtering.

Nadelen: vereist een publiek toegankelijk eindpunt (HTTPS), HMAC-verificatie en idempotentiebeheer.

Polling (pull)

Uw ERP roept periodiek GET /api/v2/leads/matches?since=… aan (bijv. elke 5 min). Geeft overeenkomsten terug sinds het opgegeven tijdstempel.

Voordelen: geen publiek eindpunt vereist, eenvoudige implementatie, herstart-bestendig.

Nadelen: latentie 5–10 min, beperking door rate-limit (60 req/min beheer-API), verspilde lege responsen.

Aanbevolen: primaire webhook + dagelijkse polling (since=yesterday) als vangnet in geval de webhook zijn retry-budget heeft uitgeput.

Overeenkomsten ophalen

GET/api/v2/leads/matches

Queryparameters

ParameterTypeBeschrijving
filterIdstringFilteren op een specifiek filter
qTextstringZoektekst (volledige tekst in titel + beschrijving).
sincestring (ISO 8601 datetime)Alleen overeenkomsten vanaf deze datum (ISO-datum/tijd)
deliveredbooleanfalse = alleen niet-afgeleverd, true = alleen afgeleverd
view"starred" | "excluded"Speciale weergave: starred (favorieten) | excluded (verborgen).
sort"newest" | "deadline" | "value"Sorteren: nieuwste (standaard), deadline (oplopend), waarde (aflopend).
limitnumberAantal resultaten, max. 1000 (standaard: 50). Voor >1k resultaten gebruikt u nextCursor-paginering of /matches/export.
cursorstringKeyset-cursor van vorige pagina (pagination.nextCursor).

Geretourneerde overeenkomsten worden automatisch gemarkeerd als delivered=true. Elke aanvraag verbruikt 1 credit.

matchId-formaten

matchId heeft twee formaten op basis van herkomst:

  • cm… (cuid-prefix) — geretourneerd vanuit de voorberekende LeadMatch-tabel (dagelijkse cron). Stabiel over aanroepen heen, bruikbaar voor markeren-als-bekeken.
  • live-12345 Synthetische prefix voor live-gedetecteerde overeenkomsten via zoek-/browsemodus (parameter qText, view=starred/excluded). Niet aanwezig in de LeadMatch-tabel → markeren-als-bekeken heeft geen effect. tenderId is het achtervoegsel na het streepje.

Voorbeeld

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

Respons

{
  "data": [
    {
      "matchId": "cm…",
      "filterId": "cm…",
      "filterName": "Bouw in Praag",
      "matchedAt": "2026-04-03T06:00:00.000Z",
      "viewedAt": null,
      "delivered": true,
      "tender": {
        "id": 12345,
        "title": "Brugreconstructie reg.nr. 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 }
}

Geef voor de volgende pagina pagination.nextCursor door als ?cursor=-parameter.

Matchacties

Ster (favoriet), uitsluiten (verbergen) en bekijken (markeren als gelezen) zijn aanbestedingsspecifieke voorkeuren opgeslagen in UserTenderPreference.

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

Respons: zelfde structuur als het lijstitem van 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

Geen actie voor synthetische live-:id-matches (geen rij om te markeren).

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

Taxonomie

Statische referentiecatalogi voor filterwaarden. Taalafhankelijke 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

Respons: boomstructuur divisies → groepen → klassen → categorieën → subcategorieën.

Webhook

Webhook-eindpunten (CRUD, geheimrotatie) zijn op accountniveau — eenmalig gedocumenteerd in Account-API → Webhooks. Dit gedeelte behandelt alleen de leads-specifieke eventpayload (leads.match.created) en HMAC-verificatie.

Eventlevering

Bij een nieuwe match sturen wij een event van het type leads.match.created met een HMAC-SHA256-handtekening in de X-Signature-256 header, idempotentiesleutel in X-Idempotency-Key, en het type in X-Event-Type. Bij een fout proberen we het opnieuw met exponentiële backoff gedurende maximaal ~33 uur.

Herhaalpolicy

Als uw eindpunt een niet-2xx-respons retourneert (of niet binnen 10 s reageert), probeert Veritra het opnieuw met exponentiële backoff: 1 min, 5 min, 30 min, 2 u, 12 u, 24 u. Na 6 mislukte pogingen wordt de webhook als mislukt gemarkeerd en gemarkeerd in het dashboard. De Idempotency-key blijft gelijk bij elke poging — uw eindpunt MOET dedupliceren (anders wordt dezelfde overeenkomst 6× verwerkt).

Eindpunten beheren via API

U kunt webhookeindpunten beheren zonder het dashboard te openen. Limiet: 5 eindpunten per account.

GET/api/v2/account/webhooks
POST/api/v2/account/webhooks
Het webhookgeheim wordt slechts eenmalig getoond! Wanneer POST /webhooks slaagt, bevat de respons een veld 'secret' — sla dit onmiddellijk op in uw omgeving (bijv. VERITRA_WEBHOOK_SECRET). Het kan later niet meer worden opgevraagd. Indien verloren, gebruik dan /rotate-secret om een nieuw geheim te genereren (het oude geheim wordt onmiddellijk ongeldig).
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-handtekeningverificatie

De server ondertekent de payload als HMAC-SHA256(secret, raw_body) en stuurt dit mee in de header X-Signature-256 in het formaat sha256=hex. Verifieer dit in constante tijd, anders is de integratie kwetsbaar voor timing-aanvallen.

// 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": "Bouw in Praag",
    "matchId": "cm…",
    "tender": { "id": "12345", "title": "…", "estimatedValue": 12500000 }
  }
}

E-maildigest

Met emailDigest: true ontvangt u dagelijks een e-mail met een samenvatting van nieuwe aanbestedingen. De digest wordt 's ochtends (05:00 UTC) verzonden naar het e-mailadres van uw account. U kunt deze uitschakelen door het filter te bewerken.

Limieten

ParameterTypeBeschrijving
Proefperiode500 verzoeken/maand7 dagen gratis. Geen creditcard of factureringsprofiel vereist. Standaard ApiKey.requestsLimit.
Betaald abonnementonbeperktMaandelijkse limiet is opgeheven. Alleen de technische snelheidslimiet van 100/u/sleutel is van toepassing.
Filters20Maximaal aantal actieve filters per account.
Webhook-eindpunten5Maximaal aantal actieve webhook-URL's per account.

Bij overschrijding van de maandlimiet retourneert de API 429 met een Retry-After-header. De limiet wordt op de 1e van de maand gereset.