Reeve
Developers

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-sdk

The 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

FieldTypeDescription
orgIdstringMerchant organization ID
userIdstringCurrent user ID
theme'light' | 'dark'Active Cockpit theme
localestringUser locale (e.g. en-US)
appIdstringYour app's manifest ID
permissionsstring[]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:

FieldTypeRequiredDescription
promptstringThe user message / main prompt
systemPromptstringSystem prompt for model behavior
intentAIIntentHints model routing (see intents below)
maxTokensnumberMax output tokens (default: 1000)
temperaturenumberSampling temperature 0–2 (default: 0.7)
contextobjectExtra context merged into the prompt

AIIntent values:

IntentBest ForModel Tier
'chat'Conversational responsesStandard
'summarization'Condensing textFast/efficient
'generation'Creative writing, copyStandard
'analysis'Data analysis, reasoningAdvanced
'extraction'Structured data from textFast/efficient
'classification'Categorization, labelingFast/efficient
'code'Code generation/explanationAdvanced

Returns: AICompletionResult

FieldTypeDescription
contentstringGenerated text
modelstringModel 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:

FieldTypeDescription
deltastringNew text fragment
donebooleantrue on final chunk
tokensTokenUsage | undefinedOnly 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 collection

List options:

FieldTypeDefaultDescription
limitnumber50Documents per page (max 200)
cursorstringPagination cursor from previous call
orderBystring'_meta.updatedAt'Field to sort by
orderDir'asc' | 'desc''desc'Sort direction
filterobjectField-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 cached

Options:

FieldTypeRequiredDescription
sourceDataSourceWhich integration to query
typestringData type within that source
filtersobjectSource-specific filter fields
limitnumberMax records (default: 25, max: 250)
fieldsstring[]Fields to include (reduces payload)
cursorstringPagination cursor

Available sources and types:

sourcetype optionsPermission
shopifyorders, products, customers, inventory, revenuedata.shopify
emailcampaigns, flows, segments, subscribersdata.email
socialposts, metrics, growthdata.social
adscampaigns, adsets, ads, performancedata.ads
analyticspageviews, sessions, conversions, funnelsdata.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:

EventPayloadTrigger
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:*anyCustom 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:

PathDestination
/cockpit/dashboardMain dashboard
/cockpit/agentsAgents list
/cockpit/appsApp store
/cockpit/connectorsIntegrations
/cockpit/settingsSettings

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:

VariableLightDarkUsage
--reeve-bg#ffffff#0f0f0fPage background
--reeve-surface#f5f5f5#1a1a1aCards, panels
--reeve-surface-2#ebebeb#242424Nested surfaces
--reeve-border#e0e0e0#2a2a2aBorders, dividers
--reeve-text#111111#f0f0f0Primary text
--reeve-text-muted#666666#888888Secondary text
--reeve-accent#6366f1#6366f1Primary accent
--reeve-accent-hover#4f46e5#4f46e5Accent hover state
--reeve-danger#ef4444#ef4444Error / destructive
--reeve-success#22c55e#22c55eSuccess states
--reeve-warning#f59e0b#f59e0bWarning states
--reeve-radius8px8pxBorder radius
--reeve-radius-sm4px4pxSmall radius
--reeve-radius-lg12px12pxLarge radius
--reeve-font'Inter', system-ui, sans-serifsameFont 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
  }
}

On this page