@dispatch9/client-sdk
Version:
Official Node.js SDK for Dispatch9 API - Complete solution with email/phone client creation, order management, client management, and dual-method authentication
1,050 lines (884 loc) • 35 kB
Markdown
# Dispatch9 Node.js SDK
Official Node.js SDK for the Dispatch9 API. Simplify integration with Dispatch9's delivery and logistics platform.
## Core Operations
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
## Installation
```bash
npm install @dispatch9/client-sdk
```
## Quick Start
```javascript
const 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);
```
## Configuration
### Constructor Options
```javascript
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
});
```
## API Reference
### Important: Address Management
**⚠️ Note**: This SDK focuses on the 4 core operations listed above. Address management is handled separately through the Dispatch9 API.
**Address Workflow:**
1. **Create addresses first** using the address API endpoints (not included in this SDK)
2. **Save the returned address IDs** from the address creation responses
3. **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 window
- `start` (Date) - Start time
- `end` (Date) - End time
- `timezone` (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 address
- `place_id` (string) - Google Place ID
- `geometry` (object) - Geographic coordinates
- `location` (object) - Point coordinates `{type: "Point", coordinates: [lng, lat]}`
**Example Address Creation Flow:**
```javascript
// 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: [/* ... */]
});
```
### Order Management
#### `createOrder(orderData)`
Create a new order.
**Required Fields:**
- `orderTotal` (number) - Total order amount (min: 0)
- `client` (string) - Client ID (ObjectId)
**Optional Fields:**
- `orderNumber` (string) - Custom order number
- `orderCurrency` (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 assignment
- `status` (string) - Order status (default: 'created')
- `completeAfter` (number) - Earliest completion timestamp
- `completeBefore` (number) - Latest completion timestamp
- `pickupLocation` (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 if `hasServices=true`)
- `specialInstructions` (string) - Special delivery instructions
- `customerNotes` (string) - Customer notes
- `metadata` (Object) - Additional metadata
- `isRecurring` (boolean) - Is recurring order (default: false)
- `recurringSettings` (Object) - Recurring settings (required if `isRecurring=true`)
- `requiredProof` (Object) - Proof of delivery requirements
- `signature` (boolean) - Require signature (default: false)
- `photo` (boolean) - Require photo (default: false)
**Conditional Required Fields:**
- `items` (Array) - Items array (required if `hasGoods=true`)
- `services` (Array) - Services array (required if `hasServices=true`)
- `workers` (Array) - Workers array (required if `hasWorkers=true`)
**Item Schema** (when `hasGoods=true`):
- `SKU` (string, required) - Item SKU
- `itemName` (string, required) - Item name
- `price` (number, required) - Item price (min: 0)
- `quantity` (number, required) - Quantity (min: 1)
- `category` (string) - Item category
- `description` (string) - Item description
- `currency` (string) - Item currency (default: 'USD')
- `weight` (number) - Item weight
- `weightUnit` (string) - Weight unit: `kg`, `lb`, `g`, `oz` (default: 'kg')
- `dimensionH` (number) - Height
- `dimensionW` (number) - Width
- `dimensionL` (number) - Length
- `dimensionUnit` (string) - Dimension unit: `cm`, `in` (default: 'cm')
- `packaging` (string) - Packaging requirements
- `handling` (string) - Handling instructions
- `notes` (string) - Item notes
**Service Schema** (when `hasServices=true`):
- `serviceCode` (string, required) - Service code/identifier
- `serviceName` (string, required) - Service name
- `category` (string, required) - Service category (any string value for flexible categorization)
- `description` (string) - Service description
- `estimatedDuration` (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 }`
- `notes` (string) - Service notes or special instructions
**Example:**
```javascript
// 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:**
```javascript
// 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
});
```
### 🔧 **Service Requirements & Multiple Jobs**
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:**
```javascript
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
#### `updateOrder(orderId, updateData)`
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 number
- `hasGoods` (boolean) - Order contains goods/items
- `hasServices` (boolean) - Order contains services
- `hasWorkers` (boolean) - Order requires worker transport
- `priority` (number) - Order priority (0-10)
- `autoAssign` (boolean) - Auto-assign to workers
- `status` (string) - Order status: `created`, `confirmed`, `in_progress`, `completed`, `cancelled`, `partially_completed`
- `items` (Array) - Items array (follows same schema as createOrder)
- `services` (Array) - Services array (follows same schema as createOrder)
- `workers` (Array) - Workers array
- `pickupLocation` (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 instructions
- `customerNotes` (string) - Customer notes
- `statusNotes` (string) - Status notes
- `metadata` (Object) - Additional metadata
- `isRecurring` (boolean) - Is this a recurring order
- `recurringSettings` (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:**
```javascript
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'
}
]
});
```
#### `getOrderById(orderId, options)`
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 options
- `populate` (string) - Comma-separated list of fields to populate
- Available fields: `client`, `pickupLocation`, `deliveryLocation`, `serviceLocation`, `workers.worker`
- `includeJobs` (boolean) - Include associated jobs in response
**Returns:** Promise<Order> - Complete order details
**Examples:**
```javascript
// 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;
}
};
```
### Client Management
#### `getClients(options)`
Retrieve clients with filtering and pagination.
**Parameters:**
- `options` (Object, optional) - Query options
- `name` (string) - Filter by client name
- `status` (string) - Filter by status: `active`, `inactive`, `suspended`
- `businessType` (string) - Filter by business type: `restaurant`, `retail`, `grocery`, `pharmacy`, `other`
- `sortBy` (string) - Sort field
- `limit` (number) - Items per page
- `page` (number) - Page number
**Example:**
```javascript
const clients = await dispatch9.getClients({
businessType: 'retail',
status: 'active',
limit: 20
});
```
#### `createClient(clientData)`
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`, `other`
- `taxId` (string) - Tax identification number
- `address` (string) - Address ID (ObjectId)
- `webhookURL` (string) - Webhook URL for notifications (must be valid URI)
**Optional Objects:**
- `apiConfig` (Object) - API configuration
- `enabled` (boolean) - Enable API access (default: true)
- `rateLimit` (number) - API rate limit (default: 1000, min: 1)
- `integrations` (Array) - Third-party integrations
- `platform` (string, required) - Platform name
- `enabled` (boolean) - Integration enabled (default: false)
- `config` (Object) - Integration configuration (default: {})
- `webhookSecret` (string) - Webhook secret
- `syncOrders` (boolean) - Sync orders (default: true)
- `orderSettings` (Object) - Order settings
- `autoAccept` (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 permissions
- `modify` (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 settings
- `enablePortalAccess` (boolean, required) - Enable portal access
- `phone` (string) - Phone number
- `password` (string) - Password
- `firstName` (string) - First name
- `lastName` (string) - Last name
- `businessName` (string) - Business name
**Examples:**
```javascript
// 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'
}
});
```
#### `updateClient(clientId, updateData)`
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`, `suspended`
- `businessType` (string) - Business type: `restaurant`, `retail`, `grocery`, `pharmacy`, `other`
- `taxId` (string) - Tax ID
- `address` (string) - Address ID (ObjectId)
- `contactName` (string) - Contact name (min: 1 character)
- `contactPhone` (string) - Contact phone (min: 10 characters)
- `timeWindow` (Object) - Time window
- `start` (Date) - Start time
- `end` (Date) - End time
- `webhookURL` (string) - Webhook URL (must be valid URI)
- `apiConfig` (Object) - API configuration
- `enabled` (boolean) - Enable API access
- `rateLimit` (number) - API rate limit (min: 1)
- `permissions` (Object) - Client permissions
- `modify` (boolean) - Can modify settings
- `delete` (boolean) - Can delete data
- `createOrders` (boolean) - Can create orders
- `viewOrders` (boolean) - Can view orders
- `orderSettings` (Object) - Order settings
- `autoAccept` (boolean) - Auto-accept orders
- `autoAssign` (boolean) - Auto-assign workers
- `maxOrdersPerHour` (number) - Max orders per hour (min: 1)
- `preparationTime` (number) - Preparation time (min: 1)
- `deliveryRadius` (number) - Delivery radius (min: 0)
**Example:**
```javascript
const updatedClient = await dispatch9.updateClient('507f1f77bcf86cd799439011', {
name: 'Acme Corp (Updated)',
websiteURL: 'https://www.newacmecorp.com',
orderSettings: {
maxOrdersPerHour: 30,
deliveryRadius: 20
}
});
```
#### `getClientById(clientId, options)`
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 options
- `populate` (string) - Comma-separated list of fields to populate
- Available fields: `addresses`, `primaryAddress`, `billingAddress`
- `includeStats` (boolean) - Include client statistics (order count, total revenue, etc.)
**Returns:** Promise<Client> - Complete client details
**Examples:**
```javascript
// 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');
}
```
### Client Authentication
The SDK now supports comprehensive client authentication with both email and phone number login capabilities.
#### `loginClient(credentials)`
Authenticate a client using email address or phone number with password.
**Parameters:**
- `credentials` (Object, required) - Login credentials
- `identifier` (string, required) - Email address or phone number
- `password` (string, required) - Client password
- `providerId` (string, optional) - Provider ID for multi-provider clients
**Returns:** Promise<Object> - Authentication result with tokens and client data
**Examples:**
```javascript
// 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)
}
```
#### `selectClientProvider(selection)`
Select a provider for clients with multiple provider access.
**Parameters:**
- `selection` (Object, required) - Provider selection data
- `clientId` (string, required) - Client ID (ObjectId format)
- `providerId` (string, required) - Provider ID to select (ObjectId format)
**Returns:** Promise<Object> - Authentication result with tokens
**Examples:**
```javascript
// 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
}
```
#### `verifyClientLoginOTP(verification)`
Verify OTP for first-time client login security.
**Parameters:**
- `verification` (Object, required) - OTP verification data
- `clientId` (string, required) - Client ID (ObjectId format)
- `otp` (string, required) - 6-digit OTP code
**Returns:** Promise<Object> - Verification result with authentication tokens
**Examples:**
```javascript
// 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
}
```
#### `getClientProfile()`
Get the current authenticated client's profile information.
**Returns:** Promise<Object> - Client profile data
**Examples:**
```javascript
// 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');
}
```
### Complete Authentication Flow Examples
**Basic Email/Phone Login:**
```javascript
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:**
```javascript
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:**
```javascript
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');
```
## Error Handling
The SDK provides detailed error messages for validation and API errors:
```javascript
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"
}
```
### Common Error Types
- **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
## Environment Variables
Create a `.env` file in your project root:
```env
DISPATCH9_API_KEY=your-api-key-here
DISPATCH9_BASE_URL=https://api.dispatch9.com
```
Then use in your code:
```javascript
require('dotenv').config();
const dispatch9 = new Dispatch9Client({
apiKey: process.env.DISPATCH9_API_KEY,
baseURL: process.env.DISPATCH9_BASE_URL
});
```
## Examples
See the `examples/` directory for complete working examples:
- `basic-usage.js` - Basic SDK usage with all 4 core operations
- `order-management.js` - Order creation and management
- `client-management.js` - Client operations and settings
- `complete-workflow.js` - End-to-end business workflow
- `address-creation-example.js` - Complete address creation workflow (shows how to create addresses before orders)
## Testing
Run the test suite:
```bash
npm test
```
## API Key Requirements
Your API key must have the following permissions:
- `orders.create` - Create orders
- `orders.update` - Update orders
- `clients.create` - Create clients (may require elevated permissions)
- `clients.read` - View clients
- `clients.update` - Update clients (may require elevated permissions)
## Support
- **Documentation**: [https://docs.dispatch9.com](https://docs.dispatch9.com)
- **API Reference**: [https://api.dispatch9.com/docs](https://api.dispatch9.com/docs)
- **Issues**: [GitHub Issues](https://github.com/dispatch9/client-sdk/issues)
- **Support**: support@dispatch9.com
## License
MIT License. See [LICENSE](LICENSE) file for details.