/v1/health Liveness probe
| Field | Type |
|---|---|
status | string |
service | string |
api_version | string |
version | string |
The CAI Score API turns the CAI Framework into a single HTTP call. Send five behavioural answers plus a role roster, and receive a score, a zone, and suggestions per role.
Quickstart
Send the five behavioural answers plus an industry, and read the score back.
curl https://api.caiscore.com/v1/score \
-H "Authorization: Bearer $CAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"answers": {
"level": "integrated",
"competence": "harness",
"regulation": "mixed",
"reversibility": "costly",
"detection": "review"
},
"industry": "accounting-firm"
}' import os
import requests
r = requests.post(
"https://api.caiscore.com/v1/score",
headers={"Authorization": f"Bearer {os.environ['CAI_API_KEY']}"},
json={
"answers": {
"level": "integrated",
"competence": "harness",
"regulation": "mixed",
"reversibility": "costly",
"detection": "review",
},
"industry": "accounting-firm",
},
)
print(r.json()["aggregate"]["score"]) const response = await fetch("https://api.caiscore.com/v1/score", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.CAI_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
answers: {
level: "integrated",
competence: "harness",
regulation: "mixed",
reversibility: "costly",
detection: "review",
},
industry: "accounting-firm",
}),
});
const { aggregate } = await response.json();
console.log(aggregate.score); Reference
Every parameter, request body, and response field below is read straight from the vendored OpenAPI document — nothing here is hand-written.
/v1/health Liveness probe
| Field | Type |
|---|---|
status | string |
service | string |
api_version | string |
version | string |
/v1/questions The five CAI questions and their option values
| Field | Type |
|---|---|
meta | object |
questions | array |
/v1/industries Search the curated industry roster
| Name | In | Required | Type | Constraints | Description |
|---|---|---|---|---|---|
q | query | Optional | string | Free-text match against label and curated synonyms. |
| Field | Type |
|---|---|
meta | object |
industries | array |
/v1/industries/{id} One industry, identifier and label only
| Name | In | Required | Type | Constraints | Description |
|---|---|---|---|---|---|
id | path | Required | string |
| Field | Type |
|---|---|
industry | object |
/v1/occupations Search-only occupation discovery
| Name | In | Required | Type | Constraints | Description |
|---|---|---|---|---|---|
q | query | Required | string | At least two characters. | |
limit | query | Optional | integer | min 1; max 10; default 10 |
| Field | Type |
|---|---|
meta | object |
occupations | array |
/v1/occupations/{soc} One occupation, identifier and label only
| Name | In | Required | Type | Constraints | Description |
|---|---|---|---|---|---|
soc | path | Required | string |
| Field | Type |
|---|---|
occupation | object |
/v1/openapi.json This document
/v1/score Score a role roster against the five answers
| Field | Type | Required |
|---|---|---|
answers | object | Required |
occupations | array | Optional |
industry | string | Optional |
| Field | Type |
|---|---|
meta | object |
params | object |
posture | array |
roles | array |
aggregate | object |
suggestions | array |
Errors
Every non-2xx response uses one envelope,
{ "error": { "code": "...", "message": "..." } }. Route
error handling off code, read straight from the vendored
reference below.
| Code | Status | Where |
|---|---|---|
unauthorized | 401 | A missing, malformed, or unknown key on any non-exempt route |
rate_limited | 429 | An exhausted rate limit on any non-exempt route |
not_found | 404 | Any request whose path matches no route |
method_not_allowed | 405 | A matched path with the wrong HTTP method |
internal_error | 500 | An unhandled failure, including a misconfigured key or rate-limit binding |
industry_not_found | 404 | GET /v1/industries/:id — unknown id |
query_required | 400 | GET /v1/occupations — missing or too-short q |
invalid_parameter | 400 | GET /v1/occupations — out-of-range limit, or an offset parameter |
occupation_not_found | 404 | GET /v1/occupations/:soc — unknown soc |
unsupported_media_type | 415 | POST /v1/score — Content-Type is not application/json |
invalid_json | 400 | POST /v1/score — body is not valid JSON |
invalid_body | 400 | POST /v1/score — body is not a JSON object |
invalid_answers | 400 | POST /v1/score — answers missing or malformed |
invalid_occupations | 400 | POST /v1/score — occupations is not an array of strings |
invalid_industry | 400 | POST /v1/score — industry is not a string |
industry_not_found | 404 | POST /v1/score — unknown industry id |
missing_roster | 400 | POST /v1/score — neither occupations nor industry given |
empty_roster | 400 | POST /v1/score — resolved roster has zero entries |
roster_too_large | 400 | POST /v1/score — resolved roster exceeds 25 entries |
occupation_not_found | 400 | POST /v1/score — an occupations entry is an unknown SOC code |