Integration Examples

Real-world integration examples for common use cases with the Flotac API.

Customer Portal Integration

Build a customer-facing portal where customers can view their vehicles and service history.

Customer Dashboard

const BASE_URL = 'https://chwjbbvcqsxxgaytqlix.supabase.co/functions/v1/api-gateway';

class CustomerPortal {
  constructor(apiKey) {
    this.apiKey = apiKey;
    this.headers = {
      'Authorization': `Bearer ${apiKey}`,
      'Content-Type': 'application/json'
    };
  }

  async getCustomerDashboard(customerId) {
    // Fetch customer details, vehicles, and recent service orders in parallel
    const [customer, vehicles, serviceOrders, invoices] = await Promise.all([
      this.fetch(`/api/v1/customers/${customerId}`),
      this.fetch(`/api/v1/vehicles?customer_id=${customerId}`),
      this.fetch(`/api/v1/service-orders?customer_id=${customerId}&sort_by=created_at&sort_order=desc&page_size=10`),
      this.fetch(`/api/v1/invoices?customer_id=${customerId}&status=sent&sort_by=due_date`)
    ]);

    return {
      customer: customer.data,
      vehicles: vehicles.data,
      recentServiceOrders: serviceOrders.data,
      pendingInvoices: invoices.data.filter(inv => inv.payment_status !== 'paid')
    };
  }

  async fetch(endpoint) {
    const response = await fetch(`${BASE_URL}${endpoint}`, { headers: this.headers });
    return response.json();
  }
}

// Usage
const portal = new CustomerPortal(process.env.FLOTAC_API_KEY);
const dashboard = await portal.getCustomerDashboard('customer-uuid');

Fleet Management System

Track all vehicles in a fleet with maintenance schedules and alerts.

Fleet Monitor

class FleetManager {
  constructor(apiKey) {
    this.apiKey = apiKey;
    this.baseUrl = 'https://chwjbbvcqsxxgaytqlix.supabase.co/functions/v1/api-gateway';
  }

  async getFleetOverview() {
    const vehicles = await this.fetchAll('/api/v1/vehicles?status=active');

    const fleetStats = {
      totalVehicles: vehicles.length,
      byStatus: this.groupBy(vehicles, 'status'),
      byMake: this.groupBy(vehicles, 'make'),
      maintenanceDue: []
    };

    // Check for vehicles needing maintenance
    for (const vehicle of vehicles) {
      const serviceOrders = await this.fetch(
        `/api/v1/service-orders?vehicle_id=${vehicle.id}&status=completed&sort_by=completed_at&sort_order=desc&page_size=1`
      );

      if (serviceOrders.data.length > 0) {
        const lastService = new Date(serviceOrders.data[0].completed_at);
        const daysSinceService = Math.floor((Date.now() - lastService) / (1000 * 60 * 60 * 24));

        if (daysSinceService > 90) {
          fleetStats.maintenanceDue.push({
            vehicle,
            daysSinceService,
            lastServiceDate: lastService
          });
        }
      }
    }

    return fleetStats;
  }

  async schedulePreventiveMaintenance(vehicleId, description, scheduledDate) {
    const vehicle = await this.fetch(`/api/v1/vehicles/${vehicleId}`);

    return this.post('/api/v1/service-orders', {
      customer_id: vehicle.data.customer_id,
      vehicle_id: vehicleId,
      status: 'pending',
      priority: 'normal',
      description: description,
      scheduled_start: scheduledDate,
      estimated_hours: 2
    });
  }

  groupBy(array, key) {
    return array.reduce((acc, item) => {
      const value = item[key] || 'unknown';
      acc[value] = (acc[value] || 0) + 1;
      return acc;
    }, {});
  }

  async fetch(endpoint) {
    const response = await fetch(`${this.baseUrl}${endpoint}`, {
      headers: {
        'Authorization': `Bearer ${this.apiKey}`,
        'Content-Type': 'application/json'
      }
    });
    return response.json();
  }

  async post(endpoint, data) {
    const response = await fetch(`${this.baseUrl}${endpoint}`, {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${this.apiKey}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(data)
    });
    return response.json();
  }

  async fetchAll(endpoint) {
    let allData = [];
    let page = 1;
    let hasMore = true;

    while (hasMore) {
      const response = await this.fetch(`${endpoint}&page=${page}&page_size=100`);
      allData.push(...response.data);
      hasMore = response.meta.pagination.hasNextPage;
      page++;
    }

    return allData;
  }
}

Inventory Management

Monitor parts inventory and automate reorder alerts.

Low Stock Monitor

class InventoryMonitor {
  constructor(apiKey) {
    this.apiKey = apiKey;
    this.baseUrl = 'https://chwjbbvcqsxxgaytqlix.supabase.co/functions/v1/api-gateway';
  }

