POST
/
wp-json
/
latepoint-api
/
v1
/
customers
Create Customer
curl --request POST \
  --url https://your-site.com/wp-json/latepoint-api/v1/customers \
  --header 'Content-Type: application/json' \
  --header 'X-API-Key: <x-api-key>' \
  --data '{
  "first_name": "<string>",
  "last_name": "<string>",
  "email": "<string>",
  "phone": "<string>",
  "status": "<string>",
  "date_of_birth": "<string>",
  "gender": "<string>",
  "address": "<string>",
  "city": "<string>",
  "state": "<string>",
  "zipcode": "<string>",
  "country": "<string>",
  "custom_fields": {},
  "notes": "<string>",
  "is_guest": true,
  "wordpress_user_id": 123
}'
{
  "status": "error",
  "error": {
    "code": "validation_failed",
    "message": "The provided data is not valid",
    "details": {
      "first_name": "First name is required and must be at least 2 characters",
      "email": "Email format is not valid",
      "phone": "Phone format is not valid"
    }
  }
}

Description

This endpoint allows you to create a new customer in the LatePoint system. You can provide basic or complete customer information, including contact details, address, preferences, and custom fields.

Authentication

X-API-Key
string
required
Your LatePoint API Key with write permissions

Request Body

Required Fields

first_name
string
required
Customer’s first nameValidations:
  • Minimum 2 characters
  • Maximum 50 characters
  • Only letters, spaces, and hyphens
last_name
string
required
Customer’s last nameValidations:
  • Minimum 2 characters
  • Maximum 50 characters
  • Only letters, spaces, and hyphens
email
string
required
Customer’s email (must be unique)Validations:
  • Valid email format
  • Unique in the system
  • Maximum 100 characters

Optional Fields

phone
string
Customer’s phone numberValidations:
  • Valid phone format
  • Can include country code
  • Maximum 20 characters
status
string
default:"active"
Initial customer statusPossible values:
  • active - Active customer (default)
  • inactive - Inactive customer
  • pending - Pending verification
date_of_birth
string
Customer’s date of birth (format: YYYY-MM-DD)
gender
string
Customer’s genderPossible values:
  • male - Male
  • female - Female
  • other - Other
  • prefer_not_to_say - Prefer not to say

Address Information

address
string
Customer’s street address
city
string
Customer’s city
state
string
Customer’s state or province
zipcode
string
Customer’s ZIP or postal code
country
string
Customer’s country

Custom Fields

custom_fields
object
Custom fields defined in your configurationExample:
{
  "emergency_contact": "Mary Smith - +1 555 987 6543",
  "insurance_provider": "Health Insurance Co",
  "referral_source": "Google",
  "medical_notes": "Allergic to penicillin"
}
notes
string
Additional notes about the customerValidations:
  • Maximum 1000 characters

Additional Information

is_guest
boolean
default:"false"
Whether the customer is a guest (no account)
wordpress_user_id
integer
Associated WordPress user ID (if applicable)

Response

Successful Response (201 Created)

status
string
Response status (“success”)
message
string
Confirmation message
data
object
Created customer information

Examples

Basic Customer

curl -X POST "https://your-site.com/wp-json/latepoint-api/v1/customers" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: lp_live_1234567890abcdef" \
  -d '{
    "first_name": "John",
    "last_name": "Doe",
    "email": "john.doe@email.com",
    "phone": "+1 555 123 4567"
  }'

Complete Customer with Address

curl -X POST "https://your-site.com/wp-json/latepoint-api/v1/customers" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: lp_live_1234567890abcdef" \
  -d '{
    "first_name": "Sarah",
    "last_name": "Johnson",
    "email": "sarah.johnson@email.com",
    "phone": "+1 555 987 6543",
    "date_of_birth": "1990-05-15",
    "gender": "female",
    "address": "123 Main Street, Apt 4B",
    "city": "New York",
    "state": "NY",
    "zipcode": "10001",
    "country": "US",
    "custom_fields": {
      "emergency_contact": "John Johnson - +1 555 111 2222",
      "insurance_provider": "Blue Cross Blue Shield",
      "referral_source": "Referral"
    },
    "notes": "VIP customer, prefers morning appointments"
  }'

