App Data Store
Per-app, per-org key-value storage for configuration, state, and preferences.
Overview
The App Data Store provides persistent key-value storage scoped to your app and the installing organization. Use it to store configuration, user preferences, sync state, and any other data your app needs without maintaining your own database.
This API requires the app-data:read scope for read operations and app-data:write scope for write operations.
Data Model
Data is organized by namespace and key:
App (your app)
└── Org (installing organization)
└── Namespace (logical grouping)
├── key1 → value
├── key2 → value
└── key3 → value- Namespace — A logical grouping for related keys (e.g.,
settings,sync-state,cache) - Key — A unique identifier within a namespace
- Value — Any JSON-serializable data (objects, arrays, strings, numbers, booleans)
Limits
| Limit | Value |
|---|---|
| Max value size | 10 KB per value |
| Max keys per namespace | 1,000 |
| Max namespaces per app per org | 50 |
| Key length | 1–256 characters |
| Namespace length | 1–64 characters |
Set a Value
PUT /v1/app-data/:namespace/:keyawait fetch('https://api.lend.works/v1/app-data/settings/sync-config', {
method: 'PUT',
headers: {
Authorization: 'Bearer lw_at_xxxxxxxxxxxxxxxx',
'Content-Type': 'application/json',
},
body: JSON.stringify({
value: {
syncInterval: 3600,
enabledModules: ['leads', 'funded-deals'],
lastSyncAt: '2026-03-15T10:00:00Z',
},
}),
})Response
{
"data": {
"namespace": "settings",
"key": "sync-config",
"value": {
"syncInterval": 3600,
"enabledModules": ["leads", "funded-deals"],
"lastSyncAt": "2026-03-15T10:00:00Z"
},
"updatedAt": "2026-03-15T10:00:00Z"
}
}If the key already exists, its value is overwritten.
Get a Value
GET /v1/app-data/:namespace/:keyconst response = await fetch(
'https://api.lend.works/v1/app-data/settings/sync-config',
{
headers: {
Authorization: 'Bearer lw_at_xxxxxxxxxxxxxxxx',
},
}
)
const { data } = await response.json()
console.log(data.value.syncInterval) // 3600Returns 404 if the key does not exist.
List Keys in a Namespace
GET /v1/app-data/:namespacecurl -H "Authorization: Bearer lw_at_xxxxxxxxxxxxxxxx" \
https://api.lend.works/v1/app-data/settingsResponse
{
"data": [
{
"namespace": "settings",
"key": "sync-config",
"updatedAt": "2026-03-15T10:00:00Z"
},
{
"namespace": "settings",
"key": "notification-prefs",
"updatedAt": "2026-03-14T08:30:00Z"
}
],
"meta": {
"total": 2
}
}Values are not included in the list response — fetch individual keys to retrieve values.
Delete a Value
DELETE /v1/app-data/:namespace/:keycurl -X DELETE \
-H "Authorization: Bearer lw_at_xxxxxxxxxxxxxxxx" \
https://api.lend.works/v1/app-data/settings/sync-configReturns 204 No Content on success.
Common Patterns
Storing Configuration
// Save the user's configuration for your app
await fetch('https://api.lend.works/v1/app-data/config/main', {
method: 'PUT',
headers: {
Authorization: `Bearer ${accessToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
value: {
crmEndpoint: 'https://crm.example.com/api',
fieldMappings: {
businessName: 'company_name',
email: 'primary_email',
},
autoSync: true,
},
}),
})Tracking Sync State
// After a successful sync, record the cursor
await fetch('https://api.lend.works/v1/app-data/sync-state/leads', {
method: 'PUT',
headers: {
Authorization: `Bearer ${accessToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
value: {
lastSyncAt: new Date().toISOString(),
cursor: 'eyJpZCI6IjEyMyJ9',
recordsSynced: 847,
},
}),
})Per-User Preferences
// Store preferences keyed by user ID
const userId = '550e8400-e29b-41d4-a716-446655440000'
await fetch(`https://api.lend.works/v1/app-data/user-prefs/${userId}`, {
method: 'PUT',
headers: {
Authorization: `Bearer ${accessToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
value: {
dashboardLayout: 'compact',
notifications: { email: true, slack: false },
},
}),
})Best Practices
- Use namespaces to organize related data (e.g.,
config,sync-state,cache) - Keep values small — The 10 KB limit is per value. For larger data, store references to external storage.
- Don't store secrets — Use environment variables or a secrets manager for API keys and credentials. The data store is not encrypted at rest.
- Handle 404 gracefully — Initialize default values when a key doesn't exist
- Avoid high-frequency writes — The data store is designed for configuration, not as a real-time database