Tools API
The complete tools API available to app actions, backends, and webhook handlers
Every action, backend, and webhook handler receives a tools object with the
full platform API. This page is the complete reference for all available tools.
tools.data
Store and retrieve structured data. Both tools.data.public and
tools.data.private share the same interface.
- public data is visible to all server members and searchable by the AI
- private data is scoped to the app install (sync cursors, tokens, state)
set(key, value, meta?) stores a key/value pair. The value should be a string
(JSON-stringify objects). The optional meta object supports:
| Field | Type | Description |
|---|---|---|
title | string | human-readable title |
kind | string | data kind for filtering |
status | string | status indicator |
tags | string[] | tags for filtering |
weight | number | sort weight |
format | string | 'text' | 'json' | 'markdown' |
url | string | external URL |
externalId | string | ID in the external system |
startsAt | number | start timestamp |
endsAt | number | end timestamp |
createdAt | number | creation timestamp |
updatedAt | number | last update timestamp |
syncedAt | number | last sync timestamp |
aiVisibility | string | 'context' | 'searchable' | 'hidden' |
scope | string | 'user' | 'server' | 'channel' |
extra | object | arbitrary extra metadata |
get(key) returns a Datum object { key, value, title, kind, ... } or
null.
getAll(query?) returns an array of all data entries. The optional query object filters results:
| Field | Type | Description |
|---|---|---|
kind | string | filter by kind |
status | string | filter by status |
tags | string[] | filter by tags |
limit | number | max results |
sort | string | 'newest' | 'oldest' |
delete(key) removes an entry by key.
stat(key, value, meta?) stores a point-in-time metric. The value is a record with numeric fields. Updating the same key overwrites the previous value. Use this for current counts, gauges, and snapshots:
series(name, value, options?) stores a time-series data point with automatic
aggregation. Each call creates a period-keyed entry and updates running
aggregates (sum, count, min, max, avg) plus a rolling points array (up to 30
values) on the aggregate row. Use this for tracking values over time:
The period option controls bucketing: 'hourly', 'daily' (default),
'weekly', or 'monthly'. Within each period, updating the same series
replaces the value and recalculates aggregates.
The aggregate row (key = series name, type = series-agg) stores running stats
and the points array. Chart blocks can load this single row via
previewQuery="daily-revenue" for sparkline rendering without querying all
individual series entries. For multi-field values, points is an object keyed
by field name.
See Data for the full data model and querying patterns.
tools.message
Post messages to channels:
React to a message with an emoji:
Pin or unpin a message (toggles):
tools.notify
Imperatively create notifications in the workspace. Use this for durable alerts
instead of returning notification-like values through outputs:
Send a push notification to a specific user:
tools.generate
Uncached AI generation. Always calls the model — use sparingly for creative or dynamic tasks where caching doesn’t make sense. For deterministic extractions that repeat across runs, use tools.strategy instead.
text(tier, prompt)
Generate a text response:
object(tier, prompt, schema?)
Generate a structured JSON object. The optional schema constrains the output shape:
Model tiers: 'xxs', 'xs', 'sm', 'md', 'lg', 'xl'. Use
'xxs' for lightweight tasks like categorization, extraction, and filtering.
'xl' uses the most capable models for complex reasoning. You can also pass a
specific model ID string.
tools.abort
Stop the current flow pipeline. Throws immediately and skips remaining steps:
tools.progress
Update a progress indicator visible in the app UI (0-100):
tools.webhooks
Webhook subscriptions are managed through flow event triggers. When a flow uses
on app.event, the platform automatically registers and manages the webhook
with the external service. See When Flows Run
for details on event triggers.
Apps declare the events they emit in their events definition, and flows listen
with on app.event syntax. The onWebhook handler on the app processes
incoming payloads and returns a matching event name.
tools.flows
Programmatically create and delete flows:
tools.helpers
tools.crypto
Sign JWTs and encode data:
tools.strategy
Cached AI that learns on first run and replays deterministically after. All strategy methods generate a JS snippet on first run, cache it per step, and execute it instantly on subsequent runs. Self-healing: if the cached code fails, it regenerates with error context and retries.
Core principle: strategies must be time-independent
Strategies are cached and reused across runs. Never put timestamps, dates, or any time-dependent values into a strategy. The strategy should capture the shape of the operation, and the app computes concrete values at runtime.
object(opts)
Extract a structured object from the step’s input context. The generated JS
receives { instruction, summary, data } and returns an object matching the schema.
Options:
| Field | Type | Description |
|---|---|---|
task | string | what the strategy needs to figure out |
schema | object | expected output shape (optional) |
modelOrTier | ModelOrTier | model tier (default: 'xxs') |
forceRegenerate | boolean | regenerate even if cached |
list(opts)
Same as object() but the generated JS returns an array. Schema describes the
shape of each item.
Takes the same options as object().
data(opts)
Map raw API data to standardized AppResultDataItem format. Generates a JS
function that receives the raw data and returns { summary, items, count, meta? }. Items conform to AppResultDataItem — ready to return from your
action as data: result.items.
Options:
| Field | Type | Description |
|---|---|---|
data | unknown | raw API response data |
task | string | what this data represents (optional) |
fields | string[] | fields to prioritize in mapping (optional) |
modelOrTier | ModelOrTier | model tier |
forceRegenerate | boolean | regenerate even if cached |
maxRetries | number | retry attempts if generated code fails (default: 2) |
hints | object | context hints (see keyFormats below) |
Key format hints: pass hints: { keyFormats: { issue: 'github-issue:{number}' } }
to constrain item keys. This ensures sync, actions, and webhooks all write to
the same data row for a given entity. The generated JS will use these exact key
patterns.
Return shape:
| Field | Type | Description |
|---|---|---|
summary | string | human-readable summary |
items | AppResultDataItem[] | standardized data items |
count | number | always items.length |
meta | object | optional aggregates and computed fields |
The generated JS snippet maps API-specific fields to the well-defined
AppResultDataItem shape (type, key, value, title, status, kind, url, tags,
externalId). The UI can render these automatically as tables, lists, badges, and
links without knowing anything about the source API.
Design guidelines
- Cache the shape, not the data. Strategies are saved and reused — they must produce the same correct result regardless of when they run.
- Use relative descriptions for time. Instead of Unix timestamps, use types
like
"today","last-n-days","this-month"and resolve to timestamps in your action code at runtime. - Strategies generate once, run forever. Think of them as learning a deterministic extraction plan on first run. If the plan depends on the current date, it will be wrong on the second run.
- Use
forceRegeneratesparingly. Only when you know the cached strategy is invalid (e.g., schema changed). Flow-level strategy clearing handles most recovery.
strategy.fetch()
Combines strategy.object() + fetch in one call:
The strategy determines the API parameters once (cached). Every subsequent run skips the AI and goes straight to the fetch. See Strategies for more on caching, self-healing, and time-independence.
tools.files (desktop only)
File system access scoped to ~/.start/{serverSlug}/apps/{appSlug}/files/. Only
available in actions with runsOn: 'desktop'.