# Integration Examples Real-world examples of payroll reporting and employee sync with Audit1 # Integration Examples Complete, production-ready code examples for submitting payroll data, syncing employees, and handling webhook events. ## 🚀 Quick Start Examples ### 1. Submit Payroll (cURL) Copy-paste ready example: ```bash export AUDIT1_API_KEY="audit1_test_sk_your_key_here" curl -X POST https://api.audit1.info/api/v1/payroll/reports \ -H "Authorization: Bearer $AUDIT1_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "employer_id": "EMP_12345", "period_start": "2024-01-01", "period_end": "2024-03-31", "employees": [ { "employee_id": "EE_001", "name": "John Smith", "classification_code": "8810", "hours_worked": 520, "gross_wages": 26000 } ], "payroll_data": { "total_payroll": 26000, "total_hours": 520, "officer_payroll": 0, "subcontractor_payments": 0 } }' ``` ### 2. Sync Employee (cURL) ```bash curl -X POST https://api.audit1.info/api/v1/employees/sync \ -H "Authorization: Bearer $AUDIT1_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "employer_id": "EMP_12345", "employees": [ { "employee_id": "EE_NEW_001", "action": "hire", "name": "Jane Doe", "hire_date": "2024-01-15", "classification_code": "8810", "hourly_rate": 30.00 } ] }' ``` ### 3. Check File Status (cURL) ```bash curl -X GET https://api.audit1.info/api/v1/files/status/FILE_123 \ -H "Authorization: Bearer $AUDIT1_API_KEY" ``` *** ## Complete Working Examples ### Submit Quarterly Payroll Report ```bash curl -X POST https://api.audit1.info/api/v1/payroll/reports \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "employer_id": "EMP_12345", "period_start": "2024-01-01", "period_end": "2024-03-31", "employees": [ { "employee_id": "EE_001", "name": "John Smith", "classification_code": "8810", "hours_worked": 520, "gross_wages": 26000, "overtime_hours": 20, "overtime_pay": 1500 }, { "employee_id": "EE_002", "name": "Jane Doe", "classification_code": "5403", "hours_worked": 480, "gross_wages": 38400 } ], "payroll_data": { "total_payroll": 64400, "total_hours": 1000, "officer_payroll": 0, "subcontractor_payments": 0 } }' ``` **Response**: ```json { "message": "Payroll report submitted successfully", "report_id": "PR_1705932000123", "status": "processing", "employee_count": 2, "estimated_processing_time": "5-10 minutes" } ``` *** ## Node.js Example ### Production-Ready Implementation ```javascript const axios = require('axios'); // Configure client with retry logic class Audit1Client { constructor(apiKey) { this.client = axios.create({ baseURL: 'https://api.audit1.info/api/v1', headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json' }, timeout: 30000 // 30 second timeout }); } async submitPayrollReport(employerId, periodStart, periodEnd, employees) { // Validate inputs if (!employerId || !periodStart || !periodEnd || !employees?.length) { throw new Error('Missing required parameters'); } // Calculate totals const payrollData = { total_payroll: employees.reduce((sum, e) => sum + (e.gross_wages || 0), 0), total_hours: employees.reduce((sum, e) => sum + (e.hours_worked || 0), 0), officer_payroll: employees .filter(e => e.is_officer) .reduce((sum, e) => sum + (e.gross_wages || 0), 0), subcontractor_payments: 0 }; const payload = { employer_id: employerId, period_start: periodStart, period_end: periodEnd, employees: employees, payroll_data: payrollData }; try { const response = await this.retryRequest(() => this.client.post('/payroll/reports', payload) ); console.log('Payroll report submitted:', { report_id: response.data.report_id, status: response.data.status, environment: response.data.environment, employee_count: response.data.employee_count }); return response.data; } catch (error) { console.error('Failed to submit payroll:', { error: error.message, status: error.response?.status, details: error.response?.data }); throw error; } } async retryRequest(fn, maxRetries = 3) { let lastError; for (let i = 0; i < maxRetries; i++) { try { return await fn(); } catch (error) { lastError = error; // Don't retry client errors (except rate limits) const status = error.response?.status; if (status >= 400 && status < 500 && status !== 429) { throw error; } // Calculate exponential backoff const backoffMs = Math.min(Math.pow(2, i) * 1000, 30000); console.log(`Retry ${i + 1}/${maxRetries} after ${backoffMs}ms`); await new Promise(resolve => setTimeout(resolve, backoffMs)); } } throw lastError; } } // Usage Example const client = new Audit1Client(process.env.AUDIT1_API_KEY); const employees = [ { employee_id: 'EE_001', name: 'John Smith', classification_code: '8810', hours_worked: 520, gross_wages: 26000, overtime_hours: 20, overtime_pay: 1500 }, { employee_id: 'EE_002', name: 'Jane Doe', classification_code: '5403', hours_worked: 480, gross_wages: 38400, is_officer: true } ]; // Submit payroll client.submitPayrollReport( 'EMP_12345', '2024-01-01', '2024-03-31', employees ).then(result => { console.log('Success:', result.report_id); }).catch(error => { console.error('Failed:', error.message); process.exit(1); }); ``` ### Environment Configuration ```javascript // config.js module.exports = { development: { apiKey: process.env.AUDIT1_TEST_KEY, environment: 'sandbox' }, production: { apiKey: process.env.AUDIT1_LIVE_KEY, environment: 'production' } }; // app.js const config = require('./config'); const env = process.env.NODE_ENV || 'development'; const client = new Audit1Client(config[env].apiKey); ``` *** ## Python Example ### Production-Ready Implementation ```python import os import time import requests from typing import List, Dict, Optional from datetime import datetime class Audit1Client: def __init__(self, api_key: str): self.base_url = 'https://api.audit1.info/api/v1' self.headers = { 'Authorization': f'Bearer {api_key}', 'Content-Type': 'application/json' } self.session = requests.Session() self.session.headers.update(self.headers) def submit_payroll_report( self, employer_id: str, period_start: str, period_end: str, employees: List[Dict] ) -> Dict: """Submit payroll report with automatic retry logic.""" # Calculate totals total_payroll = sum(e.get('gross_wages', 0) for e in employees) total_hours = sum(e.get('hours_worked', 0) for e in employees) officer_payroll = sum( e.get('gross_wages', 0) for e in employees if e.get('is_officer', False) ) payload = { 'employer_id': employer_id, 'period_start': period_start, 'period_end': period_end, 'employees': employees, 'payroll_data': { 'total_payroll': total_payroll, 'total_hours': total_hours, 'officer_payroll': officer_payroll, 'subcontractor_payments': 0 } } return self._retry_request( lambda: self.session.post( f'{self.base_url}/payroll/reports', json=payload ) ) def _retry_request(self, fn, max_retries: int = 3) -> Dict: """Execute request with exponential backoff retry.""" last_error = None for attempt in range(max_retries): try: response = fn() response.raise_for_status() return response.json() except requests.exceptions.HTTPError as e: last_error = e status = e.response.status_code # Don't retry client errors (except rate limits) if 400 <= status < 500 and status != 429: raise # Calculate backoff backoff = min(2 ** attempt, 30) print(f'Retry {attempt + 1}/{max_retries} after {backoff}s') time.sleep(backoff) raise last_error # Usage if __name__ == '__main__': client = Audit1Client(os.getenv('AUDIT1_API_KEY')) employees = [ { 'employee_id': 'EE_001', 'name': 'John Smith', 'classification_code': '8810', 'hours_worked': 520, 'gross_wages': 26000 } ] try: result = client.submit_payroll_report( employer_id='EMP_12345', period_start='2024-01-01', period_end='2024-03-31', employees=employees ) print(f"Success: {result['report_id']}") except Exception as e: print(f"Failed: {str(e)}") exit(1) ``` *** ## Common Integration Patterns ### Pattern 1: Batch Processing ```javascript // Process payroll reports in batches async function processBatchPayroll(reports, batchSize = 10) { const results = []; for (let i = 0; i < reports.length; i += batchSize) { const batch = reports.slice(i, i + batchSize); // Process batch in parallel const batchResults = await Promise.allSettled( batch.map(report => client.submitPayrollReport( report.employer_id, report.period_start, report.period_end, report.employees )) ); results.push(...batchResults); // Rate limit: wait between batches if (i + batchSize < reports.length) { await new Promise(resolve => setTimeout(resolve, 1000)); } } return results; } ``` ### Pattern 2: Webhook Verification ```javascript const crypto = require('crypto'); function verifyWebhookSignature(payload, signature, secret) { const expectedSignature = crypto .createHmac('sha256', secret) .update(JSON.stringify(payload)) .digest('hex'); return crypto.timingSafeEqual( Buffer.from(signature), Buffer.from(expectedSignature) ); } // Express endpoint app.post('/webhooks/audit1', (req, res) => { const signature = req.headers['x-audit1-signature']; const isValid = verifyWebhookSignature( req.body, signature, process.env.WEBHOOK_SECRET ); if (!isValid) { return res.status(401).json({ error: 'Invalid signature' }); } // Process webhook console.log('Webhook received:', req.body.event_type); res.status(200).json({ received: true }); }); ``` ### Pattern 3: Idempotency ```javascript // Track processed reports to prevent duplicates const processedReports = new Set(); async function submitPayrollIdempotent(data) { const idempotencyKey = `${data.employer_id}-${data.period_start}-${data.period_end}`; if (processedReports.has(idempotencyKey)) { console.log('Report already processed:', idempotencyKey); return { skipped: true }; } const result = await client.submitPayrollReport( data.employer_id, data.period_start, data.period_end, data.employees ); processedReports.add(idempotencyKey); return result; } ``` *** ## Node.js Example ```javascript const submitPayrollReport = async (employerId, periodStart, periodEnd, employees) => { const apiKey = process.env.AUDIT1_API_KEY; const payload = { employer_id: employerId, period_start: periodStart, period_end: periodEnd, employees: employees, payroll_data: { total_payroll: employees.reduce((sum, e) => sum + e.gross_wages, 0), total_hours: employees.reduce((sum, e) => sum + e.hours_worked, 0), officer_payroll: 0, subcontractor_payments: 0 } }; const response = await fetch('https://api.audit1.info/api/v1/payroll/reports', { method: 'POST', headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${await response.text()}`); } return await response.json(); }; // Usage const employees = [ { employee_id: 'EE_001', name: 'John Smith', classification_code: '8810', hours_worked: 520, gross_wages: 26000 } ]; const result = await submitPayrollReport('EMP_12345', '2024-01-01', '2024-03-31', employees); console.log('Report ID:', result.report_id); ``` *** ## Python Example ```python import requests import os def submit_payroll_report(employer_id, period_start, period_end, employees): api_key = os.environ['AUDIT1_API_KEY'] total_payroll = sum(e['gross_wages'] for e in employees) total_hours = sum(e['hours_worked'] for e in employees) payload = { 'employer_id': employer_id, 'period_start': period_start, 'period_end': period_end, 'employees': employees, 'payroll_data': { 'total_payroll': total_payroll, 'total_hours': total_hours, 'officer_payroll': 0, 'subcontractor_payments': 0 } } headers = { 'Authorization': f'Bearer {api_key}', 'Content-Type': 'application/json' } response = requests.post( 'https://api.audit1.info/api/v1/payroll/reports', headers=headers, json=payload ) response.raise_for_status() return response.json() # Usage employees = [ { 'employee_id': 'EE_001', 'name': 'John Smith', 'classification_code': '8810', 'hours_worked': 520, 'gross_wages': 26000 } ] result = submit_payroll_report('EMP_12345', '2024-01-01', '2024-03-31', employees) print(f"Report ID: {result['report_id']}") ``` *** ## Sync Employee Changes ### New Hire ```bash curl -X POST https://api.audit1.info/api/v1/employees/sync \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "employer_id": "EMP_12345", "employees": [ { "employee_id": "EE_NEW_003", "action": "hire", "name": "Alice Williams", "hire_date": "2024-02-15", "classification_code": "8810", "hourly_rate": 28.50 } ] }' ``` ### Termination ```bash curl -X POST https://api.audit1.info/api/v1/employees/sync \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "employer_id": "EMP_12345", "employees": [ { "employee_id": "EE_002", "action": "terminate", "termination_date": "2024-03-31", "reason": "voluntary" } ] }' ``` ### Bulk Changes ```javascript const changes = [ { employee_id: 'EE_NEW_004', action: 'hire', name: 'Charlie Brown', hire_date: '2024-03-01', classification_code: '5403', hourly_rate: 45.00 }, { employee_id: 'EE_001', action: 'update', hourly_rate: 32.00 // Wage increase }, { employee_id: 'EE_002', action: 'terminate', termination_date: '2024-03-15', reason: 'retirement' } ]; const response = await fetch('https://api.audit1.info/api/v1/employees/sync', { method: 'POST', headers: { 'Authorization': `Bearer ${process.env.AUDIT1_API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ employer_id: 'EMP_12345', employees: changes }) }); const result = await response.json(); console.log('Sync ID:', result.sync_id); ``` *** ## Check File Processing Status ```bash curl -X GET https://api.audit1.info/api/v1/files/status/FILE_67890 \ -H "Authorization: Bearer YOUR_API_KEY" ``` **Response**: ```json { "file_id": "FILE_67890", "status": "completed", "uploaded_at": "2024-01-15T14:30:00Z", "processed_at": "2024-01-15T14:35:22Z", "records_total": 150, "records_processed": 150, "records_failed": 0, "validation_errors": [] } ``` ### Polling Implementation ```javascript const pollFileStatus = async (fileId, maxAttempts = 30) => { for (let i = 0; i < maxAttempts; i++) { const response = await fetch( `https://api.audit1.info/api/v1/files/status/${fileId}`, { headers: { 'Authorization': `Bearer ${process.env.AUDIT1_API_KEY}` } } ); const status = await response.json(); if (status.status === 'completed') { return status; } if (status.status === 'failed') { throw new Error('File processing failed'); } // Wait 10 seconds before next poll await new Promise(resolve => setTimeout(resolve, 10000)); } throw new Error('Timeout waiting for file processing'); }; ``` *** ## Handle Webhook Events > Configure webhooks in your portal dashboard (Webhooks section) to **receive** events from Audit1. ### Node.js/Express Webhook Handler ```javascript const express = require('express'); const app = express(); app.use(express.json()); app.post('/webhooks/audit1', (req, res) => { const { event, data } = req.body; switch (event) { case 'audit.completed': console.log('Audit completed:', data.audit_id); console.log('Premium:', data.premium_calculated); break; case 'payroll.processed': console.log('Payroll processed:', data.report_id); console.log('Records:', data.records_processed); break; case 'policy.updated': console.log('Policy updated:', data.policy_id); break; } // Respond quickly (< 5 seconds) res.status(200).send('OK'); }); app.listen(3000); ``` ### Example Webhook Payloads **audit.completed**: ```json { "event": "audit.completed", "timestamp": "2024-01-15T16:45:30Z", "data": { "audit_id": "AUD_123456", "employer_id": "EMP_12345", "status": "completed", "premium_calculated": 15234.50 } } ``` **payroll.processed**: ```json { "event": "payroll.processed", "timestamp": "2024-01-15T14:35:22Z", "data": { "report_id": "PR_1705932000123", "employer_id": "EMP_12345", "status": "completed", "records_processed": 150, "records_failed": 0 } } ``` *** ## Error Handling ### Common Errors **400 Bad Request**: ```json { "error": "Missing required fields", "required": ["employer_id", "period_start", "period_end", "employees", "payroll_data"] } ``` **401 Unauthorized**: ```json { "error": "Unauthorized", "message": "Invalid API key. Get yours from your portal dashboard (API Keys section)" } ``` ### Retry Logic ```javascript async function submitWithRetry(payload, maxRetries = 3) { for (let attempt = 1; attempt <= maxRetries; attempt++) { try { const response = await fetch('https://api.audit1.info/api/v1/payroll/reports', { method: 'POST', headers: { 'Authorization': `Bearer ${process.env.AUDIT1_API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }); if (response.ok) { return await response.json(); } // Don't retry client errors if (response.status >= 400 && response.status < 500) { throw new Error(`Client error: ${response.status}`); } // Retry server errors with backoff console.log(`Attempt ${attempt} failed, retrying...`); await new Promise(resolve => setTimeout(resolve, 1000 * Math.pow(2, attempt))); } catch (error) { if (attempt === maxRetries) throw error; } } } ``` *** ## Best Practices 1. **Secure API Keys**: Use environment variables, never commit to version control 2. **Test with Sandbox Keys**: Use `audit1_test_sk_...` keys for development (same URL, test data) 3. **Validate Data**: Check classification codes, wage amounts before submitting 4. **Handle Errors**: Implement retry logic with exponential backoff 5. **Use Webhooks**: Don't poll - configure webhooks for real-time events *** ## Next Steps * [API Reference](https://docs.audit1.info/api-reference) - Complete endpoint documentation * [Webhooks Guide](https://docs.audit1.info/webhooks-guide) - Set up event notifications * [API Keys Guide](https://docs.audit1.info/api-keys-guide) - Create and manage credentials **Need help?** Email developer-support@audit1.com