πŸ“š Resources
πŸ‘€ Customers

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

FeatureRegister with LinkFull Registration
EndpointPOST /api/v1/client/customersPOST /api/v1/client/customers/full
Required DataBasic user info (name, email, phone, DOB)All user info + address + documents
KYC DocumentsNot required (customer submits via link)Required (base64 encoded images)
KYC WorkflowCustomer completes KYC via provided linkAll KYC data submitted in one request
ResponseReturns onboarding linkNo link returned (KYC submitted directly)
Use CaseSelf-service customer onboardingProgrammatic/automated onboarding
TransactionUser creation onlyUser 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

FieldTypeRequiredDescriptionExample
emailstringYesCustomer's email address (must be valid email format)"customer@example.com"
first_namestringYesCustomer's first name"John"
last_namestringYesCustomer's last name"Doe"
middle_namestringNoCustomer's middle name"A"
passwordstringYesCustomer's password (minimum 8 characters)"SecurePass123!"
date_of_birthstringYesCustomer's date of birth (YYYY-MM-DD format)"1990-01-15"
phone_numberstringYesCustomer's phone number (E.164 format)"+1234567890"
country_codestringYesISO 3166-1 alpha-2 country code (2 characters)"US", "NG", "CA"
user_tagstringNoOptional 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

FieldTypeDescription
messagestringSuccess message
userobjectCreated customer information
user.idstring (UUID)Unique customer identifier
user.emailstringCustomer's email address
user.first_namestringCustomer's first name
user.last_namestringCustomer's last name
user.rolestringUser role (always "CUSTOMER")
user.created_atstring (ISO 8601)Account creation timestamp
user.updated_atstring (ISO 8601)Last update timestamp
user_idstring (UUID)Customer user ID (same as user.id)
onboarding_dataobject (optional)KYC onboarding session data (only present if email is not verified)
onboarding_data.linkstringURL to send to your customer for KYC completion
onboarding_data.expires_atstring (ISO 8601)When the onboarding link expires (typically 3 days)

Error Responses

Status CodeDescription
400Invalid request format or missing required fields
401Unauthorized - Invalid API key or signature
403Phone number or email already registered
422Password validation failed (must be at least 8 characters)
500Internal 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:

  1. Set Environment Variables in Postman:

    • Go to your Postman environment
    • Add api_key with your API key value
    • Add secret_key with your secret key value
  2. 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
  3. 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

  1. Business Association: Customers registered through this endpoint are automatically linked to your business account in the business_customers table.

  2. Customer Type: All customers registered via this API are set as INDIVIDUAL type by default.

  3. KYC Status: New customers start with PENDING KYC status. A KYC onboarding URL is automatically generated and returned in the onboarding_data field if the email is not verified. You can share this URL with your customer to complete their verification.

  4. 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
  5. Password Requirements:

    • Minimum 8 characters
    • Should include a mix of letters, numbers, and special characters for security
  6. User Tag: If provided, the user tag must be:

    • 4-20 characters long
    • Alphanumeric only
    • Lowercase only
    • Unique across the system
  7. Audit Trail: All customer registrations are logged in the business customer audit logs with action "ADDED".

Next Steps

After registering a customer, you can:


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

FieldTypeRequiredDescriptionExample
user_idstring (UUID)YesCustomer's user ID (must be a valid UUID)"123e4567-e89b-12d3-a456-426614174000"
currencystringYesCurrency 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

FieldTypeDescription
messagestringSuccess message indicating onboarding completion
currencystringCurrency code that was onboarded
user_idstring (UUID)Customer user ID that was onboarded

Error Responses

Status CodeDescription
400Invalid request format, missing required fields, invalid UUID, or invalid currency
401Unauthorized - Invalid API key or signature
403User has not completed KYC verification
404No KYC record found for the provided user ID
500Internal 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

  1. 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 403 error if KYC is not completed.

  2. Supported Currencies: Currently supports the following currencies:

    • CAD (Canadian Dollar)
    • USD (US Dollar)
    • EUR (Euro)
    • GBP (British Pound)
  3. Idempotency: If a customer is already onboarded for a specific currency, the endpoint may return success if the wallet already exists.

  4. Processing Time: The onboarding process typically completes within seconds.

  5. Error Handling: If the onboarding fails, a 500 error 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

