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:
| Method | Best For | Setup Complexity |
|---|---|---|
| SFTP | Batch file uploads | 🟢 Low |
| Connectors | Supported payroll systems | 🟡 Medium |
| Webhooks | Receiving notifications | 🟡 Medium |
| API | Custom 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/healthNo 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/reportsHeaders:
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/syncRequest 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
| Code | Meaning | Action |
|---|---|---|
200 | Success | Request completed successfully |
201 | Created | Resource created successfully |
400 | Bad Request | Check your request format and data |
401 | Unauthorized | Verify your credentials |
403 | Forbidden | Check your permissions |
404 | Not Found | Verify endpoint URL |
429 | Rate Limited | Slow down, retry after delay |
500 | Server Error | Contact 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
| Code | Description | Solution |
|---|---|---|
INVALID_CREDENTIALS | Authentication failed | Check Client ID and Secret |
INVALID_REQUEST | Request format incorrect | Verify JSON structure |
MISSING_FIELD | Required field missing | Add missing fields |
INVALID_FIELD | Field value invalid | Check data types and formats |
DUPLICATE_ENTRY | Record already exists | Check for duplicates |
RATE_LIMIT_EXCEEDED | Too many requests | Implement 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:
- Create webhook endpoint in your application
- Register webhook via API:
POST /api/v1/webhooks{
"url": "https://your-domain.com/webhooks/audit1",
"events": ["file.processed", "file.failed", "audit.completed"]
}- Verify webhook signatures for security
- Process events asynchronously
Getting Help
Documentation Resources
- 📚 Full API Reference: developer.audit1.info/api-reference
- 🚀 Quick Start: quick-start.md
- 🎣 Webhooks Guide: webhook-integration-tutorial.md
- 📘 Integration Examples: integration-examples.md
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
- Request API Credentials from Audit1 support
- Set up sandbox environment for testing
- Review code examples in your preferred language
- Build initial integration with basic payroll submission
- Test thoroughly in sandbox
- Set up webhooks for status notifications
- Deploy to production once testing complete
- Monitor and optimize your integration
Related Guides:
- SFTP Connection Guide - File-based alternative
- Connectors Overview - Pre-built integrations
- Webhook Tutorial - Real-time notifications
Ready to start building? Contact [email protected] to request your API credentials today!
Updated about 2 months ago
