Skip to content

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

MethodPathDescriptionAuthRate Limit
POST/ai/spark/suggestionsCreate a context-aware suggestionJWT + Spark entitlement30/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
HeaderRequiredDescription
AuthorizationYesBearer <token>
Content-TypeYesapplication/json
idempotency-keyNoUnique key to deduplicate the request

Request Body

All fields are optional. If no fields are provided the server uses the auto_suggest mode.

FieldTypeConstraintsDescription
modestringauto_suggest, prompt_suggest, next_bit, comment_resultExplicit mode. Omit to let the server resolve automatically.
promptstringmax 4000 charsPrimary instruction or question.
commentstringmax 4000 charsRaw comment to transform into a suggestion.
chainyIdsstring[]max 10, each a valid UUIDGround the suggestion in specific Chainies.
bitIdsstring[]max 25, each a valid UUIDGround the suggestion in specific Bits.
contextOptions.useMemorybooleanSet false to suppress memory context for this request.
contextOptions.useLatestSessionsbooleanSet false to suppress recent-sessions context for this request.

Mode auto-resolution (when mode is omitted):

  1. comment provided → comment_result
  2. prompt provided → prompt_suggest
  3. chainyIds provided → next_bit
  4. Nothing → auto_suggest

Response

Response Example
json
{
  "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
FieldTypeDescription
suggestionIdstringPersisted suggestion ID
modestringResolved mode used: auto_suggest, prompt_suggest, next_bit, comment_result
titlestringShort suggestion headline (max 200 chars)
contentstringFull suggestion text (max 2000 chars)
structuredResultobject | nullOptional structured output (see below)
contextUsedstring[]Context categories that fed the suggestion
blockedContextobject[]Context categories that were suppressed and why
chainyIdsUsedstring[]Chainy IDs that grounded the suggestion
bitIdsUsedstring[]Bit IDs that grounded the suggestion
personaUsedstring | nullAI persona key if a persona was applied
providerstringAI provider used (e.g. google, anthropic)
modelstringModel identifier
createdAtstringISO 8601 timestamp

structuredResult object

Present when the LLM returns structured data in addition to the narrative text.

FieldTypeDescription
typestringnext_bit, idea, transformation, or generic
payloadobjectType-specific fields (e.g. suggestedTitle, suggestedPriority)

contextUsed values
ValueMeaning
promptUser's prompt field was used
commentUser's comment field was used
chainiesChainy data was retrieved
bitsBit data was retrieved
memoryPersonalization memory was used
sessionsRecent AI sessions were used
preferencesAI persona or language preference was applied

blockedContext object
FieldTypeDescription
categorystringThe contextUsed category that was blocked
reasonstringWhy it was blocked (see table below)
ReasonCause
memory_disabled_by_preferenceMemory disabled in your AI preferences
memory_disabled_by_clientRequest sent contextOptions.useMemory: false
sessions_disabled_by_preferenceSession context disabled in your AI preferences
sessions_disabled_by_clientRequest sent contextOptions.useLatestSessions: false
empty_inputNo prompt, comment, or Chainy IDs were provided
no_bits_availableThe requested Chainies exist but contain no Bits

Error Responses
StatusCodeWhen
400VALIDATION_FAILEDMode/input mismatch (e.g. next_bit without chainyIds)
403FORBIDDENchainyIds or bitIds do not belong to your account
429RATE_LIMIT_EXCEEDEDMore than 30 requests per minute
502BAD_GATEWAYAI provider returned an empty or failed response
503SERVICE_UNAVAILABLENo AI provider available

Code Examples

Use the id of one of your Chainies as $CHAINY_ID in the "next bit" examples below.

bash
curl -X POST "$BASE_URL/ai/spark/suggestions" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{}'
bash
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?"
  }'
bash
curl -X POST "$BASE_URL/ai/spark/suggestions" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "mode": "next_bit",
    "chainyIds": ["'"$CHAINY_ID"'"]
  }'
bash
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."
  }'
javascript
// 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();
python
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

PlanSpark accessMonthly suggestion limit
Explorer (free)No
BitterYes150
ChainerYes500
ArchitectYesUnlimited

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:

json
{
  "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.

bash
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.

Built with purpose.