---
Source: https://docs.microblink.com/verify/migrate-v3
Title: Migrate to Verify v3
Description: How to migrate from Verify v2 to Verify v3
---

# Migrate to Verify v3

## API changes

The v3 API brings numerous breaking changes over the v2 API.
It introduces a completely different API surface, both for requests and responses, aiming to simplify things for the end user, and to make responses more clear.

This document lists how the API has changed, and what you need to change in your v2 integration to start using v3.

For a full API reference and OpenAPI schema, see:

- [Cloud API reference](/verify/api/ref/v3-cloud)
- [On-prem API reference](/on-prem/api)

:::note
We will use the `us-east` server to illustrate with examples, but they all apply to other servers as well. Check your [region](./api.md#regions).
:::

## New URL

The new verification endpoint is **`/api/v3/verify`**.

| v2                                                  | v3                                                  |
|-----------------------------------------------------|-----------------------------------------------------|
| https://us-east.verify.microblink.com/api/v2/docver | https://us-east.verify.microblink.com/api/v3/verify |

In addition to the verification endpoint, the API also has an extraction-only endpoint; see the [Extraction API](#extraction-api) section.

### Schema

Get the OpenAPI specification [here](/verify/openapi-v3-cloud.json).

## Changes in the request

### Only multipart

In v2, you had three ways to send an image: as a URL, as a base64-encoded string, or as a `multipart/form-data` upload.

The v3 API accepts only multipart uploads.
It doesn't support uploading images as base64-encoded blobs, nor does it support passing images using a URL.

<ApiSample
  method="POST"
  url="https://us-east.verify.microblink.com/api/v3/verify"
  headers={{ Authorization: "Basic <credentials>" }}
  formData={[
    { name: "imageFirstSide", fileName: "front_id.jpg" },
    { name: "imageSecondSide", fileName: "back_id.png" },
  ]}
/>

### Changes in request parameters

#### JSON object in multipart upload

In v2, you used `application/json` for the request and uploaded a JSON object with the configuration.

In v3, you use `multipart/form-data` and pass a (JSON-encoded) configuration object as one of the form-data fields.

Using `configuration.json` as an example:

```json title="configuration.json"
{
  "verification": {
    "useCase": {
      "verificationPolicy": "HighAssurance",
      "verificationContext": "Remote",
      "manualReviewStrategy": "RejectedOnly"
    },
    "settings": {
      "rejectExpiredDocuments": true,
      "screenPresenceSensitivity": "Level5",
      "dataMatchSensitivity": "Level7"
    }
  },
  "extraction": {
    "redactionSettings": {
      "globalMode": "ImageOnly"
    },
    "documentCaptureModuleSettings": {
      "documentImageReturnEnabled": true,
      "faceImageExtractionEnabled": true
    }
  }
}
```

The request looks like this now:

<ApiSample
  method="POST"
  url="https://us-east.verify.microblink.com/api/v3/verify"
  headers={{ Authorization: "Basic <credentials>" }}
  formData={[
    { name: "imageFirstSide", fileName: "front_id.jpg" },
    { name: "imageSecondSide", fileName: "back_id.png" },
    {
      name: "configuration",
      fileName: "configuration.json",
      contentType: "application/json",
    },
  ]}
/>

#### Renamed and removed parameters

- `imageFront` is renamed to `imageFirstSide`
- `imageBack` is renamed to `imageSecondSide`
- `imageBarcode` has the same name
- a `configuration` field has been added; `configuration.verification` and `configuration.imageAssessment` are relevant for this migration  guide
- `options` is renamed to `settings` and moved to `configuration.verification`
- `useCase` is moved to `configuration.verification` 
- `captureSessionId` and `sessionID` are removed; use `traceId` to correlate a request with your own records

#### Changes in use cases

- All use cases no longer support `"Unknown"` as a value.
- `verificationContext` is unchanged.
- `manualReviewStrategy` is unchanged.
- `manualReviewSensitivity` has been removed.
- `documentVerificationPolicy` has changed, see below
- `captureConditions` has changed, see below

##### Verification policy

`documentVerificationPolicy` is renamed to `verificationPolicy`.

Its allowed values are now: 

- `"HighConversion"` (previously `"Permissive"`)
- `"Balanced"` (previously `"Standard"`)
- `"HighAssurance"` (previously `"Strict"`/`"VeryStrict"`)

:::warning
Because the boundaries of `verificationPolicy` moved (3 values vs. 4 previously), re-tune rather than re-map.
Run a sample of known-good and known-fraudulent documents through your candidate policy and compare verdicts before you switch production traffic.
:::

##### Capture conditions

`captureConditions` has been renamed to `cropAffectsVerdict` and moved to `configuration.verification.settings`.

It also changes shape, not just name.

In v2 it was an enum (`NoControl`, `Basic`, `Hybrid`).

In v3, `cropAffectsVerdict` is a boolean and answers one question: can a cropped document receive an `"Accept"` in its `verdict`?

If `cropAffectsVerdict` is `true` (default), a cropped document will not be accepted; it can only be rejected or sent to manual review.
If `cropAffectsVerdict` is `false`, a cropped document can be accepted.

#### Changes in options (settings)

The v2 `options` object splits in two.
Verification tuning goes to `configuration.verification.settings`; everything about extracted data and returned images goes under `configuration.extraction`.

Every `MatchLevel` becomes a `Sensitivity`.
The values are unchanged (`Level1` through `Level10`, and `Disabled`), except that `Unknown` is no longer accepted.

All sensitivity values default to `"Level5"`, except `barcodeAuthenticityCheck` which remains at a default of `"Level7"`.

- `options.screenMatchLevel` is now `verification.settings.screenPresenceSensitivity`
- `options.photocopyMatchLevel` is now `verification.settings.photocopySensitivity`
- `options.barcodeAnomalyMatchLevel` is now `verification.settings.barcodeAuthenticitySensitivity`
- `options.photoForgeryMatchLevel` is now `verification.settings.portraitForgerySensitivity`
- `options.dataMatchMatchLevel` is now `verification.settings.dataMatchSensitivity`
- `options.generativeAiMatchLevel` is now `verification.settings.generativeAiSensitivity`

The seven image-quality match levels collapse into one setting.
`blurMatchLevel`, `glareMatchLevel`, `lightingMatchLevel`, `sharpnessMatchLevel`, `handOcclusionMatchLevel`, `dpiMatchLevel`, and `tiltMatchLevel` are all replaced by `imageAssessment.imageQualitySensitivity`, which applies uniformly to every image-quality dimension.

**Renamed:**

- `treatExpirationAsFraud` is now `verification.settings.rejectExpiredDocuments`
- `imageQualityInterpretation` is now `imageQualityRetryPolicy`
  - `VeryHighConversion` has been removed
  - the rest of the values are renamed and describe when to ask for a retake: `NeverRetryBadQuality`, `RetryBadQualityAlways`, `RetryBadQualityForAcceptances`, and `RetryBadQualityForRejections`.

**Moved under `extraction`:**

- `returnFullDocumentImage` is now `extraction.documentCaptureModuleSettings.documentImageReturnEnabled`
- `returnFaceImage` is now `extraction.documentCaptureModuleSettings.faceImageExtractionEnabled`
- `returnSignatureImage` is now `extraction.vizModuleSettings.signatureImageExtractionEnabled`
- `anonymizationMode` is now `extraction.redactionSettings.globalMode`, with its values unchanged (`None`, `ImageOnly`, `ResultFieldsOnly`, `FullResult`)

**Removed:**

- `staticSecurityFeaturesMatchLevel`: the check still runs and is still reported, at `verification.checks.visualCheck.securityFeaturesCheck`, but it is no longer tunable
- `returnImageFormat`, and the `ImageFormat` multipart field: returned images are always base64-encoded JPG


### Consent object

:::note[Cloud only]

The consent object is mandatory if you use the cloud API.
For self-hosted deployments, it isn't used.

:::

v2 had no consent concept.
In v3, a cloud request registers the consent you collected from the end user, as a `consent` text form value alongside the images.


```json title="consent"
{
  "userId": "<your-id-for-the-end-user>",
  "durationDays": 365,
  "givenOn": "2026-07-31T09:00:00Z",
  "note": "User agreed to identity verification terms.",
  "customerContext": {
    "customerId": "<your-customer-id>",
    "transactionId": "<your-transaction-id>"
  }
}
```

<ApiSample
  method="POST"
  url="https://us-east.verify.microblink.com/api/v3/verify"
  headers={{ Authorization: "Basic <credentials>" }}
  formData={[
    { name: "imageFirstSide", fileName: "front_id.jpg" },
    { name: "imageSecondSide", fileName: "back_id.png" },
    {
      name: "consent",
      value: {
        userId: "<your-id-for-the-end-user>",
        durationDays: 365,
      },
    },
  ]}
/>

Only two properties are required:

- `userId`: your own unique identifier for the end user granting consent
- `durationDays`: how long the consent stays valid, used to calculate its expiry

The rest are optional.
`givenOn` timestamps when consent was granted, `note` records the agreement text or statement the user accepted, and `customerContext` links the record to your own `customerId` and `transactionId`.

Because `userId` is your identifier, this is the field that ties consent records to a person in your system, so it should be stable across requests for the same end user.

Requests that don't send a consent object return a 400 response.

For the full field reference and the error responses consent can produce, see [Get your users' consent](./consent.md).

## Changes in the response

For a field-by-field walkthrough of the v3 response rather than a migration diff, see [Interpret the response](./response.md).

### `verdict` replaces `recommendedOutcome`

In v2, `verification.recommendedOutcome` carried the overall decision.

In v3, the decision is `verification.verdict`.

Two values are renamed, and `Unknown` is gone:

- `Undeterminable` is now `Unverifiable`
- `ManuallyReview` is now `Review`
- `Accept`, `Reject`, and `Retry` are unchanged

### `certaintyLevel` is no more

In v2, you would inspect `verification.certaintyLevel` to assess how certain the verification process was.

In v3, this field no longer exists, and all verifications can be considered to be of high certainty.

### Checks are a structured object, not a flat array

In v2, `checks` was an array of `Check` objects, each identified by a `name` string and able to nest further checks in its own `checks` array.
Finding a specific check meant walking the array and matching on names.

In v3, `verification.checks` is a typed object with four named groups:

```json title="v3"
{
  "verification": {
    "verdict": "Reject",
    "failedChecks": ["checks.extractedDataCheck.matchCheck.dateOfBirthCheck"],
    "checks": {
      "extractedDataCheck": {},
      "documentLivenessCheck": {},
      "visualCheck": {},
      "documentValidityCheck": {}
    }
  }
}
```

Each group has named sub-checks, so a check you care about has a stable path instead of a position in an array.
`result` keeps its v2 values, `Pass`, `Fail`, and `NotPerformed`, minus `Unknown`.

`verification.failedChecks` is new and has no v2 equivalent.
It lists the flattened path of every check that ran and failed, which is the quickest way to find why a document was rejected without walking the tree.

### Tiered checks report a threshold, not a level

In v2, a `TieredCheck` carried a `matchLevel` telling you the level at which the check was evaluated.

In v3, a tiered check carries `passesAtOrBelowSensitivity`: the highest sensitivity at which this check would still pass.

Instead of seeing which level you configured, you see what you would have had to configure for this document to pass, which is what you need in order to re-tune.

### Process indicators become image assessment

V2 returned a `processIndicators` array of named indicators, resolving to `Pass`, `Fail`, `Warn`, or `NotPerformed`.

V3 replaces it with a top-level `imageAssessment` object holding three checks:

```json title="v3"
{
  "imageAssessment": {
    "imageQualityCheck": { "result": "Pass" },
    "croppedDocumentCheck": {
      "result": "Pass",
      "firstSide": { "result": "Pass" },
      "secondSide": { "result": "Pass" }
    },
    "handPresenceCheck": { "result": "Pass" }
  }
}
```

The `Warn` result has no v3 equivalent: a check either passes, fails, or wasn't performed.

### Images are keyed, not listed

v2 returned `images` as an array of `{ name, base64 }` pairs, so you looked an image up by its `name`.

v3 returns an object with a key per image, and the value is the base64 string directly:

- `face`
- `signature`
- `firstSideCropped`
- `secondSideCropped`
- `barcode`

### Extracted data is typed

This is the largest change in the response.

v2 returned extraction results as generic containers: `extraction.overall`, `extraction.mrz`, and `extraction.barcode` were arrays of `Result` objects, each a `{ type, field, details }` bag that could nest more `Result` objects, with the field identified by a `FieldValueType` string.
Reading a first name meant finding the entry whose `field` was `FirstName`.

In v3, extracted data is a typed object with named properties, so for example a first name is at `extraction.result.firstName`.
Per-side and per-module results move under `extraction.result.subResults.{side}.{viz,mrz,barcode}`, and the document class moves to `extraction.result.documentClassInfo`.

The per-script values that v2 expressed with `ExtractedScript` (`Latin`, `Cyrillic`, `Arabic`, `Greek`) are now the lowercase keys of an `AlphabetString`:

```json title="v3"
{
  "extraction": {
    "result": {
      "firstName": { "latin": "JOHN" }
    }
  }
}
```

`extraction.recognitionStatus`, which carried the v2 `ResultState` (`Empty`, `Uncertain`, `Valid`, `StageValid`), is removed.
Use `extraction.processingStatus` instead.

### Configuration echo

V2 returned `optionsUsed` and `useCaseUsed` as two separate objects.

V3 returns a single `configurationUsed` that mirrors the structure of the `configuration` you sent, so the effective value of a setting is at the same path you would have used to set it.

:::tip

Currently we don't indicate the defaults in the schema, so reading `configurationUsed` is also a reliable way to discover the default configuration.

:::

### Pipeline status replaces the root processing status

v2 had a root-level `processingStatus` describing the run as a whole, with values like `Completed`, `PartiallyCompleted`, `ExtractionFailed`, `CompletedAfterFront`, and `ServerModelError`.

v3 splits this into a `pipeline` object with one entry per stage, each carrying its own status:

```json title="v3"
{
  "pipeline": {
    "extraction": { "status": "Completed" },
    "verification": { "status": "Completed" }
  }
}
```

`CompletedAfterFront` has no v3 equivalent, because a v3 request carries all of its images at once and is processed as a whole.

### Messages lose their status

- Messages were revised to be more consistent and informative, and they will also be appearing more often with the intention of guiding and providing additional feedback where individual checks/settings or their use in conjunction might be confusing.
- Clients should **not** build validations or business logic on messages.
- `messages.status` field is gone.
- Error messages are removed.

See the current supported [messages](./messages.md).

### New response codes

Three status codes are worth handling before you switch.
These apply to both the cloud and self-hosted deployments.

`503` is returned when the workers aren't ready to accept a request.
In v2, `503` came only from the `/health/live` and `/health/ready` endpoints, never from a verification request, so a client that treats it as "endpoint down" rather than "retry shortly" needs adjusting.

`413` is new, for payloads over the size limit.

`429` itself is not new, since v2 returned it when the rate limit was exceeded.
What's new is that v3 documents a `Retry-After` header carrying a suggested delay in seconds, and distinguishes two limiters: the memory-pressure limiter returns a JSON error body, while the concurrency limiter can return an empty one.
If you already handle `429`, check that your client tolerates an empty body and reads `Retry-After` rather than using a fixed backoff.

## Changes in self-hosted deployment

### New image

The self-hosted deployment now bundles the [self-hosted extraction API](/extraction/api), and has changed its name.

Grab it here:

```bash
docker pull us-docker.pkg.dev/document-verification-public/on-prem/core:4000.0.0
```

The new GitHub repo with the Helm charts and other deployment-related information is [here](https://github.com/microblink/on-prem-ops).

```bash
git clone https://github.com/microblink/on-prem-ops.git
```

### Licensing

Your existing licenses and license environment variables are unchanged.


### Single-image

If you're still on a release earlier than 3.19.0, you're moving from the multi-service stack at the same time.
Remove the old `conf/doc-ver-api`, `conf/doc-ver-runner`, and `conf/bundle-doc-ver` directories as part of that move.
See [On-prem deployment](/on-prem) for how the current image is deployed.

The container reports its own sizing in the `runtime` object of every response (`workerCount`, `inflightLimit`, `cpus`, `ram`, and others), which is useful when you are capacity planning against v2 numbers.

### Extraction API

V2 was verification-only.
V3 adds an extraction-only endpoint, `POST /api/v3/extract`, which runs the extraction half of the pipeline and skips verification.

It's the right endpoint if you only need the data off the document and don't need a verdict.
The extraction results are identical in shape to the `extraction` object of a verification response, so you can move between the two endpoints without changing your parsing.

The extraction endpoint accepts only the `extraction` and `imageAssessment` sections of the configuration; verification settings are rejected.
There is also a barcode-only endpoint, `POST /api/v3/extract-barcode`.

If you are coming to this endpoint from the standalone self-hosted extraction API rather than from Verify v2, see [Migrate from self-hosted extraction API](/on-prem/migrate-from-sh-extraction-api), which covers that migration in full.


Last updated on Aug 6, 2026
