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
npm install @stack0/sdk
# or
yarn add @stack0/sdk
# or
pnpm add @stack0/sdk
# or
bun add @stack0/sdkimport { 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' },
});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 |
The SDK is organized into modules, each accessible as a property on the main Stack0 client:
| Module | Accessor | Description |
|---|---|---|
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.
// 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>',
});// 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');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' });The mail module exposes several sub-clients for managing related resources:
// 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');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' } });// 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] });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}%`);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);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' } },
],
});| 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 |
Upload, manage, and transform files. Supports images, video transcoding, private files, download bundles, and S3 imports.
// 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);// 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' });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 |
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 contents// 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);
// Get a thumbnail at a timestamp (generated on miss; waits until ready)
const thumb = await stack0.cdn.getThumbnailAndWait({
assetId: 'video-asset-id',
timestamp: 10.5,
width: 320,
format: 'webp',
});
// Non-blocking variant: returns { status: 'pending', url: null } while generating
const maybe = await stack0.cdn.getThumbnail({ assetId: 'video-asset-id', timestamp: 0 });
// Extract audio
const { jobId } = await stack0.cdn.extractAudio({
projectSlug: 'my-project',
assetId: 'video-asset-id',
format: 'mp3',
bitrate: 192,
});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);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' },
});
// Composite a screen recording onto a held phone (screen keyed #F500FA):
// the keyed background sits on top, so fingers occlude the inset recording
const composited = await stack0.cdn.createMergeJob({
projectSlug: 'my-project',
inputs: [
{
assetId: 'app-screen-recording',
composite: {
backgroundAssetId: 'holding-phone-shot',
quad: {
topLeft: { x: 320, y: 480 },
topRight: { x: 760, y: 500 },
bottomLeft: { x: 300, y: 1400 },
bottomRight: { x: 740, y: 1440 },
},
fit: 'cover',
chromaKey: { color: '#F500FA', similarity: 0.34, blend: 0.06 },
},
},
],
});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
});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,
});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' });Capture high-quality screenshots of any webpage with options for format, viewport, device emulation, and more.
// 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);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',
});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);
}// 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}`);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/off| 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 |
Extract structured data from any webpage using AI. Supports markdown, JSON schema, and raw HTML extraction modes.
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);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 }| 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 |
const job = await stack0.extraction.batchAndWait({
urls: [
'https://example.com/article-1',
'https://example.com/article-2',
],
config: { mode: 'markdown' },
});const { id } = await stack0.extraction.createSchedule({
name: 'Daily price monitor',
url: 'https://competitor.com/pricing',
frequency: 'daily',
config: { mode: 'schema', schema: { /* ... */ } },
});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({ /* ... */ });| 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 |
Unified API for connecting to third-party services. Manage OAuth connections and interact with CRM, storage, communication, and productivity platforms through a single interface.
// 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}`);// 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',
});// 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' });// 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');// 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]' },
});AI-powered trend discovery, content opportunity generation, script creation, and content calendar management.
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',
});const { opportunitiesGenerated } = await stack0.marketing.generateOpportunities({
projectSlug: 'my-project',
environment: 'production',
});
const opportunities = await stack0.marketing.listOpportunities({
projectSlug: 'my-project',
environment: 'production',
status: 'pending',
});// 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!',
});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'),
});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',
});Create and execute AI-powered workflows with step-by-step orchestration.
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 },
],
});// 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'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' });| 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 |
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 |
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';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
);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: '...' });