Full OCR + Datavalid
This endpoint does two things in one call:
- Full OCR — classifies the document sent and extracts its fields, like Full OCR and with the differences described below, including the cross-check against the Brazilian Federal Revenue Service.
- Datavalid — sends the extracted data to Datavalid, the validation service run by SERPRO (the Brazilian federal data processing agency), and asks, field by field, whether it checks out against the government's official record.
The difference from plain Full OCR is the second step. Full OCR answers "what is written on this document, and does it match the Federal Revenue Service". Here you also get "and does it match Datavalid" — name, parents' names, date of birth, driver's license status, ID card number, and the facial biometrics.
This endpoint does not perform liveness (use Liveness) and does not compare the face on the document with a selfie you send. If your case is checking a selfie against the official biometrics, use Face Match + Datavalid.
Request
POST/full-ocr-and-datavalid/v3Headers
Authorization: ApiKey <your-api-key>This endpoint also accepts the JWT token, in the format Authorization: Bearer <accessToken>. See JWT Token.
SERPRO keys
The Datavalid query can be made with our SERPRO access or with your own credentials. What enables the second mode is the scope of your API key:
| Scope of your key | Credential headers |
|---|---|
nextid.bureaus.fullOcrAndDatavalid | Optional |
nextid.bureaus.fullOcrAndDatavalid.customerKeys | Required |
Send the credentials in two headers:
x-customer-key: <your-serpro-key>
x-customer-secret: <your-serpro-secret>🚧 Send the raw value, with no prefix
The two headers are read exactly as you send them. Do not put Key or Secret before the value: the prefix would become part of the credential sent to SERPRO, and the query would fail with 401.
‼️ With one of the two headers missing, the request never starts
With the customerKeys scope, both headers are mandatory. If either one is missing or empty, the request is rejected immediately with 401 and the message Datavalid customer keys are required — before any file is read. Sending only one of the two is the same as sending neither.
🚧 If you send the headers, they get used
The two headers are read and forwarded to SERPRO whenever they are present, under either scope. The customerKeys scope determines that they are required, not that they start being used.
In other words: an API key with the ordinary scope that sends x-customer-key and x-customer-secret starts querying with your credentials. If the intent was to use Nextcode's access, do not send the headers.
TIP
This is the only endpoint that accepts your own SERPRO keys. Face Match + Datavalid always uses Nextcode's access and ignores those two headers.
Parameters
| Parameter | Description | Required |
|---|---|---|
| federalRevenueNumber | A CPF (the Brazilian individual taxpayer number) you already know for the person, to guide the search at the Federal Revenue Service. Accepts formatting. Can be repeated for more than one CPF. | No |
🚧 With no CPF resolved, there is no Datavalid query
The Datavalid query is made by CPF. That CPF can come from the document itself, read by OCR, or from the federalRevenueNumber parameter — but it has to exist.
When no CPF is resolved for the document, the Datavalid step is skipped silently: the response is 200, with the whole extraction as normal and "datavalid": {}. Passing federalRevenueNumber when you already know it is the safest way to guarantee the query.
🚧 federalRevenueNumber is validated by its check digits
A CPF that fails validation brings the whole request down with 422, before any processing.
Accepted files
| Item | Value |
|---|---|
| Formats | application/pdf, image/png, image/jpeg |
| Files per call | up to 7 files |
| Maximum size (multipart) | 15 MB per file |
The form field names are up to you — they come back in the response. Sending more than seven files is rejected with 422.
The front and back of the same document, sent in different files or pages, are merged into a single data entry. That holds on both versions of this endpoint.
Besides multipart/form-data, the files can be sent as JSON, in the base64 field, as described in Sending files.
Example request
With Nextcode's access:
curl -i -X POST 'https://api-homolog.nxcd.app/full-ocr-and-datavalid/v3?federalRevenueNumber=123.456.789-09' \
--header 'Authorization: ApiKey YOUR_API_KEY' \
--form 'frente=@./cnh-frente.jpg' \
--form 'verso=@./cnh-verso.jpg'With your own SERPRO credentials:
curl -i -X POST 'https://api-homolog.nxcd.app/full-ocr-and-datavalid/v3?federalRevenueNumber=123.456.789-09' \
--header 'Authorization: ApiKey YOUR_API_KEY' \
--header 'x-customer-key: YOUR_SERPRO_KEY' \
--header 'x-customer-secret: YOUR_SERPRO_SECRET' \
--form 'documento=@./cnh.jpg'Response
This section describes v3, the recommended version. v2 returns a data in a different format — see Versions.
The envelope is the API standard:
| Field | Description | Type |
|---|---|---|
| id | Unique identifier of the request | String |
| version | API version that served the call | String |
| data | List with one entry per document recognized. Comes back empty when none was recognized. | Object[] |
| metadata | Metadata of the request | Object |
Each item of data carries the blocks below:
| Block | What it is |
|---|---|
datavalid | What SERPRO answered about the extracted data |
extraction | The fields read from the document |
federalRevenueData | The cross-check against the Federal Revenue Service |
classification | Which document it is, from which country, which sides |
face | The face found on the document |
metadata | Files sent and processing time. Lives at the root, outside data[]. |
data[].datavalid
The result of the validation at SERPRO. Every field named after a piece of data is a check boolean, not the value of that data: name: true means "the name read from the document checks out against the official record", and does not return the official name.
| Field | Description | Type |
|---|---|---|
| datavalid.federalRevenueNumberAvailability | Indicates whether the CPF exists in the official database | Boolean |
| datavalid.federalRevenueNumberStatus | Indicates whether the CPF's registration status in the official database is regular | Boolean |
| datavalid.name | Indicates whether the name read matches the one in the official database | Boolean |
| datavalid.nameSimilarity | Similarity between the name read and the one in the official database, from 0 to 1 | Number |
| datavalid.mothersName | Indicates whether the mother's name matches | Boolean |
| datavalid.mothersNameSimilarity | Similarity of the mother's name, from 0 to 1 | Number |
| datavalid.fathersName | Indicates whether the father's name matches | Boolean |
| datavalid.fathersNameSimilarity | Similarity of the father's name, from 0 to 1 | Number |
| datavalid.birthdate | Indicates whether the date of birth matches | Boolean |
| datavalid.gender | Indicates whether the gender matches | Boolean |
| datavalid.isBrazilian | Indicates whether the person is recorded as Brazilian in the official database | Boolean |
| datavalid.croppedFaceBase64 | Result of comparing the face on the document with the official biometrics. It carries no base64. | Object |
| datavalid.croppedFaceBase64.availability | Indicates whether facial biometrics are on record for that CPF | Boolean |
| datavalid.croppedFaceBase64.similarity | Similarity between the face on the document and the official biometrics, from 0 to 1 | Number |
| datavalid.croppedFaceBase64.probability | Probability band: VeryHigh, High, Low or VeryLow | String |
| datavalid.document | Check of the identity document cited | Object |
| datavalid.document.type | Indicates whether the document carries the Brazilian ID card (RG) number | Boolean |
| datavalid.document.number | Indicates whether the RG number matches the one in the official database | Boolean |
| datavalid.document.numberConfidence | Similarity of the RG number, from 0 to 1 | Number |
| datavalid.driversLicense | Check of the driver's license (CNH) data | Object |
| datavalid.driversLicense.status | Indicates whether the driver's license is confirmed in the SERPRO database | Boolean |
| datavalid.driversLicense.issuedAt | Indicates whether the issue date matches | Boolean |
| datavalid.driversLicense.expireAt | Indicates whether the expiry date matches | Boolean |
| datavalid.driversLicense.firstIssuedAt | Indicates whether the first licensing date matches | Boolean |
| datavalid.driversLicense.registerNumber | Indicates whether the register number matches | Boolean |
| datavalid.driversLicense.driversLicenseCategory | Indicates whether the category matches | Boolean |
‼️ datavalid comes back as {} when the query did not happen
The empty object is not a failure of the document: it is the absence of a query. It happens whenever no CPF was resolved for that document.
Code that reads data[0].datavalid.name gets an error in that case. Check that the object is not empty before reading any field of it.
🚧 croppedFaceBase64 carries no face crop at all
Despite the name, datavalid.croppedFaceBase64 is the object holding the biometric comparison result — availability, similarity and probability. None of the three is base64, and the Datavalid response returns no image.
The name comes from the Datavalid API and does not change. The crop of the face on the document, which really is base64, is in data[].face.croppedBase64.
The document and driversLicense blocks depend on the document sent
driversLicense only has content when the document analyzed is a CNH; document carries the check of the RG. For a document that does not feed those fields, they come back with the values missing.
{
"datavalid": {
"federalRevenueNumberAvailability": true,
"federalRevenueNumberStatus": true,
"name": true,
"nameSimilarity": 1,
"gender": true,
"birthdate": true,
"mothersName": true,
"mothersNameSimilarity": 0.97,
"fathersName": false,
"fathersNameSimilarity": 0.62,
"isBrazilian": true,
"croppedFaceBase64": {
"availability": true,
"similarity": 0.96,
"probability": "VeryHigh"
},
"document": {
"type": true,
"number": true,
"numberConfidence": 1
},
"driversLicense": {
"status": true,
"issuedAt": true,
"expireAt": true,
"firstIssuedAt": true,
"registerNumber": true,
"driversLicenseCategory": true
}
}
}data[].extraction
The fields read from the document, in a flat object. The set of keys depends on the type and the model of the document: a CNH carries registerNumber, driversLicenseCategory, expireAt and company; an RG carries documentId, issuer, origin and naturalness. The four person fields — name, federalRevenueNumber, mothersName and birthdate — appear on most models.
This is the text as it came out of the OCR, untreated. This endpoint does not have the enhanced object with the cleaned-up version that v4 of Full OCR offers.
{
"extraction": {
"name": "JOAO DA SILVA SANTOS",
"federalRevenueNumber": "12345678909",
"mothersName": "MARIA DA SILVA SANTOS",
"birthdate": "01/02/1990",
"fathersName": "ANTONIO SANTOS",
"registerNumber": "01234567890",
"driversLicenseCategory": "AB",
"firstIssuedAt": "15/04/2010",
"issuedAt": "10/03/2019",
"expireAt": "10/03/2029",
"locale": "SAO PAULO",
"originDocumentId": "12.345.678-9",
"originDocumentIssuer": "SSP SP"
}
}data[].federalRevenueData
The cross-check against the Federal Revenue Service, made before and independently of Datavalid. Here, unlike in the datavalid block, the fields carry the values the Federal Revenue Service has on record.
| Field | Description | Type |
|---|---|---|
| federalRevenueData.name | Name on record at the Federal Revenue Service | String |
| federalRevenueData.federalRevenueNumber | CPF found at the Federal Revenue Service | String |
| federalRevenueData.mothersName | Mother's name on record at the Federal Revenue Service | String |
| federalRevenueData.birthdate | Date of birth on record at the Federal Revenue Service | String |
| federalRevenueData.matches.name.matched | true when the name read matches the Federal Revenue Service's | Boolean |
| federalRevenueData.matches.federalRevenueNumber.matched | true when the CPF matches | Boolean |
| federalRevenueData.matches.mothersName.matched | true when the mother's name matches | Boolean |
| federalRevenueData.matches.birthdate.matched | true when the date of birth matches | Boolean |
The Federal Revenue Service and Datavalid are two different checks
federalRevenueData compares the document with the Federal Revenue Service database. datavalid compares it with the record in SERPRO's Datavalid. They are distinct sources and they can disagree — treat the two as independent signals.
{
"federalRevenueData": {
"name": "JOAO DA SILVA SANTOS",
"federalRevenueNumber": "12345678909",
"mothersName": "MARIA DA SILVA SANTOS",
"birthdate": "01/02/1990",
"matches": {
"name": { "matched": true },
"federalRevenueNumber": { "matched": true },
"mothersName": { "matched": true },
"birthdate": { "matched": true }
}
}
}data[].classification
| Field | Description | Type |
|---|---|---|
| classification.type | Type of the document: DriversLicense, FederalID… | String |
| classification.subtype | Model within the type: Printed, Digital, Decree2018Paper… | String |
| classification.country | Issuing country, in three letters | String |
| classification.sides | One entry per side recognized | Object[] |
| classification.sides[].side | OnlyFront, OnlyBack or FrontAndBack | String |
| classification.sides[].page | Page where the side was recognized. Starts at 0. | Number |
| classification.sides[].fieldname | Name of the field the file was sent in | String |
| classification.sides[].confidence | Confidence of the classification of that side | Number |
{
"classification": {
"type": "DriversLicense",
"subtype": "Printed",
"country": "BRA",
"sides": [
{ "side": "OnlyFront", "page": 0, "fieldname": "frente", "confidence": 0.99 },
{ "side": "OnlyBack", "page": 0, "fieldname": "verso", "confidence": 0.97 }
]
}
}data[].face
The face found on the document — the same one that is sent to Datavalid for the biometric comparison.
| Field | Description | Type |
|---|---|---|
| face.age | Estimated apparent age | Number |
| face.gender.value | Male or Female | String |
| face.boundingBox | Position of the face in the image, in proportions between 0 and 1 (top, left, width, height) | Object |
| face.croppedBase64 | Crop of the face in base64 | String |
When no face is found on the document, this block does not appear and the Datavalid query is made without the biometric step. datavalid.croppedFaceBase64 does not disappear from the response: the key is still there and comes back empty ({}), because availability, similarity and probability are only filled in when Datavalid returns the biometric block. The name, parents' names and date fields are still checked.
metadata
| Field | Description | Type |
|---|---|---|
| metadata.filesInfo | List with one entry per file sent | Object[] |
| metadata.filesInfo[].fieldname | Name of the field used to send the file | String |
| metadata.filesInfo[].name | Name of the file sent | String |
| metadata.filesInfo[].size | Size of the file, in bytes | Number |
| metadata.filesInfo[].pages | Number of pages of the file. For images, 1. | Number |
| metadata.filesInfo[].mimetype | Type of the file sent | String |
| metadata.filesInfo[].encoding | Encoding of the file as sent | String |
| metadata.filesInfo[].sha256 | SHA-256 hash of the file sent | String |
| metadata.timeSpent | Processing time of the request, in milliseconds | Number |
Full example
Status Code: 200{
"id": "12cfec3c-b456-7890-a4d1-5173e4c1418e",
"version": "v3",
"data": [
{
"datavalid": {
"federalRevenueNumberAvailability": true,
"federalRevenueNumberStatus": true,
"name": true,
"nameSimilarity": 1,
"gender": true,
"birthdate": true,
"mothersName": true,
"mothersNameSimilarity": 0.97,
"fathersName": false,
"fathersNameSimilarity": 0.62,
"isBrazilian": true,
"croppedFaceBase64": {
"availability": true,
"similarity": 0.96,
"probability": "VeryHigh"
},
"document": {
"type": true,
"number": true,
"numberConfidence": 1
},
"driversLicense": {
"status": true,
"issuedAt": true,
"expireAt": true,
"firstIssuedAt": true,
"registerNumber": true,
"driversLicenseCategory": true
}
},
"extraction": {
"name": "JOAO DA SILVA SANTOS",
"federalRevenueNumber": "12345678909",
"mothersName": "MARIA DA SILVA SANTOS",
"birthdate": "01/02/1990",
"fathersName": "ANTONIO SANTOS",
"registerNumber": "01234567890",
"driversLicenseCategory": "AB",
"firstIssuedAt": "15/04/2010",
"issuedAt": "10/03/2019",
"expireAt": "10/03/2029",
"locale": "SAO PAULO",
"originDocumentId": "12.345.678-9",
"originDocumentIssuer": "SSP SP"
},
"federalRevenueData": {
"name": "JOAO DA SILVA SANTOS",
"federalRevenueNumber": "12345678909",
"mothersName": "MARIA DA SILVA SANTOS",
"birthdate": "01/02/1990",
"matches": {
"name": { "matched": true },
"federalRevenueNumber": { "matched": true },
"mothersName": { "matched": true },
"birthdate": { "matched": true }
}
},
"classification": {
"type": "DriversLicense",
"subtype": "Printed",
"country": "BRA",
"sides": [
{ "side": "OnlyFront", "page": 0, "fieldname": "frente", "confidence": 0.99 },
{ "side": "OnlyBack", "page": 0, "fieldname": "verso", "confidence": 0.97 }
]
},
"face": {
"age": 34,
"gender": { "value": "Male" },
"boundingBox": {
"top": 0.2237228155136108,
"left": 0.2856607735157013,
"width": 0.1555942893028259,
"height": 0.1687679588794708
},
"croppedBase64": "iVBORw0KGgoAAAANSUhEUgAAAlgAAAJYCAMAAA...K5CYII="
}
}
],
"metadata": {
"filesInfo": [
{
"fieldname": "frente",
"name": "cnh-frente.jpg",
"size": 567098,
"pages": 1,
"mimetype": "image/jpeg",
"encoding": "7bit",
"sha256": "41c2ece339abfcf9ba1e7d5a60666162a24ef34ba95adeb41ed1d9721c91c6b0"
},
{
"fieldname": "verso",
"name": "cnh-verso.jpg",
"size": 512044,
"pages": 1,
"mimetype": "image/jpeg",
"encoding": "7bit",
"sha256": "2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae"
}
],
"timeSpent": 12480
}
}Document read, with no Datavalid query
Status Code: 200No CPF was resolved for the document — neither read from it, nor given in federalRevenueNumber. The extraction happens as normal; the Datavalid step is skipped.
{
"id": "6a1d1e8c-4a7d-4a5e-9f2c-1b0f2a9c7e11",
"version": "v3",
"data": [
{
"datavalid": {},
"extraction": {
"name": "JOAO DA SILVA SANTOS",
"birthdate": "01/02/1990",
"documentId": "12.345.678-9",
"issuer": "SSP SP"
},
"classification": {
"type": "FederalID",
"subtype": "MainModel",
"country": "BRA",
"sides": [
{ "side": "OnlyFront", "page": 0, "fieldname": "documento", "confidence": 0.98 }
]
},
"face": {
"age": 34,
"gender": { "value": "Male" },
"boundingBox": { "top": 0.22, "left": 0.28, "width": 0.15, "height": 0.16 },
"croppedBase64": "iVBORw0KGgoAAAANSUhEUgAAAlgAAAJYCAMAAA...K5CYII="
}
}
],
"metadata": {
"filesInfo": [
{
"fieldname": "documento",
"name": "rg-frente.jpg",
"size": 384210,
"pages": 1,
"mimetype": "image/jpeg",
"encoding": "7bit",
"sha256": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"
}
],
"timeSpent": 7320
}
}Response headers
| Header | When it appears |
|---|---|
Nextid-ReqId | On every response. Carries the request identifier, the same one as the id field in the body. |
Errors
| Code | When it happens |
|---|---|
| 401 Unauthorized | API key or JWT token missing, invalid, or without permission for this endpoint. Also when the scope is customerKeys and one of the credential headers is missing, and when SERPRO rejects your credentials. |
| 404 Not Found | The version given in the URL does not exist. Only v2 and v3 are accepted. |
| 406 Not Acceptable | The body was sent as JSON, but without the base64 field. |
| 413 Payload Too Large | One of the files sent as multipart/form-data is larger than 15 MB. |
| 415 Unsupported Media Type | The Content-Type header is missing or is not supported. |
| 422 Unprocessable Entity | A federalRevenueNumber that fails check-digit validation, a file format that is not accepted, or more than seven files. It is also the code returned when Datavalid rejects the face for poor image quality. |
| 500 Internal Server Error | Unexpected failure during processing, including Datavalid being unavailable. |
The two 401s of the key setup
They are different errors, with different messages, and the message is how you tell them apart.
Credential headers missing. The request is rejected before any processing:
Status Code: 401{
"id": "13cfec7c-b238-4820-a4d1-5173e4c1418e",
"error": {
"statusCode": 401,
"error": "Unauthorized",
"message": "Datavalid customer keys are required"
}
}Credentials rejected by SERPRO. The headers were there, but SERPRO answered 401 to them. Here the file has already been processed — the error happens at the query step and brings the whole request down:
{
"id": "6a1d1e8c-4a7d-4a5e-9f2c-1b0f2a9c7e11",
"error": {
"statusCode": 401,
"error": "Unauthorized",
"message": "Invalid customer datavalid credentials"
}
}This is the most common error when setting up your own keys
Invalid customer datavalid credentials means the key and the secret reached SERPRO and were rejected. By far the most frequent causes are the Key /Secret prefix stuck to the header value and the two values being swapped. Also check that your contract with SERPRO covers the service being queried.
This error does not come with a partial result: you do not get the document extraction along with it. If you need the extraction even when Datavalid is unavailable, use Full OCR.
Example response when Datavalid rejects the face for quality:
Status Code: 422{
"id": "a7f0c3d2-9e51-4b8a-8f31-2d4c6b9e0a77",
"error": {
"statusCode": 422,
"error": "Unprocessable Entity",
"message": "The face is of poor quality to be processed in the datavalid"
}
}The error response format is described in HTTP response codes.
Versions
The recommended version is v3, called at /full-ocr-and-datavalid/v3.
‼️ Omitting the version hands the call to v2
POST /full-ocr-and-datavalid, with no version in the path, is served by v2, which has a different response format. Always state the version in the URL.
| Version | Status | What changes |
|---|---|---|
| v3 | Recommended | classification in the API's standard format, with type, subtype, country and sides. |
| v2 | Legacy | classifications in the plural, with the old vocabulary of types, and a more detailed federalRevenueData. This is what answers when the version is omitted from the URL. |
There is no v4 here
Full OCR has a v4, with extraction and enhanced separated. This endpoint does not: /full-ocr-and-datavalid/v4 answers 404.
What changes in v2
The datavalid block is identical. The differences are in the other three:
classificationbecomesclassifications, in the plural, and it is a list with one entry per side recognized. Each entry haspage,fieldnameand aclassificationwithconfidence, atypein the old vocabulary (CNH,RG,CPF,PROOF-OF-RESIDENCE,SELFIE,IMPRESSOS,CARTAOCREDITOorOTHERS) and aface(front,backorfront-back). There is nosubtypeand nocountry.federalRevenueDatais more detailed. The matches live infederalRevenueData.matched— in the singular, different from v3 — and each field carriesmatched,confidence(0 to 1) andstrategy(char-to-charorby-tokens).facealso carries the gender confidence, inface.gender.confidence.
{
"id": "12cfec3c-b456-7890-a4d1-5173e4c1418e",
"version": "v2",
"data": [
{
"datavalid": {
"federalRevenueNumberAvailability": true,
"federalRevenueNumberStatus": true,
"name": true,
"nameSimilarity": 1,
"gender": true,
"birthdate": true,
"mothersName": true,
"mothersNameSimilarity": 0.97,
"fathersName": false,
"fathersNameSimilarity": 0.62,
"isBrazilian": true,
"croppedFaceBase64": {
"availability": true,
"similarity": 0.96,
"probability": "VeryHigh"
},
"document": { "type": true, "number": true, "numberConfidence": 1 },
"driversLicense": {
"status": true,
"issuedAt": true,
"expireAt": true,
"firstIssuedAt": true,
"registerNumber": true,
"driversLicenseCategory": true
}
},
"extraction": {
"name": "JOAO DA SILVA SANTOS",
"federalRevenueNumber": "12345678909",
"mothersName": "MARIA DA SILVA SANTOS",
"birthdate": "01/02/1990",
"registerNumber": "01234567890",
"driversLicenseCategory": "AB"
},
"federalRevenueData": {
"name": "JOAO DA SILVA SANTOS",
"federalRevenueNumber": "12345678909",
"mothersName": "MARIA DA SILVA SANTOS",
"birthdate": "01/02/1990",
"matched": {
"name": { "matched": true, "confidence": 1, "strategy": "char-to-char" },
"federalRevenueNumber": { "matched": true, "confidence": 1, "strategy": "char-to-char" },
"mothersName": { "matched": true, "confidence": 0.97, "strategy": "by-tokens" },
"birthdate": { "matched": true, "confidence": 1, "strategy": "char-to-char" }
}
},
"classifications": [
{
"page": 0,
"fieldname": "frente",
"classification": { "confidence": 0.99, "type": "CNH", "face": "front" }
},
{
"page": 0,
"fieldname": "verso",
"classification": { "confidence": 0.97, "type": "CNH", "face": "back" }
}
],
"face": {
"age": 34,
"gender": { "value": "Male", "confidence": 99.72 },
"boundingBox": { "top": 0.22, "left": 0.28, "width": 0.15, "height": 0.16 },
"croppedBase64": "iVBORw0KGgoAAAANSUhEUgAAAlgAAAJYCAMAAA...K5CYII="
}
}
],
"metadata": {
"filesInfo": [
{
"fieldname": "frente",
"name": "cnh-frente.jpg",
"size": 567098,
"pages": 1,
"mimetype": "image/jpeg",
"encoding": "7bit",
"sha256": "41c2ece339abfcf9ba1e7d5a60666162a24ef34ba95adeb41ed1d9721c91c6b0"
}
],
"timeSpent": 11940
}
}‼️ Switching versions breaks whoever reads the response
The result of the name check against the Federal Revenue Service is in data[].federalRevenueData.matched.name.matched on v2 and in data[].federalRevenueData.matches.name.matched on v3. And the classification moves from data[].classifications[] to data[].classification.
The datavalid block is the only one that crosses the two versions unchanged.