> ## Documentation Index
> Fetch the complete documentation index at: https://docs.compliance.legaltalent.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Face Matching

Biometric face matching API for identity verification using AWS Rekognition. Supports 1:1 face verification, 1:N face search, face registration, and blacklist management.

## Overview

The Face Matching API provides comprehensive biometric verification capabilities:

* **1:1 Verification**: Compare two face images to verify identity
* **1:N Search**: Search for a face in the registered collection
* **Face Registration**: Register faces in tenant-specific collections
* **Blacklist Management**: Flag faces for fraud prevention
* **Presigned Uploads**: Secure direct-to-S3 image uploads

<Note>
  Face matching must be enabled for your tenant. Contact support to enable this feature.
</Note>

## Endpoints

| Endpoint                            | Method | Description                   |
| ----------------------------------- | ------ | ----------------------------- |
| `/kyc/facematch/verify`             | POST   | 1:1 face verification         |
| `/kyc/facematch/register`           | POST   | Register face in collection   |
| `/kyc/facematch/search`             | POST   | 1:N face search               |
| `/kyc/facematch/blacklist`          | POST   | Add face to blacklist         |
| `/kyc/facematch/faces/{face_id}`    | DELETE | Delete face from collection   |
| `/kyc/facematch/collections/status` | GET    | Get collection status         |
| `/kyc/facematch/upload-url`         | POST   | Generate presigned upload URL |

## Authentication

All endpoints require Bearer token authentication. Required permissions vary by endpoint:

| Endpoint          | Required Permission |
| ----------------- | ------------------- |
| Verify            | `kyc:read`          |
| Register          | `kyc:create`        |
| Search            | `kyc:read`          |
| Blacklist         | `kyc:create`        |
| Delete            | `kyc:create`        |
| Collection Status | `kyc:read`          |
| Upload URL        | `kyc:create`        |

***

## 1:1 Face Verification

Compare two face images to determine if they belong to the same person.

```
POST /kyc/facematch/verify
```

### Request Parameters

<ParamFields>
  <ParamField name="source_image" type="string" required description="Source image (base64-encoded data or S3 URI)" />

  <ParamField name="target_image" type="string" required description="Target image to compare against (base64-encoded data or S3 URI)" />

  <ParamField name="threshold" type="number" description="Similarity threshold (0.0-1.0). Default from tenant config (~0.6)" />

  <ParamField name="image_format" type="string" description="Format of images: 'base64' (default) or 's3'" />
</ParamFields>

### Request Example

```bash theme={null}
curl -X POST https://stg.kyc.legaltalent.ai/kyc/facematch/verify \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "source_image": "base64_encoded_image...",
    "target_image": "base64_encoded_image...",
    "threshold": 0.7,
    "image_format": "base64"
  }'
```

### Using S3 Images

```bash theme={null}
curl -X POST https://stg.kyc.legaltalent.ai/kyc/facematch/verify \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "source_image": "s3://bucket-name/path/to/source.jpg",
    "target_image": "s3://bucket-name/path/to/target.jpg",
    "image_format": "s3"
  }'
```

### Response

```json theme={null}
{
  "status": "success",
  "data": {
    "is_match": true,
    "similarity_score": 0.9523,
    "confidence": 0.8765,
    "processing_time_ms": 1250
  }
}
```

### Response Fields

| Field                | Type    | Description                                   |
| -------------------- | ------- | --------------------------------------------- |
| `is_match`           | boolean | Whether faces match (similarity >= threshold) |
| `similarity_score`   | number  | Similarity score (0.0-1.0)                    |
| `confidence`         | number  | Confidence level of the comparison (0.0-1.0)  |
| `processing_time_ms` | integer | Processing time in milliseconds               |

### Zero Data Retention (ephemeral mode)

1:1 verification supports **ephemeral data retention**: the comparison runs in memory and no images, biometric templates, or match results are persisted. Only an aggregate, non-PII usage event is emitted for billing.

Enable it per request with a header or body flag (or tenant-wide via your data processing configuration — contact support):

```bash theme={null}
curl -X POST https://stg.kyc.legaltalent.ai/kyc/facematch/verify \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "X-Data-Retention: ephemeral" \
  -H "Content-Type: application/json" \
  -d '{
    "source_image": "base64_encoded_image...",
    "target_image": "base64_encoded_image...",
    "image_format": "base64"
  }'
```

