curl --request POST \
--url https://staging.api.payluk.ng/v1/customer/create \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"firstname": "Ada",
"lastname": "Eze",
"email": "[email protected]",
"phone": "08012345678",
"countryId": "NG",
"bvn": "22222222222"
}
'import requests
url = "https://staging.api.payluk.ng/v1/customer/create"
payload = {
"firstname": "Ada",
"lastname": "Eze",
"email": "[email protected]",
"phone": "08012345678",
"countryId": "NG",
"bvn": "22222222222"
}
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({
firstname: 'Ada',
lastname: 'Eze',
email: '[email protected]',
phone: '08012345678',
countryId: 'NG',
bvn: '22222222222'
})
};
fetch('https://staging.api.payluk.ng/v1/customer/create', 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://staging.api.payluk.ng/v1/customer/create",
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([
'firstname' => 'Ada',
'lastname' => 'Eze',
'email' => '[email protected]',
'phone' => '08012345678',
'countryId' => 'NG',
'bvn' => '22222222222'
]),
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://staging.api.payluk.ng/v1/customer/create"
payload := strings.NewReader("{\n \"firstname\": \"Ada\",\n \"lastname\": \"Eze\",\n \"email\": \"[email protected]\",\n \"phone\": \"08012345678\",\n \"countryId\": \"NG\",\n \"bvn\": \"22222222222\"\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://staging.api.payluk.ng/v1/customer/create")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"firstname\": \"Ada\",\n \"lastname\": \"Eze\",\n \"email\": \"[email protected]\",\n \"phone\": \"08012345678\",\n \"countryId\": \"NG\",\n \"bvn\": \"22222222222\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://staging.api.payluk.ng/v1/customer/create")
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 \"firstname\": \"Ada\",\n \"lastname\": \"Eze\",\n \"email\": \"[email protected]\",\n \"phone\": \"08012345678\",\n \"countryId\": \"NG\",\n \"bvn\": \"22222222222\"\n}"
response = http.request(request)
puts response.read_body{
"status": 201,
"message": "Customer created successfully",
"data": {
"customerId": "6a19b57ffa797d3af379ca2a",
"firstname": "firsty",
"lastname": "fhgvj",
"email": "[email protected]",
"phone": "09022331122",
"bvn": null,
"status": "active",
"blockedReason": null,
"blockedAt": null,
"createdAt": "2026-05-29T15:49:19.848Z",
"dob": null,
"permissions": {
"canWithdraw": true,
"canBuy": true,
"canSell": true
},
"countryId": "69b8d4c7f80e1f661b48c9ca"
}
}{
"status": 400,
"message": "amount is required",
"data": {}
}{
"status": 429,
"message": "Too many request"
}Create merchant customer
Creates a customer (buyer/seller) under your merchant account. Requires the API key to belong to a merchant super-admin account.
phone is digits only with no leading +; local and international spellings of the same number resolve to the same customer. countryId is optional and defaults to your merchant account’s country; pass an ISO 3166-1 alpha-2 code (NG) or an id from Get countries. bvn is an optional 11-digit Bank Verification Number kept on the customer’s record; virtual accounts are no longer issued, so it does not change how the customer is funded.
curl --request POST \
--url https://staging.api.payluk.ng/v1/customer/create \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"firstname": "Ada",
"lastname": "Eze",
"email": "[email protected]",
"phone": "08012345678",
"countryId": "NG",
"bvn": "22222222222"
}
'import requests
url = "https://staging.api.payluk.ng/v1/customer/create"
payload = {
"firstname": "Ada",
"lastname": "Eze",
"email": "[email protected]",
"phone": "08012345678",
"countryId": "NG",
"bvn": "22222222222"
}
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({
firstname: 'Ada',
lastname: 'Eze',
email: '[email protected]',
phone: '08012345678',
countryId: 'NG',
bvn: '22222222222'
})
};
fetch('https://staging.api.payluk.ng/v1/customer/create', 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://staging.api.payluk.ng/v1/customer/create",
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([
'firstname' => 'Ada',
'lastname' => 'Eze',
'email' => '[email protected]',
'phone' => '08012345678',
'countryId' => 'NG',
'bvn' => '22222222222'
]),
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://staging.api.payluk.ng/v1/customer/create"
payload := strings.NewReader("{\n \"firstname\": \"Ada\",\n \"lastname\": \"Eze\",\n \"email\": \"[email protected]\",\n \"phone\": \"08012345678\",\n \"countryId\": \"NG\",\n \"bvn\": \"22222222222\"\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://staging.api.payluk.ng/v1/customer/create")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"firstname\": \"Ada\",\n \"lastname\": \"Eze\",\n \"email\": \"[email protected]\",\n \"phone\": \"08012345678\",\n \"countryId\": \"NG\",\n \"bvn\": \"22222222222\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://staging.api.payluk.ng/v1/customer/create")
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 \"firstname\": \"Ada\",\n \"lastname\": \"Eze\",\n \"email\": \"[email protected]\",\n \"phone\": \"08012345678\",\n \"countryId\": \"NG\",\n \"bvn\": \"22222222222\"\n}"
response = http.request(request)
puts response.read_body{
"status": 201,
"message": "Customer created successfully",
"data": {
"customerId": "6a19b57ffa797d3af379ca2a",
"firstname": "firsty",
"lastname": "fhgvj",
"email": "[email protected]",
"phone": "09022331122",
"bvn": null,
"status": "active",
"blockedReason": null,
"blockedAt": null,
"createdAt": "2026-05-29T15:49:19.848Z",
"dob": null,
"permissions": {
"canWithdraw": true,
"canBuy": true,
"canSell": true
},
"countryId": "69b8d4c7f80e1f661b48c9ca"
}
}{
"status": 400,
"message": "amount is required",
"data": {}
}{
"status": 429,
"message": "Too many request"
}Authorizations
Your secret key as a Bearer token. The key prefix selects the environment: sk_test_... (staging) or sk_live_... (production); a key on the wrong host is refused with 403 Unauthorized Access. Each key is limited to 10 requests per minute (429 beyond that) and, on production, to the IP addresses on your dashboard allowlist when one is configured.
Body
"Ada"
"Eze"
Digits only, with no leading +. Local and international spellings of the same number are both accepted (08012345678, 2348012345678, 8012345678), and every one is stored in the local form, so one person cannot become several customers under different spellings. The stored value is what the customer endpoints return.
"08012345678"
The customer's country, as either its ISO 3166-1 alpha-2 code (NG) or the id from get countries. Optional: when you omit it, the customer inherits the country on your merchant account. The country decides which phone format is accepted.
"NG"
Optional 11-digit Bank Verification Number. When provided, the customer is issued a dedicated (permanent) virtual account via Paystack; when omitted, the customer gets a temporary 24-hour virtual account instead.
11^[0-9]{11}$"22222222222"