Skip to content

API Reference

The OpenInsure API is a versioned REST API built with Hono on Cloudflare Workers. The OpenAPI specification is auto-generated from TypeSpec route definitions and is always in sync with the deployed API.

Interactive API Explorer (Scalar)

Browse, search, and try every endpoint in the interactive Scalar explorer. Access requires Cloudflare Access authentication.

EnvironmentBase URL
Productionhttps://api.openinsure.dev/v1
Sandboxhttps://sandbox.api.openinsure.dev/v1
Local (Wrangler dev)http://localhost:8787/v1

The sandbox environment uses isolated test data. Stripe is in test mode (use 4242 4242 4242 4242 as the test card). Policies bound in the sandbox are never reported to carriers.

All API requests require auth. The platform currently supports three primary modes:

1. Auth Session Exchange → OpenInsure JWT

Section titled “1. Auth Session Exchange → OpenInsure JWT”

If the caller already has an auth session token (from the D1-backed auth worker at auth-dev.openinsure.dev), exchange it for an OpenInsure API JWT:

Terminal window
curl -X POST https://api.openinsure.dev/auth/better/exchange \
-H "Authorization: Bearer <session-token>"

Response contains a signed OpenInsure JWT (token) used as Authorization: Bearer <token> for all subsequent API calls.

2. API Secret Bearer (System/Machine Workflows)

Section titled “2. API Secret Bearer (System/Machine Workflows)”

For internal automation and demo/smoke scripts, the configured API_SECRET can be used directly as a bearer token:

Terminal window
curl https://api.openinsure.dev/v1/policies?orgId=<org-uuid> \
-H "Authorization: Bearer $API_SECRET"

You can also exchange API_SECRET for a short-lived demo JWT:

Terminal window
curl -X POST https://api.openinsure.dev/auth/demo \
-H "Content-Type: application/json" \
-d '{"secret":"'"$API_SECRET"'"}'

3. API Key (Machine-to-Machine Integrations)

Section titled “3. API Key (Machine-to-Machine Integrations)”

Long-lived API keys for server-to-server integrations. Created in the Admin UI under Settings → API Keys or via the API:

Terminal window
POST /v1/api-keys
Authorization: Bearer <admin_jwt>
Content-Type: application/json
{
"name": "Applied Epic Integration",
"scopes": ["submissions:write", "policies:read", "coi:generate"],
"expiresAt": null // null = never expires (rotate manually)
}
# Response:
{
"key": "oik_live_1a2b3c4d5e6f7g8h...", // shown ONCE store securely
"id": "key_01J8...",
"name": "Applied Epic Integration",
"scopes": ["submissions:write", "policies:read", "coi:generate"]
}

Use the API key in the X-API-Key header (or Authorization: Bearer):

Terminal window
curl https://api.openinsure.dev/v1/submissions \
-H "X-API-Key: oik_live_1a2b3c4d5e6f7g8h..."
EnvironmentLimitWindow
Sandbox100 requestsper minute per API key
Production (standard)1,000 requestsper minute per API key
Production (enterprise)10,000 requestsper minute per API key
AI endpoints (/documents/ingest)20 requestsper minute

Rate limit headers are included in every response:

X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 847
X-RateLimit-Reset: 1719878460

When you exceed the rate limit, the API returns 429 Too Many Requests with a Retry-After header.

All errors return a consistent JSON structure:

