Skip to content

Verifento API

Verify a customer against a shared incident database – straight from your own system.

Contents

What Verifento is. A shared incident database for rental and leasing businesses. When a customer damages a car at one company, fails to pay for a machine at a second or does not return skis at a third, each of those companies can report it. Before the next rental the others can ask – and learn that a record exists before the keys change hands.

This API is that question in machine form. One call from your reservation system, an answer with a risk level. Nothing is retyped and nothing is installed.

The answer is input, not a decision. We return a risk level and record counts – never “rent / refuse”. Article 22 of the GDPR prohibits a decision based solely on automated processing that produces legal effects for a person or similarly significantly affects them. Anyone who wires an automatic rejection onto our output does so on their own responsibility and must not claim we handed it to them that way.

In five minutes

You create a key in the dashboard under Settings → API keys. You get two kinds: live (vf_live_…) and test (vf_test_…). Start with the test one – it never touches the database.

curl -X POST https://verifento.com/api/v1/verify \
  -H "Authorization: Bearer vf_test_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"typ":"phone","identifikator":"+421900000004"}'
{
  "riziko": "high",
  "zaznamy": {
    "negativne": 9, "pozitivne": 0, "nahlasujucich_firiem": 5,
    "potvrdene": 9, "podozrenie": 0, "sporne": 0, "obet": 0
  },
  "posledny_incident": "2026-08-30",
  "kategorie": ["non_payment", "theft", "damage"],
  "testovaci_rezim": true,
  "upozornenie": "The result is input for a human decision, not the decision itself…",
  "id_poziadavky": "0f7c…"
}

Authentication

The key is sent in the Authorization: Bearer … header. Only its fingerprint (HMAC-SHA256) is stored, so it cannot be read back or recovered from our side – if you lose it, a new one is issued.

You can attach an IP allowlist to a key, ranges included (203.0.113.0/24). We recommend it: a leaked key is otherwise usable from anywhere in the world, while your reservation system calls from one or two known addresses.

Verifying one customer

POST /api/v1/verify

FieldRequiredDescription
typánoKind of identifier – one of: phone email doc name_dob iban id_card passport license residence_permit ico
identifikatoránoThe value exactly as the customer has it. Normalisation (dialling codes, spaces, capitals) is done by us.
datum_narodeniaonly with name_dobIn YYYY-MM-DD format.

Risk levels

ValueMeaning
neznamyThe person is not in the network. Nobody has ever checked them or rented to them – we know nothing about them. This is not confirmation that they are fine.
greenThe person is in the network and has no active record.
positive_onlyPositive references only, no problems.
lowA minor record.
mediumA more serious or repeated record.
highSerious records.
The list may grow. Adding a new level is not a change that would require a new version. Your code therefore needs a branch for an unknown value and must not crash on it – the safe reading of an unknown level is “check this by hand”. Mind the difference between neznamy and green. The first means we know nothing about the person; the second that we know them and hold nothing against them. Treating the absence of records as proof of reliability is the most common integration mistake – and for the first customer in a young network, the state neznamy is entirely normal.

Breakdown of negative records

zaznamy splits the negative records by their status: potvrdene (confirmed), podozrenie (suspected), sporne (disputed by the customer) and obet. The fields were added on 27 September 2026 without a new version – code written for the older response keeps working.

obet is not a reason to turn the customer away. It counts protective records of a person whose identity was misused – someone else rented under their name. Such a customer can come back with riziko: green and a non-zero negativne; without this field you would not know why.

Concurrent rentals

The verification response contains a subezne_prenajmy field – how many other companies have an unreturned rental with that person right now , split into the same and a different business segment.

It is not part of the risk and must not be used as such. riziko describes reported records backed by stated evidence. A concurrent rental is not an event – it is a state, and it has ordinary explanations: a family with two cars, a company renting for its staff, a move that also needs a trailer, a broken item replaced elsewhere. Automatic rejection based on this number is out of the question (Art. 22 GDPR) – just as with the traffic light.

The split by segment is not cosmetic: a car in one city and skis in the mountains in the same week is a holiday. Two vans from two companies on the same day is something you can politely ask about. Without the split both cases would look identical.

null means “we do not know”, not “zero”. The signal is computed by a separate query and its failure does not bring the verification down – you get the risk level and null in this field. Anyone who reads that as zero turns not knowing into a claim about a specific person.

