AI Reactions
React to AI runs and messages with 10 reaction types. Reactions use toggle semantics -- the same endpoint adds or removes a reaction.
Endpoints
| Method | Path | Description | Auth | Rate Limit |
|---|---|---|---|---|
| POST | /ai/reactions/toggle | Toggle a reaction | JWT + Entitlement | 30/min |
| GET | /ai/reactions/runs/:runId | Get reactions for a run | JWT + Entitlement | 60/min |
| GET | /ai/reactions/messages/:messageId | Get reactions for a message | JWT + Entitlement | 60/min |
| GET | /ai/reactions/runs/:runId/stats | Aggregate stats for a run | JWT + Entitlement | 60/min |
| GET | /ai/reactions/messages/:messageId/stats | Aggregate stats for a message | JWT + Entitlement | 60/min |
| DELETE | /ai/reactions/:id | Remove a reaction by ID | JWT + Entitlement | 30/min |
Reaction Types
| Type | Description |
|---|---|
like | General positive signal |
dislike | General negative signal |
accurate | Factually correct |
creative | Novel or surprising |
fast | Quick response |
helpful | Directly useful |
inspiring | Motivating content |
confusing | Hard to understand |
wrong | Factually incorrect |
slow | Response took too long |
POST /ai/reactions/toggle
Toggle a reaction on a run or message. If the reaction already exists, it is removed. If it does not exist, it is added.
Authentication: JWT Bearer token + active AI entitlement required. Rate limit: 30/min
Request
| Field | Type | Required | Description |
|---|---|---|---|
reactionType | string | Yes | One of the 10 reaction types |
runId | string (uuid) | No | Target run ID (at least one of runId/messageId required) |
messageId | string (uuid) | No | Target message ID |
comment | string | No | Optional comment (max 1000 chars, only for like/dislike) |
Response
Response Example (Added)
{
"data": {
"action": "added",
"reaction": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"chainerId": "u1234567-abcd-ef01-2345-6789abcdef00",
"reactionType": "helpful",
"comment": null,
"createdAt": "2026-03-17T14:00:00.000Z"
}
}
}Response Example (Removed)
{
"data": {
"action": "removed"
}
}Response Fields
| Field | Type | Description |
|---|---|---|
action | "added" | "removed" | Whether the reaction was created or deleted |
reaction | object | undefined | The created reaction (only when action is "added") |
reaction.id | string | Reaction UUID |
reaction.chainerId | string | User who reacted |
reaction.reactionType | string | The reaction type |
reaction.comment | string | null | Optional comment |
reaction.createdAt | string | ISO 8601 timestamp |
Code Examples
Use the id of an existing AI run as $RUN_ID.
curl -X POST https://api.chainabit.com/api/v1/ai/reactions/toggle \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"reactionType": "helpful",
"runId": "'"$RUN_ID"'"
}'const runId = process.env.RUN_ID; // id of the AI run you're reacting to
const res = await fetch(`${BASE_URL}/ai/reactions/toggle`, {
method: "POST",
headers: {
Authorization: `Bearer ${TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
reactionType: "helpful",
runId,
}),
});
const { data } = await res.json();import os
import requests
run_id = os.environ["RUN_ID"] # id of the AI run you're reacting to
res = requests.post(
f"{BASE_URL}/ai/reactions/toggle",
headers={"Authorization": f"Bearer {TOKEN}"},
json={
"reactionType": "helpful",
"runId": run_id,
},
)
data = res.json()["data"]GET /ai/reactions/runs/:runId
Get aggregate reaction stats and the current user's reactions for a run.
Authentication: JWT Bearer token + active AI entitlement required. Rate limit: 60/min
Request
- Path params:
runId
Response
Response Example
{
"data": {
"stats": [
{ "reactionType": "helpful", "count": 12 },
{ "reactionType": "accurate", "count": 8 },
{ "reactionType": "like", "count": 5 }
],
"userReactions": ["helpful", "like"]
}
}Response Fields
| Field | Type | Description |
|---|---|---|
stats | array | Aggregate counts per reaction type, sorted by count descending |
stats[].reactionType | string | Reaction type name |
stats[].count | number | Total reactions of this type |
userReactions | string[] | Reaction types the current user has applied |
Code Examples
Use the id of an existing AI run as $RUN_ID.
curl https://api.chainabit.com/api/v1/ai/reactions/runs/$RUN_ID \
-H "Authorization: Bearer $TOKEN"const runId = process.env.RUN_ID; // id of the AI run
const res = await fetch(
`${BASE_URL}/ai/reactions/runs/${runId}`,
{ headers: { Authorization: `Bearer ${TOKEN}` } }
);
const { data } = await res.json();import os
import requests
run_id = os.environ["RUN_ID"] # id of the AI run
res = requests.get(
f"{BASE_URL}/ai/reactions/runs/{run_id}",
headers={"Authorization": f"Bearer {TOKEN}"},
)
data = res.json()["data"]GET /ai/reactions/runs/:runId/stats
Get aggregate reaction counts for a run without user-specific data.
Authentication: JWT Bearer token + active AI entitlement required. Rate limit: 60/min
Request
- Path params:
runId
Response
Response Example
{
"data": {
"stats": [
{ "reactionType": "helpful", "count": 12 },
{ "reactionType": "accurate", "count": 8 }
],
"userReactions": []
}
}Code Examples
Use the id of an existing AI run as $RUN_ID.
curl https://api.chainabit.com/api/v1/ai/reactions/runs/$RUN_ID/stats \
-H "Authorization: Bearer $TOKEN"const runId = process.env.RUN_ID; // id of the AI run
const res = await fetch(
`${BASE_URL}/ai/reactions/runs/${runId}/stats`,
{ headers: { Authorization: `Bearer ${TOKEN}` } }
);
const { data } = await res.json();import os
import requests
run_id = os.environ["RUN_ID"] # id of the AI run
res = requests.get(
f"{BASE_URL}/ai/reactions/runs/{run_id}/stats",
headers={"Authorization": f"Bearer {TOKEN}"},
)
data = res.json()["data"]DELETE /ai/reactions/:id
Remove a specific reaction by its ID. Only the reaction owner can delete it.
Authentication: JWT Bearer token + active AI entitlement required. Rate limit: 30/min
Request
- Path params:
id
Response
Response Example
{
"data": {
"deleted": true
}
}Code Examples
Use the id from Toggle Reaction's "added" response (data.reaction.id) as $REACTION_ID.
curl -X DELETE https://api.chainabit.com/api/v1/ai/reactions/$REACTION_ID \
-H "Authorization: Bearer $TOKEN"const reactionId = process.env.REACTION_ID; // id from the "added" response of POST /ai/reactions/toggle
const res = await fetch(
`${BASE_URL}/ai/reactions/${reactionId}`,
{
method: "DELETE",
headers: { Authorization: `Bearer ${TOKEN}` },
}
);
const { data } = await res.json();import os
import requests
reaction_id = os.environ["REACTION_ID"] # id from the "added" response of POST /ai/reactions/toggle
res = requests.delete(
f"{BASE_URL}/ai/reactions/{reaction_id}",
headers={"Authorization": f"Bearer {TOKEN}"},
)
data = res.json()["data"]