{
"error": "validation_failed",
"message": "Missing required field: effective_date",
"details": {
"field": "effective_date",
"rule": "required"
},
"requestId": "req_01J8K3M4N5P6Q7R8"
}
CodeMeaning
200 OKSuccessful GET or PATCH
201 CreatedSuccessful POST that created a resource
204 No ContentSuccessful DELETE
400 Bad RequestMalformed JSON or missing required fields
401 UnauthorizedMissing or invalid Bearer token / API key
403 ForbiddenValid auth, but SpiceDB ReBAC denied the action
404 Not FoundResource does not exist in your organization
409 ConflictDuplicate resource (e.g., policy number collision)
422 Unprocessable EntityBusiness rule violation (invalid state transition, DA limit exceeded)
429 Too Many RequestsRate limit exceeded
500 Internal Server ErrorUnexpected error — include requestId in support tickets
MethodPathDescription
POST/submissionsCreate a new submission (JSON or ACORD 125 PDF)
GET/submissionsList submissions (filterable by status, producer, date)
GET/submissions/:idGet submission detail including triage results
POST/submissions/:id/quoteRate a submission and return a quote
GET/submissions/:id/quote-readinessTriage score (0–100) and binding readiness
POST/submissions/:id/bindBind a quoted submission into a policy
GET/policiesList policies
GET/policies/:idGet policy detail
POST/policies/:id/endorseApply a mid-term endorsement
POST/policies/:id/cancelCancel a policy
POST/policies/:id/reinstateReinstate a cancelled policy
POST/policies/:id/renewInitiate renewal process
GET/policies/:id/timelineFull endorsement and transaction history
MethodPathDescription
POST/claimsFile FNOL
GET/claimsList claims (filterable by status, adjuster, policy)
GET/claims/:idGet claim detail
POST/claims/:id/investigateStart investigation phase
POST/claims/:id/reservesSet or update reserves
POST/claims/:id/settleCreate settlement offer
POST/claims/:id/denyDeny coverage
POST/claims/:id/closeClose the claim
POST/claims/:id/attachmentsUpload claim document
GET/claims/:id/attachmentsList claim documents
MethodPathDescription
GET/invoicesList invoices
GET/invoices/:idGet invoice detail
POST/invoices/:id/payment-intentCreate Stripe PaymentIntent
POST/invoices/:id/payRecord a manual payment
POST/invoices/:id/voidVoid an invoice
POST/invoices/:id/refundIssue a refund
POST/invoices/:id/retryRetry a failed payment
MethodPathDescription
POST/coi/generateGenerate a Certificate of Insurance
POST/documents/ingestIngest and AI-classify a document
POST/documents/render-pdfRender HTML template to PDF
GET/documents/:idGet document metadata and download URL
MethodPathDescription
GET/webhooksList webhook subscriptions
POST/webhooksCreate a webhook endpoint
DELETE/webhooks/:idDelete a webhook
MethodPathDescription
GET/producersList producers with search + status filter
GET/producers/:idProducer detail with license info
PATCH/producers/:idUpdate producer profile
GET/producers/:id/appointmentsList state/LOB appointments
POST/producers/:id/appointmentsCreate an appointment
PATCH/producers/:id/appointments/:apptIdUpdate appointment status
MethodPathDescription
GET/deal-roomsList deal rooms for the organization
POST/deal-roomsCreate a deal room for a submission
GET/deal-rooms/:idGet deal room detail + participants
GET/deal-rooms/:id/messagesPaginated message history
POST/deal-rooms/:id/messagesPost a message
GET/deal-rooms/:id/wsUpgrade to WebSocket (returns signed wsUrl)
GET/referralsList referrals (filterable by status, entity)
POST/referralsCreate a referral (optionally linked to a deal room)
POST/referrals/:id/decisionApprove, reject, or request more info
MethodPathDescription
GET/flagsList all feature flags and per-org overrides
PUT/flags/:keySet a flag value (org_admin scoped)
DELETE/flags/:keyRemove a per-org override (revert to global default)
MethodPathDescription
GET/analytics/:orgId/portfolioPortfolio KPIs (GWP, loss ratio, binding count)
GET/analytics/:orgId/commissionsCommission summary by period
GET/analytics/:orgId/da-utilizationDA aggregate utilization

OpenInsure sends webhook notifications for all major domain events. All payloads are signed with HMAC-SHA256.