FieldTypeRequiredDescriptionExample
emailstringYesCustomer's email address (must be valid email format)"customer@example.com"
first_namestringYesCustomer's first name"John"
last_namestringYesCustomer's last name"Doe"
middle_namestringNoCustomer's middle name"A"
passwordstringYesCustomer's password (minimum 8 characters)"SecurePass123!"
date_of_birthstringYesCustomer's date of birth (YYYY-MM-DD format)"1990-01-15"
phone_numberstringYesCustomer's phone number (E.164 format)"+1234567890"
country_codestringYesISO 3166-1 alpha-2 country code (2 characters)"US", "NG", "CA"
user_tagstringNoOptional user tag for transfers (4-20 alphanumeric lowercase)"customer123"

Address Information

FieldTypeRequiredDescriptionExample
address.street_1stringYesPrimary street address"123 Main St"
address.street_2stringNoApartment, suite, or unit number"Apt 4B"
address.statestringNoState or province"California"
address.citystringYesCity name"San Francisco"
address.postal_codestringYesZIP code or postal code"94111"
address.country_codestringYesISO 3166-1 alpha-2 country code"US"

Identification Numbers (Optional)

Array of identification document numbers. Compatible with multiple document types.

FieldTypeRequiredDescriptionExample
identification_numbers[].identification_classstringYesDocument type: "visa", "cct", "passport", "id_card", "drivers_license""passport"
identification_numbers[].identification_numberstringYesDocument number"A12345678"
identification_numbers[].issuing_countrystringYesISO 3166-1 alpha-2 country code"US"
identification_numbers[].issued_atstringNoIssue date (YYYY-MM-DD format)"2020-01-15"
identification_numbers[].expires_atstringNoExpiry 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 image
  • documents.id_card_back - Base64 encoded back image
  • documents.id_card_number - Document number
  • documents.id_card_issuing_country - ISO country code
  • documents.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 image
  • documents.passport_back - Base64 encoded back image
  • documents.passport_number - Document number
  • documents.passport_issuing_country - ISO country code
  • documents.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 image
  • documents.drivers_license_back - Base64 encoded back image
  • documents.drivers_license_number - Document number
  • documents.drivers_license_issuing_country - ISO country code
  • documents.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)

FieldTypeRequiredDescriptionExample
nationalitystringNoISO 3166-1 alpha-2 country code"US"
us_tax_idstringNoUS 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

FieldTypeDescription
messagestringSuccess message
userobjectCreated customer information
user.idstring (UUID)Unique customer identifier
user.emailstringCustomer's email address
user.first_namestringCustomer's first name
user.last_namestringCustomer's last name
user.rolestringUser role (always "CUSTOMER")
user.created_atstring (ISO 8601)Account creation timestamp
user.updated_atstring (ISO 8601)Last update timestamp
user_idstring (UUID)Customer user ID (same as user.id)

Error Responses

Status CodeDescription
400Invalid request format, missing required fields, or invalid data
401Unauthorized - Invalid API key or signature
403Phone number or email already registered
500Internal 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

  1. 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.

  2. Document Format: Document images must be base64-encoded. Supported formats include JPEG, PNG, and PDF. Images should be clear and legible.

  3. 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
  4. KYC Processing: The KYC data and documents are submitted for verification immediately upon registration. The verification status will be updated via webhooks.

  5. Address Information: Complete address information is required and will be used for verification purposes.

  6. Identification Numbers: You can provide multiple identification numbers (e.g., passport, visa, etc.) in the identification_numbers array.

  7. 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

ParameterTypeRequiredDescriptionExample
customer_idstring (UUID)YesThe 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

FieldTypeRequiredDescriptionExample
first_namestringNoCustomer's first name"John"
last_namestringNoCustomer's last name"Doe"
middle_namestringNoCustomer's middle name"A"
phone_numberstringNoCustomer's phone number (E.164 format)"+1234567890"
date_of_birthstringNoCustomer's date of birth (YYYY-MM-DD format)"1990-01-15"
user_tagstringNoOptional user tag for transfers (4-20 alphanumeric lowercase)"customer123"

