Official Node.js SDK for the Dispatch9 API. Simplify integration with Dispatch9's delivery and logistics platform.
This SDK focuses on the 4 essential operations:
- β Create Orders - Create delivery and service orders
 - β Update Orders - Modify existing orders
 - β Create Clients - Add new clients to your system
 - β Update Clients - Modify existing client information
 
npm install @dispatch9/client-sdkconst Dispatch9Client = require('@dispatch9/client-sdk');
// Initialize the client
const dispatch9 = new Dispatch9Client({
  apiKey: 'your-api-key-here',
  baseURL: 'https://api.dispatch9.com' // or your server URL
});
// Create a client
const client = await dispatch9.createClient({
  name: 'Acme Corporation',
  email: 'contact@acmecorp.com',
  businessType: 'retail'
});
// Create an order (addresses must be created separately first)
const order = await dispatch9.createOrder({
  orderTotal: 49.99,
  client: client.id,
  hasGoods: true,
  items: [
    {
      SKU: 'ITEM001',
      itemName: 'Sample Product',
      price: 49.99,
      quantity: 1
    }
  ],
  pickupLocation: 'pickup-address-id', // Use ID from previously created address
  deliveryLocation: 'delivery-address-id' // Use ID from previously created address
});
// Update the order
const updatedOrder = await dispatch9.updateOrder(order.id, {
  status: 'confirmed',
  priority: 5,
  specialInstructions: 'Handle with care'
});
console.log('Order updated:', updatedOrder.status);const dispatch9 = new Dispatch9Client({
  apiKey: 'your-api-key-here',        // Required: Your API key
  baseURL: 'https://api.dispatch9.com', // Optional: API base URL
  timeout: 30000,                      // Optional: Request timeout (ms)
  debug: false,                        // Optional: Enable debug logging
  headers: {}                          // Optional: Additional headers
});Address Workflow:
- Create addresses first using the address API endpoints (not included in this SDK)
 - Save the returned address IDs from the address creation responses
 - Use those address IDs in your order creation calls
 
