Preferences API
The Preferences API allows users to read and update their personal application preferences, including notification settings, AI behavior, view modes, and localization. All fields are flat (no nested objects except quietHours, which is freeform).
AI Message Suggestion Preferences
Chainabit also stores two AI-session continuation preferences on the identity profile:
| Field | Type | Default | Description |
|---|---|---|---|
aiMessageSuggestionsEnabled | boolean | true | When true, completed assistant messages may include structured follow-up suggestion cards. |
aiMessageSuggestionCount | integer | 3 | Number of suggestion cards to generate per completed assistant message. Allowed range: 2–4. |
When aiMessageSuggestionsEnabled is false, assistant messages return suggestions: [] and no message.suggestions SSE event is emitted with cards for that message.
Example Patch
curl -X PATCH https://api.chainabit.com/api/v1/preferences \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"aiMessageSuggestionsEnabled": true,
"aiMessageSuggestionCount": 4
}'Base Path
https://api.chainabit.com/api/v1/preferencesAuthentication
All Preferences endpoints require a valid JWT Bearer token.
Environment Variables
Set these variables before running any example on this page:
export BASE_URL="https://api.chainabit.com/api/v1"
export TOKEN="your-access-token"Get your access token by calling POST /auth/login.
Endpoints
| Method | Path | Description | Auth | Rate Limit |
|---|---|---|---|---|
| GET | /preferences | Get user preferences | JWT | 60/min |
| PATCH | /preferences | Update user preferences | JWT | 30/min |
GET /preferences
Description
Retrieve the current user's full preference object.
Authentication: JWT Bearer token required. Rate limit: 60 requests per minute.
Request
No request body. No path or query parameters.
Response
{
"data": {
"emailNotifications": true,
"pushNotifications": true,
"friendActivityAlerts": true,
"aiCoachingReminders": true,
"countryCode": "TR",
"languageCode": "en",
"discoverableByEmail": false,
"allowAiTraining": false,
"aiMemoryEnabled": true,
"aiMessageSuggestionsEnabled": true,
"aiMessageSuggestionCount": 3,
"aiPersona": "supportive",
"preferredAiProvider": "google",
"chainViewMode": "snake",
"bitViewMode": "kanban",
"keyboardShortcutsEnabled": true,
"notificationDigestFrequency": "daily",
"notificationFrequencyMode": "balanced",
"quietHours": {},
"timezone": "Europe/Istanbul",
"useLatestSessions": true,
"hideTitles": false,
"updatedAt": "2026-03-17T10:00:00.000Z"
},
"meta": null,
"error": null
}Response Fields
| Field | Type | Description |
|---|---|---|
emailNotifications | boolean | Email notifications enabled |
pushNotifications | boolean | Push notifications enabled |
friendActivityAlerts | boolean | Notifications for friend activity enabled |
aiCoachingReminders | boolean | AI coaching reminder notifications enabled |
countryCode | string | ISO country code (max 2 characters) |
languageCode | string | Display language code (max 10 characters) |
discoverableByEmail | boolean | Whether the account can be found by email lookup |
allowAiTraining | boolean | Whether user content may be used for AI training |
aiMemoryEnabled | boolean | Whether the AI may persist memory across sessions |
aiMessageSuggestionsEnabled | boolean | Follow-up suggestion cards enabled |
aiMessageSuggestionCount | integer | Number of suggestion cards per message (2–4) |
aiPersona | string | AI persona; see AiPersona values |
preferredAiProvider | string | google, openai, anthropic, or mistral |
chainViewMode | string | See ChainViewMode values |
bitViewMode | string | See BitViewMode values |
keyboardShortcutsEnabled | boolean | Keyboard shortcuts enabled in the web app |
notificationDigestFrequency | string | realtime, daily, or weekly |
notificationFrequencyMode | string | minimal, balanced, or frequent |
quietHours | object | Freeform quiet-hours configuration |
timezone | string | IANA timezone string (max 64 characters) |
useLatestSessions | boolean | Whether the UI resumes the most recent AI session by default |
hideTitles | boolean | Whether titles are hidden in list views |
updatedAt | string | ISO 8601 timestamp of last update |
Code Examples
curl https://api.chainabit.com/api/v1/preferences \
-H "Authorization: Bearer $TOKEN"const response = await fetch(`${BASE_URL}/preferences`, {
headers: {
'Authorization': `Bearer ${TOKEN}`,
},
});
const data = await response.json();import requests
response = requests.get(
f"{BASE_URL}/preferences",
headers={"Authorization": f"Bearer {TOKEN}"},
)
data = response.json()PATCH /preferences
Description
Update one or more preference fields. Only the fields included in the request body are updated; all other fields remain unchanged (partial update). The request body is validated with a strict allowlist — any field not in the schema below (for example a legacy theme or nested notifications field) is rejected with 400 Bad Request (property theme should not exist).
Authentication: JWT Bearer token required. Rate limit: 30 requests per minute.
Request
All fields are optional. Only include the fields you want to change.
| Field | Type | Constraints | Description |
|---|---|---|---|
emailNotifications | boolean | — | Enable email notifications |
pushNotifications | boolean | — | Enable push notifications |
friendActivityAlerts | boolean | — | Enable friend activity notifications |
aiCoachingReminders | boolean | — | Enable AI coaching reminder notifications |
countryCode | string | max length 2 | ISO country code |
languageCode | string | max length 10 | Display language code |
discoverableByEmail | boolean | — | Allow lookup of this account by email |
allowAiTraining | boolean | — | Allow user content to be used for AI training |
aiMemoryEnabled | boolean | — | Allow the AI to persist memory across sessions |
aiMessageSuggestionsEnabled | boolean | — | Enable follow-up suggestion cards |
aiMessageSuggestionCount | integer | 2–4 | Number of suggestion cards per message |
aiPersona | string (enum) | see AiPersona values | AI persona |
preferredAiProvider | string (enum) | google, openai, anthropic, mistral | Non-google values require a paid entitlement; FREE-plan accounts are silently kept on google |
chainViewMode | string (enum) | see ChainViewMode values | The legacy value sneak is still accepted and silently mapped to snake for backward compatibility with old clients |
bitViewMode | string (enum) | see BitViewMode values | Default bit view |
keyboardShortcutsEnabled | boolean | — | Enable keyboard shortcuts |
notificationDigestFrequency | string (enum) | realtime, daily, weekly | Digest email frequency |
notificationFrequencyMode | string (enum) | minimal, balanced, frequent | minimal suppresses normal-priority reminders; frequent sends all |
quietHours | object | freeform | Quiet-hours configuration |
timezone | string | max length 64 | IANA timezone, e.g. Europe/Istanbul |
useLatestSessions | boolean | — | Resume the most recent AI session by default |
hideTitles | boolean | — | Hide titles in list views |
AiPersona values
supportive, motivational, analytical, challenger, strategist, stoic, witty, socratic, pragmatist, ruthless
ChainViewMode values
consistency, puzzle, snake, sheet, voxel
BitViewMode values
matrix, kanban, calendar, gantt, tree, singularity
Response
{
"data": {
"emailNotifications": true,
"pushNotifications": true,
"friendActivityAlerts": true,
"aiCoachingReminders": false,
"countryCode": "TR",
"languageCode": "en",
"discoverableByEmail": false,
"allowAiTraining": false,
"aiMemoryEnabled": true,
"aiMessageSuggestionsEnabled": true,
"aiMessageSuggestionCount": 3,
"aiPersona": "analytical",
"preferredAiProvider": "google",
"chainViewMode": "snake",
"bitViewMode": "kanban",
"keyboardShortcutsEnabled": true,
"notificationDigestFrequency": "weekly",
"notificationFrequencyMode": "minimal",
"quietHours": {},
"timezone": "Europe/Istanbul",
"useLatestSessions": true,
"hideTitles": false,
"updatedAt": "2026-03-17T11:00:00.000Z"
},
"meta": null,
"error": null
}Response Fields
Returns the full updated preferences object. See GET /preferences for the complete field reference.
Code Examples
curl -X PATCH https://api.chainabit.com/api/v1/preferences \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"aiCoachingReminders": false,
"aiPersona": "analytical",
"notificationDigestFrequency": "weekly",
"notificationFrequencyMode": "minimal"
}'const response = await fetch(`${BASE_URL}/preferences`, {
method: 'PATCH',
headers: {
'Authorization': `Bearer ${TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
aiCoachingReminders: false,
aiPersona: 'analytical',
notificationDigestFrequency: 'weekly',
notificationFrequencyMode: 'minimal',
}),
});
const data = await response.json();import requests
response = requests.patch(
f"{BASE_URL}/preferences",
headers={"Authorization": f"Bearer {TOKEN}"},
json={
"aiCoachingReminders": False,
"aiPersona": "analytical",
"notificationDigestFrequency": "weekly",
"notificationFrequencyMode": "minimal",
},
)
data = response.json()Notes
- Partial Updates: The
PATCHendpoint only updates the flat fields included in the request body; all other fields are left unchanged. There is no nested object to merge — every field listed above is set directly on the preferences record. - Strict Validation: The request body is validated against an allowlist. Sending any field that isn't part of
UpdatePreferencesDto(for example a legacythemefield or a nestednotificationsobject) returns400 Bad Requestwith a message likeproperty theme should not exist. This is intended behavior, not a bug — always check this page for the current field list rather than relying on older integration code. - Defaults: New users receive a default preference set. The GET endpoint always returns the full preference object.
chainViewModeLegacy Alias: The old valuesneakis still accepted onPATCHand is silently mapped tosnakefor backward compatibility with older clients;GETalways returns the current value (snake).preferredAiProviderEntitlement Gating: SettingpreferredAiProviderto anything other thangooglerequires a paid entitlement. FREE-plan accounts that attempt to set a non-googleprovider are silently kept ongooglerather than receiving an error.- Validation: Invalid values (e.g., an unsupported enum value, an out-of-range
aiMessageSuggestionCount, or acountryCode/languageCode/timezonethat exceeds its max length) return400 Bad Requestwith a validation error. - Side Effects: Changing
timezoneaffects how scheduled chain reminders are calculated. Changes take effect on the next scheduled event.