API Documentation

Integrate certificate of insurance data extraction directly into your product with a simple, secure API.

API access is part of the Pro plan. Accounts on the Starter and Plus plans can read this page, but calls to the endpoints below are refused.

Authentication

Authenticate every request with an API token created in your dashboard.

Create a personal API token, then include it in the Authorization header on each call:

Authorization: Bearer YOUR_API_TOKEN

To create an API token:

  1. Log in to your COISoftware account
  2. Navigate to the API section
  3. Name the token and tick the permissions it needs, then click "Create"

A new token comes with all four permissions ticked, which is exactly what the endpoints below need. Leave them as they are unless you want a narrower token; unticking one makes the matching call return HTTP 403:

  • upload-documents for POST /api/documents/upload
  • extract-data for POST /api/documents/extract
  • view-extractions for GET /api/documents/extraction/{hash}
  • read for GET /api/documents/types

API Endpoints

Core endpoints for upload, extraction, and results

Upload Document

POST /api/documents/upload

Upload a certificate of insurance file to begin processing.

Request

Content-Type: multipart/form-data

{
  "document": [file] // Required: The document file to upload
}

Response

{
  "success": true,
  "message": "File uploaded successfully",
  "data": {
    "file_id": 1234,
    "name": "coi.pdf"
  }
}

Extract Data

POST /api/documents/extract

Start extraction for a previously uploaded file.

Request

Content-Type: application/json

{
  "file_id": 1234,                // Required: ID of the uploaded file
  "document_type_id": 5678        // Optional: ID of the document template to use
}

Response

{
  "success": true,
  "message": "Extraction initialized successfully",
  "data": {
    "extraction_id": 9876,
    "extraction_hash": "abc123-def456-ghi789-jkl012"
  }
}

Get Extraction Results

GET /api/documents/extraction/{hash}

Fetch the parsed results for a given extraction hash.

Parameters

{hash}: The extraction hash returned by the extract endpoint

Fields in the response

  • status: pending, processing, completed, error or limit_exceeded. Poll until it is completed; before that the field list can still be empty.
  • message: why the document stopped, when status is error or limit_exceeded (the same sentence the result page shows, for example that the file is not a certificate of insurance or is over the page limit of your plan). It is null for every other status.
  • fields[].label: the template field name, for example Named Insured or Certificate Holder. It is null when the field is not tied to a template.
  • fields[].content: the extracted value itself, as read from the document.
  • fields[].is_table: true for a table field such as the coverage lines.
  • fields[].rows[].cells[]: present only on table fields. Each cell carries value and is_header; the header row comes first and names the columns.
  • Table names and columns follow the template you pick. The example below uses the Certificate of Insurance (Generic) template, which returns two tables: Coverage Lines, with the insurance company, NAIC and insurer letter on each policy row, and Insurers Affording Coverage (Insurer Letter, Insurer Name, NAIC #). The ACORD 25 template returns two tables instead: Insurers Affording Coverage (Insurer Letter, Insurer Name, NAIC #) and Coverages, where each policy row names its insurer by letter. Read the column names from the header row rather than relying on their position.

Response

{
  "success": true,
  "data": {
    "extraction_id": 9876,
    "extraction_hash": "abc123-def456-ghi789-jkl012",
    "file": {
      "id": 1234,
      "name": "coi.pdf"
    },
    "document_type": {
      "id": 5678,
      "name": "Certificate of Insurance (Generic)"
    },
    "status": "completed",
    "message": null,
    "fields": [
      {
        "field_id": 5421,
        "label": "Named Insured",
        "content": "Wrenfield Steel Erectors LLC\n2290 Foundry Road, Grove City, OH 43123",
        "is_table": false,
        "page": 1
      },
      {
        "field_id": 5424,
        "label": "Certificate Holder",
        "content": "Stonegate Industrial Partners LP\n400 Commerce Parkway, Dublin, OH 43017",
        "is_table": false,
        "page": 1
      },
      {
        "field_id": 5428,
        "label": "Certificate Date",
        "content": "09/20/2026",
        "is_table": false,
        "page": 1
      },
      {
        "field_id": 5423,
        "label": "Coverage Lines",
        "content": "Coverage Lines",
        "is_table": true,
        "page": 1,
        "rows": [
          {
            "cells": [
              {"value": "Coverage Type", "is_header": true},
              {"value": "Insurance Company", "is_header": true},
              {"value": "NAIC #", "is_header": true},
              {"value": "Policy Number", "is_header": true},
              {"value": "Effective Date", "is_header": true},
              {"value": "Expiration Date", "is_header": true},
              {"value": "Limits", "is_header": true},
              {"value": "Insurer Letter", "is_header": true}
            ]
          },
          {
            "cells": [
              {"value": "COMMERCIAL GENERAL LIABILITY", "is_header": false},
              {"value": "Aldergrove Mutual Casualty Co.", "is_header": false},
              {"value": "22357", "is_header": false},
              {"value": "GL-UCZ-7714", "is_header": false},
              {"value": "10/01/2026", "is_header": false},
              {"value": "10/01/2027", "is_header": false},
              {"value": "EACH OCCURRENCE $1,000,000 ...", "is_header": false},
              {"value": "A", "is_header": false}
            ]
          }
        ]
      },
      {
        "field_id": 5429,
        "label": "Insurers Affording Coverage",
        "content": "Insurers Affording Coverage",
        "is_table": true,
        "page": 1,
        "rows": [
          {
            "cells": [
              {"value": "Insurer Letter", "is_header": true},
              {"value": "Insurer Name", "is_header": true},
              {"value": "NAIC #", "is_header": true}
            ]
          },
          {
            "cells": [
              {"value": "A", "is_header": false},
              {"value": "Aldergrove Mutual Casualty Co.", "is_header": false},
              {"value": "22357", "is_header": false}
            ]
          }
        ]
      }
    ]
  }
}

Get Document Types

GET /api/documents/types

Retrieve a list of available document templates.

Response

{
  "success": true,
  "data": [
    {
      "id": 5678,
      "name": "ACORD 25 Certificate",
      "description": "Template for processing certificate of insurance documents",
      "is_public": true,
      "fields_count": 10
    },
    {
      "id": 5679,
      "name": "Additional Insured Endorsement",
      "description": "Template for processing endorsement documents",
      "is_public": false,
      "fields_count": 8
    }
  ]
}

Code Examples

Copy‑paste snippets to integrate certificate of insurance data extraction in minutes

Upload and extract with cURL

# Upload document
curl -X POST https://coisoftware.com/api/documents/upload \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -F "document=@/path/to/document.pdf"

# Extract data
curl -X POST https://coisoftware.com/api/documents/extract \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"file_id": 1234, "document_type_id": 5678}'

# Get results
curl -X GET https://coisoftware.com/api/documents/extraction/abc123-def456-ghi789-jkl012 \
  -H "Authorization: Bearer YOUR_API_TOKEN"

Complete certificate of insurance data extraction in PHP

<?php

$apiToken = 'YOUR_API_TOKEN';
$apiBaseUrl = 'https://coisoftware.com/api';

// 1. Upload document
$filePath = '/path/to/document.pdf';
$ch = curl_init("$apiBaseUrl/documents/upload");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    "Authorization: Bearer $apiToken"
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, [
    'document' => new CURLFile($filePath)
]);

