Coverly Public API
The v1 surface brokers and carrier partners use to push leads, request quotes, query policy details, and validate webhook delivery — programmatically, without the web UI.
https://coverly-8.polsia.app/api/v1. For api-tester conveniences, every endpoint accepts JSON bodies and returns JSON. Every authenticated request appends a row to api_request_log (path, status, latency) for audit.Authentication
Every v1 request must include a Bearer token in the Authorization header. Each key belongs to exactly one partner — the response data is scoped to your contacts only.
Authorization: Bearer ck_live_a3f9xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxKeys are produced by POST /api/partner-keys on the partner portal. You can also reuse the new POST /api/partner-keys/:id/rotate endpoint to swap a key while keeping its row.
Rate limits
Per-key sliding windows, surfaced as standard HTTP headers on every response:
- 60 requests/minute per key
- 1000 requests/hour per key
X-RateLimit-Limit: 60 X-RateLimit-Remaining: 57 X-RateLimit-Reset: 42 // seconds until the minute window resets X-RateLimit-Limit-Hour: 1000 X-RateLimit-Remaining-Hour: 973 X-RateLimit-Reset-Hour: 1820 Retry-After: 42 // present on 429 only
On exceed, the request returns 429 rate_limited with a Retry-After header (same value as the relevant reset seconds).
Versioning
v1 endpoints live under /api/v1. Breaking changes (payload shape, headers, auth) bump the prefix (/api/v2); additive changes (new optional field, new endpoint) never break existing integrations.
v1. There is no public beta channel — preview flags, if any, are off-by-default and announced in the changelog.POST /leads
Push a new lead or upsert an existing one (matched on lowercase email). Returns the assigned contact id, AI tier + score, and the partner the lead is attributed to.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
| name | string | Lead name. Defaults to "Anonymous". | |
| string | required | Lead email — used as the upsert key. | |
| phone | string | E.164 preferred. | |
| zipCode | string | required | 5-digit ZIP used for carrier routing. |
| requestedProduct | string | life (default — auto) or health. | |
| vehicle | object | { year, make, model }. Required for life leads. | |
| health | object | For health leads: { householdSize, memberAges, householdAgi, tobacco, planTierPreference, ... } |
curl -X POST https://coverly-8.polsia.app/api/v1/leads \ -H "Authorization: Bearer ck_live_a3f9xxxxxxxxxxxxxxxx" \ -H "Content-Type: application/json" \ -d '{ "name": "Jane Doe", "email": "jane@example.com", "zipCode": "90210", "vehicle": { "year": 2021, "make": "Toyota", "model": "Camry" } }'
Response
{
"id": 1842,
"ai_tier": "warm",
"ai_score": 63,
"ai_insight": "Lead created via /api/v1/leads — pending qualification.",
"status": "new",
"product_line": "life",
"partner_id": 42
}GET /leads
Paginated list of your leads (scoped to the authenticated partner).
Query parameters
| Param | Type | Description |
|---|---|---|
| status | string | Filter by new|contacted|converted|lost. |
| limit | integer | Default 25, max 100. |
| page | integer | 1-indexed. Defaults to 1. |
curl -G https://coverly-8.polsia.app/api/v1/leads \ -H "Authorization: Bearer ck_live_a3f9xxxxxxxxxxxxxxxx" \ --data-urlencode "status=converted" \ --data-urlencode "page=1" \ --data-urlencode "limit=50"
Response
{
"rows": [
{
"id": 1842,
"name": "Jane Doe",
"email": "jane@example.com",
"ai_tier": "warm",
"ai_score": 63,
"status": "contacted",
"selected_carrier": "AIG",
"monthly_premium": 187,
"bound_application_id": 412,
"bound_policy_number": "CVLY-2026-00942",
"created_at": "2026-07-17T11:42:09Z"
}
],
"total": 127,
"page": 1,
"limit": 25
}GET /leads/:id
Lead detail — contact, intake payload, applications, and event log. Returns 403 if the contact belongs to another partner, 404 if missing.
curl https://coverly-8.polsia.app/api/v1/leads/1842 \ -H "Authorization: Bearer ck_live_a3f9xxxxxxxxxxxxxxxx"
Response
{
"contact": { "id": 1842, "ai_tier": "warm", "...": "..." },
"intake": { "payload": { "..." }, "company": "...", "source": "..." },
"applications": [
{
"id": 412,
"carrier": "AIG",
"product_type": "auto",
"monthly_premium": 187,
"status": "bound",
"policy_number": "CVLY-2026-00942",
"policy_issued_at": "2026-07-17T13:11:00Z"
}
],
"events": [
{
"id": 4120,
"event_type": "carrier_selected",
"carrier_selected": "AIG",
"monthly_price": 187,
"created_at": "2026-07-17T12:55:00Z"
}
],
"bound_application_id": 412,
"bound_policy_number": "CVLY-2026-00942"
}POST /quotes
Re-run quote generation for an existing lead. Returns the same enriched carrier/AI shape as the web /quote/results page.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
| lead_id | integer | required | Contact id from POST /leads. |
| force_refresh | boolean | Informational — engine regenerates from scratch each call. |
curl -X POST https://coverly-8.polsia.app/api/v1/quotes \ -H "Authorization: Bearer ck_live_a3f9xxxxxxxxxxxxxxxx" \ -H "Content-Type: application/json" \ -d '{ "lead_id": 1842 }'
Response (life / auto)
{
"lead_id": 1842,
"product_line": "life",
"ai_tier": "warm",
"ai_score": 63,
"ai_insight": "Lead qualifies for coverage.",
"ai_angle": "Save up to $500 on auto insurance.",
"carriers": [
{ "name": "AIG", "monthly_price": 187, "is_live": true },
{ "name": "Lincoln", "monthly_price": 201, "is_live": false },
{ "name": "Northwestern", "monthly_price": 214, "is_live": false },
{ "name": "Pacific Life", "monthly_price": 229, "is_live": false }
],
"refreshed_at": "2026-07-17T13:14:22Z"
}plans[] grid (Bronze/Silver/Gold/Platinum) and an aptc_monthly subsidy estimate instead of carriers[] — same shape used by /quote/health/results.GET /policies/:id
Application + policy detail by application id. Returns a ready-to-use certificateUrl (HMAC-signed) when a policy number has been issued.
curl https://coverly-8.polsia.app/api/v1/policies/412 \ -H "Authorization: Bearer ck_live_a3f9xxxxxxxxxxxxxxxx"
Response
{
"applicationId": 412,
"contactId": 1842,
"policyNumber": "CVLY-2026-00942",
"carrier": "AIG",
"productType": "auto",
"productLine": "life",
"monthlyPremium": 187,
"coverageAmount": 250000,
"issuedAt": "2026-07-17T13:11:00Z",
"status": "bound",
"underwritingDecision": "approved",
"certificateUrl": "https://coverly-8.polsia.app/policies/412/certificate.pdf?token=…"
}POST /webhooks/test
Fire a real, signed webhook event for the authenticated partner so you can validate the delivery pipeline end to end (signing, retries, delivery log). Default event_type is lead.created.
Request body (optional)
| Field | Type | Description |
|---|---|---|
| event_type | string | One of lead.created, quote.generated, application.submitted, application.underwritten, application.signed, policy.issued, contact.tier_changed. |
curl -X POST https://coverly-8.polsia.app/api/v1/webhooks/test \ -H "Authorization: Bearer ck_live_a3f9xxxxxxxxxxxxxxxx" \ -H "Content-Type: application/json" \ -d '{ "event_type": "policy.issued" }'
Response
{
"ok": true,
"dispatched_to": 2,
"event_type": "policy.issued",
"note": "Inspect /for-brokers/webhooks for the signed delivery."
}dispatched_to is 0 with a warning field.Errors
All errors share the same envelope: { "error": "<code>", "message"?: ..., "retry_after"?: ... }
Missing or malformed JSON body, invalid email, missing ZIP / vehicle.
Missing Authorization: Bearer header, malformed token, or revoked key.
The contact or application id belongs to another partner.
No contact, application, or policy with that id.
60 rpm / 1000 rph exceeded. Retry-After header included.
Retry with backoff. If persistent, contact partners@coverly.app with the request id.
Need outbound webhooks too?
Public API covers your inbound integrations (push leads, query policy details). For outbound events (Coverly → your endpoint), see the Webhooks dashboard and reference.
lead.created, policy.issued, etc. with HMAC-signed delivery and retry.