Equivalent body flag: `"data_retention": "ephemeral"`.

Per-request ephemeral must be enabled for your account first: the endpoint identifier `facematch.verify` (or the wildcard `*`) has to appear in your tenant's `data_processing_config.allowed_ephemeral_endpoints`, otherwise the request is rejected with `400 ZDR_NOT_ENABLED_FOR_TENANT`. Tenants with `data_processing_config.mode` set to `ephemeral` run every verification ephemerally without needing the allowlist. Contact support to enable either option.

Restrictions in ephemeral mode:

| Rule                                     | Behavior                                                                                                                                   |
| ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `image_format` must be `base64`          | `"s3"` returns `400 ZDR_UNSUPPORTED_IMAGE_FORMAT` — S3-referenced images would persist in storage                                          |
| Only `/kyc/facematch/verify` supports it | `register`, `search`, `blacklist`, and `upload-url` return `400 ZDR_UNSUPPORTED_FOR_ENDPOINT` (they persist biometric artifacts by design) |
| Invalid mode value                       | `400 INVALID_DATA_RETENTION` — accepted values are `standard` and `ephemeral`                                                              |

***

## Face Registration

Register a face in the tenant's collection for future searches.

```
POST /kyc/facematch/register
```

### Request Parameters

<ParamFields>
  <ParamField name="image" type="string" required description="Face image (base64-encoded data or S3 URI)" />

  <ParamField name="session_id" type="string" required description="KYC Session ID to associate with this face" />

  <ParamField name="metadata" type="object" description="Additional metadata to store with the face" />

  <ParamField name="is_reference" type="boolean" description="Whether this is a reference face (default: false)" />

  <ParamField name="image_format" type="string" description="Format of image: 'base64' (default) or 's3'" />
</ParamFields>

### Request Example

```bash theme={null}
curl -X POST https://stg.kyc.legaltalent.ai/kyc/facematch/register \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "image": "base64_encoded_image...",
    "session_id": "sess_123456",
    "metadata": {
      "full_name": "John Doe",
      "document_type": "passport"
    },
    "is_reference": true,
    "image_format": "base64"
  }'
```

### Response

```json theme={null}
{
  "status": "success",
  "data": {
    "face_id": "face_abc123def456",
    "rekognition_face_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
    "s3_key": "faces/tenant_123/sess_123456/face_abc123def456.jpg",
    "message": "Face registered successfully"
  }
}
```

### Response Fields

| Field                 | Type   | Description                       |
| --------------------- | ------ | --------------------------------- |
| `face_id`             | string | Unique face identifier            |
| `rekognition_face_id` | string | AWS Rekognition face ID           |
| `s3_key`              | string | S3 key where face image is stored |
| `message`             | string | Success message                   |

***

## 1:N Face Search

Search for a face across all registered faces in the tenant's collection.

```
POST /kyc/facematch/search
```

### Request Parameters

<ParamFields>
  <ParamField name="image" type="string" required description="Face image to search (base64-encoded data or S3 URI)" />

  <ParamField name="max_results" type="integer" description="Maximum number of results (1-100, default: 10)" />

  <ParamField name="threshold" type="number" description="Similarity threshold (0.0-1.0). Default from tenant config (~0.8)" />

  <ParamField name="image_format" type="string" description="Format of image: 'base64' (default) or 's3'" />
</ParamFields>

### Request Example

```bash theme={null}
curl -X POST https://stg.kyc.legaltalent.ai/kyc/facematch/search \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "image": "base64_encoded_image...",
    "max_results": 5,
    "threshold": 0.85,
    "image_format": "base64"
  }'
```

### Response

```json theme={null}
{
  "status": "success",
  "data": {
    "matches": [
      {
        "face_id": "face_abc123def456",
        "session_id": "sess_123456",
        "similarity": 0.9876,
        "metadata": {
          "full_name": "John Doe",
          "document_type": "passport"
        },
        "is_blacklisted": false
      },
      {
        "face_id": "face_xyz789ghi012",
        "session_id": "sess_789012",
        "similarity": 0.8532,
        "metadata": {},
        "is_blacklisted": true,
        "blacklist_reason": "Suspected fraud"
      }
    ],
    "total_matches": 2,
    "search_time_ms": 850
  }
}
```

