curl --request POST \
--url https://crypto.westminister.tech/api/v1/users/register \
--header 'Content-Type: application/json' \
--data '
{
"email": "tigger.germain@gmail.com",
"phone_number": "+254723453286",
"first_name": "Tigger",
"last_name": "Germain",
"password": "secret",
"role": "vendor",
"kyc_status": "pending",
"pin": "123456"
}
'import requests
url = "https://crypto.westminister.tech/api/v1/users/register"
payload = {
"email": "tigger.germain@gmail.com",
"phone_number": "+254723453286",
"first_name": "Tigger",
"last_name": "Germain",
"password": "secret",
"role": "vendor",
"kyc_status": "pending",
"pin": "123456"
}
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
email: 'tigger.germain@gmail.com',
phone_number: '+254723453286',
first_name: 'Tigger',
last_name: 'Germain',
password: 'secret',
role: 'vendor',
kyc_status: 'pending',
pin: '123456'
})
};
fetch('https://crypto.westminister.tech/api/v1/users/register', 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://crypto.westminister.tech/api/v1/users/register",
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([
'email' => 'tigger.germain@gmail.com',
'phone_number' => '+254723453286',
'first_name' => 'Tigger',
'last_name' => 'Germain',
'password' => 'secret',
'role' => 'vendor',
'kyc_status' => 'pending',
'pin' => '123456'
]),
CURLOPT_HTTPHEADER => [
"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://crypto.westminister.tech/api/v1/users/register"
payload := strings.NewReader("{\n \"email\": \"tigger.germain@gmail.com\",\n \"phone_number\": \"+254723453286\",\n \"first_name\": \"Tigger\",\n \"last_name\": \"Germain\",\n \"password\": \"secret\",\n \"role\": \"vendor\",\n \"kyc_status\": \"pending\",\n \"pin\": \"123456\"\n}")
req, _ := http.NewRequest("POST", url, payload)
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://crypto.westminister.tech/api/v1/users/register")
.header("Content-Type", "application/json")
.body("{\n \"email\": \"tigger.germain@gmail.com\",\n \"phone_number\": \"+254723453286\",\n \"first_name\": \"Tigger\",\n \"last_name\": \"Germain\",\n \"password\": \"secret\",\n \"role\": \"vendor\",\n \"kyc_status\": \"pending\",\n \"pin\": \"123456\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://crypto.westminister.tech/api/v1/users/register")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n \"email\": \"tigger.germain@gmail.com\",\n \"phone_number\": \"+254723453286\",\n \"first_name\": \"Tigger\",\n \"last_name\": \"Germain\",\n \"password\": \"secret\",\n \"role\": \"vendor\",\n \"kyc_status\": \"pending\",\n \"pin\": \"123456\"\n}"
response = http.request(request)
puts response.read_body{
"message": "User registered successfully with default wallets",
"user": {
"id": "383f20af-5960-4374-a5c3-b7566ea8f771",
"email": "tigger.germain@gmail.com",
"phone_number": 254723453286,
"first_name": "Tigger",
"last_name": "Germain",
"role": "vendor",
"kyc_status": "pending"
},
"wallets": [
{
"wallet": {
"id": "ea963c72-35d4-4a4e-8e32-70971c27f188",
"user_id": "383f20af-5960-4374-a5c3-b7566ea8f771",
"name": "Ethereum Wallet",
"address": 1.0526201056375617e+48,
"wallet_type": "hot",
"network": "ethereum",
"is_default": true,
"is_active": true
},
"balances": [
{
"id": "dd9bbc6b-a3b5-4b5e-97db-e00aa8389ee1",
"user_id": "383f20af-5960-4374-a5c3-b7566ea8f771",
"wallet_id": "ea963c72-35d4-4a4e-8e32-70971c27f188",
"currency": "ETH",
"token_type": "ETH",
"token_symbol": "ETH",
"amount": "0.00000000",
"usd_value": "0.00",
"locked_amount": "0.00000000",
"last_price_usd": "0.00"
},
{
"id": "309e195d-6b6b-4a41-b793-5452f5c1ed02",
"user_id": "383f20af-5960-4374-a5c3-b7566ea8f771",
"wallet_id": "ea963c72-35d4-4a4e-8e32-70971c27f188",
"currency": "USDT",
"token_type": "USDT",
"token_symbol": "USDT",
"amount": "0.00000000",
"usd_value": "0.00",
"locked_amount": "0.00000000",
"last_price_usd": "0.00"
}
]
},
{
"wallet": {
"id": "05e36fd9-88e9-4dfb-8d8b-07493cef8e84",
"user_id": "383f20af-5960-4374-a5c3-b7566ea8f771",
"name": "TRON Wallet",
"address": "TFviB3ioZTnbPJVqqWjqZJbc6zu7hkeyEc",
"wallet_type": "hot",
"network": "tron",
"is_active": true
},
"balances": [
{
"id": "57bcec93-ea33-4fe3-bbd1-d9e10bad45ca",
"user_id": "383f20af-5960-4374-a5c3-b7566ea8f771",
"wallet_id": "05e36fd9-88e9-4dfb-8d8b-07493cef8e84",
"currency": "TRX",
"token_type": "TRX",
"token_symbol": "TRX",
"amount": "0.00000000",
"usd_value": "0.00",
"locked_amount": "0.00000000",
"last_price_usd": "0.00"
},
{
"id": "75ca706e-9461-489b-ad2d-03bb82043d01",
"user_id": "383f20af-5960-4374-a5c3-b7566ea8f771",
"wallet_id": "05e36fd9-88e9-4dfb-8d8b-07493cef8e84",
"currency": "USDT",
"token_type": "USDT",
"token_symbol": "USDT",
"amount": "0.00000000",
"usd_value": "0.00",
"locked_amount": "0.00000000",
"last_price_usd": "0.00"
}
]
}
]
}Register User, Create Default Wallets, And Return User And Wallets With Balances
Registers a new user and best-effort provisions default Ethereum, TRON, and Solana custodial wallets with zero balances. Duplicate contact guard: Before any user row or default wallet is created, email and phone_number must be unique among active users and within the request (bulk create). On conflict the API returns 409 and makes no database changes.
During registration, the user can also set an optional 6-digit numeric PIN (pin) which
can later be used to log in via the PIN-based login endpoint (POST /api/v1/auth/login-pin) or
as transaction PIN to authorize a transaction.
If pin is omitted during log in, the user can still log in with email + password but cannot use PIN login
until a PIN is set.
curl --request POST \
--url https://crypto.westminister.tech/api/v1/users/register \
--header 'Content-Type: application/json' \
--data '
{
"email": "tigger.germain@gmail.com",
"phone_number": "+254723453286",
"first_name": "Tigger",
"last_name": "Germain",
"password": "secret",
"role": "vendor",
"kyc_status": "pending",
"pin": "123456"
}
'import requests
url = "https://crypto.westminister.tech/api/v1/users/register"
payload = {
"email": "tigger.germain@gmail.com",
"phone_number": "+254723453286",
"first_name": "Tigger",
"last_name": "Germain",
"password": "secret",
"role": "vendor",
"kyc_status": "pending",
"pin": "123456"
}
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
email: 'tigger.germain@gmail.com',
phone_number: '+254723453286',
first_name: 'Tigger',
last_name: 'Germain',
password: 'secret',
role: 'vendor',
kyc_status: 'pending',
pin: '123456'
})
};
fetch('https://crypto.westminister.tech/api/v1/users/register', 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://crypto.westminister.tech/api/v1/users/register",
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([
'email' => 'tigger.germain@gmail.com',
'phone_number' => '+254723453286',
'first_name' => 'Tigger',
'last_name' => 'Germain',
'password' => 'secret',
'role' => 'vendor',
'kyc_status' => 'pending',
'pin' => '123456'
]),
CURLOPT_HTTPHEADER => [
"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://crypto.westminister.tech/api/v1/users/register"
payload := strings.NewReader("{\n \"email\": \"tigger.germain@gmail.com\",\n \"phone_number\": \"+254723453286\",\n \"first_name\": \"Tigger\",\n \"last_name\": \"Germain\",\n \"password\": \"secret\",\n \"role\": \"vendor\",\n \"kyc_status\": \"pending\",\n \"pin\": \"123456\"\n}")
req, _ := http.NewRequest("POST", url, payload)
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://crypto.westminister.tech/api/v1/users/register")
.header("Content-Type", "application/json")
.body("{\n \"email\": \"tigger.germain@gmail.com\",\n \"phone_number\": \"+254723453286\",\n \"first_name\": \"Tigger\",\n \"last_name\": \"Germain\",\n \"password\": \"secret\",\n \"role\": \"vendor\",\n \"kyc_status\": \"pending\",\n \"pin\": \"123456\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://crypto.westminister.tech/api/v1/users/register")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n \"email\": \"tigger.germain@gmail.com\",\n \"phone_number\": \"+254723453286\",\n \"first_name\": \"Tigger\",\n \"last_name\": \"Germain\",\n \"password\": \"secret\",\n \"role\": \"vendor\",\n \"kyc_status\": \"pending\",\n \"pin\": \"123456\"\n}"
response = http.request(request)
puts response.read_body{
"message": "User registered successfully with default wallets",
"user": {
"id": "383f20af-5960-4374-a5c3-b7566ea8f771",
"email": "tigger.germain@gmail.com",
"phone_number": 254723453286,
"first_name": "Tigger",
"last_name": "Germain",
"role": "vendor",
"kyc_status": "pending"
},
"wallets": [
{
"wallet": {
"id": "ea963c72-35d4-4a4e-8e32-70971c27f188",
"user_id": "383f20af-5960-4374-a5c3-b7566ea8f771",
"name": "Ethereum Wallet",
"address": 1.0526201056375617e+48,
"wallet_type": "hot",
"network": "ethereum",
"is_default": true,
"is_active": true
},
"balances": [
{
"id": "dd9bbc6b-a3b5-4b5e-97db-e00aa8389ee1",
"user_id": "383f20af-5960-4374-a5c3-b7566ea8f771",
"wallet_id": "ea963c72-35d4-4a4e-8e32-70971c27f188",
"currency": "ETH",
"token_type": "ETH",
"token_symbol": "ETH",
"amount": "0.00000000",
"usd_value": "0.00",
"locked_amount": "0.00000000",
"last_price_usd": "0.00"
},
{
"id": "309e195d-6b6b-4a41-b793-5452f5c1ed02",
"user_id": "383f20af-5960-4374-a5c3-b7566ea8f771",
"wallet_id": "ea963c72-35d4-4a4e-8e32-70971c27f188",
"currency": "USDT",
"token_type": "USDT",
"token_symbol": "USDT",
"amount": "0.00000000",
"usd_value": "0.00",
"locked_amount": "0.00000000",
"last_price_usd": "0.00"
}
]
},
{
"wallet": {
"id": "05e36fd9-88e9-4dfb-8d8b-07493cef8e84",
"user_id": "383f20af-5960-4374-a5c3-b7566ea8f771",
"name": "TRON Wallet",
"address": "TFviB3ioZTnbPJVqqWjqZJbc6zu7hkeyEc",
"wallet_type": "hot",
"network": "tron",
"is_active": true
},
"balances": [
{
"id": "57bcec93-ea33-4fe3-bbd1-d9e10bad45ca",
"user_id": "383f20af-5960-4374-a5c3-b7566ea8f771",
"wallet_id": "05e36fd9-88e9-4dfb-8d8b-07493cef8e84",
"currency": "TRX",
"token_type": "TRX",
"token_symbol": "TRX",
"amount": "0.00000000",
"usd_value": "0.00",
"locked_amount": "0.00000000",
"last_price_usd": "0.00"
},
{
"id": "75ca706e-9461-489b-ad2d-03bb82043d01",
"user_id": "383f20af-5960-4374-a5c3-b7566ea8f771",
"wallet_id": "05e36fd9-88e9-4dfb-8d8b-07493cef8e84",
"currency": "USDT",
"token_type": "USDT",
"token_symbol": "USDT",
"amount": "0.00000000",
"usd_value": "0.00",
"locked_amount": "0.00000000",
"last_price_usd": "0.00"
}
]
}
]
}Body
User's login email. Must be unique.
E.164 format phone (e.g., +2547xxxxxxx).
Given name.
Family name.
User password (min 6 chars).
Role to assign.
customer, vendor, user Initial KYC state. Usually pending.
pending, approved, rejected Optional 6-digit numeric PIN used for PIN-based login (POST /api/v1/users/login-pin). If omitted, the user can still log in with email + password but cannot use PIN login until a PIN is set.
^[0-9]{6}$