Address Creation Schema (POST /v1/addresses):
Required Fields:
street(string) - Street address (1-255 characters)city(string) - City name (1-100 characters)state(string) - State/province (1-100 characters)country(string) - Country name (1-100 characters)
Optional Fields:
building(string) - Building number/name (max 100 characters)apartment(string) - Apartment/unit number (max 50 characters)postalCode(string) - Postal/ZIP code (max 20 characters)country_code(string) - 2-letter country code (uppercase)instructions(string) - Delivery instructions (max 500 characters)contactName(string) - Contact person name (max 100 characters)contactPhone(string) - Contact phone number (valid international format)contactEmail(string) - Contact email address (valid email)timeWindow(object) - Delivery time windowstart(Date) - Start timeend(Date) - End timetimezone(string) - Timezone identifier (e.g., "America/New_York")days(Array) - Available days ["monday", "tuesday", etc.]notes(string) - Additional schedule notes (max 500 characters)
Google Geocoding Fields (Optional):
formatted_address(string) - Google formatted addressplace_id(string) - Google Place IDgeometry(object) - Geographic coordinateslocation(object) - Point coordinates{type: "Point", coordinates: [lng, lat]}
Example Address Creation Flow:
// 1. Create pickup address using Dispatch9 API (separate HTTP call)
const pickupResponse = await fetch('https://api.dispatch9.com/v1/addresses', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-API-Key': 'your-api-key'
  },
  body: JSON.stringify({
    street: '123 Warehouse Street',
    city: 'San Francisco',
    state: 'California',
    country: 'United States',
    postalCode: '94105',
    contactName: 'Warehouse Manager',
    contactPhone: '+1-555-0123',
    instructions: 'Loading dock entrance'
  })
});
const pickupAddress = await pickupResponse.json();
// Returns: { id: '507f1f77bcf86cd799439012', street: '123 Warehouse Street', ... }
// 2. Create delivery address using Dispatch9 API (separate HTTP call)
const deliveryResponse = await fetch('https://api.dispatch9.com/v1/addresses', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-API-Key': 'your-api-key'
  },
  body: JSON.stringify({
    street: '456 Customer Avenue, Apt 3B',
    city: 'San Francisco',
    state: 'California',
    country: 'United States',
    postalCode: '94107',
    contactName: 'John Customer',
    contactPhone: '+1-555-0456',
    instructions: 'Ring apartment 3B'
  })
});
const deliveryAddress = await deliveryResponse.json();
// Returns: { id: '507f1f77bcf86cd799439013', street: '456 Customer Avenue, Apt 3B', ... }
// 3. Use the address IDs in your order (using this SDK)
const order = await dispatch9.createOrder({
  orderTotal: 49.99,
  client: 'client-id',
  pickupLocation: pickupAddress.id, // '507f1f77bcf86cd799439012'
  deliveryLocation: deliveryAddress.id, // '507f1f77bcf86cd799439013'
  hasGoods: true,
  items: [/* ... */]
});Create a new order.
Required Fields:
orderTotal(number) - Total order amount (min: 0)client(string) - Client ID (ObjectId)
Optional Fields:
orderNumber(string) - Custom order numberorderCurrency(string) - Order currency (default: 'USD')isPaid(boolean) - Payment status (default: false)hasGoods(boolean) - Contains goods/items (default: false)hasServices(boolean) - Contains services (default: false)hasWorkers(boolean) - Requires worker transport (default: false)priority(number) - Priority 0-10 (default: 0)autoAssign(boolean) - Auto-assign workers (default: false)manualAssignWorker(string) - Worker ID for manual assignmentstatus(string) - Order status (default: 'created')completeAfter(number) - Earliest completion timestampcompleteBefore(number) - Latest completion timestamppickupLocation(string) - Pickup address ID (ObjectId - create address first, then use the returned ID)deliveryLocation(string) - Delivery address ID (ObjectId - create address first, then use the returned ID)serviceLocation(string) - Service address ID (ObjectId - create address first, then use the returned ID, required ifhasServices=true)specialInstructions(string) - Special delivery instructionscustomerNotes(string) - Customer notesmetadata(Object) - Additional metadataisRecurring(boolean) - Is recurring order (default: false)recurringSettings(Object) - Recurring settings (required ifisRecurring=true)requiredProof(Object) - Proof of delivery requirementssignature(boolean) - Require signature (default: false)photo(boolean) - Require photo (default: false)
Conditional Required Fields:
items(Array) - Items array (required ifhasGoods=true)services(Array) - Services array (required ifhasServices=true)workers(Array) - Workers array (required ifhasWorkers=true)
Item Schema (when hasGoods=true):
SKU(string, required) - Item SKUitemName(string, required) - Item nameprice(number, required) - Item price (min: 0)quantity(number, required) - Quantity (min: 1)category(string) - Item categorydescription(string) - Item descriptioncurrency(string) - Item currency (default: 'USD')weight(number) - Item weightweightUnit(string) - Weight unit:kg,lb,g,oz(default: 'kg')dimensionH(number) - HeightdimensionW(number) - WidthdimensionL(number) - LengthdimensionUnit(string) - Dimension unit:cm,in(default: 'cm')packaging(string) - Packaging requirementshandling(string) - Handling instructionsnotes(string) - Item notes
Service Schema (when hasServices=true):
serviceCode(string, required) - Service code/identifierserviceName(string, required) - Service namecategory(string, required) - Service category (any string value for flexible categorization)description(string) - Service descriptionestimatedDuration(number) - Estimated duration in minutes (default: 60)price(number, required) - Service price (min: 0)currency(string) - Service currency (default: 'USD')requirements(Array) - Service requirements - can be strings or structured objects:- String requirements: 
'Tools','Safety equipment', etc. - Object requirements for workers: 
{ type: 'workers', count: 2, description: 'Technicians required' } - Object requirements for other resources: 
{ type: 'tools'|'qualifications'|'equipment', description: 'Details', mandatory: true } 
- String requirements: 
 notes(string) - Service notes or special instructions
Example:
// Note: Addresses must be created first using the address API endpoints
// Then use the returned address IDs in the order
const order = await dispatch9.createOrder({
  orderTotal: 99.99,
  client: '507f1f77bcf86cd799439011',
  hasGoods: true,
  items: [
    {
      SKU: 'PROD001',
      itemName: 'Premium Widget',
      price: 99.99,
      quantity: 1,
      category: 'Electronics',
      weight: 2.5,
      weightUnit: 'kg'
    }
  ],
  pickupLocation: '507f1f77bcf86cd799439012', // ID from created address
  deliveryLocation: '507f1f77bcf86cd799439013', // ID from created address
  specialInstructions: 'Handle with care',
  priority: 5
});Service Order Example:
// Service order example with multiple workers required
const serviceOrder = await dispatch9.createOrder({
  orderTotal: 299.99,
  client: '507f1f77bcf86cd799439011',
  hasServices: true,
  services: [
    {
      serviceCode: 'INSTALL001',
      serviceName: 'Appliance Installation',
      category: 'installation',
      description: 'Install washing machine and dryer',
      estimatedDuration: 120,
      price: 199.99,
      requirements: [
        { type: 'workers', count: 2, description: '2 installers required' },
        'Tools',
        { type: 'qualifications', description: 'Electrical knowledge', mandatory: true }
      ],
      notes: 'Customer will be home between 2-4 PM'
    },
    {
      serviceCode: 'CLEAN001',
      serviceName: 'Post-Installation Cleanup',
      category: 'housekeeping', // Custom category example
      description: 'Clean up installation area',
      estimatedDuration: 30,
      price: 100.00,
      requirements: [
        { type: 'workers', count: 1, description: 'Single cleaner' },
        'Cleaning supplies'
      ]
    }
  ],
  serviceLocation: '507f1f77bcf86cd799439014', // ID from created address
  specialInstructions: 'Call customer 30 minutes before arrival',
  priority: 7
});The new requirements array supports both simple string requirements and structured object requirements. When a service specifies multiple workers, the system automatically creates separate jobs for each worker, enabling individual assignment and tracking.
Requirements Format:
requirements: [
  // String requirements (tools, materials, etc.)
  'Cleaning supplies',
  'Safety equipment',
  
  // Structured worker requirements
  { 
    type: 'workers', 
    count: 3,                    // Creates 3 separate jobs
    description: 'Experienced cleaners',
    mandatory: true 
  },
  
  // Other structured requirements
  { 
    type: 'qualifications', 
    description: 'OSHA certification',
    mandatory: true 
  },
  { 
    type: 'tools', 
    description: 'Industrial vacuum cleaners' 
  },
  { 
    type: 'equipment', 
    description: 'Scaffolding for high areas' 
  }
]Multiple Job Creation:
- Service with 
{ type: 'workers', count: 3 }β Creates 3 separate jobs - Service with 
{ type: 'workers', count: 1 }β Creates 1 job - Service with no worker requirement β Creates 1 job (default)
 
Benefits:
- β Individual job assignment to different workers
 - β Independent tracking and status updates
 - β Flexible resource management
 - β Better scheduling and coordination
 
Update an existing order.
Parameters:
orderId(string, required) - Order ID (ObjectId)updateData(Object, required) - Data to update (at least one field required)
All fields are optional (but at least one must be provided):
orderNumber(string) - Custom order numberhasGoods(boolean) - Order contains goods/itemshasServices(boolean) - Order contains serviceshasWorkers(boolean) - Order requires worker transportpriority(number) - Order priority (0-10)autoAssign(boolean) - Auto-assign to workersstatus(string) - Order status:created,confirmed,in_progress,completed,cancelled,partially_completeditems(Array) - Items array (follows same schema as createOrder)services(Array) - Services array (follows same schema as createOrder)workers(Array) - Workers arraypickupLocation(string) - Pickup address ID (ObjectId - must be created first)deliveryLocation(string) - Delivery address ID (ObjectId - must be created first)serviceLocation(string) - Service address ID (ObjectId - must be created first)specialInstructions(string) - Special delivery instructionscustomerNotes(string) - Customer notesstatusNotes(string) - Status notesmetadata(Object) - Additional metadataisRecurring(boolean) - Is this a recurring orderrecurringSettings(Object) - Recurring settings
Item Schema (when updating items):
Same as createOrder - all item fields follow the same validation rules.
Service Schema (when updating services):
Same as createOrder - all service fields follow the same validation rules.
Example:
const updatedOrder = await dispatch9.updateOrder('507f1f77bcf86cd799439014', {
  status: 'confirmed',
  priority: 8,
  specialInstructions: 'Urgent delivery - customer called',
  items: [
    {
      SKU: 'PROD001',
      itemName: 'Premium Widget (Updated)',
      price: 109.99,
      quantity: 2,
      category: 'Electronics'
    }
  ]
});Get a specific order by its ID with optional population of related fields.
Parameters:
orderId(string, required) - Order ID (must be valid ObjectId)options(Object, optional) - Query optionspopulate(string) - Comma-separated list of fields to populate- Available fields: 
client,pickupLocation,deliveryLocation,serviceLocation,workers.worker 
- Available fields: 
 includeJobs(boolean) - Include associated jobs in response