  async checkLowStockParts() {
    const response = await fetch(
      `${this.baseUrl}/api/v1/parts?low_stock=true&track_inventory=true`,
      {
        headers: { 'Authorization': `Bearer ${this.apiKey}` }
      }
    );

    const result = await response.json();

    if (!result.success) {
      throw new Error(result.error.message);
    }

    return result.data.map(part => ({
      partNumber: part.part_number,
      name: part.name,
      currentStock: part.quantity_on_hand,
      reorderPoint: part.reorder_point,
      suggestedOrderQty: Math.max(part.reorder_point * 2 - part.quantity_on_hand, 0),
      lastCost: part.cost,
      estimatedReorderCost: Math.max(part.reorder_point * 2 - part.quantity_on_hand, 0) * part.cost
    }));
  }

  async generateReorderReport() {
    const lowStockParts = await this.checkLowStockParts();

    const report = {
      generatedAt: new Date().toISOString(),
      totalPartsLowStock: lowStockParts.length,
      totalEstimatedCost: lowStockParts.reduce((sum, p) => sum + p.estimatedReorderCost, 0),
      parts: lowStockParts.sort((a, b) =>
        (a.currentStock / a.reorderPoint) - (b.currentStock / b.reorderPoint)
      )
    };

    return report;
  }
}

// Usage
const monitor = new InventoryMonitor(process.env.FLOTAC_API_KEY);
const report = await monitor.generateReorderReport();

console.log(`Low Stock Alert: ${report.totalPartsLowStock} parts need reordering`);
console.log(`Estimated Reorder Cost: $${report.totalEstimatedCost.toFixed(2)}`);

report.parts.forEach(part => {
  console.log(`- ${part.partNumber}: ${part.currentStock}/${part.reorderPoint} (Order: ${part.suggestedOrderQty})`);
});

Automated Invoicing

Automatically generate and send invoices when service orders are completed.

Invoice Automation

class InvoiceAutomation {
  constructor(apiKey) {
    this.apiKey = apiKey;
    this.baseUrl = 'https://chwjbbvcqsxxgaytqlix.supabase.co/functions/v1/api-gateway';
  }

  async processCompletedServiceOrders() {
    // Find completed service orders without invoices
    const serviceOrders = await this.fetch(
      '/api/v1/service-orders?status=completed&sort_by=completed_at&sort_order=desc'
    );

    const results = [];

    for (const order of serviceOrders.data) {
      // Check if invoice already exists
      const existingInvoices = await this.fetch(
        `/api/v1/invoices?service_order_id=${order.id}`
      );

      if (existingInvoices.data.length === 0) {
        // Create invoice for this service order
        const invoice = await this.createInvoiceForOrder(order);
        results.push({
          serviceOrderId: order.id,
          orderNumber: order.order_number,
          invoiceId: invoice.data.id,
          invoiceNumber: invoice.data.invoice_number,
          status: 'created'
        });
      }
    }

    return results;
  }

  async createInvoiceForOrder(serviceOrder) {
    const dueDate = new Date();
    dueDate.setDate(dueDate.getDate() + 30);

    const invoice = {
      customer_id: serviceOrder.customer_id,
      service_order_id: serviceOrder.id,
      status: 'draft',
      invoice_date: new Date().toISOString().split('T')[0],
      due_date: dueDate.toISOString().split('T')[0],
      subtotal: serviceOrder.actual_cost || serviceOrder.estimated_cost || 0,
      tax_rate: 0.08,
      tax_amount: (serviceOrder.actual_cost || serviceOrder.estimated_cost || 0) * 0.08,
      total_amount: (serviceOrder.actual_cost || serviceOrder.estimated_cost || 0) * 1.08,
      notes: `Invoice for Service Order: ${serviceOrder.order_number}`
    };

    return this.post('/api/v1/invoices', invoice);
  }

  async sendInvoice(invoiceId) {
    return this.put(`/api/v1/invoices/${invoiceId}`, {
      status: 'sent',
      sent_at: new Date().toISOString()
    });
  }

  async fetch(endpoint) {
    const response = await fetch(`${this.baseUrl}${endpoint}`, {
      headers: {
        'Authorization': `Bearer ${this.apiKey}`,
        'Content-Type': 'application/json'
      }
    });
    return response.json();
  }

  async post(endpoint, data) {
    const response = await fetch(`${this.baseUrl}${endpoint}`, {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${this.apiKey}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(data)
    });
    return response.json();
  }