It is stored nowhere – it is computed at the moment of verification from rentals that are already in the system anyway. Which company it is, what is rented or until when is never returned: you do not need it for your decision, and a shared network depends on verification not telling you who your competitors trade with. In the sandbox the field is always null.

Batch verification

POST /api/v1/verify/batch – up to 50 items at once. Useful when you run through a week of bookings in the morning, or assess a list imported from another system.

{
  "polozky": [
    { "typ": "phone", "identifikator": "+421901234567", "referencia": "REZ-001" },
    { "typ": "email", "identifikator": "jan@example.com", "referencia": "REZ-002" }
  ]
}

The vysledky array has the same length and order as the input, so it can be matched by index; if you want your own ID, send referenciaand get it back untouched.

Partial success is deliberate. The response is 200 even when some items fail – each one carries either a result or an error. If the whole batch failed on a single malformed phone number, you would have to break it into individual calls anyway, which is exactly what the batch exists to avoid.

Reporting an incident

POST /api/v1/incidents – the opposite direction to verification. A network grows by what is added to it; a member who only asks takes without giving. If you keep damages in your own system, you do not have to retype them into our dashboard.

The key must be allowed to. The incidenty:zapisscope is enabled in the dashboard under API keys. New keys do not have it – a key allowed to ask is not automatically allowed to assert. Without it the call returns 403.
curl -X POST https://verifento.com/api/v1/incidents \
  -H "Authorization: Bearer vf_live_…" \
  -H "Idempotency-Key: 6f1c0a8e-3b2d-4e77-9a51-0c5d2e8b4417" \
  -H "Content-Type: application/json" \
  -d '{
    "typ": "phone",
    "identifikator": "+421901234567",
    "kategoria": "non_payment",
    "zavaznost": "high",
    "popis": "Invoice unpaid after two reminders.",
    "datum": "2026-09-08",
    "interny_odkaz": "FA-2026-118",
    "dokazy": ["invoice_unpaid", "contract_ref"]
  }'

The Idempotency-Key header is required. Send a new unique string with every new report (a UUID, for instance); if the connection breaks and you retry, send the same one. You get the original response and an Idempotency-Replayed: true header instead of a second record. The key is valid for 24 hours. The same key with different content is a 422 – that is a mistake on your side and we would rather tell you than silently discard the second report.

A negative record without evidence is rejected. Pole dokazyarray must hold at least one known value – contract_ref,signed_protocol, photo_evidence,invoice_unpaid, police_report,damage_report, insurance_claim,internal_record, witness_statement,execution_order, court_decision,gps_record. Unknown values are discarded and if none remains, the call ends in 400. The credibility of the whole network rests on this rule – a record with nothing behind it is just an assertion.

zavaznost je low,medium alebo high. Any other value is rejected – including critical, which exists in the database but the traffic light does not understand. datum is the date of the event and must not be in the future. A positive record is sent with"kladny": true and needs no evidence.

A test key passes every check and writes nothing – the response carries"zapisane": false a "id": null. You can debug the whole integration without creating a network record about a person who does not exist.

Downloading your own records

GET /api/v1/incidents – your records, newest first. Useful for recovery after a crash, for checking that a report really landed, or for migrating to another system.

Requires the incidenty:citanie – nie incidenty:zapis. They are two different things and two different harms if a key leaks: a write key can assertsomething about a person, a read key can download your entire record history . An integration that only submits damages has no reason to read – which is why we keep them apart.
curl "https://verifento.com/api/v1/incidents?na_stranu=50&kladne=false" \
  -H "Authorization: Bearer vf_live_…"

Pagination is by cursor, not page number. Return the dalsi_kurzor value in the poparameter. When it is null, that was the last page – do not infer the end from the record count, the last page can be full.

Why not OFFSET: if a new incident appeared between two pages, one record would show twice and another not at all. When downloading history into your own system, that is silent data loss.

Filters: od, do (event date), kladne, kategoria,zavaznost, na_stranu (1–100, default 50). We ignore an unknown parameter but reject an invalid value of a known one – an ignored filter would silently not apply and you would get more than you asked for.

The customer fingerprint is not returned even to you. For matching against your own records there is interny_odkaz – a field you filled in yourself and control. A blind index is pseudonymised personal data, and the only thing it could be used for outside Verifento is building your own register of people.

