Data Overview
Structured, queryable storage scoped to each app
Most chat platforms treat integrations as notification pipes — data flows in, gets buried in a timeline, and becomes unsearchable. Start.chat takes a different approach: apps store structured data directly in your workspace, where it stays queryable, displayable, and actionable.
What this section is for
Read the Data docs after Apps if you need durable state, searchable records, dashboards, or structured context for AI. This section is about storage and query patterns inside start.chat, not external SDK or REST access.
Data is a key-value storage system that apps use to sync external information, store state, and share data across your server. Every entry has a key, a value, and rich metadata like title, kind, tags, and status — making it easy to filter, sort, and display in app UIs.
Two kinds of data output
Apps produce data in two distinct ways. Understanding the difference is key:
Action return data
Actions return AppResultDataItem[] as their primary structured output via the
data field on the result object. These items have well-defined fields the UI
knows how to render automatically — title, status, kind, url, tags, externalId,
and a flexible value field for domain-specific payloads.
Returning data does not automatically insert anything into the data table.
The items flow through the pipeline and blocks can render them, but they are a
structured “view” of what the action produced — not persisted records.
Persisted data
To store data permanently, apps explicitly call tools.data.public.set(),
tools.data.public.stat(), or tools.data.public.series(). Persisted data
lives in the data table, is searchable, and can be queried by blocks and the AI.
Many actions do both: return data items for the pipeline/UI, and persist
key metrics via stat/series for dashboards.
How data gets created
Data typically comes from app backends that sync on a schedule:
Built-in apps like GitHub, Stripe, and Linear sync their data automatically when connected. You can also import data by dragging CSV or JSON files into the Data view.
AppResultDataItem
The standardized item shape returned from actions. The UI auto-renders these fields as tables, lists, badges, and links without needing app-specific code:
| Field | Type | Description |
|---|---|---|
type | string | item type (e.g., payment, issue) |
key | string? | unique key (e.g., stripe-payment:ch_abc) |
value | string or object | domain-specific payload |
title | string? | human-readable display title |
status | string? | status indicator |
kind | string? | broad classification for grouping |
url | string? | link to external resource |
tags | string[]? | tags for filtering |
externalId | string? | ID in the external system |
weight | number? | sort priority |
Because these fields are well-defined, the UI can:
- render tables with title, status, and tags as columns
- show lists with title + status badges
- make links clickable from url
- group and filter by kind
- work without knowing anything about stripe vs github vs linear
Permissions
Data has two permission levels:
| Permission | Visibility | Use case |
|---|---|---|
public | All server members | Synced records, shared dashboards, triggers |
private | Only the app install that created it | Sync cursors, tokens, internal state |
Write access is granted to the data creator, the owner, any role with
canEditData, or a server admin.
Scope
Scope controls who the data is relevant to:
| Scope | Meaning |
|---|---|
server | Available server-wide (default) |
channel | Scoped to a specific channel |
user | Scoped to a specific user |
AI visibility
The aiVisibility field controls how the AI assistant interacts with data:
| Value | Behavior |
|---|---|
searchable | Discoverable via search (default) |
context | Automatically included in AI conversation context |
hidden | Not visible to AI |
For example, the built-in Memory app stores memories with
aiVisibility: 'context' so they are always available to the assistant.
Querying data
App UIs query data with the useData hook, which returns live results that
update in real time:
The DataTable component provides a full-featured browsing UI with filtering,
sorting, and bulk operations out of the box.
Data fields
| Field | Type | Description |
|---|---|---|
key | string | Lookup key (unique per scope) |
value | string | Stored value (typically JSON) |
title | string | Human-readable display title |
kind | string | Data kind (e.g., customer, payment) |
tags | string[] | Tags for grouping and filtering |
status | string | Status indicator |
weight | number | Priority or importance score |
format | string | Value format: text, json, or markdown |
url | string | Link to external resource |
extra | object | Arbitrary additional metadata |
aiVisibility | string | AI access level (see above) |
scope | string | Data reach: server, channel, or user |
permissions | string | public or private |
startsAt | number | Start time (for events) |
endsAt | number | End time (for events) |
externalId | string | ID in the external system |
syncedAt | number | Last sync timestamp |
Next steps
- Writing Data — store and persist data from backends
- Querying Data — display data in your UI
- Patterns — stat, series, data item, and summary patterns