Skip to content

Latest commit

 

History

28 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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 fetch API
  • Tree-shakable -- import only the modules you need
  • Isomorphic -- works in Node.js, Bun, Deno, and edge runtimes

Full API Reference

Installation

npm install @stack0/sdk
# or
yarn add @stack0/sdk
# or
pnpm add @stack0/sdk
# or
bun add @stack0/sdk

Quick 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

Mail

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 contents

Video 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);

// 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,
});

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' },
});

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

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/off

Screenshots 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: '...' });

Links

License

MIT

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages