Chainy Analysis
Analyse a Chainy's chain completion data to generate progress summaries, pattern insights, health scores, and recovery plans. Also exposes a task decomposition endpoint that breaks a free-text description into ordered sub-bits.
Endpoints
| Method | Path | Description | Auth | Rate Limit |
|---|---|---|---|---|
| POST | /ai/chainy-analysis | Trigger analysis for a Chainy | JWT + Entitlement | 10/min |
| GET | /ai/chainy-analysis/:chainyId | List past analyses for a Chainy | JWT + Entitlement | 60/min |
| POST | /ai/chainy-analysis/bit-decomposition | Decompose a task into sub-bits | JWT + Entitlement | 10/min |
POST /ai/chainy-analysis
Trigger an AI analysis of a Chainy. Results are stored and retrievable via the GET endpoint.
Request
- Auth: JWT Bearer token + active AI entitlement required
chainyIdmust be theidof an existing Chainy — e.g. from Create Chainy, shown below as$CHAINY_ID.
Request Body
| Field | Type | Required | Constraints | Description |
|---|---|---|---|---|
chainyId | string | Yes | Valid UUID | Chainy to analyse |
analysisType | string | Yes | See table below | Type of analysis to run |
Analysis Types
analysisType | Description |
|---|---|
progress | Summary stats: total chains, active count, total completions, average streak |
patterns | Temporal patterns: completion velocity, recent activity, chain distribution |
recommendations | Typed recommendation list (streak_recovery, get_started, maintain) |
decomposition | Chain listing with status for decomposition planning |
chainy_health | Composite health score (0–100): active chain ratio, streak health, velocity, fragmentation |
continuity | At-risk chain detection: chains at risk of streak break, broken, and strong performers |
blocker | Stall detection: chains with zero or declining progress |
routine | Time-of-day and day-of-week completion pattern analysis |
focus | Attention distribution: neglected vs over-served chains |
recovery | Structured recovery plan for broken streaks with prioritised next actions |
Response
Response Example
json
{
"data": {
"id": "cm9analysis01",
"chainyId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"analysisType": "chainy_health",
"result": {
"healthScore": 74,
"signals": ["strong_streaks", "low_fragmentation"],
"breakdown": {
"activeChainRatio": 0.8,
"streakHealth": 0.9,
"velocity": 0.6,
"fragmentation": 0.2
}
},
"createdAt": "2026-05-22T10:00:00.000Z"
}
}Code Examples
bash
curl -X POST "$BASE_URL/ai/chainy-analysis" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"chainyId": "'"$CHAINY_ID"'",
"analysisType": "chainy_health"
}'javascript
const chainyId = process.env.CHAINY_ID; // id of the Chainy to analyze
const res = await fetch(`${BASE_URL}/ai/chainy-analysis`, {
method: "POST",
headers: {
Authorization: `Bearer ${TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
chainyId,
analysisType: "chainy_health",
}),
});
const { data } = await res.json();python
import os
import requests
chainy_id = os.environ["CHAINY_ID"] # id of the Chainy to analyze
res = requests.post(
f"{BASE_URL}/ai/chainy-analysis",
headers={"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json"},
json={
"chainyId": chainy_id,
"analysisType": "chainy_health",
},
)
data = res.json()["data"]GET /ai/chainy-analysis/:chainyId
List past analyses for a specific Chainy. Scoped to the authenticated user.
Request
- Auth: JWT Bearer token + active AI entitlement required
- Path params:
chainyId— use the same Chainyidyou analysed above (or any Chainy'sidfrom Create Chainy) as$CHAINY_ID
Response
Response Example
json
{
"data": [
{
"id": "cm9analysis01",
"chainyId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"analysisType": "chainy_health",
"result": { "healthScore": 74 },
"createdAt": "2026-05-22T10:00:00.000Z"
}
]
}Code Examples
bash
curl "$BASE_URL/ai/chainy-analysis/$CHAINY_ID" \
-H "Authorization: Bearer $TOKEN"javascript
const chainyId = process.env.CHAINY_ID; // id of the Chainy to list analyses for
const res = await fetch(`${BASE_URL}/ai/chainy-analysis/${chainyId}`, {
headers: { Authorization: `Bearer ${TOKEN}` },
});
const { data } = await res.json();python
import os
import requests
chainy_id = os.environ["CHAINY_ID"] # id of the Chainy to list analyses for
res = requests.get(
f"{BASE_URL}/ai/chainy-analysis/{chainy_id}",
headers={"Authorization": f"Bearer {TOKEN}"},
)
data = res.json()["data"]POST /ai/chainy-analysis/bit-decomposition
Break a free-text task description into an ordered list of smaller, actionable sub-bits. Optionally scoped to an existing bit for further decomposition.
Request
- Auth: JWT Bearer token + active AI entitlement required
chainIdmust be theidof an existing chain — e.g. from Create Chain, shown below as$CHAIN_ID.
Request Body
| Field | Type | Required | Constraints | Description |
|---|---|---|---|---|
chainId | string | Yes | Valid UUID | Chain that provides context for decomposition |
description | string | Yes | max 2000 chars | Task description to decompose |
bitId | string | No | Valid UUID | Existing bit to decompose further |
Response
Response Example
json
{
"data": {
"suggestedBits": [
{ "title": "Choose CI/CD provider", "order": 1, "estimatedMinutes": 30 },
{ "title": "Configure build pipeline", "order": 2, "estimatedMinutes": 60 },
{ "title": "Add deployment stage", "order": 3, "estimatedMinutes": 45 },
{ "title": "Set up environment secrets", "order": 4, "estimatedMinutes": 20 },
{ "title": "Run first end-to-end deploy", "order": 5, "estimatedMinutes": 30 }
]
}
}Response Fields
| Field | Type | Description |
|---|---|---|
suggestedBits | object[] | Ordered list of suggested sub-tasks |
suggestedBits[].title | string | Sub-task title |
suggestedBits[].description | string | undefined | Optional elaboration |
suggestedBits[].estimatedMinutes | number | undefined | Time estimate |
suggestedBits[].order | number | Position in the sequence |
Code Examples
bash
curl -X POST "$BASE_URL/ai/chainy-analysis/bit-decomposition" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"chainId": "'"$CHAIN_ID"'",
"description": "Set up CI/CD pipeline for the mobile app"
}'javascript
const chainId = process.env.CHAIN_ID; // id of the chain that provides decomposition context
const res = await fetch(`${BASE_URL}/ai/chainy-analysis/bit-decomposition`, {
method: "POST",
headers: {
Authorization: `Bearer ${TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
chainId,
description: "Set up CI/CD pipeline for the mobile app",
}),
});
const { data } = await res.json();python
import os
import requests
chain_id = os.environ["CHAIN_ID"] # id of the chain that provides decomposition context
res = requests.post(
f"{BASE_URL}/ai/chainy-analysis/bit-decomposition",
headers={"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json"},
json={
"chainId": chain_id,
"description": "Set up CI/CD pipeline for the mobile app",
},
)
data = res.json()["data"]Error Responses
| Status | Code | When |
|---|---|---|
| 400 | VALIDATION_FAILED | Missing required fields or invalid UUID |
| 403 | FORBIDDEN | Chainy or Chain does not belong to your account |
| 503 | SERVICE_UNAVAILABLE | No AI provider available |