Developer Guide

Comprehensive guide for integrating with Audit1 APIs

Developer API Documentation

Welcome to the Audit1 Developer API! This comprehensive guide will help you integrate with our platform using API keys and webhooks.

📖 Quick Reference

# Base URL
https://api.audit1.info/api/v1


# Authentication
Authorization: Bearer audit1_test_sk_YOUR_KEY_HERE

# Content Type
Content-Type: application/json

API Endpoints at a Glance

EndpointMethodPurposeAuth Required
/payroll/reportsPOSTSubmit payroll data✅ Yes
/employees/syncPOSTReport employee changes✅ Yes
/files/status/:idGETCheck file processing✅ Yes

Response Codes

CodeMeaningAction
200SuccessRequest completed
202AcceptedProcessing async
400Bad RequestCheck request body
401UnauthorizedVerify API key
429Rate LimitedRetry with backoff
500Server ErrorContact support

Table of Contents

  1. Getting Started
  2. Authentication
  3. API Keys Management
  4. Webhooks
  5. Event Types
  6. Testing & Environments
  7. Error Handling
  8. Rate Limits
  9. Support

Getting Started

Overview

The Audit1 Developer API enables payroll providers, insurance carriers, and software integrators to:

  • Submit Payroll Reports: Send employee wages, hours, and classification codes for workers' comp audits
  • Sync Employee Data: Report hires, terminations, and wage changes in real-time
  • Track File Processing: Monitor the status of uploaded payroll files
  • Receive Webhook Events: Get notified when audits complete, policies update, etc.

Note: API keys and webhooks are managed through your Audit1 portal dashboard, not programmatically via the API.

🔗

Portal URLs by User Type:

Base URL

Production: https://api.audit1.info/api/v1

Quick Example

# Submit payroll report
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": [...],
    "payroll_data": {...}
  }'

Prerequisites

Before you begin, ensure you have:

  • An active Audit1 account
  • API key from your Audit1 portal (Dashboard → API Keys section)
  • Employer ID(s) for payroll reporting
  • Basic understanding of REST APIs and HTTP protocols

Authentication

All API requests must be authenticated using API keys created in your Audit1 portal dashboard (navigate to API Keys section).

Bearer Token Authentication

Authorization: Bearer audit1_test_sk_1234567890abcdef1234567890abcdef

Environment Auto-Detection

The API automatically detects your environment from the key prefix:

audit1_test_sk_*  →  Sandbox  (test data, faster processing)
audit1_live_sk_*  →  Production  (real data, standard processing)

Complete Request Example

curl -X POST https://api.audit1.info/api/v1/payroll/reports \
  -H "Authorization: Bearer audit1_test_sk_abc123..." \
  -H "Content-Type: application/json" \
  -d '{"employer_id": "EMP_001", ...}'

Code Examples

Node.js:

const axios = require('axios');

const client = axios.create({
  baseURL: 'https://api.audit1.info/api/v1',
  headers: {
    'Authorization': `Bearer ${process.env.AUDIT1_API_KEY}`,
    'Content-Type': 'application/json'
  }
});

// Make request
const response = await client.post('/payroll/reports', payloadData);

Python:

import requests
import os

headers = {
    'Authorization': f'Bearer {os.getenv("AUDIT1_API_KEY")}',
    'Content-Type': 'application/json'
}

response = requests.post(
    'https://api.audit1.info/api/v1/payroll/reports',
    headers=headers,
    json=payload_data
)

C# / .NET:

using System.Net.Http;
using System.Net.Http.Headers;

var client = new HttpClient();
client.BaseAddress = new Uri("https://api.audit1.info/api/v1");
client.DefaultRequestHeaders.Authorization = 
    new AuthenticationHeaderValue("Bearer", Environment.GetEnvironmentVariable("AUDIT1_API_KEY"));

var response = await client.PostAsJsonAsync("/payroll/reports", payloadData);

Authentication Errors

