API reference
The Vindex HTTP API covers registration, ticket evaluation, score callbacks, and aggregate stats. It's the same API the Jira Forge app uses; build directly on it to wire story health scoring into whatever tooling your team already relies on.
Base URL and conventions
https://edge.vindexgateway.com- JSON only. All requests and responses use
application/json, except file uploads which usemultipart/form-data. - UTC timestamps. All dates are ISO 8601 in UTC.
- Customer ID scoping. Every ticket you submit is tagged with the
customer_idyou were assigned when your integration was provisioned. Scores are only visible to that customer and to your linked company. - Standard HTTP codes.
2xxfor success,4xxfor client errors,5xxfor server errors. Error bodies include adetailfield describing what went wrong.
Authentication
Authenticated endpoints use the bearer token issued to your account when your integration is provisioned. Pass it on every request:
Authorization: Bearer <your_api_key>The /evaluate/* endpoints that power the public CSV flow are unauthenticated but rate-limited per IP; see the evaluate reference for limits.
Synchronous vs. asynchronous scoring
Vindex exposes the scoring engine in two shapes. Pick whichever fits how your integration is wired; both run the identical INVEST analysis and return the same dimensions.
Synchronous
POST /api/v1/score blocks until scoring finishes and returns the full score in the response body. Best for request/response contexts: CI checks, scripts, and the CSV evaluator. No callback endpoint required.
Asynchronous
POST /api/v1/evaluate/{source} acknowledges immediately with 202 and delivers the completed score to your webhook. This is what the Jira Forge app uses.
See the evaluate reference for full request and response payloads for both models.
Endpoint groups
Evaluate
Submit a ticket (or a CSV of tickets) for INVEST evaluation. The scoring engine runs asynchronously and delivers results via callback.
Open referenceCallbacks
Receive completed scores at your own webhook endpoint. Verify the signature, handle retries, and store results in your own system.
Open referenceStats
Read aggregate story health data (averages, trends, and per-dimension breakdowns) programmatically.
Open referenceRate limits
| Endpoint group | Limit | Window |
|---|---|---|
| /api/v1/score | Monthly story quota (new unique stories) | per calendar month (UTC) |
| /evaluate/upload | 10 requests | 1 hour (per IP) |
| /evaluate/score | 5 requests | 1 hour (per IP) |
The monthly quota counts new unique stories: re-scoring a story that has already been counted is free forever and is admitted even when the quota is exhausted. When a new story is refused, the response is 429 with code: "QUOTA_EXHAUSTED" and a machine-readable body:
{
"error": "Quota exhausted",
"code": "QUOTA_EXHAUSTED",
"message": "The monthly story quota is reached. New stories are paused until the quota resets; previously scored stories can still be re-scored at no cost.",
"plan": "starter",
"quota": 200,
"used": 200,
"reset_at": "2026-09-01T00:00:00Z",
"upgrade_url": "https://register.vindexgateway.com/portal",
"already_counted": false
}Per-IP limits on the public CSV endpoints return 429 Too Many Requests with a Retry-After header. Distinguish quota exhaustion by the code field, never by the status alone.
Story identity & billing
Billing is per unique story, once. Send a stable external_id with each request (your own record id) and every later score of that id is a free re-score, in any month. Without an external_id, Vindex falls back to a normalized fingerprint of the story's content: unchanged content stays free, edited content bills as a new story. Responses disclose the applied rule in identity_resolution (external_id or content_hash) and echo the resolved story_identity, so the billing unit is always visible at the point of use.
Error shape
Gateway endpoints (/api/v1/*, /stats/*) return a structured error body:
{
"error": "Validation failed",
"message": "Invalid request format",
"details": ["Missing required fields: ticket_id or ticket_data"]
}The unauthenticated CSV endpoints (/evaluate/*) are served directly by the scoring backend and use the framework default shape instead:
{
"detail": "CSV must have at least 2 columns"
}