Evaluate
Submit work for INVEST scoring. Choose a synchronous call that returns the score inline, or an asynchronous submit that delivers the score to a webhook. A separate CSV flow powers the public evaluator.
Choosing a method
| Method | Endpoint | Returns | Use when |
|---|---|---|---|
| Synchronous | POST /api/v1/score | 200 with the score inline | You can hold the connection open — scripts, CI gates, request/response tooling. |
| Asynchronous | POST /api/v1/evaluate/{source} | 202 + webhook callback | Event-driven integrations like the Jira Forge app, or high-volume back-pressure-friendly scoring. |
Synchronous scoring
/api/v1/scoreSubmit a single ticket and receive the completed INVEST score in the same response. Authenticate with the bearer token issued to your account when your integration is provisioned — the request is authenticated at the edge and proxied to the scoring engine, so your credentials never reach the engine directly.
Authorization: Bearer <your_api_key>
Content-Type: application/json{
"ticket_id": "PROJ-101",
"customer_id": "acme_jira_8f2c",
"ticket_data": {
"key": "PROJ-101",
"summary": "As a site admin I want to export user activity",
"description": "Full description of the story...",
"acceptance_criteria": "Given a signed-in admin, when..."
},
"context_items": []
}{
"status": "completed",
"job_id": "4ebb0359-b8fb-4973-a04e-29414ab7d01f",
"ticket_id": "PROJ-101",
"overall_score": 3.7,
"individual_scores": {
"independent": 4,
"negotiable": 5,
"valuable": 6,
"estimable": 2,
"small": 3,
"testable": 2
},
"scores": {
"independent": {
"score": 4,
"reasoning": "The story likely depends on existing activity logging and permissions, but none of these dependencies are stated.",
"improvement_suggestions": [
"Explicitly state prerequisites and link/block on any required enabling stories.",
"Define whether export is self-contained or requires backend changes."
],
"reference_context": [
"PROJ-101-title",
"PROJ-101-description",
"PROJ-101-acceptance-criteria"
]
}
// ... one entry per dimension: negotiable, valuable, estimable, small, testable
},
"reasoning_per_dimension": {
"independent": "The story likely depends on existing activity logging and permissions...",
"negotiable": "The story is phrased as a user goal, but the lack of detail forces hidden assumptions...",
"valuable": "Exporting user activity can provide audit/compliance insights, but the value is unstated...",
"estimable": "Key scope drivers are missing (fields, date ranges, volume, format)...",
"small": "\"Export user activity\" is potentially large and unlikely to fit in a single sprint...",
"testable": "Acceptance criteria are incomplete and do not define expected outputs or edge cases..."
},
"recommendations": [
"Define the export scope precisely: event types, required fields, timezone, ordering, and default range.",
"Complete acceptance criteria with concrete Given/When/Then scenarios and security checks.",
"Add non-functional requirements: max dataset size, performance, sync vs async export.",
"Split the story into smaller vertical slices so each fits in one sprint.",
"Document dependencies and assumptions and link/block on prerequisite tickets."
],
"context_references": {
"independent": ["PROJ-101-title", "PROJ-101-description", "PROJ-101-acceptance-criteria"],
"negotiable": ["PROJ-101-title", "PROJ-101-description"],
"valuable": ["PROJ-101-title"],
"estimable": ["PROJ-101-description", "PROJ-101-acceptance-criteria"],
"small": ["PROJ-101-title", "PROJ-101-description"],
"testable": ["PROJ-101-acceptance-criteria"]
},
"metadata": {
"model": {
"provider": "openai",
"model_name": "gpt-5.2",
"temperature": 0.3
},
"prompt_version": "v1.0",
"timestamp": "2026-06-17T18:27:51.957675",
"processing_time_ms": 19338.47,
"token_usage": {
"prompt_tokens": 1279,
"completion_tokens": 951,
"total_tokens": 2230
},
"service_version": "1.0.0",
"enriched_context_references": {
"independent": [], "negotiable": [], "valuable": [],
"estimable": [], "small": [], "testable": []
},
"context_usage_stats": {
"total_context_items_available": 0,
"context_items_referenced": 3,
"references_per_dimension": {
"independent": 3, "negotiable": 2, "valuable": 1,
"estimable": 2, "small": 3, "testable": 1
}
}
}
}Response fields
| Field | Type | Description |
|---|---|---|
| status | string | Always “completed” for a successful synchronous score. |
| job_id | string | Unique identifier for this scoring run. |
| ticket_id | string | Echoes the ticket_id you submitted. |
| overall_score | number | Aggregate INVEST score out of 10. |
| individual_scores | object | The six INVEST dimensions mapped to an integer score (0–10). |
| scores | object | Per-dimension detail: score, reasoning, improvement_suggestions[], and reference_context[] (the context item IDs cited for that dimension). |
| reasoning_per_dimension | object | Convenience map of each dimension to its reasoning string (mirrors scores[dimension].reasoning). |
| recommendations | string[] | Prioritized, ticket-level suggestions to raise the score. |
| context_references | object | Each dimension mapped to the context item IDs that informed it. |
| metadata | object | Run metadata: model (provider, model_name, temperature), prompt_version, timestamp, processing_time_ms, token_usage, service_version, and context usage stats. |
- • This endpoint scores against the default INVEST rubric.
customer_idis resolved from your API key, so the value in the body is optional. - •
individual_scorescarries the six INVEST dimensions, each scored 0–10.overall_scoreis the aggregate out of 10. These are the stored values — user-facing surfaces always display them as whole percentages (a stored 7 displays as 70%). - • Only
summaryis strictly required insideticket_data; the other fields improve scoring accuracy when present.context_itemsis optional and, when supplied, must be an array of retrieval context objects.
Asynchronous scoring
/api/v1/evaluate/{source}Fire-and-forget evaluation for event-driven integrations. {source} is your integration channel — e.g. jiraforge. The request authenticates with the per-installation shared secret in the X-Vindex-Secret header. Vindex acknowledges immediately and POSTs the completed score to your registered callback URL.
X-Vindex-Secret: <per-install shared secret>
Content-Type: application/json{
"cloudId": "<Jira Cloud ID>",
"issue": {
"key": "PROJ-101",
"fields": {
"summary": "As a user, I want to...",
"description": { "...": "ADF or plain text" },
"issuetype": { "name": "Story" }
}
}
}{
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "accepted",
"message": "Evaluation request queued for processing",
"estimated_completion_time": "30-60 seconds"
}The completed score is delivered to your webhook as a structured payload — see the callbacks reference for the shape, signature verification, and retry behaviour.
| Status | Meaning |
|---|---|
| 202 | Accepted and queued for scoring. Also returned when an over-limit request is dropped (the request is silently ignored, not rejected). |
| 400 | Invalid JSON, or missing required fields (e.g. cloudId). |
| 401 | Missing secret, or unknown/disabled installation — triggers re-registration. |
| 403 | Shared secret does not match the registered value. |
| 500 | Unexpected error while processing the request. |
Rate limits
| Endpoint | Limit | Window |
|---|---|---|
| POST /api/v1/score | Monthly plan quota | per calendar month |
| POST /api/v1/evaluate/{source} | Monthly plan quota | per calendar month |
| POST /evaluate/upload | 10 requests | 1 hour (per IP) |
| POST /evaluate/score | 5 requests | 1 hour (per IP) |
| GET /evaluate/results/{session_id} | 60 requests | 1 minute |
Responses that exceed the limit return 429 Too Many Requests with a Retry-After header.