  async put(endpoint, data) {
    const response = await fetch(`${this.baseUrl}${endpoint}`, {
      method: 'PUT',
      headers: {
        'Authorization': `Bearer ${this.apiKey}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(data)
    });
    return response.json();
  }
}

Python SDK Example

Complete Python client for the Flotac API.

import os
import requests
from typing import Optional, Dict, Any, List
from datetime import datetime, timedelta

class FlotacClient:
    def __init__(self, api_key: str = None):
        self.api_key = api_key or os.environ.get('FLOTAC_API_KEY')
        self.base_url = 'https://chwjbbvcqsxxgaytqlix.supabase.co/functions/v1/api-gateway'
        self.session = requests.Session()
        self.session.headers.update({
            'Authorization': f'Bearer {self.api_key}',
            'Content-Type': 'application/json'
        })

    def _request(self, method: str, endpoint: str, **kwargs) -> Dict[str, Any]:
        url = f'{self.base_url}{endpoint}'
        response = self.session.request(method, url, **kwargs)
        result = response.json()

        if not result.get('success'):
            raise FlotacAPIError(
                result.get('error', {}).get('code', 'UNKNOWN'),
                result.get('error', {}).get('message', 'Unknown error')
            )

        return result

    # Customers
    def list_customers(self, **params) -> List[Dict]:
        return self._request('GET', '/api/v1/customers', params=params)['data']

    def get_customer(self, customer_id: str) -> Dict:
        return self._request('GET', f'/api/v1/customers/{customer_id}')['data']

    def create_customer(self, data: Dict) -> Dict:
        return self._request('POST', '/api/v1/customers', json=data)['data']

    def update_customer(self, customer_id: str, data: Dict) -> Dict:
        return self._request('PUT', f'/api/v1/customers/{customer_id}', json=data)['data']

    # Vehicles
    def list_vehicles(self, customer_id: str = None, **params) -> List[Dict]:
        if customer_id:
            params['customer_id'] = customer_id
        return self._request('GET', '/api/v1/vehicles', params=params)['data']

    def create_vehicle(self, data: Dict) -> Dict:
        return self._request('POST', '/api/v1/vehicles', json=data)['data']

    # Service Orders
    def list_service_orders(self, **params) -> List[Dict]:
        return self._request('GET', '/api/v1/service-orders', params=params)['data']

    def create_service_order(self, data: Dict) -> Dict:
        return self._request('POST', '/api/v1/service-orders', json=data)['data']

    def update_service_order(self, order_id: str, data: Dict) -> Dict:
        return self._request('PUT', f'/api/v1/service-orders/{order_id}', json=data)['data']

    # Invoices
    def list_invoices(self, **params) -> List[Dict]:
        return self._request('GET', '/api/v1/invoices', params=params)['data']

    def create_invoice(self, data: Dict) -> Dict:
        return self._request('POST', '/api/v1/invoices', json=data)['data']

    # Parts
    def list_parts(self, **params) -> List[Dict]:
        return self._request('GET', '/api/v1/parts', params=params)['data']

    def get_low_stock_parts(self) -> List[Dict]:
        return self.list_parts(low_stock=True, track_inventory=True)


class FlotacAPIError(Exception):
    def __init__(self, code: str, message: str):
        self.code = code
        self.message = message
        super().__init__(f'{code}: {message}')


# Usage
if __name__ == '__main__':
    client = FlotacClient()

    # List active customers
    customers = client.list_customers(status='active')
    print(f'Found {len(customers)} active customers')

    # Create a new customer
    new_customer = client.create_customer({
        'name': 'New Fleet Co',
        'customer_type': 'fleet',
        'email': 'info@newfleet.com',
        'status': 'active'
    })
    print(f'Created customer: {new_customer["id"]}')

    # Check low stock parts
    low_stock = client.get_low_stock_parts()
    print(f'Low stock alert: {len(low_stock)} parts need reordering')

Webhook Integration

Set up webhooks to receive real-time updates.

Webhook Handler (Express.js)

const express = require('express');
const crypto = require('crypto');

const app = express();
app.use(express.json());

const WEBHOOK_SECRET = process.env.FLOTAC_WEBHOOK_SECRET;

// Verify webhook signature
function verifySignature(payload, signature) {
  const hash = crypto
    .createHmac('sha256', WEBHOOK_SECRET)
    .update(JSON.stringify(payload))
    .digest('hex');
  return `sha256=${hash}` === signature;
}

// Webhook endpoint
app.post('/webhooks/flotac', (req, res) => {
  const signature = req.headers['x-flotac-signature'];

  if (!verifySignature(req.body, signature)) {
    return res.status(401).json({ error: 'Invalid signature' });
  }

  const { event, data, timestamp } = req.body;

  switch (event) {
    case 'service_order.completed':
      handleServiceOrderCompleted(data);
      break;
    case 'invoice.paid':
      handleInvoicePaid(data);
      break;
    case 'vehicle.created':
      handleVehicleCreated(data);
      break;
    case 'part.low_stock':
      handleLowStock(data);
      break;
    default:
      console.log(`Unhandled event: ${event}`);
  }

  res.json({ received: true });
});

function handleServiceOrderCompleted(data) {
  console.log(`Service order completed: ${data.order_number}`);
  // Trigger invoice generation
  // Send customer notification
}

function handleInvoicePaid(data) {
  console.log(`Invoice paid: ${data.invoice_number}`);
  // Update accounting system
  // Send receipt to customer
}

function handleVehicleCreated(data) {
  console.log(`New vehicle registered: ${data.vin}`);
  // Schedule initial inspection
}

function handleLowStock(data) {
  console.log(`Low stock alert: ${data.part_number}`);
  // Send notification to purchasing team
  // Create purchase order
}

app.listen(3000, () => {
  console.log('Webhook server running on port 3000');
});

Next Steps