$uploadResponse = curl_exec($ch);
curl_close($ch);

$uploadResult = json_decode($uploadResponse, true);
if (!$uploadResult['success']) {
    die('Error uploading document: ' . $uploadResult['message']);
}

$fileId = $uploadResult['data']['file_id'];
echo "File uploaded with ID: $fileId\n";

// 2. Extract data
$ch = curl_init("$apiBaseUrl/documents/extract");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    "Authorization: Bearer $apiToken",
    "Content-Type: application/json"
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
    'file_id' => $fileId,
    'document_type_id' => 5678 // Optional
]));

$extractResponse = curl_exec($ch);
curl_close($ch);

$extractResult = json_decode($extractResponse, true);
if (!$extractResult['success']) {
    die('Error extracting data: ' . $extractResult['message']);
}

$extractionHash = $extractResult['data']['extraction_hash'];
echo "Extraction initialized with hash: $extractionHash\n";

// 3. Get extraction results
$ch = curl_init("$apiBaseUrl/documents/extraction/$extractionHash");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    "Authorization: Bearer $apiToken"
]);

$resultsResponse = curl_exec($ch);
curl_close($ch);

$resultsData = json_decode($resultsResponse, true);
if (!$resultsData['success']) {
    die('Error getting extraction results: ' . $resultsData['message']);
}

// Process the extracted data
$fields = $resultsData['data']['fields'];
foreach ($fields as $field) {
    echo ($field['label'] ?? 'Field') . ': ' . $field['content'] . "\n";
}

Complete certificate of insurance data extraction in JavaScript

// Complete document processing with JavaScript
const apiToken = 'YOUR_API_TOKEN';
const apiBaseUrl = 'https://coisoftware.com/api';

