Skip to content

Document Generation

The @openinsure/documents package generates all insurance documents as HTML (rendered to PDF via browser capture) and manages the full document lifecycle from generation through e-signature and archival.


Policy Documents

Declarations page, policy jacket, endorsement certificate, binder

Sales & Quotes

Quote proposal, premium indication letter, coverage comparison

Certificates

Certificate of Insurance (COI / ACORD 25), MCS-90 motor carrier certificate

Billing

Invoice, installment schedule, check (disbursement)

Claims

Reservation of rights letter, coverage denial, settlement agreement, proof of loss

Compliance

Notice of Cancellation (NOC), non-renewal notice, loss run, bordereaux report


Every document type has a typed builder function that accepts structured data and returns HTML:

import {
buildQuoteHTML,
buildCOIHTML,
buildNOCHTML,
buildDecPageHTML,
buildEndorsementHTML,
buildSettlementHTML,
buildLossRunHTML,
} from '@openinsure/documents';
const html = buildCOIHTML({
insuredName: 'Acme Roofing LLC',
policyNumber: 'GL-2025-000001',
effectiveDate: '2025-06-01',
expirationDate: '2026-06-01',
insurer: 'Vermont Mutual Insurance',
coverages: [
{ type: 'General Liability', occurrence: 1_000_000, aggregate: 2_000_000 },
{ type: 'Products & Completed Ops', aggregate: 2_000_000 },
],
certificateHolder: {
name: 'Vermont DOT',
address: '219 North Main St, Barre, VT 05641',
},
additionalInsured: true,
producerName: 'Mountain Insurance Agency',
});
const html = buildQuoteHTML({
submission,
ratingAudit, // full factor waterfall from the rating engine
validUntil: '2025-07-01',
producerName: 'Mountain Insurance Agency',
uwName: 'Michael Chen',
});

Documents are rendered to PDF using the Cloudflare Browser binding (BROWSER in wrangler.toml). The renderHTMLToPDF() utility:

  1. Launches a headless Chromium instance via the Browser binding
  2. Loads the HTML with the OpenInsure document CSS
  3. Captures a print-media PDF at A4 / US Letter
  4. Streams the result to R2
import { renderHTMLToPDF } from '../lib/pdf-renderer';
const pdfBuffer = await renderHTMLToPDF(html, { format: 'Letter', margin: '0.5in' });
await env.DOCUMENTS.put(`policies/${policyId}/dec-page.pdf`, pdfBuffer);

OpenInsure uses a self-hosted Documenso instance (openinsure-documenso.fly.dev) for e-signature workflows. Documenso is configured via ESIGN_PROVIDER=documenso in wrangler.toml.

  1. Document is generated as a PDF and uploaded to Documenso via the API 2. Signing fields are placed programmatically (producer signature, insured signature, date) 3. Signatories receive an email with a one-click signing link (no account required) 4. On completion, the signed PDF is stored in R2 and the policy/submission record is updated 5. All parties receive a signed copy by email
Terminal window
POST /v1/documents/:id/request-signature
Authorization: Bearer <token>
Content-Type: application/json
{
"documentType": "binder",
"signatories": [
{ "name": "Jane Smith", "email": "jane@acme.com", "role": "insured" },
{ "name": "Mountain Insurance Agency", "email": "agent@mountain.com", "role": "producer" }
],
"redirectUrl": "https://portal.openinsure.dev/policies/{id}?signed=true"
}
Terminal window
GET /v1/documents/:id/signature-status
# Returns: { status: "pending" | "completed" | "declined", signatories: [...] }

Beyond PDFs, the documents package provides tabular exports:

import { exportToCSV, exportToExcel } from '@openinsure/documents';
// Loss run CSV for underwriting
const csv = exportToCSV(lossRunRows, {
columns: ['claimNumber', 'dateOfLoss', 'status', 'totalIncurred'],
filename: 'loss-run-2025.csv',
});
// Bordereaux Excel workbook for carrier submission
const xlsx = await exportToExcel(bordereaux, {
sheets: ['Premium', 'Claims', 'Summary'],
});

All generated documents are stored in the oi-documents R2 bucket bound as DOCUMENTS in wrangler.toml. The key schema is:

policies/{policyId}/dec-page.pdf
policies/{policyId}/endorsements/{endorsementId}.pdf
policies/{policyId}/coi/{coiId}.pdf
claims/{claimId}/settlement-agreement.pdf
submissions/{submissionId}/quote-proposal.pdf

Documents are served through the API at:

GET /v1/documents/{documentId}/download

Presigned R2 URLs with 24-hour expiry are returned for direct download without proxying through the Worker.


  1. Create src/builders/{document - type}.ts — returns HTML string 2. Export from src/index.ts
  2. Add a route in apps/api/src/routes/documents.ts that calls the builder and stores the result
  3. Add the document type to the documentType enum in packages/types 5. Write tests in src/__tests__/ using snapshot testing (compare generated HTML to fixture)