### Response Fields

| Field            | Type    | Description                   |
| ---------------- | ------- | ----------------------------- |
| `matches`        | array   | List of matching faces        |
| `total_matches`  | integer | Total number of matches found |
| `search_time_ms` | integer | Search time in milliseconds   |

### Match Fields

| Field              | Type    | Description                             |
| ------------------ | ------- | --------------------------------------- |
| `face_id`          | string  | Unique face identifier                  |
| `session_id`       | string  | Associated KYC session ID               |
| `similarity`       | number  | Similarity score (0.0-1.0)              |
| `metadata`         | object  | Stored metadata                         |
| `is_blacklisted`   | boolean | Whether face is blacklisted             |
| `blacklist_reason` | string  | Reason for blacklisting (if applicable) |

<Warning>
  Blacklisted faces are included in search results (marked with `is_blacklisted: true`) to alert you when a flagged person is detected.
</Warning>

***

## Add to Blacklist

Mark a registered face as blacklisted for fraud prevention.

```
POST /kyc/facematch/blacklist
```

### Request Parameters

<ParamFields>
  <ParamField name="face_id" type="string" required description="Face ID to blacklist" />

  <ParamField name="reason" type="string" description="Reason for blacklisting" />
</ParamFields>

### Request Example

```bash theme={null}
curl -X POST https://stg.kyc.legaltalent.ai/kyc/facematch/blacklist \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "face_id": "face_abc123def456",
    "reason": "Suspected identity fraud"
  }'
```

### Response

```json theme={null}
{
  "status": "success",
  "data": {
    "message": "Face blacklisted successfully"
  }
}
```

***

## Delete Face

Remove a registered face from the collection.

```
DELETE /kyc/facematch/faces/{face_id}
```

### Path Parameters

| Parameter | Type   | Description       |
| --------- | ------ | ----------------- |
| `face_id` | string | Face ID to delete |

### Request Example

```bash theme={null}
curl -X DELETE https://stg.kyc.legaltalent.ai/kyc/facematch/faces/face_abc123def456 \
  -H "Authorization: Bearer YOUR_TOKEN"
```

### Response

```json theme={null}
{
  "status": "success",
  "data": {
    "message": "Face deleted successfully"
  }
}
```

***

## Collection Status

Get status information about the tenant's face collection.

```
GET /kyc/facematch/collections/status
```

### Request Example

```bash theme={null}
curl -X GET https://stg.kyc.legaltalent.ai/kyc/facematch/collections/status \
  -H "Authorization: Bearer YOUR_TOKEN"
```

### Response

```json theme={null}
{
  "status": "success",
  "data": {
    "collection_name": "kyc-faces-tenant_123-prod",
    "face_count": 1542,
    "status": "ACTIVE"
  }
}
```

### Response Fields

| Field             | Type    | Description                                |
| ----------------- | ------- | ------------------------------------------ |
| `collection_name` | string  | AWS Rekognition collection name            |
| `face_count`      | integer | Number of registered faces                 |
| `status`          | string  | Collection status (ACTIVE, CREATING, etc.) |

***

## Generate Upload URL

Generate a presigned URL for direct S3 image upload.

```
POST /kyc/facematch/upload-url
```

### Request Parameters

<ParamFields>
  <ParamField name="session_id" type="string" description="KYC Session ID for organizing uploads (optional)" />

  <ParamField name="expires_in" type="integer" description="URL expiration in seconds (60-3600, default: 3600)" />
</ParamFields>

### Request Example

```bash theme={null}
curl -X POST https://stg.kyc.legaltalent.ai/kyc/facematch/upload-url \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "session_id": "sess_123456",
    "expires_in": 900
  }'
```

### Response

```json theme={null}
{
  "status": "success",
  "data": {
    "upload_url": "https://bucket.s3.amazonaws.com/path?...",
    "s3_uri": "s3://bucket-name/faces/tenant_123/sess_123456/uuid.jpg",
    "s3_key": "faces/tenant_123/sess_123456/uuid.jpg",
    "expires_at": 1732281600
  }
}
```

