> For the complete documentation index, see [llms.txt](https://docs.voveid.com/docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.voveid.com/docs/kyc-api/get-user-verification.md).

# Get verification results

Retrieve a KYC result, government registry checks, and document records by refId.

Returns the current KYC resource associated with your stable `refId`.

```http
GET https://api.voveid.net/v2/users/customer-7f3d
x-api-key: vove_sandbox_replace_me
accept: application/json
```

### Path parameters

| Parameter | Type   | Required | Description                                                  |
| --------- | ------ | -------- | ------------------------------------------------------------ |
| `refId`   | string | Yes      | The value supplied when the verification session was created |

URL-encode `refId` when constructing the request.

## Example request

```bash
curl 'https://api.voveid.net/v2/users/customer-7f3d' \
  --header 'accept: application/json' \
  --header 'x-api-key: vove_sandbox_replace_me'
```

## Example response

The fields returned depend on the selected flow and completed checks. This example shows a representative document-verification result without configured government registry checks. Document calendar dates use `DD.MM.YYYY`; timestamps such as `createdAt` and `updatedAt` remain ISO 8601. Registry dates follow the [government registry data rules](#registry-returned-data).

```json
{
  "_id": "66f000000000000000000020",
  "refId": "customer-7f3d",
  "status": "successful",
  "createdAt": "2026-09-07T10:20:30.000Z",
  "updatedAt": "2026-09-07T10:24:12.000Z",
  "flowId": "66f000000000000000000001",
  "sessionId": "66f000000000000000000002",
  "sessions": [
    "66f000000000000000000002"
  ],
  "documents": [
    {
      "stepId": "ID_DOCUMENT",
      "type": "Passport",
      "firstName": "AMINA",
      "lastName": "EXAMPLE",
      "dateOfBirth": "01.02.1990",
      "dateOfExpiration": "15.07.2030",
      "issuingAuthority": "EXAMPLE AUTHORITY",
      "idNumber": "X00000000",
      "matchLevel": 7,
      "digitalIDSpoof": "LIKELY_PHYSICAL_ID",
      "faceOnDocumentSpoof": "LIKELY_ORIGINAL_FACE",
      "textOnDocumentSpoof": "LIKELY_ORIGINAL_TEXT",
      "mrzStatus": "SUCCESS"
    }
  ],
  "metadata": {},
  "governmentRegistryChecks": []
}
```

### Response fields

| Field                      | Type      | Description                                                                                                                                                    |
| -------------------------- | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `_id`                      | string    | VOVE ID user resource identifier                                                                                                                               |
| `refId`                    | string    | Your stable customer identifier                                                                                                                                |
| `status`                   | string    | Current KYC status; see [Statuses](/docs/api-fundamentals/statuses.md)                                                                                         |
| `createdAt`                | string    | ISO 8601 creation time                                                                                                                                         |
| `updatedAt`                | string    | ISO 8601 last-update time                                                                                                                                      |
| `flowId`                   | string    | KYC flow used for the verification                                                                                                                             |
| `sessionId`                | string    | Most recent session identifier                                                                                                                                 |
| `sessions`                 | string\[] | Session identifiers associated with this resource                                                                                                              |
| `documents`                | object\[] | Extracted document records returned by completed document steps                                                                                                |
| `metadata`                 | object    | Integration metadata when present                                                                                                                              |
| `governmentRegistryChecks` | object\[] | Configured government registry checks from the latest non-deleted session; `[]` when none exist. See [Government registry checks](#government-registry-checks) |

### TypeScript types

```typescript
type VerificationStep =
  | 'ID_DOCUMENT'
  | 'DRIVING_LICENSE'
  | 'CAR_REGISTRATION_DOCUMENT'
  | 'ADDRESS_PROOF';

type DigitalIdSpoofStatus =
  | 'LIKELY_PHYSICAL_ID'
  | 'COULD_NOT_CONFIDENTLY_DETERMINE_PHYSICAL_ID_USER_NEEDS_TO_RETRY';

type FaceOnDocumentStatus =
  | 'NOT_AVAILABLE'
  | 'LIKELY_ORIGINAL_FACE'
  | 'CANNOT_CONFIRM_ID_IS_AUTHENTIC'
  | 'OCR_TEMPLATE_DOES_NOT_SUPPORT_DETECTION';

type TextOnDocumentStatus =
  | 'NOT_AVAILABLE'
  | 'LIKELY_ORIGINAL_TEXT'
  | 'CANNOT_CONFIRM_ID_IS_AUTHENTIC';

type MrzStatus =
  | 'NO_MRZ_SPECIFIED_BY_TEMPLATE'
  | 'MRZ_READ_BUT_CHECKSUM_FAILED'
  | 'SUCCESS';

type VerificationDocument = {
  stepId: VerificationStep;
  type?: string;
  firstName?: string;
  lastName?: string;
  fullName?: string;
  dateOfBirth?: string;
  dateOfIssue?: string;
  dateOfExpiration?: string;
  issuingAuthority?: string;
  issuingCountry?: string;
  placeOfBirth?: string;
  sex?: string;
  address?: string;
  address1?: string;
  idNumber?: string;
  mrzLine1?: string;
  mrzLine2?: string;
  matchLevel?: number;
  digitalIDSpoof?: DigitalIdSpoofStatus;
  faceOnDocumentSpoof?: FaceOnDocumentStatus;
  textOnDocumentSpoof?: TextOnDocumentStatus;
  mrzStatus?: MrzStatus;
  [field: string]: unknown;
};

type GovernmentRegistryAddress = {
  line1?: string;
  line2?: string;
  city?: string;
  state?: string;
  postalCode?: string;
  country?: string;
  [field: string]: unknown;
};

type GovernmentRegistryData = {
  firstName?: string;
  middleName?: string;
  lastName?: string;
  dateOfBirth?: string;
  gender?: string;
  phone?: string;
  email?: string;
  address?: string | GovernmentRegistryAddress;
  image?: string;
  [field: string]: unknown;
};

type GovernmentRegistryCheck = {
  country: string;
  registry?: string;
  status: 'pending' | 'in_progress' | 'completed' | 'failed' | 'skipped';
  completedAt?: string;
  data: GovernmentRegistryData | null;
  result?: {
    source?: 'api' | 'sdk' | 'document';
    outcome?: 'verified' | 'not_verified' | 'provider_error';
    verificationStatus?: 'verified' | 'not_verified';
    verificationReason?: string;
    confidence?: number;
  };
};

type VerificationResult = {
  _id: string;
  refId: string;
  status: string;
  createdAt: string;
  updatedAt: string;
  flowId?: string;
  sessionId?: string;
  sessions?: string[];
  documents?: VerificationDocument[];
  metadata?: Record<string, unknown>;
  governmentRegistryChecks?: GovernmentRegistryCheck[];
};
```

Treat unknown status values as pending review rather than assuming success.

## Government registry checks

`GET /v2/users/{refId}` returns `governmentRegistryChecks` alongside the existing user and document fields. `GET /v2/users?page=1` includes the same field on each user in its `data` array. Use your server-side API key; results are scoped to its organization. An unknown `refId` or one belonging to another organization returns HTTP 404.

Configure a Government Registry Check in your KYC flow to receive entries. See the [NIN and BVN guide](/docs/kyc-api/government-registry-check.md) or [Ghana guide](/docs/kyc-api/government-registry-check-ghana.md) for setup.

Only checks from the latest non-deleted session are returned, in stored step order. Session selection uses creation time, with the session identifier breaking ties. Each configured step produces one entry. For a step allowing multiple registries, `registry` is the resolved selection and is omitted until a selection exists. A newer session without checks returns `[]`, even if an older session has results. Internal experimental and manual checks are excluded.

### Example registry result

This synthetic response fragment shows a completed lookup without face matching:

```json
{
  "refId": "customer-7f3d",
  "governmentRegistryChecks": [
    {
      "country": "NG",
      "registry": "NIN",
      "status": "completed",
      "completedAt": "2026-10-19T12:00:00.000Z",
      "data": {
        "firstName": "AMINA",
        "lastName": "EXAMPLE",
        "dateOfBirth": "1990-02-01",
        "gender": "female",
        "address": {
          "line1": "1 Example Street",
          "city": "Lagos",
          "country": "NG"
        }
      },
      "result": {
        "source": "api",
        "outcome": "verified",
        "verificationStatus": "verified",
        "verificationReason": "registry_lookup_verified"
      }
    }
  ]
}
```

### Check fields and verdicts

| Field                       | Type             | Description                                                                   |
| --------------------------- | ---------------- | ----------------------------------------------------------------------------- |
| `country`                   | string           | Country code, such as `NG` or `GH`                                            |
| `registry`                  | string, optional | Resolved registry code, such as `NIN`, `BVN`, or `CARD`                       |
| `status`                    | string           | Step lifecycle: `pending`, `in_progress`, `completed`, `failed`, or `skipped` |
| `completedAt`               | string, optional | ISO 8601 completion timestamp, when persisted                                 |
| `data`                      | object or null   | Available normalized data from this check's stored registry response          |
| `result`                    | object, optional | Last persisted attempt; absent when no supported result fields exist          |
| `result.source`             | string, optional | Identifier source: `api`, `sdk`, or `document`                                |
| `result.outcome`            | string, optional | `verified`, `not_verified`, or `provider_error`                               |
| `result.verificationStatus` | string, optional | `verified` or `not_verified`                                                  |
| `result.verificationReason` | string, optional | Reason code described below                                                   |
| `result.confidence`         | number, optional | Face-match confidence from 0 to 1; normally absent for lookup-only checks     |

`status` describes the step lifecycle; `result` describes its last persisted attempt. A `completed` step or populated `data` does not by itself mean verification passed. Use `result.outcome` and `result.verificationStatus` for the check verdict, and the user's top-level `status` for the overall KYC result. A successful lookup without face matching does not prove identity ownership.

An unstarted check is `pending` with `data: null` and normally no `result`. A running lookup is `in_progress`. A failed attempt can leave a check `pending` for a retry while retaining its previous `result`. Treat missing or unknown verdicts as unresolved.

For example, a temporary provider error may return this fragment while a retry is pending:

```json
{
  "governmentRegistryChecks": [
    {
      "country": "NG",
      "registry": "BVN",
      "status": "pending",
      "data": null,
      "result": {
        "source": "api",
        "outcome": "provider_error",
        "verificationStatus": "not_verified",
        "verificationReason": "provider_unavailable"
      }
    }
  ]
}
```

Reason codes include `registry_lookup_verified`, `face_match_verified`, `face_match_failed`, `registry_cross_check_mismatch`, `face_confidence_below_threshold`, `face_confidence_missing`, `provider_verification_failed`, `provider_rate_limited`, `provider_rejected_request`, `registry_record_not_found`, `provider_authentication_failed`, `provider_unavailable`, and `government_registry_check_failed`. Unrecognized stored reason text is returned as `government_registry_check_failed`. Tolerate future codes without treating them as success.

### Registry-returned data

`data` comes from the check's stored registry response, independently of the user-submitted profile. It includes available retained fields, including provider-specific properties, portraits, card details, nationality, occupations, contacts, addresses, and stored face metadata. It is `null` when no supported stored data is available, including unstarted checks and older checks without retained data. Fields discarded before storage cannot be recovered by reading this endpoint.

| Fields                                                                                                          | Normalization                                                                                                                                                     |
| --------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `firstName`, `middleName`, `lastName`                                                                           | Common name aliases become trimmed strings                                                                                                                        |
| `dateOfBirth`                                                                                                   | Recognized dates become `YYYY-MM-DD`; unrecognized supplied values are preserved                                                                                  |
| `gender`                                                                                                        | Recognized male/female values become `male` or `female`; other supplied values are preserved                                                                      |
| `phone`, `email`                                                                                                | Primary contact values; no phone country code is inferred. A top-level value takes precedence over a contact-list phone or nested `contact.email`                 |
| `address`, `addresses`                                                                                          | String or structured addresses; common keys are `line1`, `line2`, `city`, `state`, `postalCode`, and `country`. Additional properties and full lists are retained |
| `image`                                                                                                         | Stored registry portrait, normalized from portrait/photo aliases when available                                                                                   |
| `placeOfBirth`, `maritalStatus`, `enrollmentBank`, `enrollmentBranch`, `cardId`, `cardValidFrom`, `cardValidTo` | Common names for equivalent registry fields                                                                                                                       |
| Other fields                                                                                                    | Available provider-specific properties and structures, including `contact`, `faceData`, and `verification`, are retained                                          |

Canonical field names take precedence over aliases. When no primary phone or address exists, the first usable contact phone or populated structured address is also exposed as `phone` or `address`; the full lists remain available. Null values, blank strings, and empty containers are omitted recursively; `false` and `0` are retained. Consume fields defensively because availability varies by registry and stored record.

Registry data can accompany a negative face-match verdict. Its presence does not override `result`. Keep returned identity data and portraits out of logs and analytics, restrict access, and apply your retention policy.

Reading results does not execute or retry a check or trigger a billable registry lookup. The document-media endpoint below is unchanged.

## Retrieve document media

```http
GET https://api.voveid.net/v2/users/customer-7f3d/documents
x-api-key: vove_sandbox_replace_me
accept: application/json
```

Representative response:

```json
{
  "selfie": "base64_redacted",
  "documents": [
    {
      "stepId": "ID_DOCUMENT",
      "front": "base64_redacted",
      "back": "base64_redacted",
      "photo": "base64_redacted",
      "frontCropped": "base64_redacted",
      "backCropped": "base64_redacted",
      "type": "Passport"
    }
  ]
}
```

Document and biometric media is sensitive. Retrieve it only when required, restrict access, avoid logging response bodies, and apply your retention and deletion policy.

## Server example

```javascript
const response = await fetch(
  `https://api.voveid.net/v2/users/${encodeURIComponent(refId)}`,
  { headers: { 'x-api-key': process.env.VOVE_API_KEY } },
);

if (response.status === 404) {
  return { status: 'not_found' };
}

if (!response.ok) {
  throw new Error(`VOVE result retrieval failed (${response.status})`);
}

const verification = await response.json();
```

Use this endpoint as the server-side authority after a webhook. Browser redirects and SDK callbacks are immediate journey outcomes, not sufficient by themselves for an irreversible decision.
