SDK Reference
Full API reference for @reeve/app-sdk — AI completions, cloud storage, data queries, events, navigation, and theming.
SDK Reference — @reeve/app-sdk
The @reeve/app-sdk is the bridge between your iframe app and the Reeve Cockpit. It communicates over a verified postMessage channel and exposes a clean async API for AI, storage, data, and events.
Installation
npm install @reeve/app-sdkThe package ships with full TypeScript types. No @types/ package needed.
reeve.init()
Initialize the SDK. Must be called before any other method. Establishes the Cockpit handshake and returns the current session context.
import { reeve } from '@reeve/app-sdk';
const config = await reeve.init();Returns: ReeveConfig
| Field | Type | Description |
|---|---|---|
orgId | string | Merchant organization ID |
userId | string | Current user ID |
theme | 'light' | 'dark' | Active Cockpit theme |
locale | string | User locale (e.g. en-US) |
appId | string | Your app's manifest ID |
permissions | string[] | Granted permissions for this install |
All SDK methods throw a ReeveNotInitializedError if called before reeve.init() resolves. Always await initialization before calling other methods.
reeve.ai — AI Completions
reeve.ai.complete(options)
Generate a text completion using Reeve's managed AI routing. Reeve selects the best model for the declared intent, handles retries, and bills through the merchant's Reeve subscription — no API keys required.
Permission required: ai.complete
const result = await reeve.ai.complete({
prompt: 'Summarize this product description for an ad headline',
systemPrompt: 'You are an expert ecommerce copywriter. Be concise and punchy.',
intent: 'summarization',
maxTokens: 200,
temperature: 0.7,
});
console.log(result.content); // The generated text
console.log(result.model); // Which model was used, e.g. 'gpt-4o-mini'
console.log(result.tokens); // { prompt: 42, completion: 38, total: 80 }Options:
| Field | Type | Required | Description |
|---|---|---|---|
prompt | string | ✓ | The user message / main prompt |
systemPrompt | string | — | System prompt for model behavior |
intent | AIIntent | — | Hints model routing (see intents below) |
maxTokens | number | — | Max output tokens (default: 1000) |
temperature | number | — | Sampling temperature 0–2 (default: 0.7) |
context | object | — | Extra context merged into the prompt |
AIIntent values:
| Intent | Best For | Model Tier |
|---|---|---|
'chat' | Conversational responses | Standard |
'summarization' | Condensing text | Fast/efficient |
'generation' | Creative writing, copy | Standard |
'analysis' | Data analysis, reasoning | Advanced |
'extraction' | Structured data from text | Fast/efficient |
'classification' | Categorization, labeling | Fast/efficient |
'code' | Code generation/explanation | Advanced |
Returns: AICompletionResult
| Field | Type | Description |
|---|---|---|
content | string | Generated text |
model | string | Model that served the request |
tokens | { prompt: number; completion: number; total: number } | Token usage |
finishReason | 'stop' | 'length' | 'error' | Why generation stopped |
reeve.ai.stream(options, callback)
Same as complete() but streams tokens as they arrive. Useful for chat interfaces or long-form generation.
Permission required: ai.stream
let buffer = '';
await reeve.ai.stream(
{
prompt: 'Write a 3-paragraph product description for wireless earbuds',
intent: 'generation',
},
(chunk) => {
buffer += chunk.delta;
setOutput(buffer);
if (chunk.done) {
console.log('Stream complete. Total tokens:', chunk.tokens?.total);
}
}
);Callback receives AIStreamChunk:
| Field | Type | Description |
|---|---|---|
delta | string | New text fragment |
done | boolean | true on final chunk |
tokens | TokenUsage | undefined | Only present on final chunk |
reeve.storage — Cloud Storage
Every app gets a private key-value store scoped to the merchant org. Data persists across sessions and is isolated per app.
reeve.storage.set(collection, id, data)
Write a document.
Permission required: storage.write
await reeve.storage.set('contacts', 'id-123', {
name: 'Jane Smith',
email: 'jane@acme.com',
tags: ['vip', 'wholesale'],
createdAt: new Date().toISOString(),
});reeve.storage.get(collection, id)
Read a document by ID. Returns null if not found.
Permission required: storage.read
const doc = await reeve.storage.get('contacts', 'id-123');
if (doc) {
console.log(doc.name); // 'Jane Smith'
console.log(doc._meta.updatedAt); // ISO timestamp
}reeve.storage.list(collection, options?)
List documents in a collection, with optional filtering and pagination.
Permission required: storage.read
const result = await reeve.storage.list('contacts', {
limit: 20,
cursor: undefined, // for pagination, pass result.nextCursor
orderBy: 'createdAt',
orderDir: 'desc',
filter: { tags: { includes: 'vip' } },
});
console.log(result.items); // Document[]
console.log(result.nextCursor); // Pass to next call to paginate
console.log(result.total); // Total count in collectionList options:
| Field | Type | Default | Description |
|---|---|---|---|
limit | number | 50 | Documents per page (max 200) |
cursor | string | — | Pagination cursor from previous call |
orderBy | string | '_meta.updatedAt' | Field to sort by |
orderDir | 'asc' | 'desc' | 'desc' | Sort direction |
filter | object | — | Field-level filters (equality and array ops) |
reeve.storage.delete(collection, id)
Delete a document.
Permission required: storage.write
await reeve.storage.delete('contacts', 'id-123');reeve.storage.deleteCollection(collection)
Delete all documents in a collection. Irreversible.
Permission required: storage.write
await reeve.storage.deleteCollection('contacts');reeve.data — Ecommerce Data Queries
Query live data from connected merchant integrations (Shopify, Klaviyo, Google Ads, Meta Ads, etc.) through a unified interface.
reeve.data.query(options)
const result = await reeve.data.query({
source: 'shopify',
type: 'orders',
filters: {
status: 'fulfilled',
created_at_min: '2025-01-01',
},
limit: 50,
fields: ['id', 'name', 'total_price', 'created_at', 'line_items'],
});
console.log(result.data); // Order[]
console.log(result.total); // Total matching records
console.log(result.source); // 'shopify'
console.log(result.cachedAt); // ISO timestamp of when data was cachedOptions:
| Field | Type | Required | Description |
|---|---|---|---|
source | DataSource | ✓ | Which integration to query |
type | string | ✓ | Data type within that source |
filters | object | — | Source-specific filter fields |
limit | number | — | Max records (default: 25, max: 250) |
fields | string[] | — | Fields to include (reduces payload) |
cursor | string | — | Pagination cursor |
Available sources and types:
source | type options | Permission |
|---|---|---|
shopify | orders, products, customers, inventory, revenue | data.shopify |
email | campaigns, flows, segments, subscribers | data.email |
social | posts, metrics, growth | data.social |
ads | campaigns, adsets, ads, performance | data.ads |
analytics | pageviews, sessions, conversions, funnels | data.analytics |
reeve.events — Event Bus
Subscribe to platform events or emit events to other parts of the Cockpit.
reeve.events.subscribe(event, handler)
Subscribe to a platform event. Returns an unsubscribe function.
Permission required: events.subscribe
const unsubscribe = reeve.events.subscribe('shopify:order_created', (event) => {
console.log('New order!', event.data);
console.log('Event ID:', event.id);
console.log('Timestamp:', event.timestamp);
});
// Later, to clean up:
unsubscribe();Built-in platform events:
| Event | Payload | Trigger |
|---|---|---|
shopify:order_created | { order } | New Shopify order received |
shopify:order_fulfilled | { order } | Order marked fulfilled |
shopify:product_updated | { product } | Product edited in Shopify |
klaviyo:campaign_sent | { campaign } | Email campaign deployed |
klaviyo:subscriber_added | { subscriber } | New email subscriber |
reeve:agent_message | { message, agentId } | Agent sends a message |
reeve:theme_changed | { theme } | User switches theme |
reeve:app_focused | { appId } | User switches to an app |
app:* | any | Custom events from your app |
reeve.events.emit(event, data)
Emit a custom event. Other Cockpit components and apps (with permission) can subscribe to app:* events.
Permission required: events.emit
reeve.events.emit('app:ready', {
version: '1.0.0',
features: ['dashboard', 'export'],
});
reeve.events.emit('app:campaign_created', {
campaignId: 'camp_abc123',
name: 'Summer Sale',
});Custom event names must start with app:. Attempting to emit a shopify: or reeve: event will throw a ReevePermissionError.
reeve.navigate() — Navigation
Navigate the outer Cockpit window to a different section.
// Navigate to a built-in Cockpit route
reeve.navigate('/cockpit/agents');
reeve.navigate('/cockpit/dashboard');
reeve.navigate('/cockpit/apps');
// Open an external URL in a new tab
reeve.navigate('https://docs.meetreeve.com', { external: true });Cockpit routes:
| Path | Destination |
|---|---|
/cockpit/dashboard | Main dashboard |
/cockpit/agents | Agents list |
/cockpit/apps | App store |
/cockpit/connectors | Integrations |
/cockpit/settings | Settings |
reeve.onThemeChange() — Theme Listener
Listen for theme changes in real time. CSS variables are injected automatically, but this callback lets you update component state.
reeve.onThemeChange((theme) => {
// theme: 'light' | 'dark'
document.documentElement.setAttribute('data-theme', theme);
setIsDark(theme === 'dark');
});The current theme is also available synchronously after init via reeve.config.theme.
CSS Variables
These variables are automatically injected into your app's document.documentElement by the Cockpit. Use them in your CSS for seamless theme support:
| Variable | Light | Dark | Usage |
|---|---|---|---|
--reeve-bg | #ffffff | #0f0f0f | Page background |
--reeve-surface | #f5f5f5 | #1a1a1a | Cards, panels |
--reeve-surface-2 | #ebebeb | #242424 | Nested surfaces |
--reeve-border | #e0e0e0 | #2a2a2a | Borders, dividers |
--reeve-text | #111111 | #f0f0f0 | Primary text |
--reeve-text-muted | #666666 | #888888 | Secondary text |
--reeve-accent | #6366f1 | #6366f1 | Primary accent |
--reeve-accent-hover | #4f46e5 | #4f46e5 | Accent hover state |
--reeve-danger | #ef4444 | #ef4444 | Error / destructive |
--reeve-success | #22c55e | #22c55e | Success states |
--reeve-warning | #f59e0b | #f59e0b | Warning states |
--reeve-radius | 8px | 8px | Border radius |
--reeve-radius-sm | 4px | 4px | Small radius |
--reeve-radius-lg | 12px | 12px | Large radius |
--reeve-font | 'Inter', system-ui, sans-serif | same | Font stack |
TypeScript Types
// Full type exports from @reeve/app-sdk
export interface ReeveConfig {
orgId: string;
userId: string;
theme: 'light' | 'dark';
locale: string;
appId: string;
permissions: Permission[];
}
export type AIIntent =
| 'chat'
| 'summarization'
| 'generation'
| 'analysis'
| 'extraction'
| 'classification'
| 'code';
export interface AICompleteOptions {
prompt: string;
systemPrompt?: string;
intent?: AIIntent;
maxTokens?: number;
temperature?: number;
context?: Record<string, unknown>;
}
export interface AICompletionResult {
content: string;
model: string;
tokens: { prompt: number; completion: number; total: number };
finishReason: 'stop' | 'length' | 'error';
}
export interface AIStreamChunk {
delta: string;
done: boolean;
tokens?: { prompt: number; completion: number; total: number };
}
export interface StorageListOptions {
limit?: number;
cursor?: string;
orderBy?: string;
orderDir?: 'asc' | 'desc';
filter?: Record<string, unknown>;
}
export interface StorageListResult<T = unknown> {
items: (T & { _meta: { id: string; createdAt: string; updatedAt: string } })[];
nextCursor: string | null;
total: number;
}
export type DataSource = 'shopify' | 'email' | 'social' | 'ads' | 'analytics';
export interface DataQueryOptions {
source: DataSource;
type: string;
filters?: Record<string, unknown>;
limit?: number;
fields?: string[];
cursor?: string;
}
export interface DataQueryResult<T = unknown> {
data: T[];
total: number;
source: DataSource;
cachedAt: string;
nextCursor: string | null;
}
export interface ReeveEvent<T = unknown> {
id: string;
event: string;
data: T;
timestamp: string;
}
export type Permission =
| 'ai.complete'
| 'ai.stream'
| 'storage.read'
| 'storage.write'
| 'data.shopify'
| 'data.email'
| 'data.social'
| 'data.ads'
| 'data.analytics'
| 'events.subscribe'
| 'events.emit';
// Errors
export class ReeveNotInitializedError extends Error {}
export class ReevePermissionError extends Error {
permission: Permission;
}
export class ReeveDataSourceError extends Error {
source: DataSource;
type: string;
}Error Handling
All SDK methods throw typed errors. Wrap in try/catch for production apps:
import { reeve, ReevePermissionError, ReeveDataSourceError } from '@reeve/app-sdk';
try {
const orders = await reeve.data.query({ source: 'shopify', type: 'orders' });
} catch (err) {
if (err instanceof ReevePermissionError) {
// App doesn't have data.shopify permission
showError(`Permission denied: ${err.permission}`);
} else if (err instanceof ReeveDataSourceError) {
// Shopify not connected, or API error
showError(`Shopify unavailable. Please reconnect in Connectors.`);
} else {
throw err; // unexpected error
}
}Related
- Manifest Reference — declaring permissions
- Getting Started — your first API call
- Shopify Dashboard Tutorial — full working example