Inactive records are not returned: either their retention period expired, or they are frozen because of an objection filed under the GDPR. In the sandbox the list is always empty.

Correcting a record

PATCH /api/v1/incidents/{id} – the right to correct belongs with the right to write. If correction were possible only in the dashboard, most companies would not bother, and the network would keep a record about a person that both sides know is wrong. It works on every record of your company, including one created through the form.

curl -X PATCH https://verifento.com/api/v1/incidents/d607f3c0-… \
  -H "Authorization: Bearer vf_live_…" \
  -H "Content-Type: application/json" \
  -d '{"zavaznost": "low", "dovod": "The damage was smaller than it first looked."}'

You can change popis, kategoria,zavaznost a kladny. Thedovod je required and goes into the audit trail – correcting a record about a person should still be explainable a year later. The response carrieszmenene with the list of fields that actually changed; an empty array means the values sent were already in the record, not that something went wrong.

Do not send the Idempotency-Key header here.It is required on write; here it would be ceremony without content, because a correction carries absolute values, so applying it twice gives the same result.

Withdrawing a record means flipping it to positive("kladny": true), not deleting it – deletion via the API is not possible. The audit history remains and the person concerned can find out what happened; a silent disappearance would look like a system fault. The flip also resets the category toother and the severity to low, so that a record which is now commendatory does not glow red in the traffic light.

Another company’s record and a non-existent one both return the same 404. A difference would turn the endpoint into a tool for discovering whether a given ID exists in the network.

Test environment

A key prefixed vf_test_ never touches the database. For the numbers below it returns fixed responses – one per risk level, so every branch of your code can be exercised, including the one for a risky customer. In these responses all negative records are counted as potvrdene.

Reporting with a test key checks the identifier exactly like the live key: a malformed identifikator returns 400 in the sandbox too, so you find the mistake before going live.

IdentifierRiskWhat it represents
+421900000000greenZákazník bez akéhokoľvek záznamu.
+421900000001positive_onlyLen kladné hodnotenia od troch firiem, žiadny problém.
+421900000002lowJedno neskoré vrátenie, inak v poriadku.
+421900000003mediumOpakované poškodenie a porušenie podmienok.
+421900000004highVážne záznamy od piatich firiem vrátane neuhradenia a krádeže.

The 900 prefix is unassigned in Slovakia, so a test cannot hit a real person. The same scenarios are available through e-mail addresses of the formvysoke@test.verifento.com.

The sandbox proves you handle every shape of response. It does not prove your connection to real data – so switch the integration to a live key once before going live.

Errors

The code in the chyba je stable field – branch on it. The text in sprava is for humans and changes without notice. Every response carries id_poziadavky; quote it when you report something to us.

StatusCodeWhat to do
400neplatny_typ, chyba_identifikator, neplatny_identifikatorBad input. Retrying will not help.
401chyba_kluc, neplatny_kluc, zruseny_klucCheck the header and that the key is valid.
403firma_neaktivna, ip_nepovolenaContact us, or add the address to the allowlist.
429prekroceny_limitWait until the time in X-RateLimit-Reset.
503overenie_zlyhaloTry again.
A 503 is not “no record”. This is the most important sentence in the whole documentation. A customer with no record and a failed verification could both be returned as an empty result – and then you could not tell them apart. That is why a failure returns 503 and not 200. Anyone who only distinguishes “passed / failed” will not declare an unknown person problem-free.

Key rotation

A key can be replaced without downtime. In the dashboard you click „Zrotovať kľúč“, choose an overlap (1–30 days) and get a new one. Until the overlap ends, obaare valid, so you switch the integration when it suits you; after the deadline the old one expires on its own.

The new key inherits scopes, the IP allowlist and the sandbox flag – nothing has to be reconfigured after rotation.

While the overlap runs, every response carries a Verifento-Key-Expires header with the deadline until which the old key is valid. It is a header and not a body field on purpose: you parse the response against your own schema and would miss a new field – a header lands in your access log, where you notice it without changing any code. After the deadline the old key returns 401 zruseny_kluc that second, not „niekedy do rána“.

Limits