// 1. Upload document
async function uploadDocument(filePath) {
  const formData = new FormData();
  formData.append('document', document.querySelector('input[type="file"]').files[0]);

  const response = await fetch(`${apiBaseUrl}/documents/upload`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${apiToken}`
    },
    body: formData
  });

  const result = await response.json();
  if (!result.success) {
    throw new Error(`Error uploading document: ${result.message}`);
  }

  console.log(`File uploaded with ID: ${result.data.file_id}`);
  return result.data.file_id;
}

// 2. Extract data
async function extractData(fileId, documentTypeId = null) {
  const payload = {
    file_id: fileId
  };

  if (documentTypeId) {
    payload.document_type_id = documentTypeId;
  }

  const response = await fetch(`${apiBaseUrl}/documents/extract`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${apiToken}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(payload)
  });

  const result = await response.json();
  if (!result.success) {
    throw new Error(`Error extracting data: ${result.message}`);
  }

  console.log(`Extraction initialized with hash: ${result.data.extraction_hash}`);
  return result.data.extraction_hash;
}

// 3. Get extraction results
async function getExtractionResults(extractionHash) {
  const response = await fetch(`${apiBaseUrl}/documents/extraction/${extractionHash}`, {
    method: 'GET',
    headers: {
      'Authorization': `Bearer ${apiToken}`
    }
  });

  const result = await response.json();
  if (!result.success) {
    throw new Error(`Error getting extraction results: ${result.message}`);
  }

  console.log('Extraction results:', result.data);

  // Process the extracted data
  const fields = result.data.fields;
  fields.forEach(field => {
    console.log(`${field.label ?? 'Field'}: ${field.content}`);
  });

  return result.data;
}

// Usage
async function processDocument() {
  try {
    const fileId = await uploadDocument();
    const extractionHash = await extractData(fileId, 5678); // Optional document type ID
    const extractionData = await getExtractionResults(extractionHash);

    // Do something with the extracted data
    displayResults(extractionData);
  } catch (error) {
    console.error(error);
  }
}

function displayResults(data) {
  // Display the results in your application
  const resultsDiv = document.getElementById('results');
  resultsDiv.innerHTML = '';

  data.fields.forEach(field => {
    const fieldEl = document.createElement('div');
    fieldEl.classList.add('field');
    fieldEl.innerHTML = `${field.label ?? 'Field'}: ${field.content}`;
    resultsDiv.appendChild(fieldEl);
  });
}

Complete certificate of insurance data extraction in Python

import requests
import json
import time

api_token = 'YOUR_API_TOKEN'
api_base_url = 'https://coisoftware.com/api'

# 1. Upload document
def upload_document(file_path):
    headers = {
        'Authorization': f'Bearer {api_token}'
    }

    with open(file_path, 'rb') as file:
        files = {'document': file}
        response = requests.post(
            f'{api_base_url}/documents/upload',
            headers=headers,
            files=files
        )

    result = response.json()
    if not result.get('success'):
        raise Exception(f"Error uploading document: {result.get('message')}")

    file_id = result['data']['file_id']
    print(f"File uploaded with ID: {file_id}")
    return file_id

# 2. Extract data
def extract_data(file_id, document_type_id=None):
    headers = {
        'Authorization': f'Bearer {api_token}',
        'Content-Type': 'application/json'
    }

    payload = {
        'file_id': file_id
    }

    if document_type_id:
        payload['document_type_id'] = document_type_id

    response = requests.post(
        f'{api_base_url}/documents/extract',
        headers=headers,
        data=json.dumps(payload)
    )

    result = response.json()
    if not result.get('success'):
        raise Exception(f"Error extracting data: {result.get('message')}")

    extraction_hash = result['data']['extraction_hash']
    print(f"Extraction initialized with hash: {extraction_hash}")
    return extraction_hash

# 3. Get extraction results
def get_extraction_results(extraction_hash):
    headers = {
        'Authorization': f'Bearer {api_token}'
    }

    response = requests.get(
        f'{api_base_url}/documents/extraction/{extraction_hash}',
        headers=headers
    )

    result = response.json()
    if not result.get('success'):
        raise Exception(f"Error getting extraction results: {result.get('message')}")

    print("Extraction results:")
    fields = result['data']['fields']
    for field in fields:
        print(f"{field.get('label') or 'Field'}: {field['content']}")

    return result['data']

# Usage
def process_document(file_path, document_type_id=None):
    try:
        # Upload document
        file_id = upload_document(file_path)

        # Extract data
        extraction_hash = extract_data(file_id, document_type_id)

        # Get results (you might need to wait for processing to complete)
        time.sleep(2)  # Simple delay to wait for processing
        extraction_data = get_extraction_results(extraction_hash)

        # Process the extracted data further if needed
        return extraction_data
    except Exception as e:
        print(f"Error: {str(e)}")
        return None

# Process certificate of insurance
if __name__ == "__main__":
    result = process_document('/path/to/coi.pdf', 5678)  # Optional document type ID
    print(json.dumps(result, indent=2))

Limits & Support

What actually limits your usage, and how to get help

Limits

There is no per-hour request limit on the API today, and no HTTP 429 response. What does limit you is the monthly certificate allowance of your plan, the same pool the web application draws on: once it is used up, an extraction is refused until the allowance resets or you move up a plan.

Please still call the API at a sensible pace. A tight polling loop on GET /api/documents/extraction/{hash} gains nothing: poll every few seconds and stop as soon as status is completed.

Support

If you have any questions or need assistance with our API, please contact us: