API Integration

Complete guide to integrating with the Audit1 API

API Integration Guide

Learn how to integrate directly with the Audit1 API for programmatic payroll submissions and custom integrations.

Overview

The Audit1 Developer API provides programmatic access to submit payroll data, sync employees, manage webhooks, and retrieve audit information. This guide is for developers building custom integrations with Audit1.

When to Use the API

The API is perfect for:

  • 💻 Custom Integrations: Building proprietary connections
  • 🔧 Software Platforms: Embedding Audit1 into your application
  • ⚡ Real-Time Submissions: Submitting payroll as it's processed
  • 🤖 Automation: Creating automated workflows
  • 📊 Custom Reporting: Retrieving audit data programmatically

Alternatives to Consider

Before diving into API integration, consider if these might be easier:

MethodBest ForSetup Complexity
SFTPBatch file uploads🟢 Low
ConnectorsSupported payroll systems🟡 Medium
WebhooksReceiving notifications🟡 Medium
APICustom integration🔴 High
💡

Tip: If you're using Paylocity, UKG, Hartford XactPay, or AccountantsWorld, use our pre-built connectors instead of building custom API integration.

Prerequisites

To use the API, you need:

  • Programming Knowledge: Familiarity with REST APIs
  • Development Environment: Tools to make HTTP requests
  • Audit1 Account: Active organization account
  • API Credentials: Client ID and Client Secret
  • HTTPS Support: Your integration must use secure connections

API Basics

Base URL

Production: https://developer-api.audit1.info/api/v1
Sandbox:    https://developer-api-sandbox.audit1.info/api/v1
🚀

Recommendation: Always develop and test in sandbox before moving to production.

Authentication

Audit1 uses dual-factor authentication with Client ID and Client Secret:

X-Client-ID: audit1_live_cli_a1b2c3d4e5f6...
X-Client-Secret: audit1_live_sec_f6e5d4c3b2a1...

Key Formats

Production Keys:

Client ID:     audit1_live_cli_[32 characters]
Client Secret: audit1_live_sec_[32 characters]

Sandbox Keys:

Client ID:     audit1_test_cli_[32 characters]
Client Secret: audit1_test_sec_[32 characters]

Getting API Credentials

Step 1: Request API Access

Contact Audit1 support to enable API access:

Contact Methods:

  • 📧 Email: [email protected] with subject "API Access Request"
  • 📱 Phone: Call during business hours (Mon-Fri, 9 AM - 5 PM ET)
  • 💬 Admin Portal: Submit through Help → API Access

Information to Provide:

  • Your Audit1 organization ID
  • Use case description
  • Expected API volume (requests per day/month)
  • Developer contact information
  • Sandbox vs. production (or both)

Step 2: Receive Credentials

Audit1 will provide:

Environment: Production
Client ID:     audit1_live_cli_abc123def456...
Client Secret: audit1_live_sec_xyz789uvw012...
Owner ID:      emp_your_org_id_here
Owner Type:    employer (or payroll_company, carrier, etc.)
🔒

Security: Store these securely! Never commit to version control, expose in client-side code, or share publicly.

Step 3: Test Your Credentials

Make a test request to verify authentication:

curl -X GET https://developer-api.audit1.info/api/v1/health \
  -H "X-Client-ID: audit1_live_cli_abc123..." \
  -H "X-Client-Secret: audit1_live_sec_xyz789..."

Expected Response:

{
  "status": "ok",
  "timestamp": "2024-01-15T10:30:00.000Z",
  "message": "API is operational"
}

Core API Endpoints

Health Check

Verify API is operational and your credentials work:

GET /api/v1/health

No authentication required (but include headers to test credentials)

Response:

{
  "status": "ok",
  "timestamp": "2024-01-15T10:30:00.000Z"
}

Submit Payroll Report

Submit payroll data programmatically:

POST /api/v1/payroll/reports

Headers:

X-Client-ID: audit1_live_cli_...
X-Client-Secret: audit1_live_sec_...
Content-Type: application/json

Request Body:

{
  "employer_id": "emp_abc123",
  "pay_period": {
    "start_date": "2024-01-01",
    "end_date": "2024-01-15"
  },
  "employees": [
    {
      "employee_id": "EMP001",
      "first_name": "John",
      "last_name": "Doe",
      "gross_pay": 2500.00,
      "hours_worked": 80,
      "job_classification": "8810",
      "state": "CA"
    },
    {
      "employee_id": "EMP002",
      "first_name": "Jane",
      "last_name": "Smith",
      "gross_pay": 3000.00,
      "hours_worked": 80,
      "job_classification": "8810",
      "state": "CA"
    }
  ]
}

Response:

{
  "success": true,
  "report_id": "rpt_xyz789",
  "status": "processing",
  "employees_count": 2,
  "total_gross_pay": 5500.00,
  "message": "Payroll report submitted successfully"
}

Sync Employees

Synchronize your employee roster:

POST /api/v1/employees/sync

Request Body:

{
  "employer_id": "emp_abc123",
  "employees": [
    {
      "employee_id": "EMP001",
      "first_name": "John",
      "last_name": "Doe",
      "email": "[email protected]",
      "hire_date": "2023-01-15",
      "employment_status": "active",
      "job_title": "Office Clerk",
      "job_classification": "8810",
      "department": "Administration",
      "state": "CA"
    }
  ]
}

Response:

{
  "success": true,
  "employees_synced": 1,
  "employees_created": 1,
  "employees_updated": 0,
  "errors": []
}

Complete Integration Examples

Node.js / JavaScript

const fetch = require('node-fetch');

class Audit1Client {
  constructor(clientId, clientSecret, baseUrl) {
    this.clientId = clientId;
    this.clientSecret = clientSecret;
    this.baseUrl = baseUrl;
  }

  // Helper method for making authenticated requests
  async request(endpoint, method = 'GET', body = null) {
    const options = {
      method,
      headers: {
        'X-Client-ID': this.clientId,
        'X-Client-Secret': this.clientSecret,
        'Content-Type': 'application/json'
      }
    };

    if (body) {
      options.body = JSON.stringify(body);
    }

    const response = await fetch(`${this.baseUrl}${endpoint}`, options);
    
    if (!response.ok) {
      const error = await response.json();
      throw new Error(`API Error: ${error.message}`);
    }

    return await response.json();
  }

  // Submit payroll report
  async submitPayroll(employerId, payPeriod, employees) {
    return await this.request('/payroll/reports', 'POST', {
      employer_id: employerId,
      pay_period: payPeriod,
      employees: employees
    });
  }

  // Sync employees
  async syncEmployees(employerId, employees) {
    return await this.request('/employees/sync', 'POST', {
      employer_id: employerId,
      employees: employees
    });
  }

  // Health check
  async healthCheck() {
    return await this.request('/health');
  }
}

// Usage Example
async function main() {
  const client = new Audit1Client(
    'audit1_test_cli_abc123...',
    'audit1_test_sec_xyz789...',
    'https://developer-api-sandbox.audit1.info/api/v1'
  );

  try {
    // Test connection
    const health = await client.healthCheck();
    console.log('API Status:', health.status);

    // Submit payroll
    const payrollResult = await client.submitPayroll(
      'emp_abc123',
      {
        start_date: '2024-01-01',
        end_date: '2024-01-15'
      },
      [
        {
          employee_id: 'EMP001',
          first_name: 'John',
          last_name: 'Doe',
          gross_pay: 2500.00,
          hours_worked: 80,
          job_classification: '8810',
          state: 'CA'
        }
      ]
    );

    console.log('Payroll submitted:', payrollResult.report_id);

  } catch (error) {
    console.error('Error:', error.message);
  }
}

main();

Python

import requests
import json

class Audit1Client:
    def __init__(self, client_id, client_secret, base_url):
        self.client_id = client_id
        self.client_secret = client_secret
        self.base_url = base_url
        self.headers = {
            'X-Client-ID': client_id,
            'X-Client-Secret': client_secret,
            'Content-Type': 'application/json'
        }

    def request(self, endpoint, method='GET', data=None):
        url = f"{self.base_url}{endpoint}"
        
        if method == 'GET':
            response = requests.get(url, headers=self.headers)
        elif method == 'POST':
            response = requests.post(url, headers=self.headers, json=data)
        
        response.raise_for_status()
        return response.json()

    def submit_payroll(self, employer_id, pay_period, employees):
        return self.request('/payroll/reports', 'POST', {
            'employer_id': employer_id,
            'pay_period': pay_period,
            'employees': employees
        })

    def sync_employees(self, employer_id, employees):
        return self.request('/employees/sync', 'POST', {
            'employer_id': employer_id,
            'employees': employees
        })

    def health_check(self):
        return self.request('/health')

# Usage Example
if __name__ == '__main__':
    client = Audit1Client(
        client_id='audit1_test_cli_abc123...',
        client_secret='audit1_test_sec_xyz789...',
        base_url='https://developer-api-sandbox.audit1.info/api/v1'
    )

    try:
        # Test connection
        health = client.health_check()
        print(f"API Status: {health['status']}")

        # Submit payroll
        result = client.submit_payroll(
            employer_id='emp_abc123',
            pay_period={
                'start_date': '2024-01-01',
                'end_date': '2024-01-15'
            },
            employees=[
                {
                    'employee_id': 'EMP001',
                    'first_name': 'John',
                    'last_name': 'Doe',
                    'gross_pay': 2500.00,
                    'hours_worked': 80,
                    'job_classification': '8810',
                    'state': 'CA'
                }
            ]
        )

        print(f"Payroll submitted: {result['report_id']}")

    except requests.exceptions.HTTPError as error:
        print(f"API Error: {error}")

C# / .NET

using System;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;

public class Audit1Client
{
    private readonly HttpClient _httpClient;
    private readonly string _baseUrl;

    public Audit1Client(string clientId, string clientSecret, string baseUrl)
    {
        _baseUrl = baseUrl;
        _httpClient = new HttpClient();
        _httpClient.DefaultRequestHeaders.Add("X-Client-ID", clientId);
        _httpClient.DefaultRequestHeaders.Add("X-Client-Secret", clientSecret);
    }

    private async Task<T> RequestAsync<T>(string endpoint, HttpMethod method, object data = null)
    {
        var url = $"{_baseUrl}{endpoint}";
        var request = new HttpRequestMessage(method, url);

        if (data != null)
        {
            var json = JsonSerializer.Serialize(data);
            request.Content = new StringContent(json, Encoding.UTF8, "application/json");
        }

        var response = await _httpClient.SendAsync(request);
        response.EnsureSuccessStatusCode();

        var responseContent = await response.Content.ReadAsStringAsync();
        return JsonSerializer.Deserialize<T>(responseContent);
    }

    public async Task<PayrollResult> SubmitPayrollAsync(string employerId, PayPeriod payPeriod, Employee[] employees)
    {
        var payload = new
        {
            employer_id = employerId,
            pay_period = payPeriod,
            employees = employees
        };

        return await RequestAsync<PayrollResult>("/payroll/reports", HttpMethod.Post, payload);
    }

    public async Task<HealthCheck> HealthCheckAsync()
    {
        return await RequestAsync<HealthCheck>("/health", HttpMethod.Get);
    }
}

// Usage Example
class Program
{
    static async Task Main(string[] args)
    {
        var client = new Audit1Client(
            clientId: "audit1_test_cli_abc123...",
            clientSecret: "audit1_test_sec_xyz789...",
            baseUrl: "https://developer-api-sandbox.audit1.info/api/v1"
        );

        try
        {
            // Test connection
            var health = await client.HealthCheckAsync();
            Console.WriteLine($"API Status: {health.Status}");

            // Submit payroll
            var result = await client.SubmitPayrollAsync(
                employerId: "emp_abc123",
                payPeriod: new PayPeriod
                {
                    StartDate = "2024-01-01",
                    EndDate = "2024-01-15"
                },
                employees: new Employee[]
                {
                    new Employee
                    {
                        EmployeeId = "EMP001",
                        FirstName = "John",
                        LastName = "Doe",
                        GrossPay = 2500.00m,
                        HoursWorked = 80,
                        JobClassification = "8810",
                        State = "CA"
                    }
                }
            );

            Console.WriteLine($"Payroll submitted: {result.ReportId}");
        }
        catch (HttpRequestException ex)
        {
            Console.WriteLine($"API Error: {ex.Message}");
        }
    }
}

Error Handling

HTTP Status Codes

CodeMeaningAction
200SuccessRequest completed successfully
201CreatedResource created successfully
400Bad RequestCheck your request format and data
401UnauthorizedVerify your credentials
403ForbiddenCheck your permissions
404Not FoundVerify endpoint URL
429Rate LimitedSlow down, retry after delay
500Server ErrorContact support if persists

Error Response Format

{
  "success": false,
  "error": {
    "code": "INVALID_REQUEST",
    "message": "Missing required field: employee_id",
    "details": {
      "field": "employees[0].employee_id",
      "expected": "string",
      "received": "null"
    }
  }
}

Common Error Codes

CodeDescriptionSolution
INVALID_CREDENTIALSAuthentication failedCheck Client ID and Secret
INVALID_REQUESTRequest format incorrectVerify JSON structure
MISSING_FIELDRequired field missingAdd missing fields
INVALID_FIELDField value invalidCheck data types and formats
DUPLICATE_ENTRYRecord already existsCheck for duplicates
RATE_LIMIT_EXCEEDEDToo many requestsImplement rate limiting

Retry Strategy

Implement exponential backoff for failed requests:

async function requestWithRetry(fn, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await fn();
    } catch (error) {
      if (i === maxRetries - 1) throw error;
      
      // Exponential backoff: 1s, 2s, 4s, 8s...
      const delay = Math.pow(2, i) * 1000;
      await new Promise(resolve => setTimeout(resolve, delay));
    }
  }
}

// Usage
const result = await requestWithRetry(() => 
  client.submitPayroll(employerId, payPeriod, employees)
);

Best Practices

✅ Security

  • Store credentials securely (environment variables, secrets manager)
  • Use HTTPS only for all API requests
  • Never log credentials in application logs
  • Rotate keys periodically (every 90 days recommended)
  • Use separate keys for sandbox and production
  • Implement request signing for extra security (optional)

✅ Performance

  • Batch requests when possible (submit multiple employees at once)
  • Implement caching for frequently accessed data
  • Use webhooks instead of polling for status updates
  • Set reasonable timeouts (30-60 seconds)
  • Implement connection pooling for high-volume applications

✅ Reliability

  • Implement retry logic with exponential backoff
  • Log all API interactions for debugging
  • Monitor API health regularly
  • Handle errors gracefully with user-friendly messages
  • Set up alerting for failures

✅ Data Quality

  • Validate data before sending to API
  • Use consistent formats (dates, amounts, codes)
  • Include all required fields
  • Map classifications correctly to workers' comp codes
  • Test with sample data first

⛔ Don'ts

  • Don't hardcode credentials in your application code
  • Don't ignore error responses
  • Don't skip validation
  • Don't make excessive API calls (respect rate limits)
  • Don't submit duplicate data without checking
  • Don't store sensitive data unnecessarily

Rate Limits

Current Limits

  • Standard: 100 requests per minute
  • Burst: 1000 requests per hour
  • Daily: 10,000 requests per day

Rate Limit Headers

Audit1 returns rate limit information in response headers:

X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1640000000

Handling Rate Limits

async function makeRequestWithRateLimit(client, endpoint, data) {
  try {
    const response = await client.request(endpoint, 'POST', data);
    
    // Check rate limit headers
    const remaining = response.headers.get('X-RateLimit-Remaining');
    
    if (remaining < 10) {
      console.warn('Rate limit approaching, slowing down...');
      await delay(1000); // Wait 1 second
    }
    
    return response;
    
  } catch (error) {
    if (error.status === 429) {
      // Rate limited - wait and retry
      const resetTime = error.headers.get('X-RateLimit-Reset');
      const waitTime = resetTime - Date.now();
      
      console.log(`Rate limited. Waiting ${waitTime}ms...`);
      await delay(waitTime);
      
      // Retry request
      return makeRequestWithRateLimit(client, endpoint, data);
    }
    throw error;
  }
}

Testing Your Integration

Sandbox Environment

Always test in sandbox first:

Sandbox URL: https://developer-api-sandbox.audit1.info/api/v1

Sandbox Features:

  • Separate database from production
  • Safe to experiment
  • Test all endpoints
  • Verify error handling
  • Performance testing

Test Checklist

Before going to production:

  • Authentication works correctly
  • Can submit payroll successfully
  • Employee sync functions properly
  • Error handling works as expected
  • Retry logic functions correctly
  • Rate limiting is respected
  • Logging captures important events
  • Webhooks receive notifications (if used)
  • Data validation catches bad data
  • Performance meets requirements

Webhooks Integration

For real-time notifications about processing status:

See our detailed guide: Webhook Integration Tutorial

Quick Setup:

  1. Create webhook endpoint in your application
  2. Register webhook via API:
POST /api/v1/webhooks
{
  "url": "https://your-domain.com/webhooks/audit1",
  "events": ["file.processed", "file.failed", "audit.completed"]
}
  1. Verify webhook signatures for security
  2. Process events asynchronously

Getting Help

Documentation Resources

Support Channels

Email Support:

  • 📧 [email protected]
  • Include "API Integration" in subject
  • Attach relevant code snippets (without credentials)
  • Response time: 2-4 hours during business hours

Developer Support:

  • 💬 Developer forum: forum.audit1.info
  • 📱 Phone support for urgent issues
  • 📊 Status page: status.audit1.info

What to Include in Support Requests

  • Your organization ID
  • Endpoint you're calling
  • Request/response examples (sanitized)
  • Error messages
  • Timestamps of failures
  • Programming language/framework
  • What you've already tried

Next Steps

  1. Request API Credentials from Audit1 support
  2. Set up sandbox environment for testing
  3. Review code examples in your preferred language
  4. Build initial integration with basic payroll submission
  5. Test thoroughly in sandbox
  6. Set up webhooks for status notifications
  7. Deploy to production once testing complete
  8. Monitor and optimize your integration

Related Guides:


Ready to start building? Contact [email protected] to request your API credentials today!