API Reference

Base URL: https://apiv2.audit1.com/api/v1

Info

Every endpoint below is also reachable under /api/v2/... — the two prefixes are aliases for identical handlers. New integrations should use /api/v1; /api/v2 is preserved for clients that already point there.

All requests require X-Client-ID and X-Client-Secret headers. See Authentication. HMAC request signing (X-Signature + X-Timestamp) is optional — see the "Signed Requests" section in Authentication.


POST /payroll/reports #

Submit payroll data for workers' compensation audit.

Required Fields #

Field Type Description
employer_fein string Federal EIN ("12-3456789" or "123456789")
policy_number string Workers' comp policy number
employees array Non-empty array of employee records

Optional Fields #

Field Type Description
pay_period.start_date string Period start (YYYY-MM-DD)
pay_period.end_date string Period end (YYYY-MM-DD)

Employee Records #

Flexible schema — include whatever payroll fields you have. Common fields:

Field Description
first_name, last_name Employee name
employee_id Your internal ID
class_code WC classification code (e.g., "8810")
state Work state (e.g., "CA")
gross_wages Gross wages for the period
hours_worked Hours worked
overtime Overtime pay

Example Request #

{
  "employer_fein": "12-3456789",
  "policy_number": "WC1025561",
  "employees": [
    {
      "first_name": "Jane",
      "last_name": "Smith",
      "class_code": "8810",
      "state": "CA",
      "gross_wages": 2500.00,
      "hours_worked": 80
    }
  ],
  "pay_period": {
    "start_date": "2024-01-01",
    "end_date": "2024-01-15"
  }
}

Response (202 Accepted) #

{
  "success": true,
  "file_id": "682abc123def456789012345",
  "status": "completed",
  "environment": "sandbox",
  "employer": {
    "id": "681xyz789abc123456789012",
    "business_name": "Smith Industries LLC",
    "fein": "12-3456789"
  },
  "policy": {
    "id": "680def456ghi789012345678",
    "policy_number": "WC1025561"
  },
  "employee_count": 1,
  "file_name": "Smith-Industries-LLC_2024-01-01_2024-01-15_WC1025561_..."
}

More than 500 employees

: When employees.length > 500, the API returns immediately with file_id: null. Processing continues in the background — check status via the endpoint below or your portal dashboard.

Errors #

Status Error Cause
400 Missing required fields employer_fein, policy_number, or employees missing
400 employees must be non-empty Empty employees array
401 Missing authentication headers Missing X-Client-ID or X-Client-Secret
401 Invalid client_id Key not found or deactivated
401 Invalid client_secret Wrong secret for this client ID
404 Employer not found No employer matches the FEIN
404 Policy not found No active policy matches the number

POST /employees/sync #

Report employee hires, terminations, and wage updates.

Required Fields #

Field Type Description
employer_id string Employer identifier
employees array Non-empty array of change records

Each employee requires:

Field Type Values
employee_id string Your internal ID
action string hire, terminate, or update

Action-specific fields:

Action Required Description
hire name, hire_date New employee
terminate termination_date Employee leaving
update (any fields) Wage or info changes

Example Request #

{
  "employer_id": "681xyz789abc123456789012",
  "employees": [
    { "employee_id": "EMP001", "action": "hire", "name": "John Doe", "hire_date": "2024-01-15" },
    { "employee_id": "EMP002", "action": "terminate", "termination_date": "2024-03-01" },
    { "employee_id": "EMP003", "action": "update", "gross_wages": 3500.00 }
  ]
}

Response (202 Accepted) #

{
  "message": "Employee sync initiated",
  "sync_id": "682abc123def456789012345",
  "file_id": "682abc123def456789012345",
  "environment": "sandbox",
  "employer_id": "681xyz789abc123456789012",
  "employees_processed": 3,
  "status": "processing"
}

GET /files/status/:file_id #

Check processing status of a submitted file.

Path parameter: file_id — the ID returned from /payroll/reports or /employees/sync

Response (200) #

{
  "file_id": "682abc123def456789012345",
  "status": "NORMALIZED",
  "created_at": "2026-03-28T10:30:00.000Z",
  "updated_at": "2026-03-28T10:32:00.000Z",
  "employee_count": 150,
  "employer_id": "681xyz789abc123456789012",
  "period": { "start": "2024-01-01", "end": "2024-01-15" },
  "phase_1": {
    "status": "completed",
    "green_rows": 148,
    "red_rows": 2
  },
  "phase_2": {
    "status": "completed",
    "processed_rows": 148
  },
  "phase_3": {
    "status": "completed",
    "total_premium_cents": 18452300,
    "processed_rows": 148
  }
}