ErrorCauseSolution
Missing Authorization headerNo auth headerAdd Authorization: Bearer YOUR_KEY
Invalid API key formatWrong prefixUse audit1_test_sk_* or audit1_live_sk_*
API key not foundKey deleted/invalidCreate new key in Dashboard
API key inactiveKey disabledRe-enable in Dashboard

Bearer Token Authentication

Authorization: Bearer YOUR_API_KEY

Example Request

curl -X POST https://api.audit1.info/api/v1/payroll/reports \
  -H "Authorization: Bearer audit1_live_sk_abc123..." \
  -H "Content-Type: application/json" \
  -d '{...}'

API Key Format

API keys follow this format:

  • Sandbox: audit1_test_sk_[32-character-string]
  • Production: audit1_live_sk_[32-character-string]

Get your API key: Log into your portal → Dashboard → API Keys


Available Endpoints

Payroll Reporting

Submit Payroll Report

POST /api/v1/payroll/reports

Submit employee wages, hours, and classification codes for a reporting period.

Request Body:

{
  "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
  }
}

Employee Sync

Sync Employee Changes

POST /api/v1/employees/sync

Report employee hires, terminations, and wage updates.

Request Body:

{
  "employer_id": "EMP_12345",
  "employees": [
    {
      "employee_id": "EE_NEW_001",
      "action": "hire",
      "name": "Jane Doe",
      "hire_date": "2024-02-01",
      "classification_code": "8810",
      "hourly_rate": 30.00
    }
  ]
}

File Status

Check File Processing Status

GET /api/v1/files/status/:id

Check the processing status of an uploaded payroll file.

Response:

{
  "file_id": "FILE_123",
  "status": "completed",
  "records_total": 150,
  "records_processed": 150,
  "records_failed": 0
}

Webhooks (Receiving Events)

Create API Key

Creates a new API key for your organization.

Endpoint: POST /api-keys

Request Body:

{
  "owner_id": "your-entity-id",
  "owner_type": "employer|payroll_company|software_company", 
  "environment": "sandbox|production"
}

Response:

{
  "apiKey": {
    "_id": "507f1f77bcf86cd799439011",
    "id": "ak_12345",
    "key": "ak_prod_1234567890abcdef...",
    "environment": "production",
    "owner_id": "your-entity-id",
    "owner_type": "employer",
    "is_active": true,
    "created_at": "2024-01-15T10:30:00.000Z",
    "updated_at": "2024-01-15T10:30:00.000Z"
  },
  "secret": "sk_prod_abcdef1234567890..."
}
  ## Platform Architecture Snapshot

  The Developer API mirrors how the Audit1 workers' compensation stack is assembled:

  1. **Experience Layer** – employer and carrier portals powered by Audit1 UI components.
  2. **Service Layer** – stateless Node services (Developer API, Notification Handler, Ledger) deployed to Cloud Run.
  3. **Data Layer** – MongoDB Atlas clusters split by environment with regional failover.
  4. **Integration Fabric** – webhook dispatcher, SFTP collectors, and third-party connectors (Paylocity, UKG, Hartford, etc.).

Client -> Developer API -> Orchestrator -> Ledger / Policy Store -> MongoDB Atlas ↘ Notification/Webhook Engine -> Partner Systems


### Domain Boundaries (Workers' Comp Lens)

- **Policy & Audit**: Policies, endorsements, physical/virtual audits; authoritative source lives in Audit1 and respects bureau rules.
- **Exposure**: Payroll, employee census, WC class-code metadata, overtime modifiers; mastered by payroll reporters, cached by Audit1.
- **Financial**: Premium deltas, deposit adjustments, PayGo remittance schedules; reconciled in Ledger Service and surfaced through this API.
- **Compliance**: Audit packets, affidavit status, document retention aligned with DOI / bureau checklists.

### Data Residency & Compliance Notes

- Data residency currently in `us-central1` with warm standby in `us-east4` (keeps latency low for national carriers).
- PII is encrypted at rest with customer-specific keys; transport requires TLS 1.2+.
- SOC 2 Type II controls govern key management, access logs, and webhook replay detection.
- Default retention: 7 years for audit artifacts (matches most WC state requirements), configurable via support ticket.

### Business Metrics to Track

- **Adoption**: number of active payroll reporters / carrier divisions calling the API vs. manual portals.
- **Automation**: % of audits auto-closed because wage statements or exposure files arrived via webhook.
- **Data Freshness**: median lag between payroll event and Audit1 ingestion (goal < 15 minutes for PayGo).
- **Accuracy**: premium leakage prevented due to near-real-time exposure sync.
- **Error Budget**: 99.9% monthly SLA; use `X-Audit1-Request-Id` for support escalations.

⚠️ Important: The secret is only returned once during creation. Store it securely!

List API Keys

Retrieves all active API keys for your organization.

Endpoint: GET /api-keys?owner_id={your-entity-id}&owner_type={type}

Query Parameters:

  • owner_id (required): Your entity ID
  • owner_type (required): user, employer, payroll_company, or software_company

Response:

{
  "apiKeys": [
    {
      "_id": "507f1f77bcf86cd799439011",
      "id": "ak_12345",
      "key": "ak_prod_1234567890abcdef...",
      "environment": "production",
      "owner_id": "your-entity-id",
      "owner_type": "employer",
      "is_active": true,
      "created_at": "2024-01-15T10:30:00.000Z",
      "updated_at": "2024-01-15T10:30:00.000Z"
    }
  ]
}

Delete API Key

Soft-deletes an API key (sets is_active to false).

Endpoint: DELETE /api-keys/{api_key_id}?owner_id={your-entity-id}

Response:

{
  "message": "api_key_deleted"
}

Webhooks

Webhooks allow you to receive real-time notifications when events occur in the Audit1 system.

How Webhooks Work

  1. Event Occurs: Something happens in Audit1 (e.g., policy created, payment processed)
  2. Event Sent: We send an HTTP POST request to your webhook URL
  3. Response Required: Your endpoint must respond with a 2xx status code
  4. Retry Logic: Failed deliveries are retried with exponential backoff

Create Webhook

Endpoint: POST /webhooks

Request Body:

{
  "owner_id": "your-entity-id",
  "owner_type": "employer|payroll_company|software_company",
  "url": "https://your-domain.com/webhooks/audit1",
  "events": ["policy.created", "payment.processed", "employee.updated"]
}

Response:

{
  "webhook": {
    "_id": "507f1f77bcf86cd799439012",
    "id": "wh_12345",
    "url": "https://your-domain.com/webhooks/audit1", 
    "events": ["policy.created", "payment.processed"],
    "owner_id": "your-entity-id",
    "owner_type": "employer",
    "is_active": true,
    "created_at": "2024-01-15T10:35:00.000Z",
    "updated_at": "2024-01-15T10:35:00.000Z"
  },
  "secret": "whsec_1234567890abcdef..."
}

Webhook Security

Signature Verification

Every webhook includes a signature in the Audit1-Signature header:

Audit1-Signature: t=1642291200,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd

Verification Steps:

  1. Extract timestamp and signature:

    const signature = req.headers['audit1-signature'];
    const elements = signature.split(',');
    const timestamp = elements[0].split('=')[1];
    const v1 = elements[1].split('=')[1];
  2. Create expected signature:

    const crypto = require('crypto');
    const payload = req.body;
    const expectedSignature = crypto
      .createHmac('sha256', webhookSecret)
      .update(timestamp + '.' + JSON.stringify(payload))
      .digest('hex');
  3. Compare signatures:

    const isValid = crypto.timingSafeEqual(
      Buffer.from(v1, 'hex'),
      Buffer.from(expectedSignature, 'hex')
    );

Example Webhook Handler (Node.js/Express)

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

const app = express();
app.use(express.raw({ type: 'application/json' }));

app.post('/webhooks/audit1', (req, res) => {
  const signature = req.headers['audit1-signature'];
  const payload = req.body;
  
  // Verify signature
  if (!verifyWebhookSignature(payload, signature, process.env.WEBHOOK_SECRET)) {
    return res.status(400).send('Invalid signature');
  }
  
  const event = JSON.parse(payload);
  
  // Handle the event
  switch (event.type) {
    case 'policy.created':
      handlePolicyCreated(event.data);
      break;
    case 'payment.processed':
      handlePaymentProcessed(event.data);
      break;
    default:
      console.log(`Unhandled event type: ${event.type}`);
  }
  
  res.status(200).send('OK');
});

function verifyWebhookSignature(payload, signature, secret) {
  const elements = signature.split(',');
  const timestamp = elements[0].split('=')[1];
  const v1 = elements[1].split('=')[1];
  
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(timestamp + '.' + payload)
    .digest('hex');
    
  return crypto.timingSafeEqual(
    Buffer.from(v1, 'hex'),
    Buffer.from(expectedSignature, 'hex')
  );
}

List Webhooks

Endpoint: GET /webhooks?owner_id={your-entity-id}&owner_type={type}

Delete Webhook

Endpoint: DELETE /webhooks/{webhook_id}?owner_id={your-entity-id}


Event Types

Policy Events

  • policy.created - New insurance policy created
  • policy.updated - Policy details modified
  • policy.cancelled - Policy cancelled
  • policy.renewed - Policy renewed

Employee Events

  • employee.created - New employee added
  • employee.updated - Employee information changed
  • employee.terminated - Employee terminated

Payment Events

  • payment.processed - Payment successfully processed
  • payment.failed - Payment processing failed
  • payment.refunded - Payment refunded

Audit Events

  • audit.started - New audit process initiated
  • audit.completed - Audit process completed
  • audit.failed - Audit process failed

Example Event Payload

{
  "id": "evt_1234567890",
  "type": "policy.created", 
  "created_at": "2024-01-15T10:40:00.000Z",
  "data": {
    "policy": {
      "id": "pol_12345",
      "employer_id": "emp_67890",
      "policy_number": "POL-2024-001",
      "effective_date": "2024-02-01",
      "premium": 1500.00,
      "status": "active"
    }
  },
  "metadata": {
    "source": "audit1_system",
    "environment": "production"
  }
}

Testing & Environments

Sandbox vs Production

Audit1 uses key-based environment switching - both sandbox and production use the same base URL:

Base URL: https://api.audit1.info/api/v1

The environment is determined by your API key prefix:

EnvironmentKey PrefixData
Sandboxaudit1_test_sk_...Test data, safe for development
Productionaudit1_live_sk_...Real data, live transactions

Example:

# Sandbox request (test data)
curl -H "Authorization: Bearer audit1_test_sk_abc123..." \
  https://api.audit1.info/api/v1/payroll/reports

# Production request (real data)
curl -H "Authorization: Bearer audit1_live_sk_xyz789..." \
  https://api.audit1.info/api/v1/payroll/reports

Webhook Testing

For testing webhooks locally, use tools like:

ngrok for Local Development:

# Install ngrok
npm install -g ngrok

# Expose local server
ngrok http 3000

# Use the generated URL for webhooks
# https://abc123.ngrok.io/webhooks/audit1

Error Handling

Error Response Format

All errors follow a consistent format:

{
  "error": "Error type",
  "message": "Human-readable description",
  "details": {
    "field": "Additional context"
  }
}

Common Errors

400 Bad Request

Missing Required Fields:

{
  "error": "Validation Error",
  "message": "Missing required fields: employees",
  "details": {
    "required": ["employer_id", "period_start", "period_end", "employees", "payroll_data"]
  }
}

Solution: Ensure all required fields are included in your request.

401 Unauthorized

Invalid API Key:

{
  "error": "Authentication Failed",
  "message": "Invalid API key format. Expected audit1_test_sk_* or audit1_live_sk_*"
}

Solution: Verify your API key starts with the correct prefix.

429 Too Many Requests

{
  "error": "Rate Limit Exceeded",
  "message": "Too many requests. Please retry after 60 seconds.",
  "details": {
    "retry_after": 60,
    "limit": 100,
    "window": "1 minute"
  }
}

Solution: Implement exponential backoff retry logic.

Retry Logic Best Practices

Exponential Backoff:

async function 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 (4xx except 429)
      if (error.status >= 400 && error.status < 500 && error.status !== 429) {
        throw error;
      }
      
      // Calculate backoff: 1s, 2s, 4s, 8s...
      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
const result = await retryRequest(() => 
  submitPayrollReport(data)
);

Which Errors to Retry:

Status CodeRetry?Reason
429✅ YesRate limit - temporary
500✅ YesServer error - may be temporary
502/503✅ YesGateway/Service unavailable
400❌ NoBad request - fix payload first
401❌ NoAuth error - fix credentials first
404❌ NoNot found - check endpoint

Validation Errors

Employee Validation:

{
  "error": "Validation Error",
  "message": "Invalid employee data",
  "details": {
    "employees[0].classification_code": "Required field missing",
    "employees[1].gross_wages": "Must be a positive number"
  }
}

Date Format Errors:

{
  "error": "Validation Error",
  "message": "Invalid date format for period_start",
  "details": {
    "expected": "YYYY-MM-DD",
    "received": "01/15/2024"
  }
}

Logging Errors

Recommended Log Structure:

function logApiError(error, context) {
  console.error({
    timestamp: new Date().toISOString(),
    error_type: error.name,
    status_code: error.status,
    message: error.message,
    endpoint: context.endpoint,
    method: context.method,
    request_id: error.response?.headers?.['x-request-id'],
    environment: process.env.NODE_ENV,
    stack: error.stack
  });
}

Error Handling

HTTP Status Codes

  • 200 - Success
  • 201 - Created successfully
  • 400 - Bad request (validation error)
  • 401 - Unauthorized (invalid API key)
  • 403 - Forbidden (insufficient permissions)
  • 404 - Resource not found
  • 409 - Conflict (duplicate resource)
  • 429 - Rate limit exceeded
  • 500 - Internal server error

Error Response Format

{
  "error": "owner_id_required",
  "message": "The owner_id field is required",
  "code": "VALIDATION_ERROR",
  "timestamp": "2024-01-15T10:45:00.000Z"
}

Common Error Codes

  • owner_id_required - Missing owner ID
  • owner_type_required - Missing owner type
  • environment_required - Missing environment
  • api_key_not_found - API key doesn't exist
  • webhook_creation_failed - Webhook creation error
  • invalid_signature - Webhook signature verification failed

Rate Limits

API requests are rate-limited to ensure fair usage:

Limits by Plan

Plan TypeRequests/MinuteRequests/Hour
Sandbox1001,000
Production50010,000
Enterprise2,00050,000

Rate Limit Headers

X-RateLimit-Limit: 500
X-RateLimit-Remaining: 499  
X-RateLimit-Reset: 1642291260

Handling Rate Limits

const response = await fetch('/api/v1/api-keys', {
  headers: { 'Authorization': `Bearer ${apiKey}` }
});

if (response.status === 429) {
  const resetTime = response.headers.get('X-RateLimit-Reset');
  const waitTime = (resetTime * 1000) - Date.now();
  
  console.log(`Rate limited. Waiting ${waitTime}ms`);
  await new Promise(resolve => setTimeout(resolve, waitTime));
  
  // Retry request
}

Support

Getting Help

Community

Contact Information

Technical Support:

  • Email: [email protected]
  • Phone: 1-800-AUDIT-1 (1-800-283-481)
  • Hours: Monday-Friday, 9 AM - 6 PM EST

Partnership Inquiries:


Appendix

SDK & Libraries

Official SDKs:

  • Node.js: npm install @audit1/node-sdk
  • Python: pip install audit1-python
  • PHP: composer require audit1/php-sdk
  • .NET: Available on NuGet

Community Libraries:

  • Ruby gem: audit1-ruby
  • Go module: go get github.com/audit1/go-sdk

Postman Collection

Import our Postman collection for easy API testing: https://www.getpostman.com/collections/audit1-developer-api

Changelog

v1.2.0 - January 2024

  • Added webhook signature verification
  • Improved error messages
  • Added rate limiting headers

v1.1.0 - December 2023

  • Initial public release
  • API Keys management
  • Webhooks support

Last updated: January 3, 2026