Customers API
The Customers API allows business clients to register and manage customer accounts. Customers registered through this API are automatically linked to your business account.
Customer Registration Options
We provide two endpoints for customer registration, depending on your KYC workflow:
Option 1: Register Customer with KYC Link
Endpoint: POST /api/v1/client/customers
Use this endpoint when you want to:
- Create a customer account with basic information
- Receive a KYC onboarding link to share with your customer
- Let your customer complete KYC verification themselves through the provided link
Best for: Self-service flows where customers handle their own KYC submission.
Option 2: Full Registration with KYC Data
Endpoint: POST /api/v1/client/customers/full
Use this endpoint when you want to:
- Create a customer account and submit all KYC data in a single request
- Provide complete customer information, address, identification numbers, and document images
- Submit everything programmatically without requiring customer interaction
Best for: Programmatic onboarding where you've already collected all KYC information from your customer.
Comparison
| Feature | Register with Link | Full Registration |
|---|---|---|
| Endpoint | POST /api/v1/client/customers | POST /api/v1/client/customers/full |
| Required Data | Basic user info (name, email, phone, DOB) | All user info + address + documents |
| KYC Documents | Not required (customer submits via link) | Required (base64 encoded images) |
| KYC Workflow | Customer completes KYC via provided link | All KYC data submitted in one request |
| Response | Returns onboarding link | No link returned (KYC submitted directly) |
| Use Case | Self-service customer onboarding | Programmatic/automated onboarding |
| Transaction | User creation only | User creation + KYC submission (atomic) |
Register Customer (With KYC Link)
Register a new customer account for your business. The customer will be automatically associated with your business and can be used for quotes, transactions, and other operations. This endpoint returns a KYC onboarding link that you can share with your customer to complete verification.
Endpoint
POST /api/v1/client/customers
Authentication
This endpoint requires HMAC authentication (Business API Key). Include your API key and signature in the request headers:
x-api-key: <your-api-key>
x-timestamp: <RFC3339-timestamp>
x-signature: <HMAC-signature>For detailed information on HMAC authentication, see the Authentication Guide.
Request Body
| Field | Type | Required | Description | Example |
|---|---|---|---|---|
email | string | Yes | Customer's email address (must be valid email format) | "customer@example.com" |
first_name | string | Yes | Customer's first name | "John" |
last_name | string | Yes | Customer's last name | "Doe" |
middle_name | string | No | Customer's middle name | "A" |
password | string | Yes | Customer's password (minimum 8 characters) | "SecurePass123!" |
date_of_birth | string | Yes | Customer's date of birth (YYYY-MM-DD format) | "1990-01-15" |
phone_number | string | Yes | Customer's phone number (E.164 format) | "+1234567890" |
country_code | string | Yes | ISO 3166-1 alpha-2 country code (2 characters) | "US", "NG", "CA" |
user_tag | string | No | Optional user tag for transfers (4-20 alphanumeric lowercase) | "customer123" |
Request Example
{
"email": "customer@example.com",
"first_name": "John",
"last_name": "Doe",
"middle_name": "A",
"password": "SecurePass123!",
"date_of_birth": "1990-01-15",
"phone_number": "+1234567890",
"country_code": "US",
"user_tag": "customer123"
}Success Response (201 Created)
{
"message": "Customer created successfully",
"user": {
"id": "123e4567-e89b-12d3-a456-426614174000",
"email": "customer@example.com",
"first_name": "John",
"last_name": "Doe",
"role": "CUSTOMER",
"created_at": "2025-01-30T12:20:15Z",
"updated_at": "2025-01-30T12:20:15Z"
},
"user_id": "123e4567-e89b-12d3-a456-426614174000",
"onboarding_data": {
"link": "https://onboarding.example.com/session/abc123",
"expires_at": "2025-02-02T12:20:15Z"
}
}Note: The onboarding_data field is only present if the customer's email is not verified. It contains the KYC onboarding URL that you can share with your customer to complete their verification.
Response Fields
| Field | Type | Description |
|---|---|---|
message | string | Success message |
user | object | Created customer information |
user.id | string (UUID) | Unique customer identifier |
user.email | string | Customer's email address |
user.first_name | string | Customer's first name |
user.last_name | string | Customer's last name |
user.role | string | User role (always "CUSTOMER") |
user.created_at | string (ISO 8601) | Account creation timestamp |
user.updated_at | string (ISO 8601) | Last update timestamp |
user_id | string (UUID) | Customer user ID (same as user.id) |
onboarding_data | object (optional) | KYC onboarding session data (only present if email is not verified) |
onboarding_data.link | string | URL to send to your customer for KYC completion |
onboarding_data.expires_at | string (ISO 8601) | When the onboarding link expires (typically 3 days) |
Error Responses
| Status Code | Description |
|---|---|
400 | Invalid request format or missing required fields |
401 | Unauthorized - Invalid API key or signature |
403 | Phone number or email already registered |
422 | Password validation failed (must be at least 8 characters) |
500 | Internal server error |
Error Response Example
{
"error": "phone number already registered"
}Example: cURL
curl -X POST https://api.sznd.app/api/v1/client/customers \
-H "Content-Type: application/json" \
-H "x-api-key: your_api_key_here" \
-H "x-timestamp: 2025-01-30T12:20:15Z" \
-H "x-signature: generated_signature_here" \
-d '{
"email": "customer@example.com",
"first_name": "John",
"last_name": "Doe",
"password": "SecurePass123!",
"date_of_birth": "1990-01-15",
"phone_number": "+1234567890",
"country_code": "US"
}'Example: Postman Pre-request Script
Add this script to Postman's Pre-request Script tab to automatically generate HMAC signatures:
// Postman Pre-request Script for HMAC Authentication
// Set these variables in your Postman environment:
// - api_key: Your API key (e.g., "tf_live_abc123...")
// - secret_key: Your secret key (e.g., "sk_live_def456...")
// Get API key and secret key from environment variables
const apiKey = pm.environment.get("api_key");
const secretKey = pm.environment.get("secret_key");
if (!apiKey || !secretKey) {
console.error("Please set 'api_key' and 'secret_key' in your Postman environment variables");
throw new Error("Missing API credentials");
}
// Get request body (empty string if no body)
let requestBody = "";
if (pm.request.body && pm.request.body.raw) {
requestBody = pm.request.body.raw;
}
// Generate RFC3339 timestamp (UTC)
const now = new Date();
const timestamp = now.toISOString();
// Create data to sign: body + "|" + timestamp
const dataToSign = requestBody + "|" + timestamp;
// Generate HMAC-SHA256 signature
const signature = CryptoJS.HmacSHA256(dataToSign, secretKey).toString(CryptoJS.enc.Hex);
// Set headers automatically
pm.request.headers.add({
key: "x-api-key",
value: apiKey
});
pm.request.headers.add({
key: "x-timestamp",
value: timestamp
});
pm.request.headers.add({
key: "x-signature",
value: signature
});
// Log for debugging (optional - remove in production)
console.log("API Key:", apiKey);
console.log("Timestamp:", timestamp);
console.log("Data to sign:", dataToSign);
console.log("Signature:", signature);Setup Instructions:
-
Set Environment Variables in Postman:
- Go to your Postman environment
- Add
api_keywith your API key value - Add
secret_keywith your secret key value
-
Add Pre-request Script:
- Open your request in Postman
- Go to the Pre-request Script tab
- Paste the script above
- The script will automatically generate and add the required headers
-
Make Requests:
- The script runs automatically before each request
- No need to manually calculate signatures
- Headers are set automatically
Note: Postman uses CryptoJS for HMAC. If you don't have it available, you can use the Node.js crypto module version below.
Example: JavaScript/TypeScript (Node.js)
const crypto = require('crypto');
const apiKey = "your_api_key_here";
const secretKey = "your_secret_key_here";
const body = JSON.stringify({
email: "customer@example.com",
first_name: "John",
last_name: "Doe",
password: "SecurePass123!",
date_of_birth: "1990-01-15",
phone_number: "+1234567890",
country_code: "US"
});
// Generate timestamp
const timestamp = new Date().toISOString();
// Generate HMAC signature
const dataToSign = body + "|" + timestamp;
const signature = crypto
.createHmac('sha256', secretKey)
.update(dataToSign)
.digest('hex');
// Make request
const response = await fetch('https://api.sznd.app/api/v1/client/customers', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': apiKey,
'x-timestamp': timestamp,
'x-signature': signature
},
body: body
});
const data = await response.json();
console.log(data);Example: Python
import requests
import hmac
import hashlib
import json
from datetime import datetime
api_key = "your_api_key_here"
secret_key = "your_secret_key_here"
timestamp = datetime.utcnow().isoformat() + "Z"
body = {
"email": "customer@example.com",
"first_name": "John",
"last_name": "Doe",
"password": "SecurePass123!",
"date_of_birth": "1990-01-15",
"phone_number": "+1234567890",
"country_code": "US"
}
body_string = json.dumps(body, separators=(',', ':'))
timestamp = datetime.utcnow().isoformat() + "Z"
data_to_sign = body_string + "|" + timestamp
signature = hmac.new(
secret_key.encode(),
data_to_sign.encode(),
hashlib.sha256
).hexdigest()
headers = {
"Content-Type": "application/json",
"x-api-key": api_key,
"x-timestamp": timestamp,
"x-signature": signature
}
response = requests.post(
"https://api.sznd.app/api/v1/client/customers",
headers=headers,
json=body
)
print(response.json())Important Notes
-
Business Association: Customers registered through this endpoint are automatically linked to your business account in the
business_customerstable. -
Customer Type: All customers registered via this API are set as
INDIVIDUALtype by default. -
KYC Status: New customers start with
PENDINGKYC status. A KYC onboarding URL is automatically generated and returned in theonboarding_datafield if the email is not verified. You can share this URL with your customer to complete their verification. -
Unique Constraints:
- Email addresses must be unique across the system
- Phone numbers must be unique across the system
- If a customer with the same email or phone already exists, registration will fail
-
Password Requirements:
- Minimum 8 characters
- Should include a mix of letters, numbers, and special characters for security
-
User Tag: If provided, the user tag must be:
- 4-20 characters long
- Alphanumeric only
- Lowercase only
- Unique across the system
-
Audit Trail: All customer registrations are logged in the business customer audit logs with action "ADDED".
Next Steps
After registering a customer, you can:
- Create Quotes - Create quotes for the customer
- Create Beneficiaries - Add payment recipients for the customer
- View Transactions - Track customer transactions
- Manage Virtual Wallets - Handle customer virtual wallet operations
Virtual Wallet Onboarding
Onboard a customer to a virtual wallet for a specific currency. This endpoint initiates the onboarding process for supported currencies.
Prerequisites:
- Customer must have completed KYC verification
- Customer must be registered and linked to your business account
Endpoint
POST /api/v1/client/virtual-wallet/onboarding
Authentication
This endpoint requires HMAC authentication (Business API Key). Include your API key and signature in the request headers:
x-api-key: <your-api-key>
x-timestamp: <RFC3339-timestamp>
x-signature: <HMAC-signature>For detailed information on HMAC authentication, see the Authentication Guide.
Request Body
| Field | Type | Required | Description | Example |
|---|---|---|---|---|
user_id | string (UUID) | Yes | Customer's user ID (must be a valid UUID) | "123e4567-e89b-12d3-a456-426614174000" |
currency | string | Yes | Currency code for virtual wallet onboarding. Must be one of: CAD, USD, EUR, GBP | "CAD" |
Request Example
{
"user_id": "123e4567-e89b-12d3-a456-426614174000",
"currency": "CAD"
}Success Response (200 OK)
{
"message": "Customer onboarding completed successfully",
"currency": "CAD",
"user_id": "123e4567-e89b-12d3-a456-426614174000"
}Response Fields
| Field | Type | Description |
|---|---|---|
message | string | Success message indicating onboarding completion |
currency | string | Currency code that was onboarded |
user_id | string (UUID) | Customer user ID that was onboarded |
Error Responses
| Status Code | Description |
|---|---|
400 | Invalid request format, missing required fields, invalid UUID, or invalid currency |
401 | Unauthorized - Invalid API key or signature |
403 | User has not completed KYC verification |
404 | No KYC record found for the provided user ID |
500 | Internal server error during onboarding process |
Error Response Examples
User has not completed KYC (403):
{
"error": "user is yet to complete kyc"
}No KYC record found (404):
{
"error": "no kyc record for id provided"
}Invalid currency (400):
{
"error": "invalid currency"
}Example: cURL
curl -X POST https://api.sznd.app/api/v1/client/virtual-wallet/onboarding \
-H "Content-Type: application/json" \
-H "x-api-key: your_api_key_here" \
-H "x-timestamp: 2025-01-30T12:20:15Z" \
-H "x-signature: generated_signature_here" \
-d '{
"user_id": "123e4567-e89b-12d3-a456-426614174000",
"currency": "CAD"
}'Important Notes
-
KYC Requirement: The customer must have completed KYC verification before virtual wallet onboarding can proceed. The system checks the KYC status and will return a
403error if KYC is not completed. -
Supported Currencies: Currently supports the following currencies:
CAD(Canadian Dollar)USD(US Dollar)EUR(Euro)GBP(British Pound)
-
Idempotency: If a customer is already onboarded for a specific currency, the endpoint may return success if the wallet already exists.
-
Processing Time: The onboarding process typically completes within seconds.
-
Error Handling: If the onboarding fails, a
500error will be returned. Check the error message for details.
Full Customer Registration with KYC Data
Register a new customer with complete KYC information including documents in a single transaction. This endpoint creates the user account and submits all KYC data and documents for verification. All operations are performed in a transaction and will be rolled back if any step fails.
Use this endpoint when: You have already collected all KYC information (including documents) from your customer and want to submit everything programmatically in one request.
Endpoint
POST /api/v1/client/customers/full
Authentication
This endpoint requires HMAC authentication (Business API Key). Include your API key and signature in the request headers:
x-api-key: <your-api-key>
x-timestamp: <RFC3339-timestamp>
x-signature: <HMAC-signature>For detailed information on HMAC authentication, see the Authentication Guide.
Request Body
Basic User Information
| Field | Type | Required | Description | Example |
|---|---|---|---|---|
email | string | Yes | Customer's email address (must be valid email format) | "customer@example.com" |
first_name | string | Yes | Customer's first name | "John" |
last_name | string | Yes | Customer's last name | "Doe" |
middle_name | string | No | Customer's middle name | "A" |
password | string | Yes | Customer's password (minimum 8 characters) | "SecurePass123!" |
date_of_birth | string | Yes | Customer's date of birth (YYYY-MM-DD format) | "1990-01-15" |
phone_number | string | Yes | Customer's phone number (E.164 format) | "+1234567890" |
country_code | string | Yes | ISO 3166-1 alpha-2 country code (2 characters) | "US", "NG", "CA" |
user_tag | string | No | Optional user tag for transfers (4-20 alphanumeric lowercase) | "customer123" |
Address Information
| Field | Type | Required | Description | Example |
|---|---|---|---|---|
address.street_1 | string | Yes | Primary street address | "123 Main St" |
address.street_2 | string | No | Apartment, suite, or unit number | "Apt 4B" |
address.state | string | No | State or province | "California" |
address.city | string | Yes | City name | "San Francisco" |
address.postal_code | string | Yes | ZIP code or postal code | "94111" |
address.country_code | string | Yes | ISO 3166-1 alpha-2 country code | "US" |
Identification Numbers (Optional)
Array of identification document numbers. Compatible with multiple document types.
| Field | Type | Required | Description | Example |
|---|---|---|---|---|
identification_numbers[].identification_class | string | Yes | Document type: "visa", "cct", "passport", "id_card", "drivers_license" | "passport" |
identification_numbers[].identification_number | string | Yes | Document number | "A12345678" |
identification_numbers[].issuing_country | string | Yes | ISO 3166-1 alpha-2 country code | "US" |
identification_numbers[].issued_at | string | No | Issue date (YYYY-MM-DD format) | "2020-01-15" |
identification_numbers[].expires_at | string | No | Expiry date (YYYY-MM-DD format) | "2030-01-15" |
Document Images (Optional)
Base64-encoded document images. You can provide images for ID card, passport, driver's license, and selfie photo.
ID Card:
documents.id_card_front- Base64 encoded front imagedocuments.id_card_back- Base64 encoded back imagedocuments.id_card_number- Document numberdocuments.id_card_issuing_country- ISO country codedocuments.id_card_issued_at- Issue date (YYYY-MM-DD)documents.id_card_expires_at- Expiry date (YYYY-MM-DD)
Passport:
documents.passport_front- Base64 encoded front imagedocuments.passport_back- Base64 encoded back imagedocuments.passport_number- Document numberdocuments.passport_issuing_country- ISO country codedocuments.passport_issued_at- Issue date (YYYY-MM-DD)documents.passport_expires_at- Expiry date (YYYY-MM-DD)
Driver's License:
documents.drivers_license_front- Base64 encoded front imagedocuments.drivers_license_back- Base64 encoded back imagedocuments.drivers_license_number- Document numberdocuments.drivers_license_issuing_country- ISO country codedocuments.drivers_license_issued_at- Issue date (YYYY-MM-DD)documents.drivers_license_expires_at- Expiry date (YYYY-MM-DD)
Selfie Photo:
documents.selfie_photo- Base64 encoded selfie photo
Additional Fields (Optional)
| Field | Type | Required | Description | Example |
|---|---|---|---|---|
nationality | string | No | ISO 3166-1 alpha-2 country code | "US" |
us_tax_id | string | No | US Social Security Number (SSN9) | "123456789" |
Request Example
{
"email": "customer@example.com",
"first_name": "John",
"last_name": "Doe",
"middle_name": "A",
"password": "SecurePass123!",
"date_of_birth": "1990-01-15",
"phone_number": "+1234567890",
"country_code": "US",
"user_tag": "customer123",
"address": {
"street_1": "123 Main St",
"street_2": "Apt 4B",
"state": "California",
"city": "San Francisco",
"postal_code": "94111",
"country_code": "US"
},
"identification_numbers": [
{
"identification_class": "passport",
"identification_number": "A12345678",
"issuing_country": "US",
"issued_at": "2020-01-15",
"expires_at": "2030-01-15"
}
],
"documents": {
"passport_front": "data:image/jpeg;base64,/9j/4AAQSkZJRg...",
"passport_back": "data:image/jpeg;base64,/9j/4AAQSkZJRg...",
"passport_number": "A12345678",
"passport_issuing_country": "US",
"passport_issued_at": "2020-01-15",
"passport_expires_at": "2030-01-15",
"selfie_photo": "data:image/jpeg;base64,/9j/4AAQSkZJRg..."
},
"nationality": "US",
"us_tax_id": "123456789"
}Success Response (201 Created)
{
"message": "Customer created and KYC data submitted successfully",
"user": {
"id": "123e4567-e89b-12d3-a456-426614174000",
"email": "customer@example.com",
"first_name": "John",
"last_name": "Doe",
"role": "CUSTOMER",
"created_at": "2025-01-30T12:20:15Z",
"updated_at": "2025-01-30T12:20:15Z"
},
"user_id": "123e4567-e89b-12d3-a456-426614174000"
}Response Fields
| Field | Type | Description |
|---|---|---|
message | string | Success message |
user | object | Created customer information |
user.id | string (UUID) | Unique customer identifier |
user.email | string | Customer's email address |
user.first_name | string | Customer's first name |
user.last_name | string | Customer's last name |
user.role | string | User role (always "CUSTOMER") |
user.created_at | string (ISO 8601) | Account creation timestamp |
user.updated_at | string (ISO 8601) | Last update timestamp |
user_id | string (UUID) | Customer user ID (same as user.id) |
Error Responses
| Status Code | Description |
|---|---|
400 | Invalid request format, missing required fields, or invalid data |
401 | Unauthorized - Invalid API key or signature |
403 | Phone number or email already registered |
500 | Internal server error (transaction will be rolled back) |
Error Response Example
{
"error": "failed to submit KYC data"
}Example: cURL
Minimal Example (Required Fields Only):
curl -X POST https://api.sznd.app/api/v1/client/customers/full \
-H "Content-Type: application/json" \
-H "x-api-key: your_api_key_here" \
-H "x-timestamp: 2025-01-30T12:20:15Z" \
-H "x-signature: generated_signature_here" \
-d '{
"email": "customer@example.com",
"first_name": "John",
"last_name": "Doe",
"password": "SecurePass123!",
"date_of_birth": "1990-01-15",
"phone_number": "+1234567890",
"country_code": "US",
"address": {
"street_1": "123 Main St",
"city": "San Francisco",
"postal_code": "94111",
"country_code": "US"
}
}'Complete Example (With All KYC Data and Documents):
curl -X POST https://api.sznd.app/api/v1/client/customers/full \
-H "Content-Type: application/json" \
-H "x-api-key: your_api_key_here" \
-H "x-timestamp: 2025-01-30T12:20:15Z" \
-H "x-signature: generated_signature_here" \
-d '{
"email": "customer@example.com",
"first_name": "John",
"last_name": "Doe",
"middle_name": "A",
"password": "SecurePass123!",
"date_of_birth": "1990-01-15",
"phone_number": "+1234567890",
"country_code": "US",
"user_tag": "customer123",
"address": {
"street_1": "123 Main St",
"street_2": "Apt 4B",
"state": "California",
"city": "San Francisco",
"postal_code": "94111",
"country_code": "US"
},
"identification_numbers": [
{
"identification_class": "passport",
"identification_number": "A12345678",
"issuing_country": "US",
"issued_at": "2020-01-15",
"expires_at": "2030-01-15"
}
],
"documents": {
"passport_front": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAABAAEDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAv/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAX/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwCdABmX/9k=",
"passport_back": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAABAAEDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAv/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAX/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwCdABmX/9k=",
"passport_number": "A12345678",
"passport_issuing_country": "US",
"passport_issued_at": "2020-01-15",
"passport_expires_at": "2030-01-15",
"selfie_photo": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAABAAEDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAv/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAX/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwCdABmX/9k="
},
"nationality": "US",
"us_tax_id": "123456789"
}'Note: In the complete example above, the base64 image strings are truncated for readability. In a real request, you would include the full base64-encoded image data. The images should be clear, legible, and meet the minimum size requirements (20KB per image).
Important Notes
-
Transaction Safety: All operations (user creation, KYC submission, reference creation) are performed in a single database transaction. If any step fails, the entire operation is rolled back.
-
Document Format: Document images must be base64-encoded. Supported formats include JPEG, PNG, and PDF. Images should be clear and legible.
-
Document Requirements:
- Documents must be current and not expired (unless expiry date is not applicable)
- Both front and back images are required where applicable
- Minimum image size: 20KB per image
- Images must be clear and readable
-
KYC Processing: The KYC data and documents are submitted for verification immediately upon registration. The verification status will be updated via webhooks.
-
Address Information: Complete address information is required and will be used for verification purposes.
-
Identification Numbers: You can provide multiple identification numbers (e.g., passport, visa, etc.) in the
identification_numbersarray. -
Error Handling: If the KYC submission fails, the entire transaction (including user creation) will be rolled back to maintain data consistency.
Update Customer
Update an existing customer's information. All fields are optional - only provided fields will be updated. If documents are provided, they will be automatically uploaded to the KYC provider if the customer has an existing KYC inquiry.
Use this endpoint when: You need to update customer information, upload additional documents, or modify address details after initial registration.
Endpoint
PATCH /api/v1/client/customers/:customer_id
Authentication
This endpoint requires HMAC authentication (Business API Key). Include your API key and signature in the request headers:
x-api-key: <your-api-key>
x-timestamp: <RFC3339-timestamp>
x-signature: <HMAC-signature>For detailed information on HMAC authentication, see the Authentication Guide.
Path Parameters
| Parameter | Type | Required | Description | Example |
|---|---|---|---|---|
customer_id | string (UUID) | Yes | The UUID of the customer to update | "123e4567-e89b-12d3-a456-426614174000" |
Request Body
All fields are optional. Only fields you want to update need to be included.
Basic User Information
| Field | Type | Required | Description | Example |
|---|---|---|---|---|
first_name | string | No | Customer's first name | "John" |
last_name | string | No | Customer's last name | "Doe" |
middle_name | string | No | Customer's middle name | "A" |
phone_number | string | No | Customer's phone number (E.164 format) | "+1234567890" |
date_of_birth | string | No | Customer's date of birth (YYYY-MM-DD format) | "1990-01-15" |
user_tag | string | No | Optional user tag for transfers (4-20 alphanumeric lowercase) | "customer123" |
Note: Email cannot be updated via this endpoint.
Address Information (Optional)
| Field | Type | Required | Description | Example |
|---|---|---|---|---|
address.street_1 | string | No | Primary street address | "123 Main St" |
address.street_2 | string | No | Apartment, suite, or unit number | "Apt 4B" |
address.state | string | No | State or province | "California" |
address.city | string | No | City name | "San Francisco" |
address.postal_code | string | No | ZIP code or postal code | "94111" |
address.country_code | string | No | ISO 3166-1 alpha-2 country code | "US" |
Note: If address doesn't exist and you're creating it, street_1, city, postal_code, and country_code are required. For updates, all fields are optional and only provided fields will be updated.
Document Images (Optional)
Base64-encoded document images. If provided and the customer has an existing KYC inquiry, documents will be automatically uploaded to the KYC provider. Document uploads are best-effort - errors are logged but don't fail the update.
ID Card:
documents.id_card_front- Base64 encoded front imagedocuments.id_card_back- Base64 encoded back image
Passport:
documents.passport_front- Base64 encoded front imagedocuments.passport_back- Base64 encoded back image
Driver's License:
documents.drivers_license_front- Base64 encoded front imagedocuments.drivers_license_back- Base64 encoded back image
Selfie Photo:
documents.selfie_photo- Base64 encoded selfie photo
Additional Fields (Optional)
| Field | Type | Required | Description | Example |
|---|---|---|---|---|
nationality | string | No | ISO 3166-1 alpha-2 country code | "US" |
us_tax_id | string | No | US Social Security Number (SSN9) | "123456789" |
Request Examples
Update Basic Information:
{
"first_name": "Jane",
"last_name": "Smith",
"phone_number": "+1987654321"
}Update Address:
{
"address": {
"street_1": "456 Oak Ave",
"street_2": "Suite 200",
"city": "Los Angeles",
"postal_code": "90001",
"country_code": "US",
"state": "California"
}
}Upload Documents:
{
"documents": {
"id_card_front": "data:image/jpeg;base64,/9j/4AAQSkZJRg...",
"id_card_back": "data:image/jpeg;base64,/9j/4AAQSkZJRg...",
"selfie_photo": "data:image/jpeg;base64,/9j/4AAQSkZJRg..."
}
}Complete Update (Multiple Fields):
{
"first_name": "Jane",
"last_name": "Smith",
"date_of_birth": "1985-05-20",
"address": {
"street_1": "456 Oak Ave",
"city": "Los Angeles",
"postal_code": "90001",
"country_code": "US"
},
"documents": {
"passport_front": "data:image/jpeg;base64,/9j/4AAQSkZJRg...",
"selfie_photo": "data:image/jpeg;base64,/9j/4AAQSkZJRg..."
}
}Success Response (200 OK)
{
"message": "Customer updated successfully",
"user": {
"id": "123e4567-e89b-12d3-a456-426614174000",
"email": "customer@example.com",
"first_name": "Jane",
"last_name": "Smith",
"role": "CUSTOMER",
"created_at": "2025-01-30T12:20:15Z",
"updated_at": "2025-01-30T14:30:45Z"
},
"user_id": "123e4567-e89b-12d3-a456-426614174000"
}Response Fields
| Field | Type | Description |
|---|---|---|
message | string | Success message |
user | object | Updated customer information |
user.id | string (UUID) | Unique customer identifier |
user.email | string | Customer's email address (cannot be changed) |
user.first_name | string | Customer's first name |
user.last_name | string | Customer's last name |
user.role | string | User role (always "CUSTOMER") |
user.created_at | string (ISO 8601) | Account creation timestamp |
user.updated_at | string (ISO 8601) | Last update timestamp |
user_id | string (UUID) | Customer user ID (same as user.id) |
Error Responses
| Status Code | Description |
|---|---|
400 | Invalid request format, invalid customer ID, or invalid date format |
401 | Unauthorized - Invalid API key or signature |
403 | Customer does not belong to your business |
404 | Customer not found |
409 | Phone number or user tag already in use |
500 | Internal server error |
Error Response Examples
Customer Not Found (404):
{
"error": "customer not found"
}Customer Doesn't Belong to Business (403):
{
"error": "customer does not belong to your business"Phone Number Already in Use (409):
{
"error": "phone number already registered"
}Invalid Date Format (400):
{
"error": "invalid date format, expected YYYY-MM-DD"
}Example: cURL
Update Basic Information:
curl -X PATCH https://api.sznd.app/api/v1/client/customers/123e4567-e89b-12d3-a456-426614174000 \
-H "Content-Type: application/json" \
-H "x-api-key: your_api_key_here" \
-H "x-timestamp: 2025-01-30T14:30:45Z" \
-H "x-signature: generated_signature_here" \
-d '{
"first_name": "Jane",
"last_name": "Smith",
"phone_number": "+1987654321"
}'Update Address:
curl -X PATCH https://api.sznd.app/api/v1/client/customers/123e4567-e89b-12d3-a456-426614174000 \
-H "Content-Type: application/json" \
-H "x-api-key: your_api_key_here" \
-H "x-timestamp: 2025-01-30T14:30:45Z" \
-H "x-signature: generated_signature_here" \
-d '{
"address": {
"street_1": "456 Oak Ave",
"city": "Los Angeles",
"postal_code": "90001",
"country_code": "US"
}
}'Upload Documents:
curl -X PATCH https://api.sznd.app/api/v1/client/customers/123e4567-e89b-12d3-a456-426614174000 \
-H "Content-Type: application/json" \
-H "x-api-key: your_api_key_here" \
-H "x-timestamp: 2025-01-30T14:30:45Z" \
-H "x-signature: generated_signature_here" \
-d '{
"documents": {
"id_card_front": "data:image/jpeg;base64,/9j/4AAQSkZJRg...",
"selfie_photo": "data:image/jpeg;base64,/9j/4AAQSkZJRg..."
}
}'Example: JavaScript/TypeScript (Node.js)
const crypto = require('crypto');
const apiKey = "your_api_key_here";
const secretKey = "your_secret_key_here";
const customerId = "123e4567-e89b-12d3-a456-426614174000";
const body = JSON.stringify({
first_name: "Jane",
last_name: "Smith",
phone_number: "+1987654321",
address: {
street_1: "456 Oak Ave",
city: "Los Angeles",
postal_code: "90001",
country_code: "US"
}
});
// Generate timestamp
const timestamp = new Date().toISOString();
// Generate HMAC signature
const dataToSign = body + "|" + timestamp;
const signature = crypto
.createHmac('sha256', secretKey)
.update(dataToSign)
.digest('hex');
// Make request
const response = await fetch(`https://api.sznd.app/api/v1/client/customers/${customerId}`, {
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
'x-api-key': apiKey,
'x-timestamp': timestamp,
'x-signature': signature
},
body: body
});
const data = await response.json();
console.log(data);Example: Python
import requests
import hmac
import hashlib
import json
from datetime import datetime
api_key = "your_api_key_here"
secret_key = "your_secret_key_here"
customer_id = "123e4567-e89b-12d3-a456-426614174000"
body = {
"first_name": "Jane",
"last_name": "Smith",
"phone_number": "+1987654321",
"address": {
"street_1": "456 Oak Ave",
"city": "Los Angeles",
"postal_code": "90001",
"country_code": "US"
}
}
body_string = json.dumps(body, separators=(',', ':'))
timestamp = datetime.utcnow().isoformat() + "Z"
data_to_sign = body_string + "|" + timestamp
signature = hmac.new(
secret_key.encode(),
data_to_sign.encode(),
hashlib.sha256
).hexdigest()
headers = {
"Content-Type": "application/json",
"x-api-key": api_key,
"x-timestamp": timestamp,
"x-signature": signature
}
response = requests.patch(
f"https://api.sznd.app/api/v1/client/customers/{customer_id}",
headers=headers,
json=body
)
print(response.json())Important Notes
-
Partial Updates: All fields are optional. Only fields you provide will be updated. Fields not included in the request will remain unchanged.
-
Email Cannot Be Updated: Email addresses cannot be changed via this endpoint. If you need to change an email, you'll need to create a new customer account.
-
Document Uploads:
- Documents are automatically uploaded to the KYC provider if the customer has an existing KYC inquiry
- Document uploads are best-effort - if upload fails, the error is logged but the customer update still succeeds
- If no KYC inquiry exists, document uploads are skipped (no error returned)
-
Address Updates:
- If address doesn't exist, it will be created (requires
street_1,city,postal_code, andcountry_code) - If address exists, only provided fields will be updated
- Partial address updates are supported
- If address doesn't exist, it will be created (requires
-
Phone Number Validation:
- Phone numbers must be unique across the system
- If updating to a phone number that's already in use by another customer, the update will fail with a
409error
-
User Tag Validation:
- User tags must be 4-20 characters, alphanumeric, lowercase
- User tags must be unique across the system
- If updating to a tag that's already in use, the update will fail with a
409error
-
Date of Birth Format: Must be in
YYYY-MM-DDformat (e.g.,"1990-01-15"). -
Transaction Safety: All database updates happen within a transaction. If any update fails, the entire operation is rolled back.
-
Business Ownership: You can only update customers that belong to your business. Attempting to update a customer from another business will result in a
403error. -
Document Format: Document images must be base64-encoded. Supported formats include JPEG, PNG, and PDF. Images should be clear and legible.
Get Customer Onboarding Status
Get the KYC/onboarding status of a customer registered by your business. This endpoint returns the current KYC verification status from the KYC provider (Footprint or Persona).
Use this endpoint when: You need to check the KYC verification status of a customer to determine if they can proceed with transactions or if additional verification is required.
Endpoint
GET /api/v1/client/customers/:customer_id/onboarding-status
Authentication
This endpoint requires HMAC authentication (Business API Key). Include your API key and signature in the request headers:
x-api-key: <your-api-key>
x-timestamp: <RFC3339-timestamp>
x-signature: <HMAC-signature>For detailed information on HMAC authentication, see the Authentication Guide.
Path Parameters
| Parameter | Type | Required | Description | Example |
|---|---|---|---|---|
customer_id | string (UUID) | Yes | The unique identifier of the customer | "123e4567-e89b-12d3-a456-426614174000" |
Success Response (200 OK)
{
"kyc_status": "PASS",
"message": "KYC verification completed successfully"
}Response Fields
| Field | Type | Description |
|---|---|---|
kyc_status | string | The KYC verification status. Possible values: PASS, PENDING, KYC_NOT_INITIATED |
message | string (optional) | Human-readable message describing the KYC status |
KYC Status Values
| Status | Description |
|---|---|
PASS | KYC verification has been completed successfully. The customer has a verified status (PASS, ACCEPTED, VERIFIED, or APPROVED) from either Footprint or Persona. |
PENDING | KYC verification is in progress or pending. The customer has a KYC record but verification is not yet complete. |
KYC_NOT_INITIATED | No KYC verification has been started. The customer does not have any KYC records with Footprint or Persona. |
Error Responses
| Status Code | Description |
|---|---|
400 | Invalid customer ID format |
401 | Unauthorized - Invalid API key or signature |
403 | Forbidden - Customer does not belong to your business |
404 | Customer not found |
500 | Internal server error |
Error Response Example
{
"error": "customer does not belong to your business or customer not found"
}Request Example
cURL
curl -X GET \
"https://api.sznd.app/api/v1/client/customers/123e4567-e89b-12d3-a456-426614174000/onboarding-status" \
-H "Content-Type: application/json" \
-H "x-api-key: your_api_key_here" \
-H "x-timestamp: 2025-01-30T12:00:00Z" \
-H "x-signature: your_hmac_signature_here"JavaScript/TypeScript
const customerId = "123e4567-e89b-12d3-a456-426614174000";
const timestamp = new Date().toISOString();
const signature = generateHMACSignature("", timestamp); // Empty body for GET request
const response = await fetch(
`https://api.sznd.app/api/v1/client/customers/${customerId}/onboarding-status`,
{
method: "GET",
headers: {
"Content-Type": "application/json",
"x-api-key": apiKey,
"x-timestamp": timestamp,
"x-signature": signature,
},
}
);
const data = await response.json();
console.log(data);
// {
// "kyc_status": "PASS",
// "message": "KYC verification completed successfully"
// }Python
import requests
import hmac
import hashlib
from datetime import datetime
api_key = "your_api_key_here"
secret_key = "your_secret_key_here"
customer_id = "123e4567-e89b-12d3-a456-426614174000"
timestamp = datetime.utcnow().isoformat() + "Z"
# For GET requests, sign empty body + timestamp
data_to_sign = "" + "|" + timestamp
signature = hmac.new(
secret_key.encode(),
data_to_sign.encode(),
hashlib.sha256
).hexdigest()
headers = {
"Content-Type": "application/json",
"x-api-key": api_key,
"x-timestamp": timestamp,
"x-signature": signature
}
response = requests.get(
f"https://api.sznd.app/api/v1/client/customers/{customer_id}/onboarding-status",
headers=headers
)
print(response.json())
# {
# "kyc_status": "PASS",
# "message": "KYC verification completed successfully"
# }Important Notes
-
Business Ownership: You can only check the onboarding status of customers that belong to your business. Attempting to check a customer from another business will result in a
403error. -
KYC Provider: The endpoint checks both Footprint and Persona external references. If a customer has a successful status (PASS, ACCEPTED, VERIFIED, or APPROVED) from either provider, the status will be
PASS. -
Status Priority: The endpoint checks Footprint first, then Persona. If either provider has a successful status,
PASSis returned. Otherwise, if a record exists with a non-successful status,PENDINGis returned. -
No KYC Record: If the customer has no KYC records with either provider, the status will be
KYC_NOT_INITIATED. -
Status Updates: KYC status is updated via webhooks when the KYC provider processes verification. You can also poll this endpoint to check the current status.
-
Use Cases:
- Check if a customer can proceed with transactions (status =
PASS) - Determine if KYC verification is still pending
- Verify if a customer has started the KYC process
- Check if a customer can proceed with transactions (status =
Related Resources
- Authentication Guide - Learn how to authenticate API requests
- Quotes API - Create quotes for registered customers
- Transactions API - View customer transaction history