### Response Fields

| Field        | Type    | Description                       |
| ------------ | ------- | --------------------------------- |
| `upload_url` | string  | Presigned URL for uploading       |
| `s3_uri`     | string  | S3 URI for use in other endpoints |
| `s3_key`     | string  | S3 key where image will be stored |
| `expires_at` | integer | Unix timestamp when URL expires   |

### Upload Workflow

1. Request a presigned upload URL
2. Upload image directly to S3 using the presigned URL
3. Use the returned `s3_uri` in register/verify/search endpoints with `image_format: "s3"`

```bash theme={null}
# Step 1: Get upload URL
UPLOAD_RESPONSE=$(curl -s -X POST https://stg.kyc.legaltalent.ai/kyc/facematch/upload-url \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"session_id": "sess_123"}')

UPLOAD_URL=$(echo $UPLOAD_RESPONSE | jq -r '.data.upload_url')
S3_URI=$(echo $UPLOAD_RESPONSE | jq -r '.data.s3_uri')

# Step 2: Upload image to S3
curl -X PUT "$UPLOAD_URL" \
  -H "Content-Type: image/jpeg" \
  --data-binary @photo.jpg

# Step 3: Use S3 URI in verification
curl -X POST https://stg.kyc.legaltalent.ai/kyc/facematch/verify \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d "{
    \"source_image\": \"$S3_URI\",
    \"target_image\": \"$S3_URI_2\",
    \"image_format\": \"s3\"
  }"
```

***

## Error Responses

### 400 Bad Request

```json theme={null}
{
  "status": "error",
  "error": "Invalid input: source_image is required"
}
```

### 403 Forbidden - Feature Not Enabled

```json theme={null}
{
  "status": "error",
  "error": "Face matching is not enabled for this tenant"
}
```

### 403 Forbidden - Permission Denied

```json theme={null}
{
  "status": "error",
  "error": "Permission denied. Required: kyc:create"
}
```

### 404 Not Found

```json theme={null}
{
  "status": "error",
  "error": "Face not found"
}
```

### 400 Bad Request - No Face Detected

```json theme={null}
{
  "status": "error",
  "error": "No face detected in one or both images"
}
```

### 400 Bad Request - Max Faces Exceeded

```json theme={null}
{
  "status": "error",
  "error": "Maximum 5 faces allowed per session"
}
```

***

## Status Codes

| Code | Description                                                 |
| ---- | ----------------------------------------------------------- |
| 200  | Success                                                     |
| 201  | Created (registration)                                      |
| 400  | Bad Request - Invalid parameters                            |
| 401  | Unauthorized - Missing or invalid token                     |
| 403  | Forbidden - Feature not enabled or insufficient permissions |
| 404  | Not Found - Face not found                                  |
| 500  | Internal Server Error                                       |

***

## Usage Examples

### Python Example - Complete Verification Flow

```python theme={null}
import requests
import base64

BASE_URL = "https://stg.kyc.legaltalent.ai"
TOKEN = "YOUR_TOKEN"

headers = {
    "Authorization": f"Bearer {TOKEN}",
    "Content-Type": "application/json"
}

def encode_image(file_path):
    """Encode image file to base64."""
    with open(file_path, "rb") as f:
        return base64.b64encode(f.read()).decode("utf-8")

def verify_faces(source_path, target_path, threshold=0.7):
    """Verify if two face images match."""
    response = requests.post(
        f"{BASE_URL}/kyc/facematch/verify",
        headers=headers,
        json={
            "source_image": encode_image(source_path),
            "target_image": encode_image(target_path),
            "threshold": threshold,
            "image_format": "base64"
        }
    )
    return response.json()

def register_face(image_path, session_id, metadata=None):
    """Register a face in the collection."""
    response = requests.post(
        f"{BASE_URL}/kyc/facematch/register",
        headers=headers,
        json={
            "image": encode_image(image_path),
            "session_id": session_id,
            "metadata": metadata or {},
            "image_format": "base64"
        }
    )
    return response.json()

def search_face(image_path, threshold=0.8, max_results=10):
    """Search for matching faces in collection."""
    response = requests.post(
        f"{BASE_URL}/kyc/facematch/search",
        headers=headers,
        json={
            "image": encode_image(image_path),
            "threshold": threshold,
            "max_results": max_results,
            "image_format": "base64"
        }
    )
    return response.json()

# Example usage
if __name__ == "__main__":
    # 1:1 Verification
    result = verify_faces("id_photo.jpg", "selfie.jpg", threshold=0.7)
    print(f"Match: {result['data']['is_match']}")
    print(f"Similarity: {result['data']['similarity_score']}")
    
    # Register face
    register_result = register_face(
        "id_photo.jpg",
        "sess_123456",
        metadata={"full_name": "John Doe", "document_type": "passport"}
    )
    print(f"Face ID: {register_result['data']['face_id']}")
    
    # Search for face
    search_result = search_face("new_photo.jpg", threshold=0.85)
    print(f"Found {search_result['data']['total_matches']} matches")
    
    for match in search_result['data']['matches']:
        if match['is_blacklisted']:
            print(f"⚠️ BLACKLISTED: {match['face_id']} - {match['blacklist_reason']}")
        else:
            print(f"Match: {match['face_id']} ({match['similarity']:.2%})")
```

