The KOVA API lets you manage strategies, submit portfolios, and generate briefs programmatically. Same evaluation engine as the web app, accessible over REST.
Base URL: https://kovatools.com/api/v1
All API requests require a Bearer token in the Authorization header.
Authorization: Bearer kova_tk_xxxxxxxxxxxxxxxxxxxx
Getting a token: Log in to KOVA, go to Settings, and generate an API token. The token is shown once at creation — copy it immediately. Tokens can be revoked from the same page.
Every response wraps the resource in a named key plus a meta object:
{
"strategy": { ... },
"meta": {
"request_id": "abc-123",
"timestamp": "2026-02-15T12:00:00Z"
}
}
Collections use the plural key ("strategies", "briefs"). Errors use "error":
{
"error": {
"code": "not_found",
"message": "Strategy not found"
},
"meta": { ... }
}
GET /api/v1/strategies
Returns all strategies owned by the authenticated user.
Response (200):
{
"strategies": [
{
"uuid": "a1b2c3d4-...",
"name": "Long-Term Growth",
"content_summary": "My strategy focuses on...",
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-02-01T00:00:00Z"
}
],
"meta": { ... }
}
GET /api/v1/strategies/:uuid
Returns full details of a strategy, including the complete text.
Response (200):
{
"strategy": {
"uuid": "a1b2c3d4-...",
"name": "Long-Term Growth",
"content": "Full strategy text...",
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-02-01T00:00:00Z"
},
"meta": { ... }
}
POST /api/v1/strategies
Creates a new strategy for the authenticated user.
Request body:
{
"content": "Buy and hold growth stocks. Max 20% per position. Rebalance quarterly.",
"name": "Long-Term Growth"
}
| Field | Type | Required | Description |
|---|---|---|---|
content |
string | Yes | Full strategy text (supports Markdown) |
name |
string | No | Display name for the strategy |
Response (201 Created):
{
"strategy": {
"uuid": "a1b2c3d4-...",
"name": "Long-Term Growth",
"content": "Buy and hold growth stocks. Max 20% per position. Rebalance quarterly.",
"created_at": "2026-02-15T12:00:00Z",
"updated_at": "2026-02-15T12:00:00Z"
},
"meta": { ... }
}
PUT /api/v1/strategies/:uuid
Updates an existing strategy. Only provided fields are changed.
Request body:
{
"content": "Updated strategy text...",
"name": "Renamed Strategy"
}
Response (200): Same shape as Get Strategy.
DELETE /api/v1/strategies/:uuid
Permanently deletes a strategy.
Response: 204 No Content
POST /api/v1/strategies/:uuid/briefs
Submit a portfolio and trigger asynchronous brief generation. Returns immediately with a brief ID — poll the show endpoint for results.
Request body:
{
"portfolio": {
"source": "text",
"content": "AAPL 100 shares at $150, MSFT 50 shares at $380, $12,000 cash"
},
"context": "Risk tolerance: aggressive. Current bias: { AAPL: bearish, MSFT: neutral }. RSI(14): AAPL 72, MSFT 45. VIX: 18.5"
}
Portfolio source types:
| Source | Content | content_type |
|---|---|---|
text |
Plain text description | Not needed |
file |
Base64-encoded file | Required (application/pdf, text/csv, image/png) |
json |
Structured JSON (portfolio_v1 schema) | Not needed |
context (optional): Any string (ideally markdown) that helps Kova produce a more accurate evaluation. Use it to provide risk tolerance, market outlook, technical indicators, or any signal relevant to your strategy. Ephemeral — applies to this brief only.
Response (202 Accepted):
{
"brief": {
"id": "brief-uuid",
"status": "processing",
"poll_url": "/api/v1/strategies/strategy-uuid/briefs/brief-uuid"
},
"meta": { ... }
}
GET /api/v1/strategies/:uuid/briefs/:id
Returns the current state of a brief. Poll this endpoint after creating a brief until status is completed or error.
Status: processing
{
"brief": {
"id": "brief-uuid",
"status": "processing"
},
"meta": { ... }
}
Status: completed
{
"brief": {
"id": "brief-uuid",
"status": "completed",
"alignment": {
"score": 0.82,
"label": "Aligned"
},
"content": "## Full markdown brief...",
"summary": "Portfolio is broadly aligned with growth strategy.",
"target_allocations": [...],
"compliance_checks": [...],
"context_at_generation": "FOMC raised rates 25bps today.",
"generated_at": "2026-02-15T12:00:00Z"
},
"meta": { ... }
}
Status: error
{
"brief": {
"id": "brief-uuid",
"status": "error",
"error": {
"code": "processing_failed",
"message": "Could not process portfolio data."
}
},
"meta": { ... }
}
GET /api/v1/strategies/:uuid/briefs
Returns all completed briefs for a strategy, most recent first.
Response (200):
{
"briefs": [
{
"id": "brief-uuid",
"alignment": {
"score": 0.82,
"label": "Aligned"
},
"summary": "Portfolio is broadly aligned...",
"generated_at": "2026-02-15T12:00:00Z"
}
],
"meta": { ... }
}
Creating a brief is asynchronous. The flow:
202 Accepted with status: processingcompleted (brief is ready) or error (something failed)processing → completed
processing → error
Recommended polling interval: start at 2 seconds, back off to 5 seconds.
When using "source": "json" in the create brief endpoint, the content must match the portfolio_v1 schema.
Required fields: schema_version, stocks, options, cash, timestamp, notes
{
"schema_version": "1.0.0",
"stocks": [
{
"ticker": "AAPL",
"shares": 100,
"cost_basis": 150.00
},
{
"ticker": "MSFT",
"shares": 50,
"cost_basis": 380.00
}
],
"options": [
{
"ticker": "AAPL",
"type": "call",
"strike": 200,
"expiry": "2026-06-20",
"contracts": 2,
"side": "short",
"premium": 5.50
}
],
"cash": 12000.00,
"timestamp": "2026-02-15T12:00:00Z",
"notes": ["Cost basis estimated for MSFT"]
}
| Field | Type | Required | Description |
|---|---|---|---|
ticker |
string | Yes | Ticker symbol (1-8 chars, letters/numbers/dots) |
shares |
number | Yes | Number of shares (>= 0) |
cost_basis |
number | No | Average cost per share |
| Field | Type | Required | Description |
|---|---|---|---|
ticker |
string | Yes | Underlying ticker symbol |
type |
string | Yes | "call" or "put" |
strike |
number | No | Strike price |
expiry |
string | No | Expiration date (YYYY-MM-DD) |
contracts |
integer | No | Number of contracts (>= 1) |
side |
string | No | "long" or "short" |
premium |
number | No | Premium paid/received per share |
| Field | Type | Description |
|---|---|---|
cash |
number | Cash balance in dollars (negative for margin) |
timestamp |
string | ISO 8601 UTC timestamp (YYYY-MM-DDTHH:MM:SSZ) |
notes |
array of strings | Notes about ambiguities or missing data |
| Score | Label |
|---|---|
| 0.85 – 1.00 | Strongly Aligned |
| 0.70 – 0.84 | Aligned |
| 0.55 – 0.69 | Partially Aligned |
| 0.40 – 0.54 | Drifting |
| 0.00 – 0.39 | Off-Track |
| HTTP Status | Code | Meaning |
|---|---|---|
| 401 | unauthorized |
Missing or invalid API token |
| 404 | not_found |
Resource doesn't exist or doesn't belong to you |
| 422 | invalid_params |
Invalid request parameters (for example, malformed portfolio payload or unsupported live_inputs) |
| 422 | validation_failed |
Model validation failed (for example, missing strategy content on create/update) |