Identificatori v1 / Identificatori
Validate a personal numeric code
It does not go through the caching service: the value must never end up in a cache key, not even hashed. It is computed on every request; it is pure arithmetic anyway.
POST
https://oneapi.ro/api/identificatori/v1/cnp
Request body
Sent as JSON in the request body (Content-Type: application/json) — not in the query string.
NameTypeDescriptionAccepted values
cnprequired
text
Maximum 20.
max 20 car.
A man born in 1980
Request body
{
"cnp": "1800101410013"
}
A woman, leap year
Request body
{
"cnp": "2990230412345"
}
Born after 2000
Request body
{
"cnp": "5041225410010"
}
Query parameters
This route accepts no query parameters.
How it behaves
- The body has a single field,
cnp(string, required, at most 20 characters). Body fields do not appear in the parameter table, hence the description here. - Why POST. In a GET, the code would land in the server access logs, the browser history and the next page's
Refererheader — three places you cannot get it out of. The equivalent GET route answers 405 and points here. - Zero retention. The code is never written anywhere: not to the database, not to logs, not into a cache key. The response does not even echo the value —
valoareis always(ascuns), because the caller already has it and we have no reason to put it in a body that may end up in their own logs. Error messages do not repeat it either. - The components are DERIVED from the code sent, not looked up: date of birth, sex and county are literally written into it. They do not confirm the person exists.
- There is no generator endpoint for valid codes, not even "for testing". A generator would mostly help someone trying to get past a validation, not someone trying to validate.
- The county codes in the personal numeric code are neither SIRUTA codes nor vehicle-plate letters: they are the alphabetical order of the counties as of 1978. Călărași and Giurgiu, created in 1981, received 51 and 52. Code 47 is Sectorul Agricol Ilfov, which existed from 1981 to 1997 and appears in codes issued then.
- For a leading digit of 7 or 8 (foreign residents) and 9 (no Romanian citizenship), the century is not encoded, so the date of birth cannot be derived — it is absent from the components, which is correct rather than an oversight.
What it is for
- Catch a typo while a form is being filled in, before the data travels further.
- Check a file for consistency: the declared date of birth must match the one in the code.
- Derive a person's age from the code you already hold, without querying a database.
Response structure
All fields below live inside data.
FieldTypeDescription
tip
string
The type used for validation.
valoare
string
Always
(ascuns). The code received is not echoed.
valid
boolean
Whether it passes the check digit and all consistency checks.
componente.data_nasterii
string
Derived from the code. Absent for foreign residents, where the century is not encoded.
componente.varsta
integer
Age in completed years, at the time of the request.
componente.sex
string
masculin or feminin, from the leading digit.
componente.judet_nastere
string
The county or Bucharest sector of registration.
componente.cod_judet
string
The code as written in the personal numeric code, two digits.
componente.rezident_strain
boolean
Leading digit 7 or 8.
componente.fara_cetatenie_romana
boolean
Leading digit 9.
observatie
string
A note on how to read the response: a computation convention or a limit of the data.
motive
array
What is wrong: check digit, non-existent date, unallocated county code.
Response examples
body: {"cnp": "1800101410013"}
POSThttps://oneapi.ro/api/identificatori/v1/cnp
{
"data": {
"tip": "cnp",
"valoare": "(ascuns)",
"valid": true,
"componente": {
"data_nasterii": "1980-01-01",
"varsta": 46,
"sex": "masculin",
"judet_nastere": "București — Sectorul 1",
"cod_judet": "41",
"rezident_strain": false,
"fara_cetatenie_romana": false
},
"observatie": "Componentele sunt DERIVATE din CNP-ul trimis — nu vin dintr-o bază de date și nu confirmă că persoana există. CNP-ul nu se stochează și nu se loghează nicăieri."
}
}
body: {"cnp": "2990230412345"}
POSThttps://oneapi.ro/api/identificatori/v1/cnp
{
"data": {
"tip": "cnp",
"valoare": "(ascuns)",
"valid": false,
"motive": [
"data nașterii 1999-02-30 nu există",
"cifra de control nu se potrivește"
]
}
}
body: {"cnp": "5041225410010"}
POSThttps://oneapi.ro/api/identificatori/v1/cnp
{
"data": {
"tip": "cnp",
"valoare": "(ascuns)",
"valid": false,
"motive": [
"cifra de control nu se potrivește"
]
}
}
Error codes
| Code | Slug | When it occurs |
|---|---|---|
| 401 | neautentificat | Token missing, invalid, expired or revoked. |
| 403 | feature_indisponibil | The current plan does not include the feature the route depends on. |
| 422 | validare | The cnp field is missing or too long. The message does NOT repeat the value received. |
| 429 | cota_depasita | The plan's monthly quota has been exceeded (see Retry-After). |
All errors share the same shape. eroare.cod is stable and is the value you should branch on in code — not the text in mesaj, which may change.
{
"eroare": {
"cod": "validare",
"mesaj": "The cnp field is missing or too long. The message does NOT repeat the value received."
}
}