Kova API

API reference

Kova's API lets you push customer activity in from your own systems and read back each account's health. It's a small REST API that takes and returns JSON. Everything here works against your sandbox, so you can try each request as you read.

Getting started

Create a sandbox to get a key. Each sandbox holds one customer, Brightline Logistics, with 30 days of history, and lasts 7 days. Keep the Kova page open while you send requests and you'll see each one arrive.

Authentication

Send your sandbox key as a bearer token on every request.

Authorization: Bearer kova_test_...

Sandbox keys start with kova_test_. A production API would add live keys, key rotation and scoped permissions; the sandbox keeps it to one key per account.

GET/api/v1/account

Returns the account, its current health score broken down by component, the suggested next step, recent events, alerts and the integration log.

curl https://<this-site>/api/v1/account \ -H "Authorization: Bearer kova_test_..."
{ "account": { "name": "Brightline Logistics", "renewal_date": "2026-12-27", ... }, "health": { "score": 86, "band": "healthy", "components": [ { "key": "usage", "label": "Usage", "score": 32.1, "max": 35, "reason": "38 of 50 seats active this week, 4.3 logins each" }, ... ] }, "next_action": "Account is in good shape. Use the momentum: ask for a case study or a referral.", "history": [ { "at": "...", "score": 64, "type": "usage.weekly" }, ... ], "events": [ ... ], "alerts": [ ... ], "integration_log": [ ... ] }

POST/api/v1/events

Records something that happened at the customer. Kova replays the account's full history, rescores it, and returns the new score. If the account drops into a worse band, Kova writes an alert and sends it.

curl -X POST https://<this-site>/api/v1/events \ -H "Authorization: Bearer kova_test_..." \ -H "Content-Type: application/json" \ -d '{"type":"usage.weekly","data":{"active_users":14,"logins":30}}'
{ "event": { "id": "...", "type": "usage.weekly", "description": "Weekly usage: 14 active users, 30 logins", ... }, "health": { "previous": 76, "score": 58, "band": "at_risk" }, "alert_triggered": true }

Event types

typedataWhat it changes
usage.weeklyactive_users, loginsUsage score
feature.usedfeature: dashboards, alerts, reports, integrations, playbooks, api, segments or surveysFeature adoption
ticket.openedseverity: low, medium or high; subjectSupport
ticket.resolvedticket_id (optional, oldest if left out)Support
contact.addedrole: champion or sponsor; nameRelationship
contact.leftrole: champion or sponsorRelationship
nps.submittedscore: 0 to 10Relationship
seats.changedseatsCommercial and usage
invoice.overduenoneCommercial
invoice.paidnoneCommercial

GET/api/v1/events

Returns the last 100 events, newest first, each with a plain-English description.

POST/api/v1/qbr

Drafts a quarterly business review outline from the account's data. If an AI model is connected it writes the draft; otherwise Kova uses its own template. The response says which.

{ "generated_by": "ai", "draft": "Quarterly business review: Brightline Logistics\n\nWhere things stand\n- ..." }

POST/api/v1/reset

Puts the sandbox back to its starting point: the original 30 days of history, no alerts and an empty integration log. The key and alert email stay the same.

Errors and limits

Errors come back with an HTTP status and a JSON body you can show to a user or act on in code.

{ "error": { "code": "unknown_event_type", "message": "type must be one of: usage.weekly, ..." } }
StatuscodeWhen
400invalid_jsonThe body isn't valid JSON
401missing_api_key, invalid_api_keyNo key, or a key Kova doesn't recognise
410sandbox_expiredThe sandbox is more than 7 days old
413payload_too_largedata is over 2KB
422unknown_event_typeThe event type isn't on the list above
429rate_limitedMore than 30 events, 120 reads, 4 QBR drafts or 6 resets a minute

How scoring works

Kova doesn't store a score. It stores events, and rebuilds the account's state by replaying them in order every time something changes. That means every score can be traced back to what caused it, and changing the model rescores history too.

ComponentWeightBased on
Usage35Share of seats active this week, and logins per active user
Feature adoption20How many of the eight features are in use (six or more scores full marks)
Support15Open tickets, weighted by severity
Relationship20Champion in place, exec sponsor engaged, latest NPS
Commercial10Seats kept against the contract, invoices paid on time

70 and above is healthy, 50 to 69 is at risk, below 50 is critical. An alert fires only when an account moves into a worse band, at most once a minute per sandbox. Each sandbox emails at most 3 alerts in its lifetime, and a reset doesn't clear that count.