@rashidazarang/servicefusion-adapter
v1.0.0
Published
ServiceFusion REST API adapter for field service management integrations
Maintainers
Readme
ServiceFusion Adapter
A TypeScript/JavaScript adapter for integrating with ServiceFusion's REST API. This adapter provides a clean, promise-based interface for field service management operations.
🚀 Features
- Full REST API Coverage for customers, jobs, estimates, invoices, and more
- Built-in Rate Limiting (0.5 requests/second as per ServiceFusion limits)
- OAuth 2.0 Authentication with automatic token refresh
- Batch Operations for efficient data synchronization
- TypeScript Support with complete type definitions
- Smart Retry Logic with exponential backoff
- Webhook Support for real-time updates
- Comprehensive Error Handling
📦 Installation
npm install @rashidazarang/servicefusion-adapteror
yarn add @rashidazarang/servicefusion-adapter🔧 Configuration
import { ServiceFusionAdapter } from '@rashidazarang/servicefusion-adapter';
const adapter = new ServiceFusionAdapter({
clientId: 'your-client-id',
clientSecret: 'your-client-secret',
baseUrl: 'https://api.servicefusion.com/v1',
options: {
rateLimit: 0.5, // requests per second (default: 0.5)
retryAttempts: 3, // number of retry attempts (default: 3)
timeout: 30000, // request timeout in ms (default: 30000)
autoRefreshToken: true // auto refresh OAuth token (default: true)
}
});
// Initialize and authenticate
await adapter.connect();📖 Usage Examples
Basic Operations
Get All Customers
const customers = await adapter.getCustomers({
limit: 100,
offset: 0
});
console.log(`Found ${customers.length} customers`);Create a Job
const job = await adapter.createJob({
customerId: 'customer-123',
jobType: 'Maintenance',
priority: 'High',
scheduledDate: new Date('2025-01-15'),
description: 'HVAC maintenance',
address: {
street: '123 Main St',
city: 'Austin',
state: 'TX',
zipCode: '78701'
}
});
console.log(`Created job: ${job.id}`);Update Job Status
const updatedJob = await adapter.updateJob(job.id, {
status: 'In Progress',
assignedTo: 'tech-456',
notes: 'Technician en route'
});Advanced Features
Batch Sync Jobs
// Sync jobs from external system
const externalJobs = await getExternalJobs(); // Your external data
const syncResults = await adapter.batchSyncJobs(externalJobs, {
matchBy: 'externalId', // or 'customerId'
updateExisting: true,
createNew: true
});
console.log(`Created: ${syncResults.created}`);
console.log(`Updated: ${syncResults.updated}`);
console.log(`Errors: ${syncResults.errors}`);Search with Filters
// Find all high-priority jobs for today
const urgentJobs = await adapter.searchJobs({
priority: 'High',
scheduledDate: new Date(),
status: ['Open', 'In Progress']
});
// Find customers by location
const localCustomers = await adapter.searchCustomers({
city: 'Austin',
state: 'TX',
type: 'Commercial'
});Pagination Helper
// Automatically handle pagination
const allJobs = await adapter.getAllJobs({
status: 'Open',
pageSize: 100
});
console.log(`Total open jobs: ${allJobs.length}`);Webhook Management
// Register a webhook
const webhook = await adapter.createWebhook({
url: 'https://your-app.com/webhooks/servicefusion',
events: ['job.created', 'job.updated', 'job.completed'],
secret: 'your-webhook-secret'
});
// List webhooks
const webhooks = await adapter.getWebhooks();
// Delete webhook
await adapter.deleteWebhook(webhook.id);Error Handling
try {
const job = await adapter.getJob('invalid-id');
} catch (error) {
if (error.code === 'NOT_FOUND') {
console.error('Job not found');
} else if (error.code === 'RATE_LIMIT_EXCEEDED') {
console.error('Rate limit hit, retry after:', error.retryAfter);
} else if (error.code === 'UNAUTHORIZED') {
console.error('Authentication failed');
}
}🏗️ API Reference
Authentication
connect(): Promise<void>- Authenticate and get access tokendisconnect(): Promise<void>- Revoke token and cleanuprefreshToken(): Promise<void>- Manually refresh OAuth token
Customer Operations
getCustomers(options?: PaginationOptions): Promise<Customer[]>getCustomer(id: string): Promise<Customer>createCustomer(data: CustomerInput): Promise<Customer>updateCustomer(id: string, data: Partial<CustomerInput>): Promise<Customer>deleteCustomer(id: string): Promise<void>searchCustomers(filters: CustomerFilters): Promise<Customer[]>
Job Operations
getJobs(options?: JobFilters): Promise<Job[]>getJob(id: string): Promise<Job>createJob(data: JobInput): Promise<Job>updateJob(id: string, data: Partial<JobInput>): Promise<Job>deleteJob(id: string): Promise<void>searchJobs(filters: JobSearchFilters): Promise<Job[]>batchSyncJobs(jobs: JobInput[], options: SyncOptions): Promise<SyncResult>
Estimate Operations
getEstimates(jobId?: string): Promise<Estimate[]>getEstimate(id: string): Promise<Estimate>createEstimate(data: EstimateInput): Promise<Estimate>updateEstimate(id: string, data: Partial<EstimateInput>): Promise<Estimate>convertEstimateToJob(id: string): Promise<Job>
Invoice Operations
getInvoices(filters?: InvoiceFilters): Promise<Invoice[]>getInvoice(id: string): Promise<Invoice>createInvoice(data: InvoiceInput): Promise<Invoice>updateInvoice(id: string, data: Partial<InvoiceInput>): Promise<Invoice>sendInvoice(id: string, email: string): Promise<void>
Technician Operations
getTechnicians(options?: PaginationOptions): Promise<Technician[]>getTechnician(id: string): Promise<Technician>getSchedule(technicianId: string, date: Date): Promise<Schedule>assignJob(jobId: string, technicianId: string): Promise<Job>
🔒 Rate Limiting
ServiceFusion enforces a rate limit of 0.5 requests per second (30 requests per minute). This adapter automatically handles rate limiting with:
- Built-in request throttling using Bottleneck
- Automatic queuing of requests
- Configurable rate limits
- Retry logic with exponential backoff for rate limit errors
// Configure custom rate limiting
const adapter = new ServiceFusionAdapter({
// ... credentials
options: {
rateLimit: 0.3, // Even more conservative: 0.3 requests per second
retryAttempts: 5,
retryDelay: 2000, // Initial retry delay in ms
maxConcurrent: 2 // Max concurrent requests
}
});🧪 Testing
# Run all tests
npm test
# Run tests in watch mode
npm run test:watch
# Run with coverage
npm run test:coverage🤝 Contributing
We welcome contributions! Please see our Contributing Guide for details.
📝 License
This project is licensed under the MIT License - see the LICENSE file for details.
🆘 Support
- Documentation: Full API Documentation
- Issues: GitHub Issues
- Discussions: GitHub Discussions
🔗 Related Projects
- PMIP Core - The main integration platform
- PropertyWare Adapter - PropertyWare SOAP API adapter
📈 Roadmap
- [ ] GraphQL API support
- [ ] Bulk import/export utilities
- [ ] Advanced caching strategies
- [ ] Real-time event streaming
- [ ] CLI tools for testing
- [ ] Terraform provider for infrastructure
⚠️ Disclaimer
This adapter is not officially affiliated with or endorsed by ServiceFusion. ServiceFusion is a registered trademark of ServiceFusion, Inc.
