Initiate KYC Verification
curl --request POST \
--url https://api.fyatu.com/api/v3/cardholders/{id}/kyc/session \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.fyatu.com/api/v3/cardholders/{id}/kyc/session"
headers = {"Authorization": "Bearer <token>"}
response = requests.post(url, headers=headers)
print(response.text)const options = {method: 'POST', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.fyatu.com/api/v3/cardholders/{id}/kyc/session', 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 => "https://api.fyatu.com/api/v3/cardholders/{id}/kyc/session",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.fyatu.com/api/v3/cardholders/{id}/kyc/session"
req, _ := http.NewRequest("POST", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.fyatu.com/api/v3/cardholders/{id}/kyc/session")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.fyatu.com/api/v3/cardholders/{id}/kyc/session")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"success": true,
"status": 200,
"message": "KYC session already in progress",
"data": {
"cardholderId": "ch_1a2b3c4d5e6f7890abcdef1234567890",
"sessionId": "ses_abc123def456",
"verificationUrl": "https://verify.didit.me/session/ses_abc123def456",
"kycStatus": "PENDING"
}
}{
"success": true,
"status": 201,
"message": "KYC verification session created",
"data": {
"cardholderId": "ch_1a2b3c4d5e6f7890abcdef1234567890",
"sessionId": "ses_abc123def456",
"verificationUrl": "https://verify.didit.me/session/ses_abc123def456",
"kycStatus": "PENDING",
"fee": 0.6
}
}{
"success": false,
"status": 401,
"message": "Unable to identify business",
"error": {
"code": "AUTH_TOKEN_INVALID"
},
"meta": {
"requestId": "req_abc123",
"timestamp": "2026-01-05T10:30:00+00:00"
}
}{
"success": false,
"status": 402,
"message": "Insufficient balance for KYC verification fee ($0.60)",
"error": {
"code": "INSUFFICIENT_BALANCE"
}
}{
"success": false,
"status": 404,
"message": "Wallet not found",
"error": {
"code": "RESOURCE_NOT_FOUND"
},
"meta": {
"requestId": "req_abc123",
"timestamp": "2026-01-05T10:30:00+00:00"
}
}{
"success": false,
"status": 409,
"message": "KYC has already been verified for this cardholder",
"error": {
"code": "CONFLICT"
}
}{
"success": false,
"status": 503,
"message": "Failed to create verification session. Please try again.",
"error": {
"code": "PROVIDER_ERROR"
}
}Cardholders
Initiate KYC Verification
Start identity verification for a cardholder via automated ID + liveness check. Returns a verification URL. POST /cardholders//kyc/session.
POST
/
cardholders
/
{id}
/
kyc
/
session
Initiate KYC Verification
curl --request POST \
--url https://api.fyatu.com/api/v3/cardholders/{id}/kyc/session \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.fyatu.com/api/v3/cardholders/{id}/kyc/session"
headers = {"Authorization": "Bearer <token>"}
response = requests.post(url, headers=headers)
print(response.text)const options = {method: 'POST', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.fyatu.com/api/v3/cardholders/{id}/kyc/session', 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 => "https://api.fyatu.com/api/v3/cardholders/{id}/kyc/session",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.fyatu.com/api/v3/cardholders/{id}/kyc/session"
req, _ := http.NewRequest("POST", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.fyatu.com/api/v3/cardholders/{id}/kyc/session")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.fyatu.com/api/v3/cardholders/{id}/kyc/session")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"success": true,
"status": 200,
"message": "KYC session already in progress",
"data": {
"cardholderId": "ch_1a2b3c4d5e6f7890abcdef1234567890",
"sessionId": "ses_abc123def456",
"verificationUrl": "https://verify.didit.me/session/ses_abc123def456",
"kycStatus": "PENDING"
}
}{
"success": true,
"status": 201,
"message": "KYC verification session created",
"data": {
"cardholderId": "ch_1a2b3c4d5e6f7890abcdef1234567890",
"sessionId": "ses_abc123def456",
"verificationUrl": "https://verify.didit.me/session/ses_abc123def456",
"kycStatus": "PENDING",
"fee": 0.6
}
}{
"success": false,
"status": 401,
"message": "Unable to identify business",
"error": {
"code": "AUTH_TOKEN_INVALID"
},
"meta": {
"requestId": "req_abc123",
"timestamp": "2026-01-05T10:30:00+00:00"
}
}{
"success": false,
"status": 402,
"message": "Insufficient balance for KYC verification fee ($0.60)",
"error": {
"code": "INSUFFICIENT_BALANCE"
}
}{
"success": false,
"status": 404,
"message": "Wallet not found",
"error": {
"code": "RESOURCE_NOT_FOUND"
},
"meta": {
"requestId": "req_abc123",
"timestamp": "2026-01-05T10:30:00+00:00"
}
}{
"success": false,
"status": 409,
"message": "KYC has already been verified for this cardholder",
"error": {
"code": "CONFLICT"
}
}{
"success": false,
"status": 503,
"message": "Failed to create verification session. Please try again.",
"error": {
"code": "PROVIDER_ERROR"
}
}Overview
Initiate an optional automated KYC (Know Your Customer) verification session for a cardholder. This creates a secure verification session where the cardholder completes identity document capture and liveness verification. KYC verification is not required for card issuance — you can issue cards to cardholders without completing KYC. Use this endpoint when you need to verify a cardholder’s identity for compliance or enhanced trust. This is the self-service KYC path where the cardholder completes verification themselves. If you already have the cardholder’s ID documents and want to submit them on their behalf, use Submit KYC Documents instead. The verification result is delivered asynchronously via webhook (cardholder.kyc_approved or cardholder.kyc_rejected).
Endpoint
POST /api/v3/cardholders/{cardholderId}/kyc/session
cardholders:write
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
cardholderId | string | Yes | Unique cardholder identifier |
Request Body
No request body required.How It Works
- Your app calls this endpoint to get a verification URL
- Redirect the cardholder to the
verificationUrl - The cardholder completes ID document capture and liveness verification
- FYATU sends a webhook to your app with the result (
cardholder.kyc_approvedorcardholder.kyc_rejected)
Verification Fee
A fee is charged per successful verification, based on your plan:| Plan | Fee per verification |
|---|---|
| Startup | $1.20 |
| Enterprise | $0.80 |
| Premium | $0.40 |
- The fee is not charged upfront - no wallet hold or deduction when initiating verification
- Added to your invoice only when verification is approved (successful)
- Not charged when verification is declined, abandoned, or expires
- The fee appears as a line item on your next monthly invoice
Prerequisites
- Cardholder
kycStatusmust beUNSUBMITTEDorREJECTED
Example Usage
<?php
$cardholderId = 'CH1a2b3c4d5e6f';
$response = file_get_contents(
"https://api.fyatu.com/api/v3/cardholders/{$cardholderId}/kyc/session",
false,
stream_context_create([
'http' => [
'method' => 'POST',
'header' => [
'Authorization: Bearer ' . $accessToken,
'Content-Type: application/json'
]
]
])
);
$result = json_decode($response, true);
// Redirect cardholder to the verification URL
$verificationUrl = $result['data']['verificationUrl'];
echo "Redirect cardholder to: {$verificationUrl}\n";
const cardholderId = 'CH1a2b3c4d5e6f';
const response = await fetch(
`https://api.fyatu.com/api/v3/cardholders/${cardholderId}/kyc/session`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json'
}
}
);
const result = await response.json();
// Redirect cardholder to the verification URL
const { verificationUrl } = result.data;
console.log('Redirect cardholder to:', verificationUrl);
import requests
cardholder_id = 'CH1a2b3c4d5e6f'
response = requests.post(
f'https://api.fyatu.com/api/v3/cardholders/{cardholder_id}/kyc/session',
headers={
'Authorization': f'Bearer {access_token}',
'Content-Type': 'application/json'
}
)
result = response.json()
verification_url = result['data']['verificationUrl']
print(f'Redirect cardholder to: {verification_url}')
Example Response
Success (201)
{
"success": true,
"status": 201,
"message": "KYC verification session created",
"data": {
"cardholderId": "CH1a2b3c4d5e6f",
"sessionId": "ses_abc123def456",
"verificationUrl": "https://verify.didit.me/session/ses_abc123def456",
"kycStatus": "PENDING",
"fee": 0.60
},
"meta": {
"requestId": "req_kyc789xyz",
"timestamp": "2026-04-02T10:30:00+00:00"
}
}
Session Already In Progress (200)
If a verification session is already active, the existing session is returned:{
"success": true,
"status": 200,
"message": "KYC session already in progress",
"data": {
"cardholderId": "CH1a2b3c4d5e6f",
"sessionId": "ses_abc123def456",
"verificationUrl": "https://verify.didit.me/session/ses_abc123def456",
"kycStatus": "PENDING"
},
"meta": {
"requestId": "req_kyc790xyz",
"timestamp": "2026-04-02T10:35:00+00:00"
}
}
No-KYC / Minimal Business (200)
If the business runs in Minimal (No-KYC) mode, no verification session is created — the cardholder is waived instead:{
"success": true,
"status": 200,
"message": "KYC is waived for this business — no verification required",
"data": {
"cardholderId": "CH1a2b3c4d5e6f",
"kycStatus": "WAIVED"
},
"meta": {
"requestId": "req_kyc791xyz",
"timestamp": "2026-04-02T10:40:00+00:00"
}
}
KYC Status Flow
UNSUBMITTED ──> PENDING (session initiated) ──> VERIFIED (verification approved)
^ |
| ├──> REJECTED (verification failed) ──> UNSUBMITTED (can retry)
| |
| └──> UNSUBMITTED (session abandoned/expired, can retry)
|
└── REJECTED ──> PENDING (new session initiated)
| Status | Description | Can Initiate Session |
|---|---|---|
UNSUBMITTED | No verification started | Yes |
PENDING | Verification in progress | No (returns existing session) |
VERIFIED | Verification approved | No |
REJECTED | Verification failed | Yes (retry allowed) |
Webhook Events
After the cardholder completes (or abandons) verification, you’ll receive one of these webhooks:cardholder.kyc_approved
{
"event": "cardholder.kyc_approved",
"version": "3.0",
"eventId": "77d958cb-128d-4927-bd2c-c351a153fb39",
"sign": "e5902a90747d0a43dd74498dedaaf09c40e1a51cb99ba651d5a3fdd9847d901e",
"data": {
"cardholderId": "CH1a2b3c4d5e6f",
"firstName": "John",
"lastName": "Smith",
"kycStatus": "VERIFIED",
"kycLevel": "VERIFIED",
"idFrontUrl": "https://cdn.fyatu.com/user/kyc/front_1234567890.jpg",
"idBackUrl": "https://cdn.fyatu.com/user/kyc/back_1234567890.jpg",
"idSelfieUrl": "https://cdn.fyatu.com/user/kyc/selfie_1234567890.jpg",
"appId": "D0H6R7Z6R1C2N5O5",
"timestamp": "2026-04-02T10:45:00Z"
}
}
cardholder.kyc_rejected
{
"event": "cardholder.kyc_rejected",
"version": "3.0",
"eventId": "77d958cb-128d-4927-bd2c-c351a153fb39",
"sign": "e5902a90747d0a43dd74498dedaaf09c40e1a51cb99ba651d5a3fdd9847d901e",
"data": {
"cardholderId": "CH1a2b3c4d5e6f",
"firstName": "John",
"lastName": "Smith",
"status": "REJECTED",
"reason": "{\"feature\":\"DOCUMENT\",\"risk\":\"EXPIRED_DOCUMENT\",\"short_description\":\"Document expired\"}",
"appId": "D0H6R7Z6R1C2N5O5",
"timestamp": "2026-04-02T10:45:00Z"
}
}
status key (not kycStatus), and reason is a JSON-encoded string from the KYC provider — parse it for the structured detail.
Error Responses
Already Accepted (409)
{
"success": false,
"status": 409,
"message": "KYC has already been accepted for this cardholder",
"error": { "code": "CONFLICT" }
}
Provider Error (503)
{
"success": false,
"status": 503,
"message": "Failed to create verification session. Please try again.",
"error": { "code": "PROVIDER_ERROR" }
}
KYC Service Not Configured (503)
{
"success": false,
"status": 503,
"message": "KYC session service is not configured",
"error": { "code": "SERVICE_UNAVAILABLE" }
}
Store the
verificationUrl and provide it to the cardholder. If the cardholder doesn’t complete verification, you can call this endpoint again to get a new session after the previous one expires.The verification fee is only charged on successful verification and added to your next monthly invoice. If the cardholder abandons the session or verification fails, no fee is charged.
Authorizations
JWT access token obtained from /auth/token
Path Parameters
Unique cardholder identifier