Note: Email cannot be updated via this endpoint.

Address Information (Optional)

FieldTypeRequiredDescriptionExample
address.street_1stringNoPrimary street address"123 Main St"
address.street_2stringNoApartment, suite, or unit number"Apt 4B"
address.statestringNoState or province"California"
address.citystringNoCity name"San Francisco"
address.postal_codestringNoZIP code or postal code"94111"
address.country_codestringNoISO 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 image
  • documents.id_card_back - Base64 encoded back image

Passport:

  • documents.passport_front - Base64 encoded front image
  • documents.passport_back - Base64 encoded back image

Driver's License:

  • documents.drivers_license_front - Base64 encoded front image
  • documents.drivers_license_back - Base64 encoded back image

Selfie Photo:

  • documents.selfie_photo - Base64 encoded selfie photo

Additional Fields (Optional)

FieldTypeRequiredDescriptionExample
nationalitystringNoISO 3166-1 alpha-2 country code"US"
us_tax_idstringNoUS 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

FieldTypeDescription
messagestringSuccess message
userobjectUpdated customer information
user.idstring (UUID)Unique customer identifier
user.emailstringCustomer's email address (cannot be changed)
user.first_namestringCustomer's first name
user.last_namestringCustomer's last name
user.rolestringUser role (always "CUSTOMER")
user.created_atstring (ISO 8601)Account creation timestamp
user.updated_atstring (ISO 8601)Last update timestamp
user_idstring (UUID)Customer user ID (same as user.id)

Error Responses

Status CodeDescription
400Invalid request format, invalid customer ID, or invalid date format
401Unauthorized - Invalid API key or signature
403Customer does not belong to your business
404Customer not found
409Phone number or user tag already in use
500Internal 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

  1. Partial Updates: All fields are optional. Only fields you provide will be updated. Fields not included in the request will remain unchanged.

  2. 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.

  3. 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)
  4. Address Updates:

    • If address doesn't exist, it will be created (requires street_1, city, postal_code, and country_code)
    • If address exists, only provided fields will be updated
    • Partial address updates are supported
  5. 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 409 error
  6. 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 409 error
  7. Date of Birth Format: Must be in YYYY-MM-DD format (e.g., "1990-01-15").

  8. Transaction Safety: All database updates happen within a transaction. If any update fails, the entire operation is rolled back.

  9. Business Ownership: You can only update customers that belong to your business. Attempting to update a customer from another business will result in a 403 error.

  10. 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

ParameterTypeRequiredDescriptionExample
customer_idstring (UUID)YesThe unique identifier of the customer"123e4567-e89b-12d3-a456-426614174000"

Success Response (200 OK)

{
  "kyc_status": "PASS",
  "message": "KYC verification completed successfully"
}

Response Fields

FieldTypeDescription
kyc_statusstringThe KYC verification status. Possible values: PASS, PENDING, KYC_NOT_INITIATED
messagestring (optional)Human-readable message describing the KYC status

KYC Status Values

StatusDescription
PASSKYC verification has been completed successfully. The customer has a verified status (PASS, ACCEPTED, VERIFIED, or APPROVED) from either Footprint or Persona.
PENDINGKYC verification is in progress or pending. The customer has a KYC record but verification is not yet complete.
KYC_NOT_INITIATEDNo KYC verification has been started. The customer does not have any KYC records with Footprint or Persona.

Error Responses

Status CodeDescription
400Invalid customer ID format
401Unauthorized - Invalid API key or signature
403Forbidden - Customer does not belong to your business
404Customer not found
500Internal 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

  1. 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 403 error.

  2. 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.

  3. Status Priority: The endpoint checks Footprint first, then Persona. If either provider has a successful status, PASS is returned. Otherwise, if a record exists with a non-successful status, PENDING is returned.

  4. No KYC Record: If the customer has no KYC records with either provider, the status will be KYC_NOT_INITIATED.

  5. 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.

  6. 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

Related Resources