> For the complete documentation index, see [llms.txt](https://docs.fraudcheck.co.za/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.fraudcheck.co.za/consumer-service/identity/identity-verification-document-with-liveness/identity-verification-document-with-liveness-without-sdk.md).

# Document with Liveness - Without the SDK

Use this page if you capture the identity document images in your own system instead of using the `@fraudcheck/capture-flow` SDK or Fraudcheck's hosted capture page. It covers direct document upload and the helper endpoints that support it.

{% hint style="warning" %}
We recommend the hosted capture page or the `@fraudcheck/capture-flow` SDK. Both guide the consumer through capture and check image quality as the photo is taken, which gives the best verification results. See [Identity Verification - Document with Liveness](/consumer-service/identity/identity-verification-document-with-liveness.md). Use direct upload only if you need to capture the document images in your own system.
{% endhint %}

## How it works

1. **Create a verification session** with `POST /doc-auth/verify`. See [Request](/consumer-service/identity/identity-verification-document-with-liveness.md#request).
2. **Find the document's `template_id`.** List the [countries](#countries), then the [documents for the consumer's country](#documents-by-country). Each document gives its `template_id` and the `capture_sides` to photograph.
3. **Check each image (optional).** [Crop](#crop) the document out of the photo, then run a [quality check](#quality-check) on each side.
4. **Upload the images** with [`POST /doc-auth/upload`](#uploading-the-document-images).
5. **Retrieve the result** with `GET /doc-auth/results/{transaction_id}`. See [Retrieving the result](/consumer-service/identity/identity-verification-document-with-liveness.md#retrieving-the-result).

## Endpoints

```http
POST /doc-auth/upload                              (access token required)
GET  /doc-auth/countries                           (no token)
GET  /doc-auth/countries/{country_code}/documents  (no token)
POST /doc-auth/crop                                (no token)
POST /doc-auth/quality-check                       (no token)
```

`POST /doc-auth/upload` needs `Authorization: Bearer {your_access_token}`. See [Authentication](/consumer-service/authentication.md).

***

## Helper endpoints

These endpoints support the capture flow. None of them needs an access token.

### Countries

List the countries that have supported documents.

```http
GET /doc-auth/countries
```

| Field                      | Type   | Description                     |
| -------------------------- | ------ | ------------------------------- |
| `countries`                | array  | One entry per supported country |
| `countries[].country_code` | string | Three-letter ISO country code   |
| `countries[].country_name` | string | Country name                    |

#### Example response (truncated)

```json
{
  "countries": [
    { "country_code": "DZA", "country_name": "Algeria" },
    { "country_code": "BWA", "country_name": "Botswana" },
    { "country_code": "KEN", "country_name": "Kenya" },
    { "country_code": "ZAF", "country_name": "South Africa" }
  ]
}
```

### Documents by country

List the documents (templates) that can be captured for a country.

```http
GET /doc-auth/countries/{country_code}/documents
```

| Parameter      | Type   | Required | Description                                  |
| -------------- | ------ | -------- | -------------------------------------------- |
| `country_code` | string | Yes      | Three-letter country code, for example `ZAF` |

| Field                         | Type           | Description                                                                 |
| ----------------------------- | -------------- | --------------------------------------------------------------------------- |
| `country_code`                | string         | The country requested                                                       |
| `documents[].document_family` | string         | Document family, for example `identity_card`, `passport`, `drivers_license` |
| `documents[].document_slug`   | string         | Document identifier, for example `smart_id_card`                            |
| `documents[].display_name`    | string         | Human-readable document name                                                |
| `documents[].capture_sides`   | array          | Sides to photograph: `front`, or `front` and `back`                         |
| `documents[].template_id`     | integer        | Pass this as `template_id` to upload and quality check                      |
| `documents[].capture_frame`   | object \| null | Physical size (`width_mm`, `height_mm`) and `orientation`, where known      |

Returns `400` if `country_code` is not three letters, and `404` if the country has no supported documents.

#### Example response (truncated)

```json
{
  "country_code": "ZAF",
  "documents": [
    {
      "document_family": "drivers_license",
      "document_slug": "drivers_license",
      "display_name": "South African Driving Licence",
      "capture_sides": ["front", "back"],
      "template_id": 109,
      "capture_frame": { "width_mm": 85.6, "height_mm": 53.98, "orientation": "landscape" }
    },
    {
      "document_family": "identity_card",
      "document_slug": "green_id_book",
      "display_name": "South African Green ID Book",
      "capture_sides": ["front"],
      "template_id": 110,
      "capture_frame": null
    },
    {
      "document_family": "identity_card",
      "document_slug": "smart_id_card",
      "display_name": "South Africa Smart Id Card",
      "capture_sides": ["front", "back"],
      "template_id": 108,
      "capture_frame": { "width_mm": 85.6, "height_mm": 53.98, "orientation": "landscape" }
    }
  ]
}
```

### Crop

Detect the document in a photo and crop it out. Send the request as `multipart/form-data`. The response is the cropped image itself (binary image data), not JSON.

```http
POST /doc-auth/crop
Content-Type: multipart/form-data
```

| Form field | Type | Required | Description                   |
| ---------- | ---- | -------- | ----------------------------- |
| `image`    | file | Yes      | Photo containing the document |

```bash
curl -X POST "$BASE_URL/doc-auth/crop" \
  -F "image=@photo.jpg" \
  --output cropped.jpg
```

### Quality check

Check whether a photo of one side of a document is good enough to process. Send the request as `multipart/form-data`.

```http
POST /doc-auth/quality-check
Content-Type: multipart/form-data
```

| Form field    | Type | Required | Description                                        |
| ------------- | ---- | -------- | -------------------------------------------------- |
| `file`        | file | Yes      | Photo of one side of the document                  |
| `template_id` | text | Yes      | From [Documents by country](#documents-by-country) |
| `side`        | text | Yes      | `front` or `back`                                  |

```bash
curl -X POST "$BASE_URL/doc-auth/quality-check" \
  -F "file=@front.jpg" \
  -F "template_id=110" \
  -F "side=front"
```

| Field        | Type           | Description                                                        |
| ------------ | -------------- | ------------------------------------------------------------------ |
| `good_image` | boolean        | `true` if the image is good enough to process                      |
| `reason`     | string \| null | Short code for why the image failed, for example `barcode_missing` |
| `message`    | string         | Human-readable explanation, suitable for showing to the consumer   |

#### Example response (good image)

```json
{
  "good_image": true,
  "reason": null,
  "message": "Image quality and required machine-readable features look good."
}
```

#### Example response (barcode missing)

```json
{
  "good_image": false,
  "reason": "barcode_missing",
  "message": "The back of this document should show PDF417, but it wasn't found or wasn't readable."
}
```

#### Example response (unknown `template_id`)

```json
{
  "detail": "No processing template with id 180"
}
```

{% hint style="info" %}
Quality check and crop are limited to 20 requests per minute per client; above that they return HTTP `429`. If the image service is unavailable they return `502`, or `504` on timeout.
{% endhint %}

***

## Uploading the document images

Submit the images against the `transaction_id` returned by `POST /doc-auth/verify`, using the `template_id` from [Documents by country](#documents-by-country).

```http
POST /doc-auth/upload
Authorization: Bearer {your_access_token}
Content-Type: application/json
```

| Field            | Type    | Required                                      | Description                                              |
| ---------------- | ------- | --------------------------------------------- | -------------------------------------------------------- |
| `transaction_id` | string  | Yes                                           | The `transaction_id` returned by `POST /doc-auth/verify` |
| `template_id`    | integer | Yes                                           | The document's `template_id` from the catalog            |
| `front`          | object  | Yes                                           | Image of the front of the document                       |
| `back`           | object  | If the document has a back in `capture_sides` | Image of the back of the document                        |
| `*.media_type`   | string  | Yes                                           | Image type, for example `image/png` or `image/jpeg`      |
| `*.image_base64` | string  | Yes                                           | Base64-encoded image data                                |

### Example request

```json
{
  "transaction_id": "f2e0272fd426d3499b4459ba6eccbd61",
  "template_id": 109,
  "front": {
    "media_type": "image/png",
    "image_base64": "iVBORw0KGgoAAAANSUhEUgAA..."
  },
  "back": {
    "media_type": "image/png",
    "image_base64": "iVBORw0KGgoAAAANSUhEUgAA..."
  }
}
```

### Upload response

| Field            | Type    | Description                                                                  |
| ---------------- | ------- | ---------------------------------------------------------------------------- |
| `transaction_id` | string  | The transaction the images were uploaded to                                  |
| `template_id`    | integer | The template used to read the document                                       |
| `sides`          | object  | Fields read from each side. The field names depend on the document template. |
| `portraits`      | object  | Base64-encoded portrait cropped from each side, where one was found          |

### Example response (South African driving licence)

```json
{
  "transaction_id": "f2e0272fd426d3499b4459ba6eccbd61",
  "template_id": 109,
  "sides": {
    "front": {
      "sex": "MALE",
      "card_category": "C1",
      "date_of_birth": "15/01/1990 ZA",
      "date_of_issue": "13/08/2025",
      "date_of_expiry": "12/08/2030",
      "document_number": "************",
      "identity_number": "02/*************",
      "country_of_issue": "ZA",
      "first_issue_date": "04/10/2020",
      "vehicle_restrictions": "0"
    },
    "back": {}
  },
  "portraits": {
    "front": "base64_encoded_portrait..."
  }
}
```

The upload response is the data read from the images, not the verification decision. Once the consumer has also completed the liveness check, get the final outcome from `GET /doc-auth/results/{transaction_id}`.

***

## Errors

| HTTP | Endpoint                    | Meaning                                | Action                               |
| ---- | --------------------------- | -------------------------------------- | ------------------------------------ |
| 401  | Upload                      | Token missing or expired               | Refresh your access token            |
| 400  | Documents by country        | `country_code` is not three letters    | Use a three-letter country code      |
| 404  | Documents by country        | The country has no supported documents | Check the code against Countries     |
| 429  | Quality check, crop         | More than 20 requests per minute       | Slow down and retry                  |
| 502  | Upload, quality check, crop | The image service is unavailable       | Retry; contact support if persistent |
| 504  | Upload, quality check, crop | The image service timed out            | Retry; contact support if persistent |


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.fraudcheck.co.za/consumer-service/identity/identity-verification-document-with-liveness/identity-verification-document-with-liveness-without-sdk.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