### JavaScript Example

```javascript theme={null}
const BASE_URL = 'https://stg.kyc.legaltalent.ai';
const TOKEN = 'YOUR_TOKEN';

const headers = {
  'Authorization': `Bearer ${TOKEN}`,
  'Content-Type': 'application/json'
};

async function verifyFaces(sourceBase64, targetBase64, threshold = 0.7) {
  const response = await fetch(`${BASE_URL}/kyc/facematch/verify`, {
    method: 'POST',
    headers,
    body: JSON.stringify({
      source_image: sourceBase64,
      target_image: targetBase64,
      threshold,
      image_format: 'base64'
    })
  });
  return response.json();
}

async function searchFace(imageBase64, threshold = 0.8, maxResults = 10) {
  const response = await fetch(`${BASE_URL}/kyc/facematch/search`, {
    method: 'POST',
    headers,
    body: JSON.stringify({
      image: imageBase64,
      threshold,
      max_results: maxResults,
      image_format: 'base64'
    })
  });
  return response.json();
}

// Example: File input handler
async function handleFileUpload(idFile, selfieFile) {
  const toBase64 = file => new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.readAsDataURL(file);
    reader.onload = () => resolve(reader.result.split(',')[1]);
    reader.onerror = reject;
  });
  
  const idBase64 = await toBase64(idFile);
  const selfieBase64 = await toBase64(selfieFile);
  
  const result = await verifyFaces(idBase64, selfieBase64);
  
  if (result.data.is_match) {
    console.log(`✅ Face verified! Similarity: ${(result.data.similarity_score * 100).toFixed(1)}%`);
  } else {
    console.log(`❌ Face mismatch. Similarity: ${(result.data.similarity_score * 100).toFixed(1)}%`);
  }
  
  return result;
}
```

***

## Tenant Configuration

Face matching behavior is configured per tenant:

| Setting                  | Default     | Description                            |
| ------------------------ | ----------- | -------------------------------------- |
| `enabled`                | false       | Whether face matching is enabled       |
| `verification_threshold` | 0.6         | Default threshold for 1:1 verification |
| `search_threshold`       | 0.8         | Default threshold for 1:N search       |
| `max_faces_per_user`     | 5           | Maximum faces per session              |
| `enable_blacklist`       | true        | Enable blacklist functionality         |
| `model_version`          | Rekognition | Face detection model                   |

Contact support to modify these settings for your tenant.

***

## Best Practices

### Image Quality

* Use high-quality images (minimum 640x480 pixels)
* Ensure good lighting with face clearly visible
* Avoid heavily compressed images
* Face should occupy at least 20% of the image

### Security

* Use presigned URLs for large images to avoid base64 overhead
* Store face IDs securely - they link to biometric data
* Implement rate limiting on verification endpoints
* Review blacklist matches with human oversight

### Performance

* Use S3 image format for large images (>1MB)
* Keep `max_results` reasonable in searches
* Cache collection status if needed frequently
* Batch registrations when onboarding multiple users

### Compliance

* Inform users about biometric data collection
* Implement data retention policies
* Provide mechanism for users to request data deletion
* Log all face matching operations for audit trails
