The do Tool
Complete reference for the single unified do tool
The do Tool
The do tool is MCP.do's unified interface for Business-as-Code. Instead of 69+ narrow-purpose tools, AI models write TypeScript code directly against the SDK.
Why Code Mode?
"LLMs have seen a lot of code. They have not seen a lot of 'tool calls'."
Traditional Approach requires synthetic tool schemas:
{
"name": "database_create_order",
"arguments": {
"customer_name": "Alice",
"items": [{ "product": "Widget", "price": 10 }]
}
}Code Mode uses natural TypeScript:
await db.create('Order', {
customer: $.Person({ name: 'Alice' }),
items: [$.Product({ name: 'Widget', price: 10 })],
})AI models excel at writing code. Code mode leverages this native capability.
Tool Definition
The do tool accepts two parameters:
{
"name": "do",
"description": "Execute TypeScript against sdk.do modules. Semantic Business-as-Code primitives.",
"inputSchema": {
"type": "object",
"properties": {
"script": {
"type": "string",
"description": "TypeScript code to execute, or '{module}.md' for documentation"
},
"module": {
"type": "string",
"description": "MDXLD module definition with yaml frontmatter ($type, $id, $context), markdown content, and MDX/JSX components. Defines persistent entities like Workflow, Agent, or WebSite."
}
}
}
}Parameters
script(optional): TypeScript code to execute immediately. Returns the result of the final expression.module(optional): MDXLD module with:- YAML frontmatter: Define
$type(e.g., Workflow, Agent, WebSite),$id(pathname, full URL or relative to$context), and$context(namespace) - Imports/Exports: Standard ES module syntax
- Markdown content: Documentation and metadata
- MDX/JSX components: UI components and interactive elements
- YAML frontmatter: Define
At least one parameter must be provided. When both are provided, the module is registered first, then the script executes.
Type Signatures
The tool description includes concise type signatures for all SDK modules:
$: Proxy<$.Subject.predicate.Object>
db: {
;(list<T>(type), get<T>(type, id), create<T>(type, data), update<T>(type, id, data), delete (type, id), relate(from, rel, to))
}
ai: {
;(generate<T>(opts), embed(text), batch(requests))
}
api: {
;(fetch(url, opts), proxy(service, path, opts))
}
on: <Event>(pattern, handler)
send: <Event>(pattern, data)
every: (schedule, handler)
user: {
;(current(), session(), can(action, resource))
}These signatures provide just enough information for AI models to understand capabilities without overwhelming the context window.
Execution Modes
The do tool supports two primary execution modes: Script Execution and Module Execution. Understanding when and how to use each mode is critical for effective Business-as-Code development.
Script Execution: Imperative Operations
Scripts are ephemeral, self-contained programs that execute immediately and return a result. They're ideal for one-time operations, queries, and data transformations.
Script Lifecycle
- Parse: TypeScript code is parsed and validated
- Execute: Code runs in a sandboxed environment with SDK access
- Return: Final expression value is returned to caller
- Cleanup: Execution context is destroyed
Script Characteristics
// Simple query - executes and returns immediately
await db.list('Business')
// Complex multi-step operation
const customer = await db.create('Person', {
name: 'Alice',
email: '[email protected]',
})
const order = await db.create('Order', {
customer,
items: [$.Product({ name: 'Widget', price: 29.99 })],
total: 29.99,
})
// Send notification
await send($.Email.send, {
to: customer.email,
subject: 'Order Confirmation',
body: `Your order #${order.id} has been confirmed`,
})
// Return final result
orderKey Points:
- Executes in a sandboxed environment
- Has access to all SDK primitives
- Returns the final expression value
- Times out after 10 seconds (configurable)
- No persistence after execution
- Ideal for: queries, one-time operations, data analysis, API calls
Script Return Semantics
Scripts return the final expression in the code:
// Returns the result of db.list
await db.list('Order')
// Returns 42
const x = 10
const y = 32
x + y
// Returns the order object
const order = await db.create('Order', { ... })
order
// Returns nothing (undefined)
const result = await db.create('Order', { ... })
await send($.Email.send, { ... })
// Returns array of results
const orders = await db.list('Order')
orders.map(o => ({ id: o.id, total: o.total }))Module Execution: Persistent Workflows
Modules define persistent behavior that continues running after the initial execution. They're ideal for event handlers, scheduled tasks, and background workflows.
Module Lifecycle
- Parse: TypeScript code is parsed and validated
- Register: Event handlers and schedules are registered in the system
- Persist: Subscriptions remain active indefinitely
- Trigger: Handlers execute when events occur or schedules fire
- Continue: Module continues processing events/schedules
Module Characteristics
// Event handler - registers and persists
on($.Order.created, async (order) => {
await send($.Email.send, {
to: order.customer.email,
subject: 'Order Confirmation',
})
})
// Scheduled task - registers and persists
every('0 9 * * *', async () => {
const pending = await db.list('Order', {
where: { status: 'pending' },
})
for (const order of pending) {
await send($.Order.reminder, { orderId: order.id })
}
})Key Points:
- Creates persistent subscriptions/schedules
- Handlers execute when triggered (not immediately)
- No return value (subscriptions are the "output")
- Continues running indefinitely
- Requires authentication
- Ideal for: event handlers, scheduled tasks, background jobs, workflows
Module Registration Semantics
Modules don't return values - they register behaviors:
// This registers a handler but doesn't execute it immediately
on($.Order.created, async (order) => {
console.log('New order:', order.id)
await send($.Email.send, { ... })
})
// Multiple handlers can be registered
on($.Order.created, handler1)
on($.Order.updated, handler2)
every('0 * * * *', handler3)
// Handlers execute when triggered:
// - Order created → handler1 fires
// - Order updated → handler2 fires
// - Every hour → handler3 firesUsing the module Parameter
The module parameter accepts MDXLD (MDX with Linked Data) to define persistent entities with metadata, documentation, and code:
MDXLD Module Structure
---
$type: Workflow
$id: /workflows/order-fulfillment
$context: https://platform.do
title: Order Fulfillment Workflow
description: Automated order processing and notification
---
# Order Fulfillment
This workflow handles the complete order lifecycle from creation to delivery.
## Features
- Automatic customer notifications
- Payment processing
- Inventory management
- Shipping coordination
import { OrderStatus } from './components'
export const config = {
timeout: 30000,
retries: 3,
}
<OrderStatus showProgress={true} />
on($.Order.created, async (order) => {
await send($.Payment.process, { orderId: order.id })
await send($.Email.send, {
to: order.customer.email,
subject: 'Order Confirmation',
})
})
on($.Payment.completed, async (payment) => {
await db.update('Order', payment.orderId, { status: 'paid' })
await send($.Inventory.allocate, { orderId: payment.orderId })
})
every('0 */6 * * *', async () => {
const stale = await db.list('Order', {
where: { status: 'pending', createdAt: { before: '24 hours ago' } },
})
for (const order of stale) {
await send($.Order.reminder, { orderId: order.id })
}
})Module Parameter Features
$type: Entity type (Workflow, Agent, WebSite, Service, etc.)$id: Unique identifier pathname (absolute or relative to$context)$context: Namespace/base URL for the module- Frontmatter: YAML metadata for module configuration
- Markdown: Human-readable documentation
- Imports/Exports: Standard ES modules for code reuse
- MDX/JSX: UI components for visualization
- Code: Event handlers, scheduled tasks, and business logic
When to Use module vs script
Use module when... | Use script when... |
|---|---|
| Defining workflows | Running queries |
| Creating agents | One-time operations |
| Building services | Data analysis |
| Registering event handlers | API calls |
| Scheduling tasks | Testing code |
| Need documentation | Quick experiments |
| Need UI components | Simple calculations |
Example: Creating a Module
await client.useTool('do', {
module: `---
$type: Agent
$id: /agents/customer-support
$context: https://platform.do
---
# Customer Support Agent
Handles customer inquiries and support tickets.
on($.Ticket.created, async (ticket) => {
const response = await ai.generate({
prompt: \`Respond to: \${ticket.message}\`,
schema: $.Response
})
await db.create('Response', {
ticketId: ticket.id,
message: response.text,
sentiment: response.sentiment
})
if (response.escalate) {
await send($.Human.review, { ticketId: ticket.id })
}
})
`,
})Combined Execution: Scripts + Modules
You can combine both modes to set up workflows and trigger immediate actions:
// Register persistent event handlers
on($.Order.created, async (order) => {
await send($.Email.send, {
to: order.customer.email,
subject: 'Order Confirmation',
})
})
on($.Payment.succeeded, async (payment) => {
await db.update('Order', payment.orderId, {
status: 'paid',
})
})
// Execute immediate operation
const order = await db.create('Order', {
customer: $.Person({ name: 'Alice' }),
items: [$.Product({ name: 'Widget', price: 29.99 })],
total: 29.99,
})
// Trigger the event (handlers above will fire)
await send($.Order.created, order)
// Return the order
orderUse Case: Set up event handlers and then perform an action that triggers them.
Documentation Requests
Pass {module}.md to request documentation:
// Request $ documentation
'$.md'
// Request db documentation
'db.md'
// Request ai documentation
'ai.md'Returns comprehensive module documentation with:
- Complete type signatures
- Usage examples
- Best practices
- Related modules
Available Modules
$ - Semantic Context Proxy
Create semantic types using Schema.org vocabulary:
const person = $.Person({ name: 'Alice' })
const org = $.Organization({ name: 'Acme Corp' })
const order = $.Order({ customer: person, seller: org })
// Event patterns
on($.Order.created, handler)
await send($.Email.send, { ... })db - Database Operations
Type-safe database with semantic types:
// CRUD operations
await db.list('Business')
await db.get('Order', 'ord_123')
await db.create('Order', { ... })
await db.update('Order', 'ord_123', { ... })
await db.delete('Order', 'ord_123')
// Relationships
await db.relate(order, 'customer', person)ai - AI Operations
AI generation, embeddings, and batch processing:
// Generate with schema
const plan = await ai.generate({
prompt: 'Create a business plan',
schema: $.BusinessPlan,
model: 'claude-sonnet-4.5',
})
// Embeddings
const embedding = await ai.embed('search text')
// Batch processing
const results = await ai.batch([
{ type: 'generate', prompt: 'Write a slogan' },
{ type: 'embed', text: 'product description' },
])api - HTTP Operations
HTTP client and service proxy:
// External APIs
const data = await api.fetch('https://api.example.com/data')
// Integrated services
const customer = await api.proxy('stripe', '/v1/customers/cus_123')on - Event Handlers
Subscribe to semantic events:
on($.Order.created, async (order) => {
await send($.Email.send, {
to: order.customer.email,
subject: 'Order Confirmation',
})
})
on($.Payment.succeeded, async (payment) => {
await db.update('Order', payment.orderId, {
status: 'paid',
})
})send - Event Publishing
Publish events to queues:
// Send email
await send($.Email.send, {
to: '[email protected]',
subject: 'Welcome!',
body: 'Thank you for signing up',
})
// Domain event
await send($.Order.created, {
orderId: 'ord_123',
customerId: 'cus_456',
})every - Scheduled Workflows
Schedule recurring tasks:
// Run every hour
every('0 * * * *', async () => {
const pending = await db.list('Order', {
where: { status: 'pending' },
})
for (const order of pending) {
await send($.Order.reminder, { orderId: order.id })
}
})
// Daily at 9 AM
every('0 9 * * *', async () => {
await send($.Report.daily, { date: new Date() })
})user - User Context
Access user information and permissions:
// Current user
const currentUser = user.current()
if (currentUser) {
console.log('User ID:', currentUser.id)
console.log('Email:', currentUser.email)
}
// Permissions
if (user.can('create', 'Order')) {
await db.create('Order', { ... })
}Examples
Simple Queries
// List all businesses
await db.list('Business')
// Get specific order
await db.get('Order', 'ord_123')
// Filter and sort
await db.list('Order', {
where: { status: 'pending' },
sort: { createdAt: 'desc' },
limit: 10,
})Creating Records
// Create customer
const customer = await db.create('Person', {
name: 'Alice',
email: '[email protected]',
})
// Create order with relationship
const order = await db.create('Order', {
customer: customer,
items: [$.Product({ name: 'Widget', price: 29.99 })],
total: 29.99,
status: 'pending',
})
// Send confirmation
await send($.Email.send, {
to: customer.email,
subject: 'Order Confirmation',
body: `Your order #${order.id} has been confirmed`,
})
orderAI-Powered Workflows
// Get all products
const products = await db.list('Product')
// Generate descriptions with AI
const results = await Promise.all(
products.map(async (product) => {
const description = await ai.generate({
prompt: `Write a compelling product description for ${product.name}`,
schema: $.Text,
model: 'gpt-5',
})
await db.update('Product', product.id, {
description: description.text,
})
return { id: product.id, updated: true }
})
)
resultsEvent-Driven Architecture
// Order created handler
on($.Order.created, async (order) => {
// Send confirmation email
await send($.Email.send, {
to: order.customer.email,
subject: 'Order Confirmation',
body: `Your order #${order.id} has been received`,
})
// Update inventory
for (const item of order.items) {
const product = await db.get('Product', item.productId)
await db.update('Product', item.productId, {
stock: product.stock - item.quantity,
})
}
// Trigger fulfillment
await send($.Fulfillment.start, { orderId: order.id })
})
// Payment succeeded handler
on($.Payment.succeeded, async (payment) => {
await db.update('Order', payment.orderId, {
status: 'paid',
paidAt: new Date(),
})
await send($.Order.paid, {
orderId: payment.orderId,
amount: payment.amount,
})
})Scheduled Tasks
// Clean up stale orders every 6 hours
every('0 */6 * * *', async () => {
const staleOrders = await db.list('Order', {
where: {
status: 'pending',
createdAt: { lt: Date.now() - 24 * 60 * 60 * 1000 }, // 24 hours ago
},
})
for (const order of staleOrders) {
await db.update('Order', order.id, {
status: 'cancelled',
cancelledAt: new Date(),
})
await send($.Order.cancelled, { orderId: order.id })
}
})
// Daily report at 9 AM
every('0 9 * * *', async () => {
const yesterday = new Date(Date.now() - 24 * 60 * 60 * 1000)
const orders = await db.list('Order', {
where: { createdAt: { gte: yesterday } },
})
const revenue = orders.reduce((sum, o) => sum + o.total, 0)
await send($.Email.send, {
to: '[email protected]',
subject: 'Daily Report',
body: `Orders: ${orders.length}, Revenue: $${revenue}`,
})
})Multi-Step Operations
// Complex workflow in single script
const businesses = await db.list('Business', {
where: { industry: 'Technology' },
})
// Filter high-growth businesses
const highGrowth = businesses.filter((b) => b.growthRate > 0.5)
// Generate AI insights for each
const insights = await Promise.all(
highGrowth.map(async (biz) => {
const insight = await ai.generate({
prompt: `Analyze growth trends for ${biz.name}. Growth rate: ${biz.growthRate}`,
schema: $.Article,
model: 'claude-sonnet-4.5',
})
// Store insight
await db.create('Article', {
about: $.Organization({ id: biz.id }),
headline: insight.headline,
articleBody: insight.articleBody,
author: $.Person({ name: 'AI Analyst' }),
})
// Notify stakeholders
await send($.Notification.send, {
type: 'business-insight',
businessId: biz.id,
insightId: insight.id,
})
return insight
})
)
insightsSecurity & Permissions
Readonly Mode (Anonymous)
Anonymous users (no auth header) are restricted to readonly operations via AST analysis:
Allowed:
await db.list('Business')
await db.get('Order', 'ord_123')
await ai.embed('search text')
await api.fetch('https://api.example.com/data') // GET onlyBlocked:
await db.create('Order', { ... }) // Write operation
await db.update('Order', id, { ... }) // Write operation
await send($.Email.send, { ... }) // Side effect
await ai.generate({ ... }) // Side effect (cost)
every('0 * * * *', handler) // Persistent subscription
on($.Order.created, handler) // Persistent subscriptionAuthenticated Mode
With OAuth or API key, all operations are available:
// Check authentication
const currentUser = user.current()
if (currentUser) {
// Full access
await db.create('Order', { ... })
await send($.Email.send, { ... })
await ai.generate({ ... })
} else {
// Readonly only
await db.list('Business')
await db.get('Order', 'ord_123')
}Permission Checks
// Check permissions before operations
if (user.can('create', 'Order')) {
await db.create('Order', { ... })
} else {
throw new Error('Permission denied: cannot create orders')
}
if (user.can('delete', 'Order')) {
await db.delete('Order', orderId)
}
// Admin check
if (user.can('admin', '*')) {
// Admin operations
}Error Handling
The do tool returns errors as text content with isError: true:
Execution Errors
{
"content": [
{
"type": "text",
"text": "Error: Cannot read property 'name' of undefined\n at line 3:15"
}
],
"isError": true
}Readonly Violations
{
"content": [
{
"type": "text",
"text": "Readonly violations detected:\n- db.create() at line 5\n- send() at line 8"
}
],
"isError": true
}Timeout Errors
{
"content": [
{
"type": "text",
"text": "Execution timeout after 10000ms"
}
],
"isError": true
}Authentication Errors
{
"content": [
{
"type": "text",
"text": "Authentication required for write operations"
}
],
"isError": true
}Best Practices
1. Use Semantic Types
// ✅ Good: Semantic types
await db.create('Order', {
customer: $.Person({ name: 'Alice' }),
seller: $.Organization({ name: 'Acme' }),
})
// ❌ Bad: Plain objects
await db.create('Order', {
customer_name: 'Alice',
seller_name: 'Acme',
})2. Handle Async Properly
// ✅ Good: Await async operations
const result = await db.list('Business')
// ❌ Bad: Forgot await
const result = db.list('Business') // Returns Promise3. Use Type Inference
// ✅ Good: Types inferred from schema
const plan = await ai.generate({
prompt: 'Create a business plan',
schema: $.BusinessPlan,
})
// plan.executive_summary is type-safe
// ❌ Bad: No schema
const plan = await ai.generate({
prompt: 'Create a business plan',
})
// plan is 'any' type4. Compose Operations
// ✅ Good: Single script with multiple operations
const customer = await db.create('Person', { ... })
const order = await db.create('Order', { customer, ... })
await send($.Email.send, { to: customer.email })
order
// ❌ Bad: Multiple separate tool calls
// (Requires AI round-trips between each step)5. Use Batch Operations
// ✅ Good: Single batch call
await ai.batch([
{ type: 'embed', text: texts[0] },
{ type: 'embed', text: texts[1] },
{ type: 'embed', text: texts[2] },
])
// ❌ Bad: Multiple separate calls
for (const text of texts) {
await ai.embed(text)
}6. Parallel Execution
// ✅ Good: Parallel
const [orders, customers] = await Promise.all([db.list('Order'), db.list('Customer')])
// ❌ Bad: Sequential
const orders = await db.list('Order')
const customers = await db.list('Customer')Performance Tips
1. Limit Results
// Fetch only what you need
const recentOrders = await db.list('Order', {
limit: 10,
sort: { createdAt: 'desc' },
})2. Use Filters
// Filter at database level
const activeOrders = await db.list('Order', {
where: { status: 'active' },
})
// Not in application code
const allOrders = await db.list('Order')
const activeOrders = allOrders.filter((o) => o.status === 'active')3. Batch AI Operations
// Process multiple items in single batch
const results = await ai.batch(
products.map((p) => ({
type: 'generate',
prompt: `Describe ${p.name}`,
schema: $.Text,
}))
)4. Cache Expensive Operations
// Cache AI results
const cached = await db.get('Cache', cacheKey)
if (cached) {
return cached.value
}
const result = await ai.generate({ ... })
await db.create('Cache', {
key: cacheKey,
value: result,
expiresAt: Date.now() + 3600000 // 1 hour
})
return resultDebugging
1. Use Console Logging
const orders = await db.list('Order')
console.log('Found orders:', orders.length)
for (const order of orders) {
console.log('Processing order:', order.id)
await processOrder(order)
}2. Inspect Variables
const customer = await db.get('Person', 'per_123')
console.log('Customer:', JSON.stringify(customer, null, 2))
const order = await db.create('Order', { customer, ... })
console.log('Created order:', order)3. Error Handling
try {
const result = await db.create('Order', { ... })
console.log('Success:', result)
} catch (error) {
console.error('Failed:', error.message)
throw error
}Module Exports and Composition
Modules can export reusable functions, workflows, and event handlers for composition.
Exporting Functions from Modules
Create reusable functions that can be called from scripts or other modules:
// Define reusable order processing function
export async function processOrder(orderId: string) {
const order = await db.get('Order', orderId)
const customer = await db.get('Person', order.customerId)
// Validate order
if (!order || order.status !== 'pending') {
throw new Error('Invalid order state')
}
// Process payment
const payment = await api.proxy('stripe', '/v1/charges', {
method: 'POST',
body: {
amount: order.total * 100,
currency: 'usd',
customer: customer.stripeId,
},
})
// Update order
await db.update('Order', orderId, {
status: 'paid',
paidAt: new Date(),
paymentId: payment.id,
})
// Send confirmation
await send($.Email.send, {
to: customer.email,
subject: 'Payment Confirmed',
body: `Your payment of $${order.total} has been processed.`,
})
return { orderId, paymentId: payment.id, status: 'paid' }
}
// Export utility function
export async function calculateOrderTotal(items: Array<any>) {
let total = 0
for (const item of items) {
const product = await db.get('Product', item.productId)
total += product.price * item.quantity
}
return total
}Exporting Workflows
Export complete workflows that orchestrate multiple operations:
// Customer onboarding workflow
export async function onboardCustomer(data: { name: string; email: string; company?: string }) {
// Step 1: Create customer record
const customer = await db.create('Person', {
name: data.name,
email: data.email,
status: 'active',
createdAt: new Date(),
})
// Step 2: Create company if provided
if (data.company) {
const company = await db.create('Organization', {
name: data.company,
legalName: data.company,
})
await db.relate(customer, 'worksFor', company)
}
// Step 3: Generate welcome email with AI
const welcomeEmail = await ai.generate({
prompt: `Write a warm welcome email for ${data.name}. Include a brief intro to our platform and next steps.`,
schema: $.EmailMessage,
model: 'claude-sonnet-4.5',
})
// Step 4: Send welcome email
await send($.Email.send, {
to: data.email,
from: '[email protected]',
subject: welcomeEmail.subject,
body: welcomeEmail.body,
})
// Step 5: Schedule follow-up
await send($.Schedule.create, {
type: 'email',
recipientId: customer.id,
template: 'onboarding-day-3',
sendAt: new Date(Date.now() + 3 * 24 * 60 * 60 * 1000),
})
// Step 6: Trigger onboarding event
await send($.Customer.onboarded, {
customerId: customer.id,
timestamp: new Date(),
})
return customer
}
// Product catalog sync workflow
export async function syncProductCatalog(source: string) {
const products = await api.fetch(`https://api.${source}.com/products`)
const results = []
for (const productData of products) {
// Check if product exists
const existing = await db.list('Product', {
where: { sku: productData.sku },
limit: 1,
})
if (existing.length > 0) {
// Update existing product
const updated = await db.update('Product', existing[0].id, {
name: productData.name,
price: productData.price,
stock: productData.stock,
updatedAt: new Date(),
})
results.push({ sku: productData.sku, action: 'updated', id: updated.id })
} else {
// Create new product
const created = await db.create('Product', {
sku: productData.sku,
name: productData.name,
price: productData.price,
stock: productData.stock,
source: source,
createdAt: new Date(),
})
results.push({ sku: productData.sku, action: 'created', id: created.id })
}
}
return {
source,
totalProcessed: results.length,
created: results.filter((r) => r.action === 'created').length,
updated: results.filter((r) => r.action === 'updated').length,
timestamp: new Date(),
}
}Exporting Event Handlers
Export event handler modules that can be registered together:
// E-commerce event handlers module
export function setupEcommerceHandlers() {
// Order lifecycle handlers
on($.Order.created, async (order) => {
const customer = await db.get('Person', order.customerId)
await send($.Email.send, {
to: customer.email,
subject: `Order Confirmation #${order.orderNumber}`,
body: `Thank you for your order! Total: $${order.total}`,
})
await send($.Analytics.track, {
event: 'order_created',
userId: customer.id,
properties: { orderId: order.id, total: order.total },
})
})
on($.Order.paid, async (event) => {
await db.update('Order', event.orderId, {
status: 'processing',
processingStartedAt: new Date(),
})
await send($.Fulfillment.start, {
orderId: event.orderId,
priority: event.amount > 500 ? 'high' : 'normal',
})
})
on($.Order.shipped, async (event) => {
const order = await db.get('Order', event.orderId)
const customer = await db.get('Person', order.customerId)
await send($.Email.send, {
to: customer.email,
subject: 'Your Order Has Shipped!',
body: `Tracking: ${event.trackingNumber}`,
})
})
// Inventory management handlers
on($.Product.lowStock, async (product) => {
if (product.stock < 10) {
await send($.Email.send, {
to: '[email protected]',
subject: `Low Stock Alert: ${product.name}`,
body: `Current stock: ${product.stock}. Reorder recommended.`,
})
}
})
// Customer engagement handlers
on($.Customer.inactive, async (customer) => {
const email = await ai.generate({
prompt: `Write a re-engagement email for ${customer.name}. They haven't made a purchase in 90 days. Offer 15% discount code: COMEBACK15`,
schema: $.EmailMessage,
model: 'gpt-5',
})
await send($.Email.send, {
to: customer.email,
subject: email.subject,
body: email.body,
})
})
}
// Register all handlers
setupEcommerceHandlers()Exporting Scheduled Tasks
Export scheduled task modules for recurring operations:
// Analytics and reporting tasks
export function setupAnalyticsTasks() {
// Daily sales report
every('0 9 * * *', async () => {
const yesterday = new Date(Date.now() - 24 * 60 * 60 * 1000)
const orders = await db.list('Order', {
where: { createdAt: { gte: yesterday.toISOString() } },
})
const report = {
date: yesterday.toLocaleDateString(),
totalOrders: orders.length,
totalRevenue: orders.reduce((sum, o) => sum + o.total, 0),
avgOrderValue: orders.length > 0 ? orders.reduce((sum, o) => sum + o.total, 0) / orders.length : 0,
}
await send($.Email.send, {
to: '[email protected]',
subject: `Daily Sales Report - ${report.date}`,
body: JSON.stringify(report, null, 2),
})
})
// Weekly customer segmentation
every('0 8 * * MON', async () => {
const customers = await db.list('Person')
for (const customer of customers) {
const orders = await db.list('Order', {
where: { customerId: customer.id },
})
const totalSpent = orders.reduce((sum, o) => sum + o.total, 0)
const segment = totalSpent > 1000 ? 'VIP' : totalSpent > 500 ? 'Premium' : 'Regular'
await db.update('Person', customer.id, {
segment,
totalSpent,
lastSegmentedAt: new Date(),
})
}
})
// Hourly stale order cleanup
every('0 * * * *', async () => {
const staleOrders = await db.list('Order', {
where: {
status: 'pending',
createdAt: { lt: new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString() },
},
})
for (const order of staleOrders) {
await db.update('Order', order.id, {
status: 'cancelled',
cancelledAt: new Date(),
cancelReason: 'stale',
})
await send($.Order.cancelled, { orderId: order.id })
}
})
}
setupAnalyticsTasks()Module Composition and Reusability
Compose modules by importing and calling exported functions:
// Import from another module
import { processOrder, calculateOrderTotal } from './order-processing'
import { onboardCustomer } from './customer-workflows'
import { setupEcommerceHandlers } from './event-handlers'
// Use imported functions in scripts
const items = [
{ productId: 'prod_123', quantity: 2 },
{ productId: 'prod_456', quantity: 1 },
]
const total = await calculateOrderTotal(items)
console.log('Order total:', total)
// Use in event handlers
on($.Order.pendingPayment, async (order) => {
try {
await processOrder(order.id)
} catch (error) {
console.error('Failed to process order:', error)
await send($.Order.paymentFailed, { orderId: order.id, error: error.message })
}
})
// Compose workflows
const newCustomer = await onboardCustomer({
name: 'Alice Smith',
email: '[email protected]',
company: 'Acme Corp',
})
// Set up all event handlers
setupEcommerceHandlers()Script Composition Patterns
Scripts can build complex operations from simple primitives using composition patterns.
Multi-Step Script Workflows
Chain operations together in a single script:
// Complete order fulfillment in one script
async function fulfillOrder(orderId: string) {
// Step 1: Get order and validate
const order = await db.get('Order', orderId)
if (!order || order.status !== 'paid') {
throw new Error('Order not ready for fulfillment')
}
// Step 2: Check inventory
const inventoryOk = true
for (const item of order.items) {
const product = await db.get('Product', item.productId)
if (product.stock < item.quantity) {
inventoryOk = false
break
}
}
if (!inventoryOk) {
await send($.Order.backorder, { orderId })
return { status: 'backorder' }
}
// Step 3: Reserve inventory
for (const item of order.items) {
const product = await db.get('Product', item.productId)
await db.update('Product', item.productId, {
stock: product.stock - item.quantity,
})
}
// Step 4: Generate shipping label
const customer = await db.get('Person', order.customerId)
const shipping = await api.proxy('shippo', '/shipments', {
method: 'POST',
body: {
address_to: customer.address,
parcels: order.items.map((i) => ({ weight: i.weight })),
},
})
// Step 5: Update order
await db.update('Order', orderId, {
status: 'shipped',
trackingNumber: shipping.tracking_number,
shippedAt: new Date(),
})
// Step 6: Notify customer
await send($.Email.send, {
to: customer.email,
subject: 'Your Order Has Shipped!',
body: `Tracking: ${shipping.tracking_number}`,
})
// Step 7: Trigger analytics
await send($.Analytics.track, {
event: 'order_shipped',
properties: { orderId, trackingNumber: shipping.tracking_number },
})
return {
status: 'shipped',
trackingNumber: shipping.tracking_number,
trackingUrl: shipping.tracking_url,
}
}
// Execute workflow
await fulfillOrder('ord_123')Error Handling Patterns
Implement robust error handling in scripts:
// Error handling with try-catch
async function processOrderWithErrorHandling(orderId: string) {
try {
const order = await db.get('Order', orderId)
if (!order) {
throw new Error('Order not found')
}
const result = await api.proxy('stripe', '/v1/charges', {
method: 'POST',
body: { amount: order.total * 100, currency: 'usd' },
})
await db.update('Order', orderId, {
status: 'paid',
paymentId: result.id,
})
return { success: true, paymentId: result.id }
} catch (error) {
console.error('Payment failed:', error.message)
await db.update('Order', orderId, {
status: 'payment_failed',
errorMessage: error.message,
})
await send($.Order.paymentFailed, {
orderId,
error: error.message,
timestamp: new Date(),
})
return { success: false, error: error.message }
}
}
// Retry pattern with exponential backoff
async function withRetry<T>(fn: () => Promise<T>, maxRetries = 3, baseDelay = 1000): Promise<T> {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn()
} catch (error) {
if (i === maxRetries - 1) throw error
const delay = baseDelay * Math.pow(2, i)
console.log(`Attempt ${i + 1} failed, retrying in ${delay}ms...`)
await new Promise((resolve) => setTimeout(resolve, delay))
}
}
throw new Error('Max retries reached')
}
// Use retry pattern
const result = await withRetry(
async () => {
return await api.fetch('https://api.external.com/data')
},
3,
1000
)Transaction Patterns
Implement transaction-like patterns with rollback:
// Transaction pattern with rollback
async function transferInventory(fromProductId: string, toProductId: string, quantity: number) {
const operations = []
try {
// Step 1: Deduct from source
const fromProduct = await db.get('Product', fromProductId)
if (fromProduct.stock < quantity) {
throw new Error('Insufficient stock')
}
const updatedFrom = await db.update('Product', fromProductId, {
stock: fromProduct.stock - quantity,
})
operations.push({ type: 'update', id: fromProductId, rollback: { stock: fromProduct.stock } })
// Step 2: Add to destination
const toProduct = await db.get('Product', toProductId)
const updatedTo = await db.update('Product', toProductId, {
stock: toProduct.stock + quantity,
})
operations.push({ type: 'update', id: toProductId, rollback: { stock: toProduct.stock } })
// Step 3: Create transfer record
const transfer = await db.create('InventoryTransfer', {
fromProductId,
toProductId,
quantity,
timestamp: new Date(),
})
operations.push({ type: 'create', id: transfer.id })
return { success: true, transferId: transfer.id }
} catch (error) {
console.error('Transfer failed, rolling back:', error.message)
// Rollback in reverse order
for (const op of operations.reverse()) {
try {
if (op.type === 'update') {
await db.update('Product', op.id, op.rollback)
} else if (op.type === 'create') {
await db.delete('InventoryTransfer', op.id)
}
} catch (rollbackError) {
console.error('Rollback failed:', rollbackError.message)
}
}
throw error
}
}
// Execute with rollback support
await transferInventory('prod_123', 'prod_456', 10)Batch Processing Patterns
Process large datasets efficiently:
// Batch processing with progress tracking
async function batchUpdateProducts(updates: Array<{ id: string; data: any }>) {
const batchSize = 10
const results = []
for (let i = 0; i < updates.length; i += batchSize) {
const batch = updates.slice(i, i + batchSize)
console.log(`Processing batch ${i / batchSize + 1} of ${Math.ceil(updates.length / batchSize)}`)
const batchResults = await Promise.allSettled(batch.map((update) => db.update('Product', update.id, update.data)))
batchResults.forEach((result, idx) => {
if (result.status === 'fulfilled') {
results.push({ id: batch[idx].id, success: true })
} else {
results.push({ id: batch[idx].id, success: false, error: result.reason.message })
}
})
// Progress notification
if ((i + batchSize) % 100 === 0) {
await send($.Job.progress, {
type: 'product_update',
processed: Math.min(i + batchSize, updates.length),
total: updates.length,
})
}
// Rate limiting
await new Promise((resolve) => setTimeout(resolve, 100))
}
return {
total: updates.length,
successful: results.filter((r) => r.success).length,
failed: results.filter((r) => !r.success).length,
errors: results.filter((r) => !r.success).map((r) => ({ id: r.id, error: r.error })),
}
}
// Execute batch operation
const updates = [
{ id: 'prod_1', data: { price: 29.99 } },
{ id: 'prod_2', data: { price: 39.99 } },
// ... hundreds more
]
await batchUpdateProducts(updates)Parallel Execution Patterns
Execute independent operations in parallel:
// Parallel data fetching
async function loadDashboardData() {
const [orders, customers, products, revenue] = await Promise.all([
db.list('Order', { limit: 100, sort: { createdAt: 'desc' } }),
db.list('Person', { limit: 50 }),
db.list('Product', { where: { stock: { lt: 10 } } }),
calculateRevenue(),
])
return {
recentOrders: orders,
topCustomers: customers,
lowStockProducts: products,
revenueMetrics: revenue,
}
}
async function calculateRevenue() {
const orders = await db.list('Order', {
where: { status: 'paid' },
})
return {
total: orders.reduce((sum, o) => sum + o.total, 0),
count: orders.length,
average: orders.length > 0 ? orders.reduce((sum, o) => sum + o.total, 0) / orders.length : 0,
}
}
// Execute with parallel operations
const dashboard = await loadDashboardData()Real-World Scenarios
Complete, production-ready examples for common business scenarios.
Scenario 1: E-Commerce Order Processing (Script + Module)
Complete order processing system with event-driven fulfillment:
// Module: Set up event handlers for order lifecycle
on($.Order.created, async (order) => {
console.log(`New order created: ${order.id}`)
// Validate order
const customer = await db.get('Person', order.customerId)
if (!customer) {
throw new Error('Customer not found')
}
// Send confirmation email
await send($.Email.send, {
to: customer.email,
subject: `Order Confirmation #${order.orderNumber}`,
from: '[email protected]',
body: `
Thank you for your order!
Order #${order.orderNumber}
Total: $${order.total}
We'll notify you when your order ships.
`
})
// Update inventory
for (const item of order.items) {
const product = await db.get('Product', item.productId)
await db.update('Product', item.productId, {
stock: product.stock - item.quantity
})
// Check for low stock
if (product.stock - item.quantity < 10) {
await send($.Inventory.lowStock, {
productId: product.id,
currentStock: product.stock - item.quantity
})
}
}
// Trigger fulfillment after 1 hour (to allow for cancellations)
await send($.Schedule.create, {
type: 'fulfillment',
orderId: order.id,
executeAt: new Date(Date.now() + 60 * 60 * 1000)
})
})
on($.Payment.succeeded, async (payment) => {
console.log(`Payment succeeded: ${payment.id}`)
await db.update('Order', payment.orderId, {
status: 'paid',
paidAt: new Date(),
paymentMethod: payment.method,
paymentId: payment.id
})
// Trigger immediate fulfillment for express orders
const order = await db.get('Order', payment.orderId)
if (order.shippingMethod === 'express') {
await send($.Fulfillment.start, {
orderId: payment.orderId,
priority: 'high'
})
}
})
on($.Fulfillment.start, async (fulfillment) => {
console.log(`Starting fulfillment: ${fulfillment.orderId}`)
const order = await db.get('Order', fulfillment.orderId)
const customer = await db.get('Person', order.customerId)
// Update order status
await db.update('Order', order.id, {
status: 'processing',
processingStartedAt: new Date()
})
// Generate shipping label
const shipping = await api.proxy('shippo', '/shipments', {
method: 'POST',
body: {
address_to: {
name: customer.name,
street1: customer.address.street1,
city: customer.address.city,
state: customer.address.state,
zip: customer.address.zip,
country: 'US'
},
parcels: order.items.map(item => ({
length: '10',
width: '8',
height: '4',
distance_unit: 'in',
weight: item.weight || '1',
mass_unit: 'lb'
})),
async: false
}
})
// Update with tracking
await db.update('Order', order.id, {
status: 'shipped',
shippedAt: new Date(),
trackingNumber: shipping.tracking_number,
carrier: shipping.carrier
})
// Notify customer
await send($.Email.send, {
to: customer.email,
subject: 'Your Order Has Shipped!',
from: '[email protected]',
body: `
Your order #${order.orderNumber} has shipped!
Tracking Number: ${shipping.tracking_number}
Carrier: ${shipping.carrier}
Track your package: ${shipping.tracking_url}
`
})
// Trigger shipped event
await send($.Order.shipped, {
orderId: order.id,
trackingNumber: shipping.tracking_number,
trackingUrl: shipping.tracking_url
})
})
// Script: Create a new order and trigger the workflow
const customer = await db.create('Person', {
name: 'Bob Smith',
email: '[email protected]',
address: {
street1: '123 Main St',
city: 'San Francisco',
state: 'CA',
zip: '94102'
}
})
const products = await Promise.all([
db.get('Product', 'prod_widget_001'),
db.get('Product', 'prod_gadget_002')
])
const order = await db.create('Order', {
orderNumber: `ORD-${Date.now()}`,
customer: customer,
customerId: customer.id,
items: [
{
productId: products[0].id,
name: products[0].name,
quantity: 2,
price: products[0].price,
weight: products[0].weight
},
{
productId: products[1].id,
name: products[1].name,
quantity: 1,
price: products[1].price,
weight: products[1].weight
}
],
total: products[0].price * 2 + products[1].price,
shippingMethod: 'standard',
status: 'pending',
createdAt: new Date()
})
// Trigger order created event (activates handlers above)
await send($.Order.created, order)
// Return order details
{
orderId: order.id,
orderNumber: order.orderNumber,
customer: customer.name,
total: order.total,
status: order.status,
items: order.items.length
}Scenario 2: Content Management Workflows (Module)
Automated content creation and publishing pipeline:
// Content approval workflow
on($.Article.submitted, async (article) => {
console.log(`Article submitted: ${article.id}`)
// Run AI content analysis
const analysis = await ai.generate({
prompt: `Analyze this article for quality, readability, and SEO:
Title: ${article.headline}
Content: ${article.articleBody}
Provide:
1. Quality score (1-10)
2. Readability grade level
3. SEO recommendations
4. Suggested improvements`,
schema: $.Analysis,
model: 'claude-sonnet-4.5',
})
// Store analysis
await db.update('Article', article.id, {
analysis: analysis,
analyzedAt: new Date(),
})
// Auto-approve high-quality content
if (analysis.qualityScore >= 8) {
await send($.Article.approved, {
articleId: article.id,
approvedBy: 'ai-auto-approve',
reason: `High quality score: ${analysis.qualityScore}/10`,
})
} else {
// Send to human review
await send($.Email.send, {
to: '[email protected]',
subject: `Article Needs Review: ${article.headline}`,
body: `
Article: ${article.headline}
Author: ${article.author.name}
Quality Score: ${analysis.qualityScore}/10
Analysis:
${JSON.stringify(analysis, null, 2)}
Review: https://cms.acme.com/articles/${article.id}/review
`,
})
}
})
on($.Article.approved, async (event) => {
const article = await db.get('Article', event.articleId)
// Generate social media posts
const socialPosts = await ai.generate({
prompt: `Create social media posts for this article:
Title: ${article.headline}
Summary: ${article.description}
Generate posts for:
1. Twitter (280 chars)
2. LinkedIn (professional tone)
3. Facebook (engaging, conversational)`,
schema: $.SocialMediaPosts,
model: 'gpt-5',
})
// Generate featured image
const imagePrompt = await ai.generate({
prompt: `Create an image generation prompt for this article: ${article.headline}`,
schema: $.Text,
model: 'gpt-5',
})
// Publish article
await db.update('Article', article.id, {
status: 'published',
publishedAt: new Date(),
socialPosts: socialPosts,
})
// Schedule social posts
const platforms = ['twitter', 'linkedin', 'facebook']
for (let i = 0; i < platforms.length; i++) {
await send($.Schedule.create, {
type: 'social_post',
platform: platforms[i],
content: socialPosts[platforms[i]],
articleId: article.id,
executeAt: new Date(Date.now() + i * 60 * 60 * 1000), // Stagger by 1 hour
})
}
// Notify author
await send($.Email.send, {
to: article.author.email,
subject: `Your Article Has Been Published!`,
body: `
Congratulations! Your article "${article.headline}" is now live.
View it here: https://blog.acme.com/articles/${article.slug}
Social posts scheduled:
- Twitter: ${new Date(Date.now() + 0 * 60 * 60 * 1000).toLocaleString()}
- LinkedIn: ${new Date(Date.now() + 1 * 60 * 60 * 1000).toLocaleString()}
- Facebook: ${new Date(Date.now() + 2 * 60 * 60 * 1000).toLocaleString()}
`,
})
})
// Scheduled content optimization
every('0 2 * * *', async () => {
console.log('Running content optimization...')
const articles = await db.list('Article', {
where: {
status: 'published',
updatedAt: { lt: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString() },
},
limit: 20,
})
for (const article of articles) {
// Generate updated SEO recommendations
const seo = await ai.generate({
prompt: `Analyze and improve SEO for this article:
Title: ${article.headline}
Content: ${article.articleBody.substring(0, 500)}...
Provide updated:
1. Meta description
2. Keywords
3. Internal linking suggestions`,
schema: $.SEORecommendations,
model: 'claude-sonnet-4.5',
})
await db.update('Article', article.id, {
seoRecommendations: seo,
lastOptimized: new Date(),
})
}
})Scenario 3: Analytics Pipeline (Script)
Data analysis and reporting pipeline:
// Comprehensive analytics pipeline
async function generateMonthlyReport() {
console.log('Generating monthly analytics report...')
const startDate = new Date()
startDate.setDate(1) // First day of current month
startDate.setHours(0, 0, 0, 0)
// Parallel data fetching
const [orders, customers, products, revenue, traffic] = await Promise.all([
db.list('Order', {
where: { createdAt: { gte: startDate.toISOString() } },
}),
db.list('Person', {
where: { createdAt: { gte: startDate.toISOString() } },
}),
db.list('Product'),
api.fetch('https://analytics.acme.com/api/revenue', {
method: 'POST',
body: JSON.stringify({ start: startDate.toISOString() }),
}),
api.fetch('https://analytics.acme.com/api/traffic', {
method: 'POST',
body: JSON.stringify({ start: startDate.toISOString() }),
}),
])
// Calculate metrics
const metrics = {
orders: {
total: orders.length,
revenue: orders.reduce((sum, o) => sum + o.total, 0),
avgValue: orders.length > 0 ? orders.reduce((sum, o) => sum + o.total, 0) / orders.length : 0,
byStatus: orders.reduce((acc, o) => {
acc[o.status] = (acc[o.status] || 0) + 1
return acc
}, {}),
},
customers: {
new: customers.length,
returning: orders.filter((o) => {
const customerOrders = orders.filter((ord) => ord.customerId === o.customerId)
return customerOrders.length > 1
}).length,
churnRate: calculateChurnRate(customers, orders),
},
products: {
total: products.length,
lowStock: products.filter((p) => p.stock < 10).length,
topSellers: getTopProducts(orders, products, 10),
},
revenue: {
total: revenue.total,
growth: revenue.growth,
forecast: revenue.forecast,
},
traffic: {
visitors: traffic.visitors,
pageViews: traffic.pageViews,
conversionRate: orders.length / traffic.visitors,
},
}
// Generate AI insights
const insights = await ai.generate({
prompt: `Analyze this monthly business data and provide strategic insights:
${JSON.stringify(metrics, null, 2)}
Provide:
1. Key achievements and wins
2. Areas of concern
3. Growth opportunities
4. Specific action items
5. Forecast for next month`,
schema: $.BusinessInsights,
model: 'claude-sonnet-4.5',
temperature: 0.3,
})
// Create comprehensive report
const report = await db.create('Report', {
type: 'monthly',
period: startDate.toISOString(),
metrics: metrics,
insights: insights,
generatedAt: new Date(),
})
// Generate visualizations
const charts = await Promise.all([
generateChart('revenue-trend', metrics.revenue),
generateChart('order-status', metrics.orders.byStatus),
generateChart('top-products', metrics.products.topSellers),
])
// Send email report
await send($.Email.send, {
to: '[email protected]',
subject: `Monthly Business Report - ${startDate.toLocaleDateString('en-US', { month: 'long', year: 'numeric' })}`,
from: '[email protected]',
body: `
# Monthly Business Report
## Executive Summary
${insights.summary}
## Key Metrics
**Orders**
- Total Orders: ${metrics.orders.total}
- Revenue: $${metrics.orders.revenue.toFixed(2)}
- Average Order Value: $${metrics.orders.avgValue.toFixed(2)}
**Customers**
- New Customers: ${metrics.customers.new}
- Returning Customers: ${metrics.customers.returning}
- Churn Rate: ${(metrics.customers.churnRate * 100).toFixed(2)}%
**Products**
- Low Stock Items: ${metrics.products.lowStock}
## AI Insights
${insights.analysis}
## Recommendations
${insights.recommendations.map((r, i) => `${i + 1}. ${r}`).join('\n')}
View full report: https://analytics.acme.com/reports/${report.id}
`,
attachments: charts,
})
return {
reportId: report.id,
metrics,
insights,
generated: new Date(),
}
}
function calculateChurnRate(customers, orders) {
const activeCustomers = new Set(orders.map((o) => o.customerId))
const churnedCustomers = customers.filter((c) => !activeCustomers.has(c.id)).length
return customers.length > 0 ? churnedCustomers / customers.length : 0
}
function getTopProducts(orders, products, limit) {
const sales = {}
orders.forEach((order) => {
order.items.forEach((item) => {
sales[item.productId] = (sales[item.productId] || 0) + item.quantity
})
})
return Object.entries(sales)
.sort(([, a], [, b]) => b - a)
.slice(0, limit)
.map(([id, quantity]) => {
const product = products.find((p) => p.id === id)
return {
id,
name: product?.name || 'Unknown',
quantity,
revenue: quantity * (product?.price || 0),
}
})
}
async function generateChart(type, data) {
// Generate chart using visualization service
return await api.fetch('https://charts.acme.com/api/generate', {
method: 'POST',
body: JSON.stringify({ type, data }),
})
}
// Execute monthly report
await generateMonthlyReport()Scenario 4: Customer Support Automation (Module)
AI-powered customer support system:
// Customer support ticket handling
on($.Ticket.created, async (ticket) => {
console.log(`New support ticket: ${ticket.id}`)
// Classify ticket urgency and category
const classification = await ai.generate({
prompt: `Classify this support ticket:
Subject: ${ticket.subject}
Description: ${ticket.description}
Provide:
1. Urgency (low/medium/high/critical)
2. Category (technical/billing/general/feature-request)
3. Estimated resolution time (hours)
4. Suggested response`,
schema: $.TicketClassification,
model: 'claude-sonnet-4.5',
})
// Update ticket
await db.update('Ticket', ticket.id, {
urgency: classification.urgency,
category: classification.category,
estimatedResolutionTime: classification.estimatedResolutionTime,
classification: classification,
})
// Auto-respond to low-urgency tickets
if (classification.urgency === 'low' && classification.suggestedResponse) {
await send($.Email.send, {
to: ticket.customerEmail,
subject: `Re: ${ticket.subject}`,
from: '[email protected]',
body: classification.suggestedResponse,
})
await db.update('Ticket', ticket.id, {
status: 'auto-responded',
autoResponseSent: true,
autoResponseSentAt: new Date(),
})
}
// Assign to appropriate team
const teamMapping = {
technical: '[email protected]',
billing: '[email protected]',
general: '[email protected]',
'feature-request': '[email protected]',
}
await send($.Email.send, {
to: teamMapping[classification.category],
subject: `[${classification.urgency.toUpperCase()}] New Ticket: ${ticket.subject}`,
from: '[email protected]',
body: `
New support ticket requires attention:
Ticket ID: ${ticket.id}
Customer: ${ticket.customerName} (${ticket.customerEmail})
Urgency: ${classification.urgency}
Category: ${classification.category}
Estimated Resolution: ${classification.estimatedResolutionTime} hours
Subject: ${ticket.subject}
Description: ${ticket.description}
View ticket: https://support.acme.com/tickets/${ticket.id}
`,
})
// Escalate critical tickets
if (classification.urgency === 'critical') {
await send($.SMS.send, {
to: '+1-555-ONCALL',
message: `CRITICAL support ticket ${ticket.id}: ${ticket.subject}`,
})
await send($.Slack.message, {
channel: '#support-critical',
text: `🚨 Critical ticket: ${ticket.subject}`,
link: `https://support.acme.com/tickets/${ticket.id}`,
})
}
})
// Ticket response quality check
on($.Ticket.responded, async (event) => {
const ticket = await db.get('Ticket', event.ticketId)
// Analyze response quality
const analysis = await ai.generate({
prompt: `Analyze this support response for quality:
Original Issue: ${ticket.description}
Agent Response: ${event.response}
Rate:
1. Helpfulness (1-10)
2. Clarity (1-10)
3. Professionalism (1-10)
4. Completeness (1-10)
5. Provide improvement suggestions`,
schema: $.ResponseAnalysis,
model: 'claude-sonnet-4.5',
})
// Store analysis
await db.update('Ticket', ticket.id, {
responseAnalysis: analysis,
analyzedAt: new Date(),
})
// Flag low-quality responses for review
const avgScore = (analysis.helpfulness + analysis.clarity + analysis.professionalism + analysis.completeness) / 4
if (avgScore < 7) {
await send($.Email.send, {
to: '[email protected]',
subject: `Response Quality Alert: Ticket ${ticket.id}`,
body: `
A support response may need review:
Ticket: ${ticket.id}
Agent: ${event.agentId}
Average Quality Score: ${avgScore}/10
Analysis:
${JSON.stringify(analysis, null, 2)}
Review: https://support.acme.com/tickets/${ticket.id}/review
`,
})
}
})
// Daily support metrics
every('0 18 * * *', async () => {
const today = new Date()
today.setHours(0, 0, 0, 0)
const tickets = await db.list('Ticket', {
where: { createdAt: { gte: today.toISOString() } },
})
const metrics = {
total: tickets.length,
resolved: tickets.filter((t) => t.status === 'resolved').length,
pending: tickets.filter((t) => t.status === 'pending').length,
avgResponseTime: calculateAvgResponseTime(tickets),
avgResolutionTime: calculateAvgResolutionTime(tickets),
satisfactionScore: calculateSatisfactionScore(tickets),
byCategory: tickets.reduce((acc, t) => {
acc[t.category] = (acc[t.category] || 0) + 1
return acc
}, {}),
}
await send($.Email.send, {
to: '[email protected]',
subject: `Daily Support Metrics - ${today.toLocaleDateString()}`,
body: `
# Daily Support Summary
**Overview**
- Total Tickets: ${metrics.total}
- Resolved: ${metrics.resolved}
- Pending: ${metrics.pending}
**Performance**
- Avg Response Time: ${metrics.avgResponseTime} minutes
- Avg Resolution Time: ${metrics.avgResolutionTime} hours
- Satisfaction Score: ${metrics.satisfactionScore}/10
**By Category**
${Object.entries(metrics.byCategory)
.map(([cat, count]) => `- ${cat}: ${count}`)
.join('\n')}
`,
})
})
function calculateAvgResponseTime(tickets) {
const times = tickets.filter((t) => t.firstResponseAt).map((t) => (new Date(t.firstResponseAt) - new Date(t.createdAt)) / 1000 / 60)
return times.length > 0 ? times.reduce((a, b) => a + b) / times.length : 0
}
function calculateAvgResolutionTime(tickets) {
const times = tickets.filter((t) => t.resolvedAt).map((t) => (new Date(t.resolvedAt) - new Date(t.createdAt)) / 1000 / 60 / 60)
return times.length > 0 ? times.reduce((a, b) => a + b) / times.length : 0
}
function calculateSatisfactionScore(tickets) {
const scores = tickets.filter((t) => t.satisfactionScore).map((t) => t.satisfactionScore)
return scores.length > 0 ? scores.reduce((a, b) => a + b) / scores.length : 0
}When to Use Each Mode
Understanding when to use scripts vs modules is critical for effective development.
Decision Tree
Is the operation persistent (continues after execution)?
├─ YES → Use Module (on/every)
│ ├─ Need to react to events? → Use on()
│ ├─ Need scheduled execution? → Use every()
│ └─ Need both? → Combine on() + every()
│
└─ NO → Use Script
├─ Single operation? → Simple script
├─ Multiple steps? → Multi-step script
└─ Need immediate result? → Script with return valueUse Script For:
Queries and Data Retrieval
// ✅ Script - immediate result needed
await db.list('Order', { where: { status: 'pending' } })One-Time Operations
// ✅ Script - execute once and return
const customer = await db.create('Person', { ... })
const order = await db.create('Order', { customer, ... })
orderData Analysis and Transformation
// ✅ Script - process and return results
const orders = await db.list('Order')
const revenue = orders.reduce((sum, o) => sum + o.total, 0)({ totalOrders: orders.length, revenue })API Calls and External Integrations
// ✅ Script - fetch and process external data
const data = await api.fetch('https://api.example.com/data')
const processed = data.map(item => ({ ... }))
processedAI Content Generation
// ✅ Script - generate content and return
const description = await ai.generate({
prompt: 'Write a product description',
schema: $.Text,
})
description.textAd-Hoc Administrative Tasks
// ✅ Script - one-time bulk update
const products = await db.list('Product', { where: { category: 'old' } })
for (const product of products) {
await db.update('Product', product.id, { category: 'updated' })
}
;({ updated: products.length })Use Module For:
Event Handlers
// ✅ Module - persistent event subscription
on($.Order.created, async (order) => {
await send($.Email.send, { to: order.customer.email, ... })
})Scheduled Tasks
// ✅ Module - recurring cron job
every('0 9 * * *', async () => {
const report = await generateDailyReport()
await send($.Email.send, { to: '[email protected]', body: report })
})Workflow Orchestration
// ✅ Module - multi-step workflow with events
on($.Payment.succeeded, async (payment) => {
await db.update('Order', payment.orderId, { status: 'paid' })
await send($.Fulfillment.start, { orderId: payment.orderId })
})
on($.Fulfillment.start, async (fulfillment) => {
// Process fulfillment...
await send($.Order.shipped, { orderId: fulfillment.orderId })
})Background Job Processing
// ✅ Module - async job queue
on($.Job.export, async (job) => {
const data = await db.list(job.collection)
const csv = convertToCSV(data)
await send($.Email.send, {
to: job.userEmail,
subject: 'Export Complete',
attachments: [{ filename: 'export.csv', content: csv }],
})
})Use Both For:
Setup + Trigger Pattern
// ✅ Combined - register handlers then execute
on($.Order.created, handler1)
on($.Payment.succeeded, handler2)
const order = await db.create('Order', { ... })
await send($.Order.created, order) // Triggers handler1
order // Return value from scriptImmediate Action + Future Monitoring
// ✅ Combined - do something now, watch later
const deployment = await db.create('Deployment', { status: 'starting' })
on($.Deployment.failed, async (event) => {
if (event.deploymentId === deployment.id) {
await send($.Alert.send, { type: 'deployment_failed', ... })
}
})
// Start deployment
await api.fetch('https://deploy.acme.com/start', {
method: 'POST',
body: JSON.stringify({ deploymentId: deployment.id })
})
deploymentAnti-Patterns to Avoid
❌ Using Module for One-Time Query
// ❌ Bad - module for simple query
on($.Query.business, async () => {
return await db.list('Business')
})
// ✅ Good - script for immediate result
await db.list('Business')❌ Using Script for Persistent Handler
// ❌ Bad - handler won't persist after script ends
const orders = await db.list('Order')
for (const order of orders) {
// This won't continue running
on($.Order.updated, handler)
}
// ✅ Good - module registers persistent handler
on($.Order.updated, async (order) => {
// Handles all future order updates
})❌ Forgetting to Return from Script
// ❌ Bad - no return value
const result = await db.create('Order', { ... })
await send($.Email.send, { ... })
// Returns undefined
// ✅ Good - explicit return
const result = await db.create('Order', { ... })
await send($.Email.send, { ... })
result // Returns the order❌ Using Script for Recurring Task
// ❌ Bad - script runs once then stops
const pending = await db.list('Order', { where: { status: 'pending' } })
for (const order of pending) {
await send($.Order.reminder, { orderId: order.id })
}
// ✅ Good - scheduled task runs automatically
every('0 * * * *', async () => {
const pending = await db.list('Order', { where: { status: 'pending' } })
for (const order of pending) {
await send($.Order.reminder, { orderId: order.id })
}
})Module vs Script at a Glance
Comparison Table
| Aspect | Script | Module |
|---|---|---|
| Execution | Immediate, once | Triggered, recurring |
| Lifecycle | Ephemeral (seconds) | Persistent (indefinite) |
| Return Value | Final expression | None (registers behaviors) |
| Primary Use | Queries, operations | Event handlers, schedules |
| Authentication | Optional for reads | Required |
| Timeout | 10 seconds (configurable) | Per-trigger timeout |
| State | No persistent state | Maintains subscriptions |
| Examples | await db.list() | on(), every() |
Execution Model Differences
Script Execution Flow:
Parse → Execute → Return → Cleanup
↓ ↓ ↓ ↓
AST Run code Value EndModule Execution Flow:
Parse → Register → Wait → Trigger → Execute → Repeat
↓ ↓ ↓ ↓ ↓ ↓
AST Subscribe Idle Event Run code ContinuePersistence Differences
Scripts:
- Execute once
- Return result
- Context destroyed
- No memory of execution
- Suitable for: queries, one-time tasks, data analysis
Modules:
- Register subscriptions
- Run on triggers
- Context persists
- Maintains state between triggers
- Suitable for: workflows, monitoring, automation
Return Value Differences
Scripts:
// Returns order object
const order = await db.create('Order', { ... })
order
// Returns array
await db.list('Business')
// Returns computed value
const x = 10
const y = 20
x + y // Returns 30Modules:
// Returns nothing (registers handler)
on($.Order.created, async (order) => {
await send($.Email.send, { ... })
})
// Returns nothing (registers schedule)
every('0 9 * * *', async () => {
await generateReport()
})Use Case Differences
Script Use Cases:
- Database queries
- Data transformations
- API integrations
- One-time migrations
- Ad-hoc reports
- Immediate operations
Module Use Cases:
- Event-driven workflows
- Scheduled tasks
- Background processing
- Continuous monitoring
- Automated responses
- Long-running processes
Next Steps
- Modules vs Scripts - Execution models
- Core Primitives - SDK modules guide
- Examples - Real-world patterns
- Authentication - Security & permissions
- Integration Guide - Client setup