EventDescription
policy.boundA new policy was successfully bound
policy.endorsedA mid-term endorsement was applied
policy.cancelledA policy was cancelled
policy.renewedA renewal policy was created
claim.filedA new FNOL was received
claim.reservedClaim reserves were set or updated
claim.settledA settlement was approved
claim.closedA claim was closed
payment.receivedA premium payment was processed
payment.failedA payment attempt failed
da.limit_approachingDA aggregate utilization exceeded 85%
compliance.deadline_approachingA filing deadline is within 30 days
{
"id": "evt_01J8K3M4N5P6Q7R8S9T0",
"type": "policy.bound",
"orgId": "org_01J8...",
"timestamp": "2026-03-08T14:32:00Z",
"data": {
"policyId": "pol_01J8...",
"policyNumber": "GL-2026-000142",
"grossPremium": 14250,
"effectiveDate": "2026-04-01"
}
}
import { createHmac, timingSafeEqual } from 'crypto';
function verifyWebhook(rawBody: string, signature: string, secret: string): boolean {
const expected = createHmac('sha256', secret).update(rawBody).digest('hex');
return timingSafeEqual(Buffer.from(signature), Buffer.from(`sha256=${expected}`));
}
// In your webhook handler:
const sig = request.headers.get('X-OpenInsure-Signature');
if (!verifyWebhook(rawBody, sig, process.env.WEBHOOK_SECRET)) {
return new Response('Invalid signature', { status: 401 });
}
Terminal window
# Full submission → quote → bind in bash
API="https://api.openinsure.dev/v1"
TOKEN="your_api_key_here"
# Create submission
SUB_ID=$(curl -s -X POST $API/submissions \
-H "X-API-Key: $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"insuredName": "Pacific Coast Builders",
"naicsCode": "236220",
"annualRevenue": 8000000,
"requestedLimit": 1000000,
"effectiveDate": "2026-05-01",
"state": "OR",
"lineOfBusiness": "GL"
}' | jq -r '.id')
# Get quote
QUOTE=$(curl -s -X POST $API/submissions/$SUB_ID/quote \
-H "X-API-Key: $TOKEN")
echo "Premium: $(echo $QUOTE | jq '.grossPremium')"
# Bind
POLICY=$(curl -s -X POST $API/submissions/$SUB_ID/bind \
-H "X-API-Key: $TOKEN" \
-H "Content-Type: application/json" \
-d '{"installmentPlan":"MONTHLY_10"}')
echo "Policy: $(echo $POLICY | jq -r '.policyNumber')"

The portal API serves policyholder-facing data for both the web portal and mobile app. All endpoints require a Bearer JWT with typ: 'insured'.

EndpointMethodDescription
/v1/portal/homeGETAggregated dashboard data (mobile home screen)
/v1/portal/policiesGETList policies for authenticated insured
/v1/portal/policies/:idGETPolicy detail
/v1/portal/policies/:id/coiGETPresigned COI download URL
/v1/portal/policies/:id/mcs90GETPresigned MCS-90 download URL
/v1/portal/invoicesGETList invoices
/v1/portal/invoices/:idGETInvoice detail
/v1/portal/invoices/:id/downloadGETPresigned invoice PDF download
/v1/portal/documentsGETList policy-linked documents
/v1/portal/documents/:id/downloadGETPresigned document download
/v1/portal/profileGETInsured profile
/v1/portal/profilePUTUpdate contact fields (phone, address)
/v1/portal/push-tokensPOSTRegister push notification token

Authentication: Policyholder JWTs are obtained via the OTP login flowPOST /auth/policyholder-otp-request followed by POST /auth/policyholder-token.

The TypeSpec-generated OpenAPI 3.1 spec is available at:

https://api.openinsure.dev/openapi.json

You can use this spec to:

  • Generate a typed client SDK in any language with tools like openapi-ts, openapi-generator, or Speakeasy
  • Import into Postman, Insomnia, or Bruno for local testing
  • Set up contract testing in your CI pipeline

The spec is regenerated on every deploy and is always in sync with the production API.