600 verifications per hour per company, 1,200 per hour per IP address. Batch has its own limit of 120 requests per hour, reporting 60 per hour – a write is an assertion about a person that other companies will see, and a misconfigured integration would flood not the database but the network. Test keys have the same limits, counted separately – testing never uses up the limit of your live integration. Every response carries theX-RateLimit-Limit,X-RateLimit-Remaining aX-RateLimit-Reset headers – no guessing needed. Need more? Write to us and we will work it out.

Data protection

Identifiers are never stored in readable form. We use a blind index – HMAC-SHA256 with a secret key. The database holds only the fingerprint, so the platform does not know whom you checked; it only knows that you asked. That is also why a customer cannot be “listed” out of it: a match can only be confirmed against a specific identifier you already have.

It is never returned which company filed a record – only how many distinct companies did. A shared network depends on reporting not exposing the reporter to a competitor.

Webhooky

Verification is a question you ask. A webhook is the opposite direction: when a record is filed elsewhere about a customer you have out on rental right now, we send you a message about it. A car is often rented for three weeks and a record can appear in the middle – without a webhook you would learn about it only on return.

You only get messages about customers you have a relationship with. That is, those with an ongoing rental or one that ended within the last 30 days. It is not a broadcast of incidents – the lawfulness of the whole feature rests on that (legitimate interest, Art. 6(1)(f) GDPR). Without that relationship we would be sending you personal data about people who are none of your business.

What a message looks like

POST https://your-system.example/verifento/webhook
Content-Type: application/json
Verifento-Event: incident.vytvoreny
Verifento-Delivery: 7f3a…
Verifento-Signature: t=1789167801,v1=9c2f…

{
  "udalost": "incident.vytvoreny",
  "verzia": 1,
  "vytvorene": "2026-09-12T08:14:22.001Z",
  "zakaznik": { "odtlacok": "01fecb7b…", "typ": "phone" },
  "incident": { "zavaznost": "high", "kategoria": "damage", "datum": "2026-09-12" },
  "riziko": "high",
  "upozornenie": "The result is input for a human decision…"
}

The customer is a fingerprint, not a phone number. You can compute the same fingerprint from the data in your own system (exactly as when you call verification), so you know precisely who it is. Sending the phone number would turn the webhook into a data-leak channel – to an address anyone entered themselves.

You do not learn which company filed the record – same as with verification. The riziko field is the customer state including this incident, that is, what you would get if you asked at that moment. It saves you one call.

Verifying the signature – do not skip this

Without signature verification, anyone who guesses your address can plant a fake incident and push you into cancelling a rental for an innocent person. The signing key is in the dashboard under Settings → Webhooks.

const [t, v1] = header.split(',').map(x => x.split('=')[1])
const expected = crypto
  .createHmac('sha256', SIGNING_KEY)
  .update(`${t}.${rawBody}`)
  .digest('hex')

// constant-time comparison, not ===
const ok = crypto.timingSafeEqual(Buffer.from(v1), Buffer.from(expected))

// and reject messages older than a few minutes – otherwise an old one can be replayed
if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return reject()

What is signed is čas.telo, not the body alone. The timestamp is therefore part of the signature and cannot be rewritten – otherwise an attacker would take an old message, change its time and replay it as new.

Response and retries

Reply with anything in the 2xxrange. We do not care about the body. On any other response we retry – five times, with increasing delay (1 minute, 5, 30, 2 hours, 6). On 4xx (except 408 and 429) we do not retry: when a server says it will not accept such a request, repeating it changes nothing.

After twenty failed messages the webhook disables itself and the dashboard shows the reason. You can re-enable it with one click; the counter resets.

Reply fast and process the message afterwards. We wait at most 10 seconds; a slower response counts as a failure even if you did process it – and you will get it again. The safe approach is to store Verifento-Deliveryand discard duplicates.

Versioning

The version is in the path (/api/v1/), so it is visible in logs and in configuration. Within v1 the following holds:

  • fields may be added, never renamed and never removed;
  • the values of riziko may be extended;
  • error codes are stable, their texts are not;
  • /api/verify without a version stays forever and does the same thing.

Machine-readable description

The full OpenAPI 3.1 description is at https://verifento.com/openapi.json. It is generated from the same code that serves the calls, so it cannot drift from reality. A client in any language can be generated from it.

Next

Create an account · Frequently asked questions · Data protection