Approve KYC Validation
curl --request POST \
--url http://api.gu1.ai/api/kyc/validations/{id}/approve \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"reason": "<string>"
}
'import requests
url = "http://api.gu1.ai/api/kyc/validations/{id}/approve"
payload = { "reason": "<string>" }
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({reason: '<string>'})
};
fetch('http://api.gu1.ai/api/kyc/validations/{id}/approve', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "http://api.gu1.ai/api/kyc/validations/{id}/approve",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'reason' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "http://api.gu1.ai/api/kyc/validations/{id}/approve"
payload := strings.NewReader("{\n \"reason\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("http://api.gu1.ai/api/kyc/validations/{id}/approve")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"reason\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("http://api.gu1.ai/api/kyc/validations/{id}/approve")
http = Net::HTTP.new(url.host, url.port)
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"reason\": \"<string>\"\n}"
response = http.request(request)
puts response.read_bodySession-based validation
Approve KYC Validation
Manually approve a KYC validation — in the gu1 KYC API for identity verification flows, with examples for approve validation use cases.
POST
/
api
/
kyc
/
validations
/
{id}
/
approve
Approve KYC Validation
curl --request POST \
--url http://api.gu1.ai/api/kyc/validations/{id}/approve \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"reason": "<string>"
}
'import requests
url = "http://api.gu1.ai/api/kyc/validations/{id}/approve"
payload = { "reason": "<string>" }
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({reason: '<string>'})
};
fetch('http://api.gu1.ai/api/kyc/validations/{id}/approve', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "http://api.gu1.ai/api/kyc/validations/{id}/approve",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'reason' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "http://api.gu1.ai/api/kyc/validations/{id}/approve"
payload := strings.NewReader("{\n \"reason\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("http://api.gu1.ai/api/kyc/validations/{id}/approve")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"reason\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("http://api.gu1.ai/api/kyc/validations/{id}/approve")
http = Net::HTTP.new(url.host, url.port)
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"reason\": \"<string>\"\n}"
response = http.request(request)
puts response.read_bodyOverview
This endpoint allows you to manually approve a KYC validation. When you approve:- Automatic sync with provider: The system first syncs with the KYC provider to get the latest verification status
- Status validation: You can only approve if provider status is
in_review(user has completed verification and requires manual review from compliance team) - The validation status changes to
approved - The validation is marked as verified with timestamp
- The approval reason is saved in metadata and audit logs
- With
doubleCheckRenaper: RENAPER is not re-run and cross-check enforcement is not re-applied; the API reusesmetadata.responseDoubleChecks.renaperandwarningsalready computed inin_review. The decision is human after reviewing that data in the UI.
Manual approval should only be used when you’ve performed additional verification or have sufficient reason to override the automated process. The approval reason is required for audit compliance.
When to Use This
- Additional verification completed: You’ve performed manual review of documents or biometric data
- Business exception: You have business reasons to approve despite minor issues
- Risk assessment override: Your risk team has approved the validation
- Provider delay: The verification provider is taking too long but your internal review is complete
Request
Endpoint
POST https://api.gu1.ai/api/kyc/validations/{id}/approve
Path Parameters
string
required
The validation ID to approve
Headers
{
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json"
}
Body Parameters
string
required
Reason for manually approving the validation (minimum 5 characters)Type:
string (min length: 5)Example: "Manual review completed successfully - all documents verified"Response
Success Response (200 OK)
Returns the updated validation object withapproved status:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"entityId": "123e4567-e89b-12d3-a456-426614174000",
"organizationId": "org_abc123",
"validationSessionId": "session_xyz789",
"status": "approved",
"provider": "kyc_provider",
"providerSessionUrl": "https://verify.example.com/session_xyz789",
"isCurrent": true,
"verifiedAt": "2025-01-27T10:30:00Z",
"metadata": {
"manuallyApprovedBy": "user_123",
"manuallyApprovedAt": "2025-01-27T10:30:00Z",
"approvalReason": "Manual review completed successfully - all documents verified"
},
"createdAt": "2025-01-15T10:30:00Z",
"updatedAt": "2025-01-27T10:30:00Z"
}
Example Request
const validationId = '550e8400-e29b-41d4-a716-446655440000';
const response = await fetch(
`https://api.gu1.ai/api/kyc/validations/${validationId}/approve`,
{
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json',
},
body: JSON.stringify({
reason: 'Manual review completed successfully - all documents verified'
})
}
);
const approved = await response.json();
console.log('Validation approved:', approved.status);
import requests
validation_id = '550e8400-e29b-41d4-a716-446655440000'
response = requests.post(
f'https://api.gu1.ai/api/kyc/validations/{validation_id}/approve',
headers={
'Authorization': 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json',
},
json={
'reason': 'Manual review completed successfully - all documents verified'
}
)
approved = response.json()
print('Validation approved:', approved['status'])
curl -X POST https://api.gu1.ai/api/kyc/validations/550e8400-e29b-41d4-a716-446655440000/approve \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"reason": "Manual review completed successfully - all documents verified"
}'
Error Responses
Validation Not Found (404)
{
"error": "NOT_FOUND",
"message": "Validation not found"
}
Invalid Status (400)
Validation can only be approved if provider status isin_review (user has completed verification and requires manual review from compliance team):
{
"error": "INVALID_STATUS",
"message": "Cannot manually approve. Current status is \"approved\". Manual approval is only allowed when status is \"in_review\" (validation requires manual review from compliance team)."
}
- If the provider already approved (
approved) or rejected (rejected) the validation, you cannot override that decision - If the validation is
cancelled, you cannot approve it - If it’s
expiredorabandoned, it also cannot be manually approved - If it’s
in_progress, the user is still filling out the verification form - You can only approve when the provider reports the validation as
in_review(user has completed all verification steps and requires manual review from compliance team)
Invalid Reason (400)
Reason must be at least 5 characters:{
"error": "VALIDATION_ERROR",
"message": "Reason must be at least 5 characters"
}
Important Notes
Automatic Sync Before Approval
Automatic Sync Before Approval
Before approving, the system automatically syncs with the KYC provider to get the latest verification data. This ensures you’re making decisions based on the most recent information.
Status Protection
Status Protection
You cannot approve validations that are already
approved, rejected, or cancelled. These statuses are protected to maintain data integrity.Audit Trail
Audit Trail
The approval reason is saved in both the validation metadata and audit logs. This is critical for compliance and regulatory requirements.
Only for In-Review Validations
Only for In-Review Validations
Manual approval is only allowed when the provider reports the validation as
in_review. This prevents approving validations that:- The provider has already declined or completed
- Don’t have complete verification data (status
pendingorin_progress) - The user is still filling out the form (status
in_progress)
Verification Timestamp
Verification Timestamp
When approved, the validation receives a
verifiedAt timestamp and becomes the current validation for the entity.Best Practices
- Always provide detailed reasons: Include specific details about why manual approval is necessary
- Document your process: Maintain internal documentation for manual approval criteria
- Review fresh data: The automatic sync ensures you see the latest documents and biometric results
- Use sparingly: Manual approval should be the exception, not the rule
- Train your team: Ensure staff understand when manual approval is appropriate
Next Steps
Reject Validation
Manually reject a validation
Check Verification Status
Query validation results
Was this page helpful?