curl --request POST \
--url https://api.kemycard.com/v1/kyc/{customerCode}/link \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"callback_url": "https://yourapp.com/kyc/done",
"lang": "en",
"force": false
}
'import requests
url = "https://api.kemycard.com/v1/kyc/{customerCode}/link"
payload = {
"callback_url": "https://yourapp.com/kyc/done",
"lang": "en",
"force": False
}
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({callback_url: 'https://yourapp.com/kyc/done', lang: 'en', force: false})
};
fetch('https://api.kemycard.com/v1/kyc/{customerCode}/link', 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.kemycard.com/v1/kyc/{customerCode}/link",
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([
'callback_url' => 'https://yourapp.com/kyc/done',
'lang' => 'en',
'force' => false
]),
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 := "https://api.kemycard.com/v1/kyc/{customerCode}/link"
payload := strings.NewReader("{\n \"callback_url\": \"https://yourapp.com/kyc/done\",\n \"lang\": \"en\",\n \"force\": false\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("https://api.kemycard.com/v1/kyc/{customerCode}/link")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"callback_url\": \"https://yourapp.com/kyc/done\",\n \"lang\": \"en\",\n \"force\": false\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.kemycard.com/v1/kyc/{customerCode}/link")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"callback_url\": \"https://yourapp.com/kyc/done\",\n \"lang\": \"en\",\n \"force\": false\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"data": {
"customer_code": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"kyc_status": "pending",
"verification_url": "https://verify.kemycard.com/v/abc123",
"expires_at": "2023-11-07T05:31:56Z",
"reused": false
}
}{
"success": true,
"data": {
"customer_code": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"kyc_status": "pending",
"verification_url": "https://verify.kemycard.com/v/abc123",
"expires_at": "2023-11-07T05:31:56Z",
"reused": false
}
}{
"success": false,
"error": {
"code": "INSUFFICIENT_BALANCE",
"message": "Insufficient ops balance.",
"details": {
"required": 50,
"available": 10,
"balance_type": "cards_mastercard"
}
}
}{
"success": false,
"error": {
"code": "RESOURCE_NOT_FOUND",
"message": "Resource not found.",
"details": null
}
}{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Missing required fields.",
"details": {}
},
"meta": {
"request_id": "a4f8e2c1b3d97056",
"mode": "test",
"timestamp": "2023-11-07T05:31:56Z"
}
}Get KYC Link
Returns the identity verification link for a customer (basic → verified). Redirect your customer to verification_url.
You can call this endpoint as many times as you need:
- First call — opens a verification session and returns its link (
201). - Link still valid — returns the same link (
200,reused: true). - Link expired, verification rejected or resubmission requested — returns a new link (
201). force: true— always returns a new link.
The verification is billed only when it is approved (2 USD, debited from your others ops balance).
Generating a link is free, but your others balance must cover the price of one verification.
The customer profile must be complete (first_name, last_name, email, phone_country_code, phone,
address, city, state, postal_code, country). Otherwise the call returns a 422 with the list of
missing_fields: update the customer, then request the link again.
The result is sent to your webhook endpoint (kyc.approved, kyc.rejected, kyc.expired,
kyc.pending_review, kyc.resubmission_requested) and is available through the KYC status endpoint.
If the KYC is approved but the identity read on the document differs from the one you declared, the customer
becomes restricted and a kyc.identity_mismatch webhook is sent: call the synchronize endpoint.
curl --request POST \
--url https://api.kemycard.com/v1/kyc/{customerCode}/link \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"callback_url": "https://yourapp.com/kyc/done",
"lang": "en",
"force": false
}
'import requests
url = "https://api.kemycard.com/v1/kyc/{customerCode}/link"
payload = {
"callback_url": "https://yourapp.com/kyc/done",
"lang": "en",
"force": False
}
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({callback_url: 'https://yourapp.com/kyc/done', lang: 'en', force: false})
};
fetch('https://api.kemycard.com/v1/kyc/{customerCode}/link', 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.kemycard.com/v1/kyc/{customerCode}/link",
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([
'callback_url' => 'https://yourapp.com/kyc/done',
'lang' => 'en',
'force' => false
]),
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 := "https://api.kemycard.com/v1/kyc/{customerCode}/link"
payload := strings.NewReader("{\n \"callback_url\": \"https://yourapp.com/kyc/done\",\n \"lang\": \"en\",\n \"force\": false\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("https://api.kemycard.com/v1/kyc/{customerCode}/link")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"callback_url\": \"https://yourapp.com/kyc/done\",\n \"lang\": \"en\",\n \"force\": false\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.kemycard.com/v1/kyc/{customerCode}/link")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"callback_url\": \"https://yourapp.com/kyc/done\",\n \"lang\": \"en\",\n \"force\": false\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"data": {
"customer_code": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"kyc_status": "pending",
"verification_url": "https://verify.kemycard.com/v/abc123",
"expires_at": "2023-11-07T05:31:56Z",
"reused": false
}
}{
"success": true,
"data": {
"customer_code": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"kyc_status": "pending",
"verification_url": "https://verify.kemycard.com/v/abc123",
"expires_at": "2023-11-07T05:31:56Z",
"reused": false
}
}{
"success": false,
"error": {
"code": "INSUFFICIENT_BALANCE",
"message": "Insufficient ops balance.",
"details": {
"required": 50,
"available": 10,
"balance_type": "cards_mastercard"
}
}
}{
"success": false,
"error": {
"code": "RESOURCE_NOT_FOUND",
"message": "Resource not found.",
"details": null
}
}{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Missing required fields.",
"details": {}
},
"meta": {
"request_id": "a4f8e2c1b3d97056",
"mode": "test",
"timestamp": "2023-11-07T05:31:56Z"
}
}Authorizations
API key (sk_test_... or sk_live_...)
Path Parameters
Customer UUID
Body
Optional. HTTPS URL your customer is redirected to after the verification. If omitted, your customer is not redirected back to your application at the end of the flow
"https://yourapp.com/kyc/done"
2-letter language code of the verification screen
"en"
Set to true to get a new link even if the current one is still valid