> For the complete documentation index, see [llms.txt](https://docs.tylt.money/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.tylt.money/tylt-embedded-wallet-service/user-management/submit-kyc.md).

# Submit KYC

Submits KYC information for an existing end user. This endpoint can also be used to update the user’s KYC information when `forceKycUpdate` is enabled.

### Endpoint

```http
PUT /whitelabel/users/submitKyc
```

### Request Headers

```http
x-tlp-apikey: <api-key>
x-tlp-signature: <hmac-signature>
Content-Type: application/json
```

### Request Body

```json
{
  "externalUserId": "user-10021",
  "kyc": {
    "userCountry": "IND",
    "firstName": "Joe",
    "lastName": "Doe",
    "dateOfBirth": "1990-01-01",

    "poiType": "PAN",
    "poiNumber": "ABC123456D",
    "poiImageUrl": "https://example.com/documents/pan.jpg",

    "poaType": "AADHAR",
    "poaNumber": "123456789012",
    "poaImageFrontUrl": "https://example.com/documents/aadhaar-front.jpg",
    "poaImageBackUrl": "https://example.com/documents/aadhaar-back.jpg",

    "addressLine1": "123 Example Street",
    "city": "Mumbai",
    "stateProvinceRegion": "MH",
    "postalCode": "400001",
    "country": "IND",

    "selfieImageUrl": "https://example.com/documents/selfie.jpg",
    "faceMatchScore": 0.98,
    "livenessScore": 0.97,

    "verification": {
      "provider": "didit",
      "sessionId": "didit-session-id",
      "status": "approved"
    }
  },
  "forceKycUpdate": false,
  "timestamp": 1784793600000,
  "nonce": "9d8934143ca747ebaaba3100f15cb4a1"
}
```

### Request Parameters

<table><thead><tr><th>Field</th><th width="187">Type</th><th align="right">Required</th><th>Description</th></tr></thead><tbody><tr><td><code>endUserEmail</code></td><td>String</td><td align="right">Conditional</td><td>End user’s registered email address</td></tr><tr><td><code>endUserId</code></td><td>Number</td><td align="right">Conditional</td><td>Tylt's end-user ID</td></tr><tr><td><code>externalUserId</code></td><td>String</td><td align="right">Conditional</td><td>Merchant’s own user identifier</td></tr><tr><td><code>kyc</code></td><td>Object</td><td align="right">Yes</td><td>KYC information and supporting verification data (refer to the request body)</td></tr><tr><td><code>forceKycUpdate</code></td><td>Boolean</td><td align="right">No</td><td>Allows an existing approved KYC record to be overwritten</td></tr><tr><td><code>timestamp</code></td><td>String</td><td align="right">Yes</td><td>Unix epoch milliseconds; allowed skew is ±120 seconds</td></tr><tr><td><code>nonce</code></td><td>String</td><td align="right">Yes</td><td>Unique random request value used to prevent replay attack</td></tr></tbody></table>

One end-user identifier must be provided.

### Standard KYC Submission

```json
{
  "endUserId": 20001, // Optional: Provide one user identifier only
  "endUserEmail": "joe@example.com", // Optional: Alternative to endUserId and externalUserId
  "externalUserId": "user-10021", // Optional: Alternative to endUserId and endUserEmail
  "kyc": {
    "userCountry": "IND",
    "firstName": "Joe",
    "lastName": "Doe",
    "dateOfBirth": "1990-01-01",

    "poiType": "PAN",
    "poiNumber": "ABC123456D",
    "poiImageUrl": "https://example.com/documents/pan.jpg",

    "poaType": "AADHAR",
    "poaNumber": "123456789012",
    "poaImageFrontUrl": "https://example.com/documents/aadhaar-front.jpg",
    "poaImageBackUrl": "https://example.com/documents/aadhaar-back.jpg",

    "addressLine1": "123 Example Street",
    "city": "Mumbai",
    "stateProvinceRegion": "MH",
    "postalCode": "400001",
    "country": "IND",

    "selfieImageUrl": "https://example.com/documents/selfie.jpg",
    "faceMatchScore": 0.98,
    "livenessScore": 0.97,

    "verification": {
      "provider": "didit",
      "sessionId": "didit-session-id",
      "status": "approved"
    }
  },
  "timestamp": 1784793600000,
  "nonce": "9d8934143ca747ebaaba3100f15cb4a1"
}
```

### Force KYC Update

Use `forceKycUpdate` only when the user’s previously submitted information must be replaced.

```json
{
  "endUserId": 20001, // Optional: Provide one user identifier only
  "endUserEmail": "joe@example.com", // Optional: Alternative to endUserId and externalUserId
  "externalUserId": "user-10021", // Optional: Alternative to endUserId and endUserEmail
  "forceKycUpdate": true,
  //Updated KYC Fields
  "kyc": { 
    "userCountry": "IND",
    "firstName": "Joe",
    "lastName": "Doe",
    "dateOfBirth": "1990-01-01",

    "poiType": "PAN",
    "poiNumber": "ABC123456D",
    "poiImageUrl": "https://example.com/documents/pan.jpg",

    "poaType": "AADHAR",
    "poaNumber": "123456789012",
    "poaImageFrontUrl": "https://example.com/documents/aadhaar-front.jpg",
    "poaImageBackUrl": "https://example.com/documents/aadhaar-back.jpg",

    "addressLine1": "123 Example Street",
    "city": "Mumbai",
    "stateProvinceRegion": "MH",
    "postalCode": "400001",
    "country": "IND",

    "selfieImageUrl": "https://example.com/documents/selfie.jpg",
    "faceMatchScore": 0.98,
    "livenessScore": 0.97,

    "verification": {
      "provider": "didit",
      "sessionId": "didit-session-id",
      "status": "approved"
    }
  },
  "timestamp": 1784793600000,
  "nonce": "9d8934143ca747ebaaba3100f15cb4a1"
}
```

The complete body, including `forceKycUpdate`, must be included when calculating the HMAC signature.

### Code Snippet

{% tabs %}
{% tab title="JavaScript (Axios)" %}

```javascript
import crypto from 'crypto';
import axios from 'axios';

const apiKey = 'your-api-key';
const apiSecret = 'your-api-secret';

const requestBody = {
    endUserId: 20001, // Optional: Provide one user identifier only
    endUserEmail: "joe@example.com", // Optional: Alternative to endUserId and externalUserId
    externalUserId: "user-10021", // Optional: Alternative to endUserId and endUserEmail
    kyc: {
        "userCountry": "IND",
        "firstName": "Joe",
        "lastName": "Doe",
        "dateOfBirth": "1990-01-01",
    
        "poiType": "PAN",
        "poiNumber": "ABC123456D",
        "poiImageUrl": "https://example.com/documents/pan.jpg",
    
        "poaType": "AADHAR",
        "poaNumber": "123456789012",
        "poaImageFrontUrl": "https://example.com/documents/aadhaar-front.jpg",
        "poaImageBackUrl": "https://example.com/documents/aadhaar-back.jpg",
    
        "addressLine1": "123 Example Street",
        "city": "Mumbai",
        "stateProvinceRegion": "MH",
        "postalCode": "400001",
        "country": "IND",
    
        "selfieImageUrl": "https://example.com/documents/selfie.jpg",
        "faceMatchScore": 0.98,
        "livenessScore": 0.97,
    
        "verification": {
          "provider": "didit",
          "sessionId": "didit-session-id",
          "status": "approved"
    }
  },
    forceKycUpdate: false,
    timestamp: Date.now(),
    nonce: crypto.randomBytes(8).toString('hex')
};

const raw = JSON.stringify(requestBody);
const signature = crypto.createHmac('sha256', apiSecret).update(raw).digest('hex');

const headers = {
    'x-tlp-apikey': apiKey,
    'x-tlp-signature': signature,
    'Content-Type': 'application/json'
};

axios.put('https://api.tylt.money/whitelabel/users/submitKyc', raw, { headers })
    .then(response => console.log(response.data))
    .catch(error => console.error('Error:', error.response ? error.response.data : error.message));
```

{% endtab %}
{% endtabs %}

### Successful Response

```json
{
  "msg": "KYC submitted successfully.",
  "data": {
    "endUserId": 1012,
    "kycStatus": "approved",
    "verificationStored": true
  }
}
```

### Response Fields

| Field                | Description                                                      |
| -------------------- | ---------------------------------------------------------------- |
| `endUserId`          | Tylt end-user ID                                                 |
| `kycStatus`          | Current overall KYC status                                       |
| `verificationStored` | Confirms whether the verification record was successfully stored |

The returned KYC status may include values such as:

| Status            | Description                                       |
| ----------------- | ------------------------------------------------- |
| `incomplete`      | Required information has not been fully submitted |
| `pending`         | KYC is being processed or reviewed                |
| `approved`        | KYC has been successfully approved                |
| `rejected`        | KYC was rejected                                  |
| `requires_review` | Additional review or documentation is required    |

Actual status values may depend on the configured verification workflow.

### Already Approved Response

When KYC has already been approved and `forceKycUpdate` is not provided:

```json
{
  "msg": "KYC already approved.",
  "data": {
    "endUserId": 1012,
    "kycStatus": "approved"
  }
}
```

### Possible Errors

| HTTP Status | Message                                                         | Description                                           |
| ----------- | --------------------------------------------------------------- | ----------------------------------------------------- |
| `400`       | Validation error                                                | Required KYC fields are missing or invalid            |
| `400`       | `One of endUserEmail, endUserId or externalUserId is required.` | No user identifier was supplied                       |
| `401`       | `Api Key authentication failed!`                                | Authentication failed                                 |
| `403`       | `API key or owner is inactive.`                                 | API access or merchant account is inactive            |
| `403`       | `IP not whitelisted.`                                           | Request originated from an unauthorized IP address    |
| `403`       | `End user is suspended.`                                        | The identified user is suspended                      |
| `404`       | `End user not found for this owner.`                            | No matching user exists under the merchant            |
| `409`       | `Failed to write KYC record.`                                   | A duplicate or conflicting verification record exists |
| `500`       | `Failed to submit KYC. Contact support.`                        | KYC processing failed unexpectedly                    |

***