The phase_1 / phase_2 / phase_3 blocks appear only after the Auditor pipeline has run on the file. Until then they are absent or { "status": "pending" }.

File Statuses #

The top-level status reflects the ingestion stage:

Status Description
pending ⏳ Received, waiting to process
NORMALIZED Raw data converted to the canonical CSV format and persisted
failed ✗ Processing failed — see error field

Pipeline progress for normalized files is reported via the three phase_* blocks (validation, wage calculation, premium calculation). Do not parse these strings to drive billing logic — read the structured phase_3.total_premium_cents instead.


POST /webhook/inbound/:connection_id #

Push payroll data via webhook. Uses HMAC signature instead of API keys. See Webhooks for setup and signature details.

Body: Same format as /payroll/reports

Required headers:

Header Description
X-Webhook-Signature HMAC-SHA256(secret, "${timestamp}.${body}") hex digest
X-Webhook-Timestamp Unix milliseconds (within 5 min of server time)

Response: Same shape as /payroll/reports


️ Error Format #

All errors return JSON:

{
  "error": "Error type",
  "message": "Human-readable description"
}
Status Meaning Action
202 ✓ Accepted Success — processing
400 ✗ Bad Request Check request body
401 Unauthorized Check credentials
404 Not Found Check IDs/FEIN
500 Server Error Retry, then contact support
502 Upstream Error Retry

Client Libraries #

JavaScript #

class Audit1Client {
  constructor(clientId, clientSecret) {
    this.baseUrl = "https://apiv2.audit1.com/api/v2";
    this.headers = {
      "X-Client-ID": clientId,
      "X-Client-Secret": clientSecret,
      "Content-Type": "application/json",
    };
  }

  async submitPayroll(employerFein, policyNumber, employees, payPeriod) {
    const res = await fetch(`${this.baseUrl}/payroll/reports`, {
      method: "POST",
      headers: this.headers,
      body: JSON.stringify({ employer_fein: employerFein, policy_number: policyNumber, employees, pay_period: payPeriod }),
    });
    if (!res.ok) throw new Error(`API Error (${res.status}): ${(await res.json()).error}`);
    return res.json();
  }

  async syncEmployees(employerId, employees) {
    const res = await fetch(`${this.baseUrl}/employees/sync`, {
      method: "POST",
      headers: this.headers,
      body: JSON.stringify({ employer_id: employerId, employees }),
    });
    if (!res.ok) throw new Error(`API Error (${res.status}): ${(await res.json()).error}`);
    return res.json();
  }

  async getFileStatus(fileId) {
    const res = await fetch(`${this.baseUrl}/files/status/${fileId}`, { headers: this.headers });
    if (!res.ok) throw new Error(`API Error (${res.status}): ${(await res.json()).error}`);
    return res.json();
  }
}

// Usage
const client = new Audit1Client(process.env.AUDIT1_CLIENT_ID, process.env.AUDIT1_CLIENT_SECRET);
const result = await client.submitPayroll("12-3456789", "WC1025561", [
  { first_name: "Jane", last_name: "Smith", class_code: "8810", state: "CA", gross_wages: 2500 }
], { start_date: "2024-01-01", end_date: "2024-01-15" });

Python #

import os, requests

class Audit1Client:
    def __init__(self, client_id, client_secret):
        self.base_url = "https://apiv2.audit1.com/api/v2"
        self.headers = {"X-Client-ID": client_id, "X-Client-Secret": client_secret}

    def submit_payroll(self, employer_fein, policy_number, employees, pay_period=None):
        r = requests.post(f"{self.base_url}/payroll/reports", headers=self.headers,
            json={"employer_fein": employer_fein, "policy_number": policy_number,
                  "employees": employees, "pay_period": pay_period}, timeout=30)
        r.raise_for_status()
        return r.json()

    def sync_employees(self, employer_id, employees):
        r = requests.post(f"{self.base_url}/employees/sync", headers=self.headers,
            json={"employer_id": employer_id, "employees": employees}, timeout=30)
        r.raise_for_status()
        return r.json()

    def get_file_status(self, file_id):
        r = requests.get(f"{self.base_url}/files/status/{file_id}", headers=self.headers, timeout=30)
        r.raise_for_status()
        return r.json()

# Usage
client = Audit1Client(os.environ["AUDIT1_CLIENT_ID"], os.environ["AUDIT1_CLIENT_SECRET"])
result = client.submit_payroll("12-3456789", "WC1025561", [
    {"first_name": "Jane", "last_name": "Smith", "class_code": "8810", "state": "CA", "gross_wages": 2500}
], {"start_date": "2024-01-01", "end_date": "2024-01-15"})