Returns: Promise - Complete order details
Examples:
// Get basic order details
const order = await dispatch9.getOrderById('507f1f77bcf86cd799439011');
console.log(`Order Status: ${order.status}`);
console.log(`Order Total: $${order.orderTotal}`);
// Get order with populated client and location details  
const orderWithDetails = await dispatch9.getOrderById('507f1f77bcf86cd799439011', {
  populate: 'client,pickupLocation,deliveryLocation,serviceLocation'
});
console.log(`Client: ${orderWithDetails.client.name}`);
console.log(`Pickup: ${orderWithDetails.pickupLocation.street}`);
// Get order with associated jobs for tracking
const orderWithJobs = await dispatch9.getOrderById('507f1f77bcf86cd799439011', {
  includeJobs: true
});
console.log(`Jobs: ${orderWithJobs.jobs.length}`);
orderWithJobs.jobs.forEach(job => {
  console.log(`Job ${job.jobId}: ${job.status}`);
});
// Check order status for tracking/monitoring
const checkOrderStatus = async (orderId) => {
  try {
    const order = await dispatch9.getOrderById(orderId);
    return {
      id: order.id,
      status: order.status,
      progress: order.completionPercentage || 0,
      estimatedDelivery: order.estimatedDeliveryTime,
      isCompleted: order.status === 'completed'
    };
  } catch (error) {
    if (error.message.includes('404')) {
      return { error: 'Order not found' };
    }
    throw error;
  }
};Retrieve clients with filtering and pagination.
Parameters:
options(Object, optional) - Query optionsname(string) - Filter by client namestatus(string) - Filter by status:active,inactive,suspendedbusinessType(string) - Filter by business type:restaurant,retail,grocery,pharmacy,othersortBy(string) - Sort fieldlimit(number) - Items per pagepage(number) - Page number
Example:
const clients = await dispatch9.getClients({
  businessType: 'retail',
  status: 'active',
  limit: 20
});Create a new client with email or phone number.
Required Fields:
name(string) - Client/business name (min: 1 character)
Contact Information (at least one required):
email(string, optional) - Client email (must be valid email if provided)phone(string, optional) - Client phone number (must be valid phone if provided)
Note: At least one of email OR phone is required for client creation.
Optional Fields:
websiteURL(string) - Website URL (must be valid URI)logoURL(string) - Logo URL (must be valid URI)businessType(string) - Business type:restaurant,retail,grocery,pharmacy,othertaxId(string) - Tax identification numberaddress(string) - Address ID (ObjectId)webhookURL(string) - Webhook URL for notifications (must be valid URI)
Optional Objects:
apiConfig(Object) - API configurationenabled(boolean) - Enable API access (default: true)rateLimit(number) - API rate limit (default: 1000, min: 1)
integrations(Array) - Third-party integrationsplatform(string, required) - Platform nameenabled(boolean) - Integration enabled (default: false)config(Object) - Integration configuration (default: {})webhookSecret(string) - Webhook secretsyncOrders(boolean) - Sync orders (default: true)
orderSettings(Object) - Order settingsautoAccept(boolean) - Auto-accept orders (default: false)autoAssign(boolean) - Auto-assign workers (default: false)maxOrdersPerHour(number) - Max orders per hour (default: 50, min: 1)preparationTime(number) - Preparation time in minutes (default: 15, min: 1)deliveryRadius(number) - Delivery radius (default: 10, min: 0)
permissions(Object) - Client permissionsmodify(boolean) - Can modify settings (default: false)delete(boolean) - Can delete data (default: false)createOrders(boolean) - Can create orders (default: true)viewOrders(boolean) - Can view orders (default: true)
authentication(Object) - Authentication settingsenablePortalAccess(boolean, required) - Enable portal accessphone(string) - Phone numberpassword(string) - PasswordfirstName(string) - First namelastName(string) - Last namebusinessName(string) - Business name
Examples:
// Create client with email
const clientWithEmail = await dispatch9.createClient({
  name: 'Acme Corporation',
  email: 'contact@acmecorp.com',
  businessType: 'retail',
  websiteURL: 'https://www.acmecorp.com',
  taxId: '12-3456789',
  orderSettings: {
    autoAccept: true,
    maxOrdersPerHour: 25,
    deliveryRadius: 15
  },
  permissions: {
    createOrders: true,
    viewOrders: true
  }
});
// Create client with phone number only
const clientWithPhone = await dispatch9.createClient({
  name: 'Mobile Business',
  phone: '+1234567890',
  businessType: 'restaurant'
});
// Create client with both email and phone
const clientWithBoth = await dispatch9.createClient({
  name: 'Full Contact Business',
  email: 'info@business.com',
  phone: '+1 (234) 567-8900',
  businessType: 'grocery',
  authentication: {
    enablePortalAccess: true,
    password: 'securePassword123',
    firstName: 'John',
    lastName: 'Doe'
  }
});Update an existing client.
Parameters:
clientId(string, required) - Client ID (ObjectId)updateData(Object, required) - Data to update (at least one field required)
All fields are optional (but at least one must be provided):
name(string) - Client name (min: 1 character)email(string) - Client email (must be valid email)phone(string) - Client phone number (must be valid phone)websiteURL(string) - Website URL (must be valid URI)logoURL(string) - Logo URL (must be valid URI)status(string) - Status:active,inactive,suspendedbusinessType(string) - Business type:restaurant,retail,grocery,pharmacy,othertaxId(string) - Tax IDaddress(string) - Address ID (ObjectId)contactName(string) - Contact name (min: 1 character)contactPhone(string) - Contact phone (min: 10 characters)timeWindow(Object) - Time windowstart(Date) - Start timeend(Date) - End time
webhookURL(string) - Webhook URL (must be valid URI)apiConfig(Object) - API configurationenabled(boolean) - Enable API accessrateLimit(number) - API rate limit (min: 1)
permissions(Object) - Client permissionsmodify(boolean) - Can modify settingsdelete(boolean) - Can delete datacreateOrders(boolean) - Can create ordersviewOrders(boolean) - Can view orders
orderSettings(Object) - Order settingsautoAccept(boolean) - Auto-accept ordersautoAssign(boolean) - Auto-assign workersmaxOrdersPerHour(number) - Max orders per hour (min: 1)preparationTime(number) - Preparation time (min: 1)deliveryRadius(number) - Delivery radius (min: 0)
Example:
const updatedClient = await dispatch9.updateClient('507f1f77bcf86cd799439011', {
  name: 'Acme Corp (Updated)',
  websiteURL: 'https://www.newacmecorp.com',
  orderSettings: {
    maxOrdersPerHour: 30,
    deliveryRadius: 20
  }
});Get a specific client by its ID with optional population of related fields and statistics.
Parameters:
clientId(string, required) - Client ID (must be valid ObjectId)options(Object, optional) - Query optionspopulate(string) - Comma-separated list of fields to populate- Available fields: 
addresses,primaryAddress,billingAddress 
- Available fields: 
 includeStats(boolean) - Include client statistics (order count, total revenue, etc.)