Create Customer from Web Form

// Function to create customer from form
async function createCustomerFromForm(formData) {
  try {
    // Validate form data
    const validation = validateCustomerData(formData);
    if (!validation.isValid) {
      throw new Error('Invalid data: ' + validation.errors.join(', '));
    }
    
    // Prepare customer data
    const customerData = {
      first_name: formData.firstName.trim(),
      last_name: formData.lastName.trim(),
      email: formData.email.toLowerCase().trim(),
      phone: formData.phone?.trim(),
      date_of_birth: formData.dateOfBirth,
      gender: formData.gender,
      preferences: {
        language: formData.language || 'en',
        communication_preferences: {
          email_notifications: formData.emailNotifications !== false,
          sms_notifications: formData.smsNotifications === true,
          marketing_emails: formData.marketingEmails === true
        }
      },
      send_welcome_email: formData.sendWelcomeEmail !== false,
      create_wp_user: formData.createAccount === true
    };
    
    // Add address if complete
    if (formData.street && formData.city) {
      customerData.address = {
        street: formData.street,
        city: formData.city,
        state: formData.state,
        postal_code: formData.postalCode,
        country: formData.country || 'US'
      };
    }
    
    // Add custom fields
    if (formData.customFields) {
      customerData.custom_fields = formData.customFields;
    }
    
    // Create customer
    const response = await fetch('/wp-json/latepoint-api/v1/customers', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'X-API-Key': 'your_api_key'
      },
      body: JSON.stringify(customerData)
    });
    
    if (!response.ok) {
      throw new Error(`HTTP Error: ${response.status}`);
    }
    
    const result = await response.json();
    
    if (result.status === 'success') {
      return {
        success: true,
        customer: result.data,
        message: 'Customer created successfully'
      };
    } else {
      throw new Error(result.error.message);
    }
  } catch (error) {
    console.error('Error creating customer:', error);
    return {
      success: false,
      error: error.message
    };
  }
}

function validateCustomerData(data) {
  const errors = [];
  
  if (!data.firstName || data.firstName.trim().length < 2) {
    errors.push('First name must be at least 2 characters');
  }
  
  if (!data.lastName || data.lastName.trim().length < 2) {
    errors.push('Last name must be at least 2 characters');
  }
  
  if (!data.email || !isValidEmail(data.email)) {
    errors.push('Invalid email');
  }
  
  if (data.phone && !isValidPhone(data.phone)) {
    errors.push('Invalid phone number');
  }
  
  return {
    isValid: errors.length === 0,
    errors
  };
}

function isValidEmail(email) {
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  return emailRegex.test(email);
}

function isValidPhone(phone) {
  const phoneRegex = /^[\+]?[1-9][\d\s\-\(\)]{7,15}$/;
  return phoneRegex.test(phone);
}

// Usage
const formData = {
  firstName: 'Anna',
  lastName: 'Martinez',
  email: 'anna.martinez@email.com',
  phone: '+1 555 444 3333',
  emailNotifications: true,
  smsNotifications: false,
  sendWelcomeEmail: true,
  createAccount: false
};

const result = await createCustomerFromForm(formData);

if (result.success) {
  console.log('Customer created:', result.customer);
  // Redirect or show success message
} else {
  console.error('Error:', result.error);
  // Show error message to user
}

Example Response

Customer Created Successfully

{
  "status": "success",
  "message": "Customer created successfully",
  "data": {
    "id": 47,
    "first_name": "Sarah",
    "last_name": "Johnson",
    "email": "sarah.johnson@email.com",
    "phone": "+1 555 987 6543",
    "status": "active",
    "customer_code": "CUST-2024-047",
    "avatar_url": "https://your-site.com/wp-content/plugins/latepoint/images/default-avatar.png",
    "date_of_birth": "1990-05-15",
    "gender": "female",
    "address": {
      "street": "123 Main Street, Apt 4B",
      "city": "New York",
      "state": "NY",
      "postal_code": "10001",
      "country": "US"
    },
    "preferences": {
      "preferred_agent_id": 3,
      "preferred_location_id": 1,
      "preferred_time_slots": ["09:00-12:00", "14:00-17:00"],
      "language": "en",
      "communication_preferences": {
        "email_notifications": true,
        "sms_notifications": true,
        "reminder_hours": 48,
        "marketing_emails": false
      }
    },
    "custom_fields": {
      "emergency_contact": "John Johnson - +1 555 111 2222",
      "insurance_provider": "Blue Cross Blue Shield",
      "referral_source": "Referral"
    },
    "notes": "VIP customer, prefers morning appointments",
    "created_at": "2024-01-20T15:30:00Z",
    "updated_at": "2024-01-20T15:30:00Z",
    "wp_user_id": 156,
    "welcome_email_sent": true
  }
}

