Spark Suggestions
Spark produces one focused AI suggestion per request. It resolves context from your Chainies, Bits, personalization memory, and active sessions, then generates a concise suggestion without starting a full conversation session.
Use Spark for lightweight in-app moments: "what should I work on next?", "turn this comment into an action", "suggest a Bit for this Chainy".
Endpoint
| Method | Path | Description | Auth | Rate Limit |
|---|---|---|---|---|
| POST | /ai/spark/suggestions | Create a context-aware suggestion | JWT + Spark entitlement | 30/min |
POST /ai/spark/suggestions
Authentication: JWT Bearer token required.
Entitlement: Active Spark entitlement required. Available on Bitter and above (not included in the free Explorer plan).
Rate limit: 30 requests per minute.
Idempotency: Pass an idempotency-key header to deduplicate retries — identical keys return the cached result without re-invoking the LLM.
Request
Request Headers
| Header | Required | Description |
|---|---|---|
Authorization | Yes | Bearer <token> |
Content-Type | Yes | application/json |
idempotency-key | No | Unique key to deduplicate the request |
Request Body
All fields are optional. If no fields are provided the server uses the auto_suggest mode.
| Field | Type | Constraints | Description |
|---|---|---|---|
mode | string | auto_suggest, prompt_suggest, next_bit, comment_result | Explicit mode. Omit to let the server resolve automatically. |
prompt | string | max 4000 chars | Primary instruction or question. |
comment | string | max 4000 chars | Raw comment to transform into a suggestion. |
chainyIds | string[] | max 10, each a valid UUID | Ground the suggestion in specific Chainies. |
bitIds | string[] | max 25, each a valid UUID | Ground the suggestion in specific Bits. |
contextOptions.useMemory | boolean | — | Set false to suppress memory context for this request. |
contextOptions.useLatestSessions | boolean | — | Set false to suppress recent-sessions context for this request. |
Mode auto-resolution (when mode is omitted):
commentprovided →comment_resultpromptprovided →prompt_suggestchainyIdsprovided →next_bit- Nothing →
auto_suggest
Response
Response Example
{
"data": {
"suggestionId": "cm9spark01",
"mode": "next_bit",
"title": "Add a pronunciation practice session",
"content": "Your Spanish Chainy has strong vocabulary coverage but no pronunciation Bits. Adding a 10-minute daily audio session would round out the practice cycle.",
"structuredResult": {
"type": "next_bit",
"payload": {
"suggestedTitle": "Pronunciation: 10-min daily audio",
"suggestedPriority": "high"
}
},
"contextUsed": ["chainies", "bits", "memory"],
"blockedContext": [
{
"category": "sessions",
"reason": "sessions_disabled_by_preference"
}
],
"chainyIdsUsed": ["a1b2c3d4-e5f6-7890-abcd-ef1234567890"],
"bitIdsUsed": [
"b2c3d4e5-f6a7-8901-bcde-f12345678901",
"c3d4e5f6-a7b8-9012-cdef-123456789012"
],
"personaUsed": "coach",
"provider": "google",
"model": "gemini-2.5-flash",
"createdAt": "2026-05-22T09:30:00.000Z"
}
}Response Fields
| Field | Type | Description |
|---|---|---|
suggestionId | string | Persisted suggestion ID |
mode | string | Resolved mode used: auto_suggest, prompt_suggest, next_bit, comment_result |
title | string | Short suggestion headline (max 200 chars) |
content | string | Full suggestion text (max 2000 chars) |
structuredResult | object | null | Optional structured output (see below) |
contextUsed | string[] | Context categories that fed the suggestion |
blockedContext | object[] | Context categories that were suppressed and why |
chainyIdsUsed | string[] | Chainy IDs that grounded the suggestion |
bitIdsUsed | string[] | Bit IDs that grounded the suggestion |
personaUsed | string | null | AI persona key if a persona was applied |
provider | string | AI provider used (e.g. google, anthropic) |
model | string | Model identifier |
createdAt | string | ISO 8601 timestamp |
structuredResult object
Present when the LLM returns structured data in addition to the narrative text.
| Field | Type | Description |
|---|---|---|
type | string | next_bit, idea, transformation, or generic |
payload | object | Type-specific fields (e.g. suggestedTitle, suggestedPriority) |
contextUsed values
| Value | Meaning |
|---|---|
prompt | User's prompt field was used |
comment | User's comment field was used |
chainies | Chainy data was retrieved |
bits | Bit data was retrieved |
memory | Personalization memory was used |
sessions | Recent AI sessions were used |
preferences | AI persona or language preference was applied |
blockedContext object
| Field | Type | Description |
|---|---|---|
category | string | The contextUsed category that was blocked |
reason | string | Why it was blocked (see table below) |
| Reason | Cause |
|---|---|
memory_disabled_by_preference | Memory disabled in your AI preferences |
memory_disabled_by_client | Request sent contextOptions.useMemory: false |
sessions_disabled_by_preference | Session context disabled in your AI preferences |
sessions_disabled_by_client | Request sent contextOptions.useLatestSessions: false |
empty_input | No prompt, comment, or Chainy IDs were provided |
no_bits_available | The requested Chainies exist but contain no Bits |
Error Responses
| Status | Code | When |
|---|---|---|
| 400 | VALIDATION_FAILED | Mode/input mismatch (e.g. next_bit without chainyIds) |
| 403 | FORBIDDEN | chainyIds or bitIds do not belong to your account |
| 429 | RATE_LIMIT_EXCEEDED | More than 30 requests per minute |
| 502 | BAD_GATEWAY | AI provider returned an empty or failed response |
| 503 | SERVICE_UNAVAILABLE | No AI provider available |
Code Examples
Use the id of one of your Chainies as $CHAINY_ID in the "next bit" examples below.
curl -X POST "$BASE_URL/ai/spark/suggestions" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{}'curl -X POST "$BASE_URL/ai/spark/suggestions" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-H "idempotency-key: req_abc123" \
-d '{
"mode": "prompt_suggest",
"prompt": "What is the highest priority thing I should tackle today?"
}'curl -X POST "$BASE_URL/ai/spark/suggestions" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"mode": "next_bit",
"chainyIds": ["'"$CHAINY_ID"'"]
}'curl -X POST "$BASE_URL/ai/spark/suggestions" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"mode": "comment_result",
"comment": "I keep losing focus after lunch and end up skipping my language practice."
}'// Auto suggest
const res = await fetch(`${BASE_URL}/ai/spark/suggestions`, {
method: "POST",
headers: {
Authorization: `Bearer ${TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({}),
});
const { data } = await res.json();
// Prompt mode with idempotency
const res = await fetch(`${BASE_URL}/ai/spark/suggestions`, {
method: "POST",
headers: {
Authorization: `Bearer ${TOKEN}`,
"Content-Type": "application/json",
"idempotency-key": "req_abc123",
},
body: JSON.stringify({
mode: "prompt_suggest",
prompt: "What is the highest priority thing I should tackle today?",
}),
});
const { data } = await res.json();
// Next bit for a Chainy
const chainyId = process.env.CHAINY_ID; // id of the Chainy to ground the suggestion in
const res = await fetch(`${BASE_URL}/ai/spark/suggestions`, {
method: "POST",
headers: {
Authorization: `Bearer ${TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
mode: "next_bit",
chainyIds: [chainyId],
}),
});
const { data } = await res.json();import os
import requests
BASE_URL = "https://api.chainabit.com/api/v1"
headers = {"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json"}
# Auto suggest
res = requests.post(f"{BASE_URL}/ai/spark/suggestions", headers=headers, json={})
data = res.json()["data"]
# Prompt mode
res = requests.post(
f"{BASE_URL}/ai/spark/suggestions",
headers={**headers, "idempotency-key": "req_abc123"},
json={
"mode": "prompt_suggest",
"prompt": "What is the highest priority thing I should tackle today?",
},
)
data = res.json()["data"]
# Next bit
chainy_id = os.environ["CHAINY_ID"] # id of the Chainy to ground the suggestion in
res = requests.post(
f"{BASE_URL}/ai/spark/suggestions",
headers=headers,
json={
"mode": "next_bit",
"chainyIds": [chainy_id],
},
)
data = res.json()["data"]Plan availability
| Plan | Spark access | Monthly suggestion limit |
|---|---|---|
| Explorer (free) | No | — |
| Bitter | Yes | 150 |
| Chainer | Yes | 500 |
| Architect | Yes | Unlimited |
Calling this endpoint on a plan without Spark access returns 403 FORBIDDEN.
Modes
auto_suggest
No inputs required. Spark reads your recent Bits, Chainies, memory, and sessions and generates an unprompted suggestion — useful for "surprise me" or "what's next?" moments.
prompt_suggest
Requires prompt. Generates a suggestion grounded in your explicit question or instruction combined with your available context.
next_bit
Requires at least one chainyId. Spark analyses the Chainy's existing Bits and suggests a concrete next Bit to create or action to take.
comment_result
Requires comment. Transforms a raw user comment (a note, thought, or observation) into an actionable suggestion.
Context controls
Grounding with specific IDs
Provide chainyIds and/or bitIds to pin the suggestion to specific content. Without IDs Spark pulls from your most recent Chainies and Bits automatically.
Suppressing context per request
Use contextOptions to override your account-level preferences for a single request:
{
"contextOptions": {
"useMemory": false,
"useLatestSessions": false
}
}Account-level preferences
Memory and session context follow your AI preferences (ai_memory_enabled, use_latest_sessions). Manage these via the Preferences API.
Idempotency
Pass an idempotency-key header to make retries safe. If a suggestion was already created with the same key, the persisted result is returned immediately without calling the LLM again.
curl -X POST "$BASE_URL/ai/spark/suggestions" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-H "idempotency-key: client-generated-unique-id" \
-d '{ "prompt": "What should I focus on today?" }'Keys are scoped to your account. Use a UUID or a hash of the request inputs as the key.