@stack0/sdk
v0.5.12
Published
Official Stack0 SDK for Node.js
Downloads
498
Maintainers
Readme
Stack0 SDK
Official TypeScript/JavaScript SDK for Stack0 -- a modular platform for email, CDN, screenshots, web extraction, integrations, marketing, and AI workflows.
- Fully typed -- written in TypeScript with complete type definitions
- Zero dependencies -- uses the native
fetchAPI - Tree-shakable -- import only the modules you need
- Isomorphic -- works in Node.js, Bun, Deno, and edge runtimes
Installation
npm install @stack0/sdk
# or
yarn add @stack0/sdk
# or
pnpm add @stack0/sdk
# or
bun add @stack0/sdkQuick Start
import { Stack0 } from '@stack0/sdk';
const stack0 = new Stack0({
apiKey: process.env.STACK0_API_KEY!,
});
// Send an email
await stack0.mail.send({
from: '[email protected]',
to: '[email protected]',
subject: 'Welcome!',
html: '<h1>Hello World</h1>',
});
// Upload a file to CDN
const asset = await stack0.cdn.upload({
projectSlug: 'my-project',
file: fileBuffer,
filename: 'photo.jpg',
mimeType: 'image/jpeg',
});
// Capture a screenshot
const screenshot = await stack0.screenshots.captureAndWait({
url: 'https://example.com',
format: 'png',
fullPage: true,
});
// Extract structured data from a page
const result = await stack0.extraction.extractAndWait({
url: 'https://example.com/article',
mode: 'markdown',
});
// Run an AI workflow
const run = await stack0.workflows.runAndWait({
workflowSlug: 'content-pipeline',
variables: { topic: 'AI trends' },
});Configuration
const stack0 = new Stack0({
apiKey: 'stack0_...', // Required -- your API key
baseUrl: 'https://api.stack0.dev/v1', // Optional -- custom API endpoint
cdnUrl: 'https://cdn.myproject.stack0.dev', // Optional -- for client-side image transforms
});| Option | Type | Default | Description |
|--------|------|---------|-------------|
| apiKey | string | -- | Your Stack0 API key (required) |
| baseUrl | string | https://api.stack0.dev/v1 | API base URL |
| cdnUrl | string | -- | CDN base URL for client-side image transform URLs |
Modules
The SDK is organized into modules, each accessible as a property on the main Stack0 client:
| Module | Accessor | Description |
|--------|----------|-------------|
| Mail | stack0.mail | Transactional email, campaigns, sequences, contacts |
| CDN | stack0.cdn | File upload, asset management, image transforms, video |
| Screenshots | stack0.screenshots | Webpage screenshot capture |
| Extraction | stack0.extraction | AI-powered web data extraction |
| Integrations | stack0.integrations | Unified CRM, storage, communication APIs |
| Marketing | stack0.marketing | Trend discovery, content generation, scheduling |
| Workflows | stack0.workflows | AI workflow orchestration |
Send transactional emails, manage domains, templates, contacts, campaigns, and automated sequences. The Mail API is designed to be compatible with the Resend API for easy migration.
Sending Emails
// Simple email
const { id } = await stack0.mail.send({
from: '[email protected]',
to: '[email protected]',
subject: 'Welcome!',
html: '<h1>Hello World</h1>',
});
// With name, CC, BCC, reply-to, and attachments
await stack0.mail.send({
from: { name: 'Acme Inc', email: '[email protected]' },
to: { name: 'Jane Doe', email: '[email protected]' },
cc: '[email protected]',
bcc: ['[email protected]'],
replyTo: '[email protected]',
subject: 'Invoice #1234',
html: '<p>Your invoice is attached.</p>',
text: 'Your invoice is attached.',
tags: ['transactional', 'invoice'],
metadata: { orderId: '1234' },
attachments: [
{
filename: 'invoice.pdf',
content: 'base64-encoded-content',
contentType: 'application/pdf',
},
],
});
// Using a template
await stack0.mail.send({
from: '[email protected]',
to: '[email protected]',
subject: 'Welcome {{name}}',
templateId: 'template-uuid',
templateVariables: { name: 'Jane', activationUrl: 'https://...' },
});
// Batch send (up to 100 emails, each with different content)
await stack0.mail.sendBatch({
emails: [
{ from: '[email protected]', to: '[email protected]', subject: 'Hi', html: '<p>Hello User 1</p>' },
{ from: '[email protected]', to: '[email protected]', subject: 'Hi', html: '<p>Hello User 2</p>' },
],
});
// Broadcast (same content to up to 1000 recipients)
await stack0.mail.sendBroadcast({
from: '[email protected]',
to: ['[email protected]', '[email protected]', '[email protected]'],
subject: 'Monthly Update',
html: '<p>Here is what happened this month...</p>',
});Retrieving and Managing Emails
// Get email details
const email = await stack0.mail.get('email-id');
console.log(email.status); // 'delivered', 'bounced', etc.
console.log(email.openedAt); // Date or null
console.log(email.deliveredAt); // Date or null
// List emails with filters
const { emails, total } = await stack0.mail.list({
status: 'delivered',
from: '[email protected]',
startDate: '2024-01-01',
sortBy: 'createdAt',
sortOrder: 'desc',
limit: 50,
});
// Resend or cancel
await stack0.mail.resend('email-id');
await stack0.mail.cancel('scheduled-email-id');Analytics
const analytics = await stack0.mail.getAnalytics();
console.log(`Delivery rate: ${analytics.deliveryRate}%`);
console.log(`Open rate: ${analytics.openRate}%`);
const timeSeries = await stack0.mail.getTimeSeriesAnalytics({ days: 30 });
const hourly = await stack0.mail.getHourlyAnalytics();
const senders = await stack0.mail.listSenders({ search: '@yourapp.com' });Sub-clients
The mail module exposes several sub-clients for managing related resources:
Domains
// Add and verify a sending domain
const { dnsRecords } = await stack0.mail.domains.add({ domain: 'yourapp.com' });
// Configure DNS records, then verify
const { verified } = await stack0.mail.domains.verify('domain-id');
// List, get DNS records, delete, set default
const domains = await stack0.mail.domains.list({ projectSlug: 'my-project' });
const dns = await stack0.mail.domains.getDNSRecords('domain-id');
await stack0.mail.domains.setDefault('domain-id');
await stack0.mail.domains.delete('domain-id');Templates
const template = await stack0.mail.templates.create({
name: 'Welcome Email',
slug: 'welcome',
subject: 'Welcome {{name}}!',
html: '<h1>Hello {{name}}</h1>',
});
const { templates } = await stack0.mail.templates.list({ search: 'welcome' });
const found = await stack0.mail.templates.getBySlug('welcome');
const preview = await stack0.mail.templates.preview({ id: template.id, variables: { name: 'Jane' } });Contacts and Audiences
// Create and manage contacts
const contact = await stack0.mail.contacts.create({
email: '[email protected]',
firstName: 'Jane',
lastName: 'Doe',
metadata: { plan: 'pro' },
});
// Bulk import contacts
const importResult = await stack0.mail.contacts.import({
audienceId: 'audience-id',
contacts: [
{ email: '[email protected]', firstName: 'Alice' },
{ email: '[email protected]', firstName: 'Bob' },
],
});
// Create audiences and add contacts
const audience = await stack0.mail.audiences.create({
name: 'Newsletter Subscribers',
description: 'Users opted in to the newsletter',
});
await stack0.mail.audiences.addContacts({ id: audience.id, contactIds: [contact.id] });Campaigns
const campaign = await stack0.mail.campaigns.create({
name: 'Product Launch',
subject: 'Introducing our new feature!',
fromEmail: '[email protected]',
audienceId: 'audience-id',
html: '<p>We are excited to announce...</p>',
});
await stack0.mail.campaigns.send({ id: campaign.id, sendNow: true });
const stats = await stack0.mail.campaigns.getStats(campaign.id);
console.log(`Open rate: ${stats.openRate}%`);Sequences
Build automated email flows with a visual node-based editor:
const sequence = await stack0.mail.sequences.create({
name: 'Onboarding Flow',
triggerType: 'contact_added',
triggerFrequency: 'once',
});
// Add nodes and connections
const emailNode = await stack0.mail.sequences.createNode({
id: sequence.id,
nodeType: 'email',
name: 'Welcome Email',
positionX: 200,
positionY: 100,
});
await stack0.mail.sequences.setNodeEmail(sequence.id, {
nodeId: emailNode.id,
subject: 'Welcome!',
html: '<p>Thanks for signing up.</p>',
});
await stack0.mail.sequences.publish(sequence.id);Events
Track custom events that can trigger sequences:
await stack0.mail.events.track({
eventName: 'purchase_completed',
contactEmail: '[email protected]',
properties: { orderId: '12345', amount: 99.99 },
});
// Batch tracking
await stack0.mail.events.trackBatch({
events: [
{ eventName: 'page_viewed', contactEmail: '[email protected]', properties: { page: '/pricing' } },
{ eventName: 'page_viewed', contactEmail: '[email protected]', properties: { page: '/features' } },
],
});Mail Method Reference
| Method | Description |
|--------|-------------|
| mail.send(req) | Send a single email |
| mail.sendBatch(req) | Send up to 100 emails in a batch |
| mail.sendBroadcast(req) | Broadcast to up to 1000 recipients |
| mail.get(id) | Get email by ID |
| mail.list(req?) | List emails with filters |
| mail.resend(id) | Resend an email |
| mail.cancel(id) | Cancel a scheduled email |
| mail.getAnalytics() | Get overall email analytics |
| mail.getTimeSeriesAnalytics(req?) | Get daily analytics breakdown |
| mail.getHourlyAnalytics() | Get hourly analytics |
| mail.listSenders(req?) | List unique senders with stats |
| mail.domains.* | Domain management (list, add, verify, delete, setDefault, getDNSRecords) |
| mail.templates.* | Template CRUD + preview and getBySlug |
| mail.audiences.* | Audience CRUD + addContacts, removeContacts, listContacts |
| mail.contacts.* | Contact CRUD + import |
| mail.campaigns.* | Campaign CRUD + send, pause, cancel, duplicate, getStats |
| mail.sequences.* | Sequence CRUD + node/connection management + publish/pause/resume/archive |
| mail.events.* | Event CRUD + track, trackBatch, listOccurrences, getAnalytics |
CDN
Upload, manage, and transform files. Supports images, video transcoding, private files, download bundles, and S3 imports.
Uploading Files
// Simple upload (handles presigned URL flow automatically)
const asset = await stack0.cdn.upload({
projectSlug: 'my-project',
file: fileBuffer, // Blob, Buffer, or ArrayBuffer
filename: 'photo.jpg',
mimeType: 'image/jpeg',
folder: '/images/avatars',
metadata: { userId: 'user_123' },
});
console.log(asset.id); // Asset ID
console.log(asset.cdnUrl); // Public CDN URL
// Manual presigned URL flow for more control
const { uploadUrl, assetId } = await stack0.cdn.getUploadUrl({
projectSlug: 'my-project',
filename: 'document.pdf',
mimeType: 'application/pdf',
size: file.size,
});
await fetch(uploadUrl, { method: 'PUT', body: file, headers: { 'Content-Type': 'application/pdf' } });
const confirmed = await stack0.cdn.confirmUpload(assetId);Asset Management
// Get, update, delete
const asset = await stack0.cdn.get('asset-id');
await stack0.cdn.update({ id: 'asset-id', alt: 'Sunset photo', tags: ['nature'] });
await stack0.cdn.delete('asset-id');
await stack0.cdn.deleteMany(['asset-1', 'asset-2']);
// List with filters
const { assets, total, hasMore } = await stack0.cdn.list({
projectSlug: 'my-project',
type: 'image',
folder: '/images',
search: 'avatar',
sortBy: 'createdAt',
sortOrder: 'desc',
limit: 20,
});
// Move assets between folders
await stack0.cdn.move({ assetIds: ['asset-1', 'asset-2'], folder: '/archive' });Image Transformations
Generate optimized image URLs client-side (no API call required):
const url = stack0.cdn.getTransformUrl(asset.cdnUrl, {
width: 800,
height: 600,
fit: 'cover',
format: 'webp',
quality: 80,
});
// Advanced transforms
const advancedUrl = stack0.cdn.getTransformUrl(asset.cdnUrl, {
width: 400,
crop: 'attention', // Smart crop: 'attention', 'entropy', 'center'
blur: 5,
sharpen: 1.5,
brightness: 10,
saturation: -50,
grayscale: true,
rotate: 90,
flip: true,
flop: true,
});| Transform | Type | Description |
|-----------|------|-------------|
| width | number | Target width (snapped to nearest allowed width for caching) |
| height | number | Target height |
| fit | string | cover, contain, fill, inside, outside |
| format | string | webp, jpeg, png, avif, auto |
| quality | number | Output quality (1-100) |
| crop | string | Smart crop strategy: attention, entropy, center |
| blur | number | Gaussian blur sigma (0.3-100) |
| sharpen | number | Sharpen sigma |
| brightness | number | -100 to 100 |
| saturation | number | -100 to 100 |
| grayscale | boolean | Convert to grayscale |
| rotate | number | 0, 90, 180, 270 |
| flip | boolean | Flip vertically |
| flop | boolean | Flip horizontally |
Folders
const tree = await stack0.cdn.getFolderTree({ projectSlug: 'my-project', maxDepth: 3 });
const folder = await stack0.cdn.createFolder({ projectSlug: 'my-project', name: 'avatars' });
const byPath = await stack0.cdn.getFolderByPath('/images/avatars');
await stack0.cdn.updateFolder({ id: folder.id, name: 'profile-pictures' });
await stack0.cdn.moveFolder({ id: folder.id, newParentId: 'other-folder-id' });
await stack0.cdn.deleteFolder(folder.id, true); // true = delete contentsVideo Transcoding
// Start a transcoding job
const job = await stack0.cdn.transcode({
projectSlug: 'my-project',
assetId: 'video-asset-id',
outputFormat: 'hls',
variants: [
{ quality: '720p', codec: 'h264' },
{ quality: '1080p', codec: 'h264' },
],
webhookUrl: 'https://your-app.com/webhook',
});
// Check job status
const status = await stack0.cdn.getJob(job.id);
console.log(`Progress: ${status.progress}%`);
// Get streaming URLs for playback
const urls = await stack0.cdn.getStreamingUrls('asset-id');
console.log(urls.hlsUrl);
// Generate thumbnails
const thumb = await stack0.cdn.getThumbnail({
assetId: 'video-asset-id',
timestamp: 10.5,
width: 320,
format: 'webp',
});
// Extract audio
const { jobId } = await stack0.cdn.extractAudio({
projectSlug: 'my-project',
assetId: 'video-asset-id',
format: 'mp3',
bitrate: 192,
});GIF Generation
const gif = await stack0.cdn.generateGif({
projectSlug: 'my-project',
assetId: 'video-asset-id',
startTime: 5,
duration: 3,
width: 480,
fps: 10,
});
// Poll for completion
let result = await stack0.cdn.getGif(gif.id);
while (result?.status === 'pending' || result?.status === 'processing') {
await new Promise(r => setTimeout(r, 1000));
result = await stack0.cdn.getGif(gif.id);
}
console.log(result?.url);Video Merge
const mergeJob = await stack0.cdn.createMergeJob({
projectSlug: 'my-project',
inputs: [
{ assetId: 'intro-video-id' },
{ assetId: 'image-id', duration: 5 },
{ assetId: 'main-video-id', startTime: 10, endTime: 60 },
],
audioTrack: { assetId: 'music-id', loop: true, fadeIn: 2, fadeOut: 3 },
output: { format: 'mp4', quality: '1080p', filename: 'final.mp4' },
});Private Files
Private files are stored securely and can only be accessed through authorized, time-limited download URLs.
// Upload a private file
const privateFile = await stack0.cdn.uploadPrivate({
projectSlug: 'my-project',
file: fileBuffer,
filename: 'confidential.pdf',
mimeType: 'application/pdf',
});
// Generate a time-limited download URL
const { downloadUrl, expiresAt } = await stack0.cdn.getPrivateDownloadUrl({
fileId: privateFile.id,
expiresIn: 86400, // 24 hours in seconds
});Download Bundles
const { bundle } = await stack0.cdn.createBundle({
projectSlug: 'my-project',
name: 'December Assets',
assetIds: ['asset-1', 'asset-2'],
privateFileIds: ['file-1'],
expiresIn: 86400,
});
const { downloadUrl } = await stack0.cdn.getBundleDownloadUrl({
bundleId: bundle.id,
expiresIn: 3600,
});Usage Tracking
const usage = await stack0.cdn.getUsage({ projectSlug: 'my-project' });
console.log(`Bandwidth: ${usage.bandwidthFormatted}`);
const history = await stack0.cdn.getUsageHistory({ projectSlug: 'my-project', days: 30 });
const breakdown = await stack0.cdn.getStorageBreakdown({ projectSlug: 'my-project', groupBy: 'type' });Screenshots
Capture high-quality screenshots of any webpage with options for format, viewport, device emulation, and more.
Basic Capture
// Capture and wait for the result (recommended for most use cases)
const screenshot = await stack0.screenshots.captureAndWait({
url: 'https://example.com',
format: 'png',
fullPage: true,
blockAds: true,
blockCookieBanners: true,
});
console.log(screenshot.imageUrl);
console.log(screenshot.imageWidth, screenshot.imageHeight);Capture Options
const screenshot = await stack0.screenshots.captureAndWait({
url: 'https://example.com',
format: 'png', // 'png' | 'jpeg' | 'webp' | 'pdf'
quality: 90, // JPEG/WebP quality (1-100)
fullPage: true, // Capture entire scrollable page
deviceType: 'desktop', // 'desktop' | 'tablet' | 'mobile'
viewportWidth: 1280,
viewportHeight: 720,
blockAds: true,
blockCookieBanners: true,
blockChatWidgets: true,
waitForSelector: '.main-content',
waitForTimeout: 2000, // Wait ms after page load
darkMode: true,
hideSelectors: ['.popup', '.banner'],
customCss: 'body { background: white; }',
customJs: 'document.querySelector(".modal")?.remove()',
headers: { 'Authorization': 'Bearer token' },
cookies: [{ name: 'session', value: 'abc123' }],
clip: { x: 0, y: 0, width: 800, height: 600 },
cacheKey: 'homepage-v2',
cacheTtl: 3600,
webhookUrl: 'https://your-app.com/webhook',
webhookSecret: 'secret',
});Async Capture (Manual Polling)
const { id } = await stack0.screenshots.capture({
url: 'https://example.com',
format: 'png',
});
// Poll for completion
const screenshot = await stack0.screenshots.get({ id });
if (screenshot.status === 'completed') {
console.log(screenshot.imageUrl);
}Batch Screenshots
// Capture multiple URLs and wait for all to complete
const job = await stack0.screenshots.batchAndWait({
urls: ['https://example.com', 'https://example.org'],
config: { format: 'png', fullPage: true },
});
console.log(`Processed: ${job.processedUrls}/${job.totalUrls}`);Scheduled Screenshots
const { id } = await stack0.screenshots.createSchedule({
name: 'Daily homepage capture',
url: 'https://example.com',
frequency: 'daily',
config: { format: 'png', fullPage: true },
});
await stack0.screenshots.toggleSchedule({ id }); // Toggle on/offScreenshots Method Reference
| Method | Description |
|--------|-------------|
| capture(req) | Start a screenshot capture |
| get(req) | Get screenshot by ID |
| list(req?) | List screenshots with filters |
| delete(req) | Delete a screenshot |
| captureAndWait(req, opts?) | Capture and poll until complete |
| batch(req) | Start a batch capture job |
| getBatchJob(req) | Get batch job status |
| listBatchJobs(req?) | List batch jobs |
| cancelBatchJob(req) | Cancel a batch job |
| batchAndWait(req, opts?) | Batch capture and poll until complete |
| createSchedule(req) | Create a recurring capture schedule |
| updateSchedule(req) | Update a schedule |
| getSchedule(req) | Get a schedule |
| listSchedules(req?) | List schedules |
| deleteSchedule(req) | Delete a schedule |
| toggleSchedule(req) | Toggle a schedule on/off |
Extraction
Extract structured data from any webpage using AI. Supports markdown, JSON schema, and raw HTML extraction modes.
Markdown Extraction
const result = await stack0.extraction.extractAndWait({
url: 'https://example.com/article',
mode: 'markdown',
includeMetadata: true,
});
console.log(result.markdown);
console.log(result.pageMetadata?.title);
console.log(result.pageMetadata?.description);Schema-Based Extraction
Extract structured data that matches a JSON schema:
const result = await stack0.extraction.extractAndWait({
url: 'https://example.com/product',
mode: 'schema',
schema: {
type: 'object',
properties: {
name: { type: 'string' },
price: { type: 'number' },
description: { type: 'string' },
inStock: { type: 'boolean' },
},
required: ['name', 'price'],
},
});
console.log(result.extractedData);
// { name: 'Product X', price: 29.99, description: '...', inStock: true }Extraction Modes
| Mode | Description |
|------|-------------|
| markdown | AI-cleaned markdown content |
| schema | Structured data matching a provided JSON schema |
| auto | AI determines the best extraction strategy |
| raw | Raw HTML content |
Batch Extraction
const job = await stack0.extraction.batchAndWait({
urls: [
'https://example.com/article-1',
'https://example.com/article-2',
],
config: { mode: 'markdown' },
});Scheduled Extraction
const { id } = await stack0.extraction.createSchedule({
name: 'Daily price monitor',
url: 'https://competitor.com/pricing',
frequency: 'daily',
config: { mode: 'schema', schema: { /* ... */ } },
});Usage Stats
const usage = await stack0.extraction.getUsage({
periodStart: '2024-01-01T00:00:00Z',
periodEnd: '2024-01-31T23:59:59Z',
});
console.log(`Extractions: ${usage.extractionsTotal}`);
console.log(`Tokens used: ${usage.extractionTokensUsed}`);
const daily = await stack0.extraction.getUsageDaily({ /* ... */ });Extraction Method Reference
| Method | Description |
|--------|-------------|
| extract(req) | Start an extraction |
| get(req) | Get extraction by ID |
| list(req?) | List extractions |
| delete(req) | Delete an extraction |
| extractAndWait(req, opts?) | Extract and poll until complete |
| batch(req) | Start a batch extraction |
| getBatchJob(req) | Get batch job status |
| listBatchJobs(req?) | List batch jobs |
| cancelBatchJob(req) | Cancel a batch job |
| batchAndWait(req, opts?) | Batch extract and poll until complete |
| createSchedule(req) | Create a recurring schedule |
| updateSchedule(req) | Update a schedule |
| getSchedule(req) | Get a schedule |
| listSchedules(req?) | List schedules |
| deleteSchedule(req) | Delete a schedule |
| toggleSchedule(req) | Toggle a schedule on/off |
| getUsage(req?) | Get usage statistics |
| getUsageDaily(req?) | Get daily usage breakdown |
Integrations
Unified API for connecting to third-party services. Manage OAuth connections and interact with CRM, storage, communication, and productivity platforms through a single interface.
Managing Connections
// Initiate OAuth flow
const { authUrl, connectionId } = await stack0.integrations.initiateOAuth({
connectorSlug: 'hubspot',
redirectUrl: 'https://yourapp.com/oauth/callback',
name: 'My HubSpot',
});
// Redirect user to authUrl...
// Complete OAuth after callback
await stack0.integrations.completeOAuth({
code: 'auth_code_from_callback',
state: 'state_from_initiate',
redirectUrl: 'https://yourapp.com/oauth/callback',
});
// List and manage connections
const { connections } = await stack0.integrations.listConnections({
status: 'connected',
});
const stats = await stack0.integrations.getStats({ environment: 'production' });
console.log(`Active connections: ${stats.activeConnections}`);CRM
// Contacts
const contacts = await stack0.integrations.crm.listContacts('conn_123');
const contact = await stack0.integrations.crm.createContact('conn_123', {
firstName: 'Jane',
lastName: 'Doe',
email: '[email protected]',
});
// Companies
const companies = await stack0.integrations.crm.listCompanies('conn_123');
// Deals
const deals = await stack0.integrations.crm.listDeals('conn_123');
const deal = await stack0.integrations.crm.createDeal('conn_123', {
name: 'Enterprise Contract',
amount: 50000,
stage: 'negotiation',
});Storage
// Files
const files = await stack0.integrations.storage.listFiles('conn_456', 'folder-id');
await stack0.integrations.storage.uploadFile('conn_456', {
name: 'report.pdf',
mimeType: 'application/pdf',
data: fileBuffer,
folderId: 'folder-id',
});
const { data, filename } = await stack0.integrations.storage.downloadFile('conn_456', 'file-id');
// Folders
const folders = await stack0.integrations.storage.listFolders('conn_456');
await stack0.integrations.storage.createFolder('conn_456', { name: 'Reports', parentId: 'root' });Communication
// Send a message via Slack, Teams, etc.
await stack0.integrations.communication.sendMessage('conn_789', {
channelId: 'C123',
content: 'Hello from Stack0!',
});
const channels = await stack0.integrations.communication.listChannels('conn_789');
const messages = await stack0.integrations.communication.listMessages('conn_789', 'C123');
const users = await stack0.integrations.communication.listUsers('conn_789');Productivity
// Documents (Notion, Google Docs, etc.)
const docs = await stack0.integrations.productivity.listDocuments('conn_abc');
await stack0.integrations.productivity.createDocument('conn_abc', {
title: 'Meeting Notes',
content: 'Agenda: ...',
});
// Tables (Airtable, Google Sheets, etc.)
const tables = await stack0.integrations.productivity.listTables('conn_abc');
const rows = await stack0.integrations.productivity.listTableRows('conn_abc', 'table-id');
await stack0.integrations.productivity.createTableRow('conn_abc', 'table-id', {
fields: { Name: 'Alice', Email: '[email protected]' },
});Marketing
AI-powered trend discovery, content opportunity generation, script creation, and content calendar management.
Discovering Trends
const { trendsDiscovered, trends } = await stack0.marketing.discoverTrends({
projectSlug: 'my-project',
environment: 'production',
});
console.log(`Discovered ${trendsDiscovered} new trends`);
const allTrends = await stack0.marketing.listTrends({
projectSlug: 'my-project',
environment: 'production',
status: 'active',
});Generating Content Opportunities
const { opportunitiesGenerated } = await stack0.marketing.generateOpportunities({
projectSlug: 'my-project',
environment: 'production',
});
const opportunities = await stack0.marketing.listOpportunities({
projectSlug: 'my-project',
environment: 'production',
status: 'pending',
});Content and Scripts
// Create content from an opportunity
const content = await stack0.marketing.createContent({
projectSlug: 'my-project',
environment: 'production',
contentType: 'tiktok_slideshow',
title: 'How AI is Changing Marketing',
opportunityId: 'opp-id',
});
// Review workflow
await stack0.marketing.approveContent({ contentId: content.id, reviewNotes: 'Looks great!' });
// or
await stack0.marketing.rejectContent({ contentId: content.id, reviewNotes: 'Needs revisions' });
// Create and version scripts
const script = await stack0.marketing.createScript({
projectSlug: 'my-project',
environment: 'production',
hook: 'Are you ready to see the future?',
slides: [{ order: 0, text: 'AI is changing everything', voiceoverText: '...', duration: 3 }],
cta: 'Follow for more!',
});Content Calendar
const entry = await stack0.marketing.scheduleContent({
projectSlug: 'my-project',
contentId: 'content-id',
scheduledFor: new Date('2024-12-25T10:00:00Z'),
autoPublish: true,
});
const entries = await stack0.marketing.listCalendarEntries({
projectSlug: 'my-project',
startDate: new Date('2024-12-01'),
endDate: new Date('2024-12-31'),
});Analytics and Usage
const overview = await stack0.marketing.getAnalyticsOverview({
projectSlug: 'my-project',
environment: 'production',
});
const performance = await stack0.marketing.getContentPerformance({
projectSlug: 'my-project',
environment: 'production',
contentType: 'tiktok_slideshow',
});
const usage = await stack0.marketing.getCurrentUsage({
projectSlug: 'my-project',
environment: 'production',
});Workflows
Create and execute AI-powered workflows with step-by-step orchestration.
Creating Workflows
const workflow = await stack0.workflows.create({
slug: 'content-pipeline',
name: 'Content Generation Pipeline',
steps: [
{
id: 'generate',
name: 'Generate Content',
type: 'llm',
provider: 'anthropic',
model: 'claude-sonnet-4-20250514',
config: {
prompt: 'Write a blog post about {{topic}}',
maxTokens: 2000,
},
},
],
variables: [
{ name: 'topic', type: 'string', required: true },
],
});Running Workflows
// Run and wait for the result (simplest approach)
const run = await stack0.workflows.runAndWait({
workflowSlug: 'content-pipeline',
variables: { topic: 'artificial intelligence' },
}, { timeout: 120000 }); // 2 minute timeout
console.log(run.output);
// Async run with webhook notification
const { id } = await stack0.workflows.run({
workflowSlug: 'content-pipeline',
variables: { topic: 'machine learning' },
webhook: {
url: 'https://your-app.com/webhook',
secret: 'webhook-secret',
},
});
// Check run status
const status = await stack0.workflows.getRun({ id });
console.log(status.status); // 'pending' | 'running' | 'completed' | 'failed' | 'cancelled'Managing Workflows
const { items } = await stack0.workflows.list({ environment: 'production', isActive: true });
await stack0.workflows.update({ id: 'workflow-id', name: 'Updated Pipeline', isActive: false });
await stack0.workflows.delete({ id: 'workflow-id' });
await stack0.workflows.cancelRun({ id: 'run-id' });Workflows Method Reference
| Method | Description |
|--------|-------------|
| create(req) | Create a new workflow |
| get(req) | Get workflow by ID or slug |
| list(req?) | List workflows |
| update(req) | Update a workflow |
| delete(req) | Delete a workflow |
| run(req) | Start a workflow run |
| getRun(req) | Get run status and output |
| listRuns(req?) | List workflow runs |
| cancelRun(req) | Cancel a running workflow |
| runAndWait(req, opts?) | Run and poll until complete |
Error Handling
All SDK methods throw errors with additional properties for API errors:
import { Stack0 } from '@stack0/sdk';
import type { Stack0Error } from '@stack0/sdk';
try {
await stack0.mail.send({
from: '[email protected]',
to: '[email protected]',
subject: 'Test',
html: '<p>Hello</p>',
});
} catch (err) {
const error = err as Stack0Error;
if (error.statusCode === 401) {
console.error('Invalid API key');
} else if (error.statusCode === 403) {
console.error('Insufficient permissions');
} else if (error.statusCode === 400) {
console.error('Validation error:', error.message);
} else if (error.statusCode === 429) {
console.error('Rate limit exceeded');
} else {
console.error('Unexpected error:', error.message);
}
// Full error response from the API
console.error(error.response);
console.error(error.code);
}The Stack0Error interface extends Error with:
| Property | Type | Description |
|----------|------|-------------|
| statusCode | number | HTTP status code |
| code | string | Machine-readable error code |
| response | unknown | Full error response body from the API |
TypeScript Types
The SDK exports all request/response types for each module. Import them directly:
import type {
// Shared
Environment,
Stack0Error,
BatchJobStatus,
ScheduleFrequency,
PaginatedRequest,
PaginatedResponse,
// Mail
SendEmailRequest,
SendEmailResponse,
EmailStatus,
ListEmailsRequest,
Template,
Campaign,
Sequence,
MailContact,
// CDN
Asset,
TransformOptions,
UploadUrlRequest,
ListAssetsRequest,
TranscodeVideoRequest,
PrivateFile,
// Screenshots
Screenshot,
CreateScreenshotRequest,
// Extraction
ExtractionResult,
CreateExtractionRequest,
// Integrations
Connection,
Contact,
Company,
Deal,
// Marketing
Trend,
Opportunity,
Content,
Script,
CalendarEntry,
// Workflows
Workflow,
WorkflowRun,
CreateWorkflowRequest,
RunWorkflowRequest,
} from '@stack0/sdk';You can also import types from individual module entry points:
import type { SendEmailRequest } from '@stack0/sdk/mail';
import type { Asset, TransformOptions } from '@stack0/sdk/cdn';
import type { Screenshot } from '@stack0/sdk/screenshots';
import type { ExtractionResult } from '@stack0/sdk/extraction';Polling Helpers
Several methods include built-in polling for async operations:
| Method | Default Poll Interval | Default Timeout |
|--------|----------------------|-----------------|
| screenshots.captureAndWait() | 1s | 60s |
| screenshots.batchAndWait() | 2s | 5min |
| extraction.extractAndWait() | 1s | 60s |
| extraction.batchAndWait() | 2s | 5min |
| workflows.runAndWait() | 2s | 10min |
Override defaults via the options parameter:
const result = await stack0.screenshots.captureAndWait(
{ url: 'https://example.com', format: 'png' },
{ pollInterval: 500, timeout: 30000 }, // 500ms interval, 30s timeout
);Migration from Resend
The Stack0 Mail API is designed for compatibility with the Resend API:
// Before (Resend)
import { Resend } from 'resend';
const resend = new Resend('re_...');
await resend.emails.send({ from: '...', to: '...', subject: '...', html: '...' });
// After (Stack0)
import { Stack0 } from '@stack0/sdk';
const stack0 = new Stack0({ apiKey: 'stack0_...' });
await stack0.mail.send({ from: '...', to: '...', subject: '...', html: '...' });