Error Codes

{
  "status": "error",
  "error": {
    "code": "validation_failed",
    "message": "The provided data is not valid",
    "details": {
      "first_name": "First name is required and must be at least 2 characters",
      "email": "Email format is not valid",
      "phone": "Phone format is not valid"
    }
  }
}

Automatic Validations

Data Validations

  1. First and Last Name:
    • Minimum 2 characters, maximum 50
    • Only letters, spaces, hyphens and apostrophes
    • Extra spaces are automatically removed
  2. Email:
    • Valid email format
    • Unique in the system
    • Automatically converted to lowercase
    • Maximum 100 characters
  3. Phone:
    • Valid format (can include country code)
    • Spaces and special characters are normalized
    • Maximum 20 characters
  4. Date of Birth:
    • YYYY-MM-DD format
    • Cannot be future date
    • Configurable minimum age (default 13 years)

Business Validations

  1. References:
    • Agent, location and service IDs must exist and be active
    • Time slot preferences must be valid
  2. Custom Fields:
    • Must match defined configuration
    • Specific validations according to field type
  3. Address:
    • Country code must be valid (ISO 3166-1 alpha-2)
    • Postal code must match country format

1. Pre-validation

// Validate email before creating
async function checkEmailAvailability(email) {
  const response = await fetch(`/wp-json/latepoint-api/v1/customers?search=${email}&search_field=email`, {
    headers: { 'X-API-Key': 'your_api_key' }
  });
  
  const result = await response.json();
  return result.data.length === 0; // true if available
}

const emailAvailable = await checkEmailAvailability('new@email.com');
if (!emailAvailable) {
  console.log('Email is already in use');
  return;
}

2. Create Customer

// Create customer with error handling
async function createCustomer(customerData) {
  try {
    const response = await fetch('/wp-json/latepoint-api/v1/customers', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'X-API-Key': 'your_api_key'
      },
      body: JSON.stringify(customerData)
    });
    
    const result = await response.json();
    
    if (result.status === 'success') {
      return result.data;
    } else {
      throw new Error(result.error.message);
    }
  } catch (error) {
    console.error('Error creating customer:', error);
    throw error;
  }
}

3. Post-Creation Handling

// Actions after creating customer
async function handleCustomerCreated(customer) {
  console.log(`Customer created: ${customer.first_name} ${customer.last_name}`);
  console.log(`ID: ${customer.id}, Code: ${customer.customer_code}`);
  
  // Register in analytics
  if (typeof gtag !== 'undefined') {
    gtag('event', 'customer_created', {
      customer_id: customer.id,
      customer_email: customer.email
    });
  }
  
  // Show success message
  showSuccessMessage(`Welcome ${customer.first_name}! Your account has been created successfully.`);
  
  // Redirect or continue flow
  if (customer.wp_user_id) {
    // WordPress user created, can login
    window.location.href = '/my-account/';
  } else {
    // Continue with booking process
    window.location.href = `/booking?customer_id=${customer.id}`;
  }
}

Common Use Cases

1. Quick Registration during Booking

// Create minimal customer during booking process
const quickCustomer = {
  first_name: 'John',
  last_name: 'Smith',
  email: 'john.smith@email.com',
  phone: '+1 555 123 4567',
  send_welcome_email: false, // Don't send email during booking
  create_wp_user: false // Don't create account for now
};

const customer = await createCustomer(quickCustomer);
console.log('Customer created for booking:', customer.id);

2. Full Registration with Account