Returns: Promise - Complete client details
Examples:
// Get basic client details
const client = await dispatch9.getClientById('507f1f77bcf86cd799439011');
console.log(`Client: ${client.name}`);
console.log(`Status: ${client.status}`);
console.log(`Business Type: ${client.businessType}`);
// Get client with populated address details
const clientWithAddresses = await dispatch9.getClientById('507f1f77bcf86cd799439011', {
  populate: 'addresses,primaryAddress,billingAddress'
});
console.log(`Primary Address: ${clientWithAddresses.primaryAddress.street}`);
console.log(`Total Addresses: ${clientWithAddresses.addresses.length}`);
// Get client with statistics for analytics
const clientWithStats = await dispatch9.getClientById('507f1f77bcf86cd799439011', {
  includeStats: true
});
console.log(`Total Orders: ${clientWithStats.stats.totalOrders}`);
console.log(`Total Revenue: $${clientWithStats.stats.totalRevenue}`);
console.log(`Average Order Value: $${clientWithStats.stats.averageOrderValue}`);
// Verify client registration and status
const verifyClient = async (clientId) => {
  try {
    const client = await dispatch9.getClientById(clientId);
    return {
      exists: true,
      id: client.id,
      name: client.name,
      email: client.email,
      status: client.status,
      isActive: client.status === 'active',
      businessType: client.businessType,
      registrationDate: client.createdAt
    };
  } catch (error) {
    if (error.message.includes('404')) {
      return { 
        exists: false, 
        error: 'Client not found or not registered' 
      };
    }
    throw error;
  }
};
// Usage for client verification
const clientStatus = await verifyClient('507f1f77bcf86cd799439011');
if (clientStatus.exists) {
  console.log(`β
 Client ${clientStatus.name} is registered and ${clientStatus.status}`);
} else {
  console.log('β Client not found - please register first');
}The SDK now supports comprehensive client authentication with both email and phone number login capabilities.
Authenticate a client using email address or phone number with password.
Parameters:
credentials(Object, required) - Login credentialsidentifier(string, required) - Email address or phone numberpassword(string, required) - Client passwordproviderId(string, optional) - Provider ID for multi-provider clients
Returns: Promise - Authentication result with tokens and client data
Examples:
// Login with email address
const emailLogin = await dispatch9.loginClient({
  identifier: 'client@example.com',
  password: 'securePassword123'
});
if (emailLogin.success) {
  console.log('β
 Email login successful');
  console.log(`Welcome ${emailLogin.client.name}`);
} else {
  console.log('β Login failed:', emailLogin.message);
}
// Login with phone number
const phoneLogin = await dispatch9.loginClient({
  identifier: '+1234567890',
  password: 'securePassword123'
});
// Login with formatted phone number
const formattedPhoneLogin = await dispatch9.loginClient({
  identifier: '+1 (234) 567-8900',
  password: 'securePassword123'
});
// Handle multi-provider client login
const result = await dispatch9.loginClient({
  identifier: 'client@example.com',
  password: 'securePassword123'
});
if (result.requiresProviderSelection) {
  console.log('Multiple providers available:');
  result.providers.forEach((provider, index) => {
    console.log(`${index + 1}. ${provider.database} (ID: ${provider.providerId})`);
  });
  
  // Select a provider (see selectClientProvider method)
}
// Handle first-time login OTP requirement
if (result.requiresOTP) {
  console.log('First-time login requires OTP verification');
  console.log(`OTP sent to: ${result.client.email}`);
  
  // User enters OTP, then verify (see verifyClientLoginOTP method)
}Select a provider for clients with multiple provider access.
Parameters:
selection(Object, required) - Provider selection dataclientId(string, required) - Client ID (ObjectId format)providerId(string, required) - Provider ID to select (ObjectId format)
Returns: Promise - Authentication result with tokens
Examples:
// Select provider after multi-provider login
const providerSelection = await dispatch9.selectClientProvider({
  clientId: '507f1f77bcf86cd799439011',
  providerId: '507f1f77bcf86cd799439012'
});
if (providerSelection.success) {
  console.log('β
 Provider selected successfully');
  console.log(`Connected to: ${providerSelection.client.selectedProvider.database}`);
  // Client is now authenticated and ready to use the API
}Verify OTP for first-time client login security.
Parameters:
verification(Object, required) - OTP verification dataclientId(string, required) - Client ID (ObjectId format)otp(string, required) - 6-digit OTP code
Returns: Promise - Verification result with authentication tokens
Examples:
// Verify OTP for first-time login
const otpVerification = await dispatch9.verifyClientLoginOTP({
  clientId: '507f1f77bcf86cd799439011',
  otp: '123456'
});
if (otpVerification.success) {
  console.log('β
 OTP verified successfully');
  console.log(`Welcome ${otpVerification.client.name}`);
  // Client is now authenticated
}Get the current authenticated client's profile information.
Returns: Promise - Client profile data
Examples:
// Get current client profile (requires authentication)
try {
  const profile = await dispatch9.getClientProfile();
  
  console.log('π Client Profile:');
  console.log(`Name: ${profile.name}`);
  console.log(`Email: ${profile.email}`);
  console.log(`Phone: ${profile.phone || 'Not provided'}`);
  console.log(`Status: ${profile.status}`);
  console.log(`Registration: ${profile.registrationStatus}`);
  console.log(`First Login: ${profile.isFirstLogin ? 'Yes' : 'No'}`);
  console.log(`Last Login: ${profile.lastLogin || 'Never'}`);
  
  console.log('\nπ’ Available Providers:');
  profile.providers.forEach((provider, index) => {
    console.log(`${index + 1}. ${provider.database} (${provider.status})`);
  });
  
} catch (error) {
  console.log('β Authentication required or profile access failed');
}Basic Email/Phone Login:
const authenticateClient = async (identifier, password) => {
  try {
    const result = await dispatch9.loginClient({
      identifier,
      password
    });
    
    if (result.success && result.tokens) {
      // Direct login success
      console.log('β
 Authentication successful');
      return { success: true, client: result.client };
      
    } else if (result.requiresProviderSelection) {
      // Handle provider selection
      console.log('π Multiple providers available');
      return { 
        success: false, 
        requiresProviderSelection: true,
        clientId: result.clientId,
        providers: result.providers
      };
      
    } else if (result.requiresOTP) {
      // Handle OTP verification
      console.log('π§ OTP verification required');
      return {
        success: false,
        requiresOTP: true,
        clientId: result.clientId
      };
    }
    
    return { success: false, message: result.message };
    
  } catch (error) {
    return { success: false, message: error.message };
  }
};
// Usage examples
const emailAuth = await authenticateClient('client@example.com', 'password123');
const phoneAuth = await authenticateClient('+1234567890', 'password123');
const formattedPhoneAuth = await authenticateClient('+1 (234) 567-8900', 'password123');Multi-Provider Authentication:
const handleMultiProviderAuth = async (identifier, password) => {
  // Step 1: Initial login
  const loginResult = await dispatch9.loginClient({
    identifier,
    password
  });
  
  if (loginResult.requiresProviderSelection) {
    console.log('Available providers:');
    loginResult.providers.forEach((provider, index) => {
      console.log(`${index + 1}. ${provider.database}`);
    });
    
    // Step 2: Select provider (in real app, user would choose)
    const selectedProvider = loginResult.providers[0]; // Example: select first
    
    const providerResult = await dispatch9.selectClientProvider({
      clientId: loginResult.clientId,
      providerId: selectedProvider.providerId
    });
    
    if (providerResult.success) {
      console.log('β
 Multi-provider authentication complete');
      return providerResult.client;
    }
  }
  
  return null;
};First-Time Login with OTP:
const handleFirstTimeLogin = async (identifier, password, otpCode) => {
  // Step 1: Initial login
  const loginResult = await dispatch9.loginClient({
    identifier,
    password
  });
  
  if (loginResult.requiresOTP) {
    console.log('π§ First-time login detected, OTP sent to email');
    
    // Step 2: Verify OTP (otpCode would come from user input)
    const otpResult = await dispatch9.verifyClientLoginOTP({
      clientId: loginResult.clientId,
      otp: otpCode
    });
    
    if (otpResult.success) {
      console.log('β
 First-time login complete');
      return otpResult.client;
    }
  }
  
  return null;
};
// Usage
const newClient = await handleFirstTimeLogin('new@client.com', 'password', '123456');The SDK provides detailed error messages for validation and API errors:
try {
  const client = await dispatch9.createClient({
    // Missing required fields
  });
} catch (error) {
  console.error('Error:', error.message);
  // Output: "name is required and must be at least 1 character"
}- Validation Errors: Field requirements, format validation, constraints
 - Authentication Errors: Invalid API key, expired tokens
 - Permission Errors: Insufficient permissions for operation
 - Rate Limit Errors: Too many requests
 - Network Errors: Connection issues, timeouts
 
Create a .env file in your project root:
DISPATCH9_API_KEY=your-api-key-here
DISPATCH9_BASE_URL=https://api.dispatch9.comThen use in your code:
require('dotenv').config();
const dispatch9 = new Dispatch9Client({
  apiKey: process.env.DISPATCH9_API_KEY,
  baseURL: process.env.DISPATCH9_BASE_URL
});See the examples/ directory for complete working examples:
basic-usage.js- Basic SDK usage with all 4 core operationsorder-management.js- Order creation and managementclient-management.js- Client operations and settingscomplete-workflow.js- End-to-end business workflowaddress-creation-example.js- Complete address creation workflow (shows how to create addresses before orders)
Run the test suite:
npm testYour API key must have the following permissions:
orders.create- Create ordersorders.update- Update ordersclients.create- Create clients (may require elevated permissions)clients.read- View clientsclients.update- Update clients (may require elevated permissions)
- Documentation: https://docs.dispatch9.com
 - API Reference: https://api.dispatch9.com/docs
 - Issues: GitHub Issues
 - Support: support@dispatch9.com
 
MIT License. See LICENSE file for details.