// Full registration with user account
const fullCustomer = {
  first_name: 'Mary',
  last_name: 'Johnson',
  email: 'mary.johnson@email.com',
  phone: '+1 555 987 6543',
  preferences: {
    language: 'en',
    communication_preferences: {
      email_notifications: true,
      sms_notifications: true,
      marketing_emails: false
    }
  },
  send_welcome_email: true,
  create_wp_user: true,
  wp_user_role: 'customer'
};

const customer = await createCustomer(fullCustomer);
console.log('Customer and user created:', customer);

3. Bulk Customer Import

// Import multiple customers
async function importCustomers(customersData) {
  const results = {
    success: [],
    errors: []
  };
  
  for (const customerData of customersData) {
    try {
      // Check if email already exists
      const emailExists = await checkEmailAvailability(customerData.email);
      if (!emailExists) {
        results.errors.push({
          email: customerData.email,
          error: 'Email already exists'
        });
        continue;
      }
      
      // Create customer
      const customer = await createCustomer({
        ...customerData,
        send_welcome_email: false, // Don't send bulk emails
        create_wp_user: false // Don't create users in bulk
      });
      
      results.success.push(customer);
      
      // Pause to avoid rate limiting
      await new Promise(resolve => setTimeout(resolve, 100));
      
    } catch (error) {
      results.errors.push({
        email: customerData.email,
        error: error.message
      });
    }
  }
  
  return results;
}

// Usage
const customersToImport = [
  { first_name: 'Anna', last_name: 'Williams', email: 'anna@email.com' },
  { first_name: 'Charles', last_name: 'Brown', email: 'charles@email.com' },
  // ... more customers
];

const importResults = await importCustomers(customersToImport);
console.log(`Imported: ${importResults.success.length}, Errors: ${importResults.errors.length}`);

Best Practices

1. Client-Side Validation

// Validate data before sending
function validateCustomerForm(formData) {
  const errors = {};
  
  // Validate name
  if (!formData.first_name || formData.first_name.trim().length < 2) {
    errors.first_name = 'Name must be at least 2 characters';
  }
  
  // Validate email
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  if (!formData.email || !emailRegex.test(formData.email)) {
    errors.email = 'Invalid email';
  }
  
  // Validate phone (optional)
  if (formData.phone) {
    const phoneRegex = /^[\+]?[1-9][\d\s\-\(\)]{7,15}$/;
    if (!phoneRegex.test(formData.phone)) {
      errors.phone = 'Invalid phone format';
    }
  }
  
  return {
    isValid: Object.keys(errors).length === 0,
    errors
  };
}

2. Robust Error Handling

// Function with automatic retries
async function createCustomerWithRetry(customerData, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      return await createCustomer(customerData);
    } catch (error) {
      if (attempt === maxRetries) {
        throw error;
      }
      
      // Only retry on network errors, not validation errors
      if (error.message.includes('validation') || error.message.includes('already_exists')) {
        throw error;
      }
      
      // Wait before next attempt
      await new Promise(resolve => setTimeout(resolve, 1000 * attempt));
    }
  }
}

3. Performance Optimization

// Create multiple customers efficiently
async function createCustomersBatch(customersData, batchSize = 5) {
  const results = [];
  
  for (let i = 0; i < customersData.length; i += batchSize) {
    const batch = customersData.slice(i, i + batchSize);
    
    const batchPromises = batch.map(customerData => 
      createCustomer(customerData).catch(error => ({ error: error.message, data: customerData }))
    );
    
    const batchResults = await Promise.all(batchPromises);
    results.push(...batchResults);
    
    // Pause between batches to respect rate limits
    if (i + batchSize < customersData.length) {
      await new Promise(resolve => setTimeout(resolve, 500));
    }
  }
  
  return results;
}

Important Notes

Unique Email: The email must be unique throughout the system. If you try to create a customer with an existing email, you will receive a 409 error.
Custom Fields: Custom fields must be previously configured in your LatePoint installation to be accepted.
Rate Limiting: Respect API rate limits, especially when creating multiple customers. Implement pauses between requests.
WordPress User: If you create an associated WordPress user, the customer will be able to access the customer area and manage their bookings.