@stack0/sdk 0.5.7 → 0.5.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1026 -369
- package/dist/cdn/index.d.mts +416 -1
- package/dist/cdn/index.d.ts +416 -1
- package/dist/cdn/index.js +293 -0
- package/dist/cdn/index.js.map +1 -1
- package/dist/cdn/index.mjs +293 -0
- package/dist/cdn/index.mjs.map +1 -1
- package/dist/index.d.mts +760 -3
- package/dist/index.d.ts +760 -3
- package/dist/index.js +837 -13
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +833 -14
- package/dist/index.mjs.map +1 -1
- package/package.json +9 -6
package/README.md
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
# Stack0 SDK
|
|
2
2
|
|
|
3
|
-
Official TypeScript/JavaScript SDK for Stack0
|
|
3
|
+
Official TypeScript/JavaScript SDK for [Stack0](https://stack0.dev) -- a modular platform for email, CDN, screenshots, web extraction, integrations, marketing, and AI workflows.
|
|
4
|
+
|
|
5
|
+
- **Fully typed** -- written in TypeScript with complete type definitions
|
|
6
|
+
- **Zero dependencies** -- uses the native `fetch` API
|
|
7
|
+
- **Tree-shakable** -- import only the modules you need
|
|
8
|
+
- **Isomorphic** -- works in Node.js, Bun, Deno, and edge runtimes
|
|
9
|
+
|
|
10
|
+
[Full API Reference](https://stack0.dev/docs)
|
|
4
11
|
|
|
5
12
|
## Installation
|
|
6
13
|
|
|
@@ -20,12 +27,12 @@ bun add @stack0/sdk
|
|
|
20
27
|
import { Stack0 } from '@stack0/sdk';
|
|
21
28
|
|
|
22
29
|
const stack0 = new Stack0({
|
|
23
|
-
apiKey:
|
|
30
|
+
apiKey: process.env.STACK0_API_KEY!,
|
|
24
31
|
});
|
|
25
32
|
|
|
26
33
|
// Send an email
|
|
27
|
-
|
|
28
|
-
from: '
|
|
34
|
+
await stack0.mail.send({
|
|
35
|
+
from: 'hello@yourapp.com',
|
|
29
36
|
to: 'user@example.com',
|
|
30
37
|
subject: 'Welcome!',
|
|
31
38
|
html: '<h1>Hello World</h1>',
|
|
@@ -35,547 +42,686 @@ const email = await stack0.mail.send({
|
|
|
35
42
|
const asset = await stack0.cdn.upload({
|
|
36
43
|
projectSlug: 'my-project',
|
|
37
44
|
file: fileBuffer,
|
|
38
|
-
filename: '
|
|
45
|
+
filename: 'photo.jpg',
|
|
39
46
|
mimeType: 'image/jpeg',
|
|
40
47
|
});
|
|
41
48
|
|
|
42
|
-
|
|
49
|
+
// Capture a screenshot
|
|
50
|
+
const screenshot = await stack0.screenshots.captureAndWait({
|
|
51
|
+
url: 'https://example.com',
|
|
52
|
+
format: 'png',
|
|
53
|
+
fullPage: true,
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// Extract structured data from a page
|
|
57
|
+
const result = await stack0.extraction.extractAndWait({
|
|
58
|
+
url: 'https://example.com/article',
|
|
59
|
+
mode: 'markdown',
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// Run an AI workflow
|
|
63
|
+
const run = await stack0.workflows.runAndWait({
|
|
64
|
+
workflowSlug: 'content-pipeline',
|
|
65
|
+
variables: { topic: 'AI trends' },
|
|
66
|
+
});
|
|
43
67
|
```
|
|
44
68
|
|
|
45
69
|
## Configuration
|
|
46
70
|
|
|
47
71
|
```typescript
|
|
48
|
-
import { Stack0 } from '@stack0/sdk';
|
|
49
|
-
|
|
50
72
|
const stack0 = new Stack0({
|
|
51
|
-
apiKey: 'stack0_...',
|
|
52
|
-
baseUrl: 'https://api.stack0.dev/v1', // Optional
|
|
53
|
-
cdnUrl: 'https://cdn.
|
|
73
|
+
apiKey: 'stack0_...', // Required -- your API key
|
|
74
|
+
baseUrl: 'https://api.stack0.dev/v1', // Optional -- custom API endpoint
|
|
75
|
+
cdnUrl: 'https://cdn.myproject.stack0.dev', // Optional -- for client-side image transforms
|
|
54
76
|
});
|
|
55
77
|
```
|
|
56
78
|
|
|
57
|
-
|
|
79
|
+
| Option | Type | Default | Description |
|
|
80
|
+
|--------|------|---------|-------------|
|
|
81
|
+
| `apiKey` | `string` | -- | Your Stack0 API key (required) |
|
|
82
|
+
| `baseUrl` | `string` | `https://api.stack0.dev/v1` | API base URL |
|
|
83
|
+
| `cdnUrl` | `string` | -- | CDN base URL for client-side image transform URLs |
|
|
58
84
|
|
|
59
|
-
|
|
85
|
+
## Modules
|
|
60
86
|
|
|
61
|
-
|
|
87
|
+
The SDK is organized into modules, each accessible as a property on the main `Stack0` client:
|
|
62
88
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
});
|
|
89
|
+
| Module | Accessor | Description |
|
|
90
|
+
|--------|----------|-------------|
|
|
91
|
+
| [Mail](#mail) | `stack0.mail` | Transactional email, campaigns, sequences, contacts |
|
|
92
|
+
| [CDN](#cdn) | `stack0.cdn` | File upload, asset management, image transforms, video |
|
|
93
|
+
| [Screenshots](#screenshots) | `stack0.screenshots` | Webpage screenshot capture |
|
|
94
|
+
| [Extraction](#extraction) | `stack0.extraction` | AI-powered web data extraction |
|
|
95
|
+
| [Integrations](#integrations) | `stack0.integrations` | Unified CRM, storage, communication APIs |
|
|
96
|
+
| [Marketing](#marketing) | `stack0.marketing` | Trend discovery, content generation, scheduling |
|
|
97
|
+
| [Workflows](#workflows) | `stack0.workflows` | AI workflow orchestration |
|
|
73
98
|
|
|
74
|
-
|
|
75
|
-
console.log(asset.cdnUrl); // CDN URL
|
|
76
|
-
console.log(asset.status); // 'ready', 'processing', etc.
|
|
77
|
-
```
|
|
99
|
+
---
|
|
78
100
|
|
|
79
|
-
|
|
101
|
+
## Mail
|
|
80
102
|
|
|
81
|
-
|
|
103
|
+
Send transactional emails, manage domains, templates, contacts, campaigns, and automated sequences. The Mail API is designed to be compatible with the [Resend](https://resend.com) API for easy migration.
|
|
104
|
+
|
|
105
|
+
### Sending Emails
|
|
82
106
|
|
|
83
107
|
```typescript
|
|
84
|
-
//
|
|
85
|
-
const {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
108
|
+
// Simple email
|
|
109
|
+
const { id } = await stack0.mail.send({
|
|
110
|
+
from: 'hello@yourapp.com',
|
|
111
|
+
to: 'user@example.com',
|
|
112
|
+
subject: 'Welcome!',
|
|
113
|
+
html: '<h1>Hello World</h1>',
|
|
90
114
|
});
|
|
91
115
|
|
|
92
|
-
//
|
|
93
|
-
await
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
116
|
+
// With name, CC, BCC, reply-to, and attachments
|
|
117
|
+
await stack0.mail.send({
|
|
118
|
+
from: { name: 'Acme Inc', email: 'noreply@acme.com' },
|
|
119
|
+
to: { name: 'Jane Doe', email: 'jane@example.com' },
|
|
120
|
+
cc: 'manager@example.com',
|
|
121
|
+
bcc: ['audit@example.com'],
|
|
122
|
+
replyTo: 'support@acme.com',
|
|
123
|
+
subject: 'Invoice #1234',
|
|
124
|
+
html: '<p>Your invoice is attached.</p>',
|
|
125
|
+
text: 'Your invoice is attached.',
|
|
126
|
+
tags: ['transactional', 'invoice'],
|
|
127
|
+
metadata: { orderId: '1234' },
|
|
128
|
+
attachments: [
|
|
129
|
+
{
|
|
130
|
+
filename: 'invoice.pdf',
|
|
131
|
+
content: 'base64-encoded-content',
|
|
132
|
+
contentType: 'application/pdf',
|
|
133
|
+
},
|
|
134
|
+
],
|
|
97
135
|
});
|
|
98
136
|
|
|
99
|
-
//
|
|
100
|
-
|
|
101
|
-
|
|
137
|
+
// Using a template
|
|
138
|
+
await stack0.mail.send({
|
|
139
|
+
from: 'hello@yourapp.com',
|
|
140
|
+
to: 'user@example.com',
|
|
141
|
+
subject: 'Welcome {{name}}',
|
|
142
|
+
templateId: 'template-uuid',
|
|
143
|
+
templateVariables: { name: 'Jane', activationUrl: 'https://...' },
|
|
144
|
+
});
|
|
102
145
|
|
|
103
|
-
|
|
146
|
+
// Batch send (up to 100 emails, each with different content)
|
|
147
|
+
await stack0.mail.sendBatch({
|
|
148
|
+
emails: [
|
|
149
|
+
{ from: 'hi@app.com', to: 'user1@example.com', subject: 'Hi', html: '<p>Hello User 1</p>' },
|
|
150
|
+
{ from: 'hi@app.com', to: 'user2@example.com', subject: 'Hi', html: '<p>Hello User 2</p>' },
|
|
151
|
+
],
|
|
152
|
+
});
|
|
104
153
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
tags: ['profile'], // Optional
|
|
113
|
-
sortBy: 'createdAt', // Optional: 'createdAt', 'filename', 'size', 'type'
|
|
114
|
-
sortOrder: 'desc', // Optional: 'asc', 'desc'
|
|
115
|
-
limit: 20, // Optional
|
|
116
|
-
offset: 0, // Optional
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
for (const asset of assets) {
|
|
120
|
-
console.log(asset.filename, asset.cdnUrl);
|
|
121
|
-
}
|
|
154
|
+
// Broadcast (same content to up to 1000 recipients)
|
|
155
|
+
await stack0.mail.sendBroadcast({
|
|
156
|
+
from: 'newsletter@app.com',
|
|
157
|
+
to: ['user1@example.com', 'user2@example.com', 'user3@example.com'],
|
|
158
|
+
subject: 'Monthly Update',
|
|
159
|
+
html: '<p>Here is what happened this month...</p>',
|
|
160
|
+
});
|
|
122
161
|
```
|
|
123
162
|
|
|
124
|
-
###
|
|
163
|
+
### Retrieving and Managing Emails
|
|
125
164
|
|
|
126
165
|
```typescript
|
|
127
|
-
|
|
166
|
+
// Get email details
|
|
167
|
+
const email = await stack0.mail.get('email-id');
|
|
168
|
+
console.log(email.status); // 'delivered', 'bounced', etc.
|
|
169
|
+
console.log(email.openedAt); // Date or null
|
|
170
|
+
console.log(email.deliveredAt); // Date or null
|
|
171
|
+
|
|
172
|
+
// List emails with filters
|
|
173
|
+
const { emails, total } = await stack0.mail.list({
|
|
174
|
+
status: 'delivered',
|
|
175
|
+
from: 'hello@yourapp.com',
|
|
176
|
+
startDate: '2024-01-01',
|
|
177
|
+
sortBy: 'createdAt',
|
|
178
|
+
sortOrder: 'desc',
|
|
179
|
+
limit: 50,
|
|
180
|
+
});
|
|
128
181
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
console.log(asset.width, asset.height); // For images/videos
|
|
182
|
+
// Resend or cancel
|
|
183
|
+
await stack0.mail.resend('email-id');
|
|
184
|
+
await stack0.mail.cancel('scheduled-email-id');
|
|
133
185
|
```
|
|
134
186
|
|
|
135
|
-
###
|
|
187
|
+
### Analytics
|
|
136
188
|
|
|
137
189
|
```typescript
|
|
138
|
-
const
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
});
|
|
190
|
+
const analytics = await stack0.mail.getAnalytics();
|
|
191
|
+
console.log(`Delivery rate: ${analytics.deliveryRate}%`);
|
|
192
|
+
console.log(`Open rate: ${analytics.openRate}%`);
|
|
193
|
+
|
|
194
|
+
const timeSeries = await stack0.mail.getTimeSeriesAnalytics({ days: 30 });
|
|
195
|
+
const hourly = await stack0.mail.getHourlyAnalytics();
|
|
196
|
+
const senders = await stack0.mail.listSenders({ search: '@yourapp.com' });
|
|
146
197
|
```
|
|
147
198
|
|
|
148
|
-
###
|
|
199
|
+
### Sub-clients
|
|
149
200
|
|
|
150
|
-
|
|
151
|
-
// Delete single asset
|
|
152
|
-
await stack0.cdn.delete('asset-id');
|
|
201
|
+
The mail module exposes several sub-clients for managing related resources:
|
|
153
202
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
203
|
+
#### Domains
|
|
204
|
+
|
|
205
|
+
```typescript
|
|
206
|
+
// Add and verify a sending domain
|
|
207
|
+
const { dnsRecords } = await stack0.mail.domains.add({ domain: 'yourapp.com' });
|
|
208
|
+
// Configure DNS records, then verify
|
|
209
|
+
const { verified } = await stack0.mail.domains.verify('domain-id');
|
|
210
|
+
|
|
211
|
+
// List, get DNS records, delete, set default
|
|
212
|
+
const domains = await stack0.mail.domains.list({ projectSlug: 'my-project' });
|
|
213
|
+
const dns = await stack0.mail.domains.getDNSRecords('domain-id');
|
|
214
|
+
await stack0.mail.domains.setDefault('domain-id');
|
|
215
|
+
await stack0.mail.domains.delete('domain-id');
|
|
160
216
|
```
|
|
161
217
|
|
|
162
|
-
|
|
218
|
+
#### Templates
|
|
163
219
|
|
|
164
220
|
```typescript
|
|
165
|
-
await stack0.
|
|
166
|
-
|
|
167
|
-
|
|
221
|
+
const template = await stack0.mail.templates.create({
|
|
222
|
+
name: 'Welcome Email',
|
|
223
|
+
slug: 'welcome',
|
|
224
|
+
subject: 'Welcome {{name}}!',
|
|
225
|
+
html: '<h1>Hello {{name}}</h1>',
|
|
168
226
|
});
|
|
169
|
-
```
|
|
170
227
|
|
|
171
|
-
|
|
228
|
+
const { templates } = await stack0.mail.templates.list({ search: 'welcome' });
|
|
229
|
+
const found = await stack0.mail.templates.getBySlug('welcome');
|
|
230
|
+
const preview = await stack0.mail.templates.preview({ id: template.id, variables: { name: 'Jane' } });
|
|
231
|
+
```
|
|
172
232
|
|
|
173
|
-
|
|
233
|
+
#### Contacts and Audiences
|
|
174
234
|
|
|
175
235
|
```typescript
|
|
176
|
-
//
|
|
177
|
-
const
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
quality: 80,
|
|
236
|
+
// Create and manage contacts
|
|
237
|
+
const contact = await stack0.mail.contacts.create({
|
|
238
|
+
email: 'user@example.com',
|
|
239
|
+
firstName: 'Jane',
|
|
240
|
+
lastName: 'Doe',
|
|
241
|
+
metadata: { plan: 'pro' },
|
|
183
242
|
});
|
|
184
243
|
|
|
185
|
-
//
|
|
186
|
-
const
|
|
244
|
+
// Bulk import contacts
|
|
245
|
+
const importResult = await stack0.mail.contacts.import({
|
|
246
|
+
audienceId: 'audience-id',
|
|
247
|
+
contacts: [
|
|
248
|
+
{ email: 'user1@example.com', firstName: 'Alice' },
|
|
249
|
+
{ email: 'user2@example.com', firstName: 'Bob' },
|
|
250
|
+
],
|
|
251
|
+
});
|
|
187
252
|
|
|
188
|
-
//
|
|
189
|
-
|
|
253
|
+
// Create audiences and add contacts
|
|
254
|
+
const audience = await stack0.mail.audiences.create({
|
|
255
|
+
name: 'Newsletter Subscribers',
|
|
256
|
+
description: 'Users opted in to the newsletter',
|
|
257
|
+
});
|
|
258
|
+
await stack0.mail.audiences.addContacts({ id: audience.id, contactIds: [contact.id] });
|
|
190
259
|
```
|
|
191
260
|
|
|
192
|
-
|
|
261
|
+
#### Campaigns
|
|
193
262
|
|
|
194
263
|
```typescript
|
|
195
|
-
const
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
crop: 'attention', // Smart crop: 'attention', 'entropy', 'center', etc.
|
|
202
|
-
blur: 5, // Blur sigma (0.3-100)
|
|
203
|
-
sharpen: 1.5, // Sharpen sigma
|
|
204
|
-
brightness: 10, // -100 to 100
|
|
205
|
-
saturation: -50, // -100 to 100
|
|
206
|
-
grayscale: true, // Convert to grayscale
|
|
207
|
-
rotate: 90, // 0, 90, 180, 270
|
|
208
|
-
flip: true, // Flip vertically
|
|
209
|
-
flop: true, // Flip horizontally
|
|
264
|
+
const campaign = await stack0.mail.campaigns.create({
|
|
265
|
+
name: 'Product Launch',
|
|
266
|
+
subject: 'Introducing our new feature!',
|
|
267
|
+
fromEmail: 'hello@yourapp.com',
|
|
268
|
+
audienceId: 'audience-id',
|
|
269
|
+
html: '<p>We are excited to announce...</p>',
|
|
210
270
|
});
|
|
271
|
+
|
|
272
|
+
await stack0.mail.campaigns.send({ id: campaign.id, sendNow: true });
|
|
273
|
+
const stats = await stack0.mail.campaigns.getStats(campaign.id);
|
|
274
|
+
console.log(`Open rate: ${stats.openRate}%`);
|
|
211
275
|
```
|
|
212
276
|
|
|
213
|
-
|
|
277
|
+
#### Sequences
|
|
278
|
+
|
|
279
|
+
Build automated email flows with a visual node-based editor:
|
|
214
280
|
|
|
215
281
|
```typescript
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
282
|
+
const sequence = await stack0.mail.sequences.create({
|
|
283
|
+
name: 'Onboarding Flow',
|
|
284
|
+
triggerType: 'contact_added',
|
|
285
|
+
triggerFrequency: 'once',
|
|
220
286
|
});
|
|
221
287
|
|
|
222
|
-
//
|
|
223
|
-
const
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
288
|
+
// Add nodes and connections
|
|
289
|
+
const emailNode = await stack0.mail.sequences.createNode({
|
|
290
|
+
id: sequence.id,
|
|
291
|
+
nodeType: 'email',
|
|
292
|
+
name: 'Welcome Email',
|
|
293
|
+
positionX: 200,
|
|
294
|
+
positionY: 100,
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
await stack0.mail.sequences.setNodeEmail(sequence.id, {
|
|
298
|
+
nodeId: emailNode.id,
|
|
299
|
+
subject: 'Welcome!',
|
|
300
|
+
html: '<p>Thanks for signing up.</p>',
|
|
227
301
|
});
|
|
228
302
|
|
|
229
|
-
|
|
230
|
-
await stack0.cdn.deleteFolder('folder-id');
|
|
231
|
-
await stack0.cdn.deleteFolder('folder-id', true); // Delete with contents
|
|
303
|
+
await stack0.mail.sequences.publish(sequence.id);
|
|
232
304
|
```
|
|
233
305
|
|
|
234
|
-
|
|
306
|
+
#### Events
|
|
235
307
|
|
|
236
|
-
|
|
308
|
+
Track custom events that can trigger sequences:
|
|
237
309
|
|
|
238
310
|
```typescript
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
outputFormat: 'hls', // 'hls' for adaptive streaming, 'mp4' for progressive download
|
|
244
|
-
variants: [
|
|
245
|
-
{ quality: '720p', codec: 'h264' },
|
|
246
|
-
{ quality: '1080p', codec: 'h264' },
|
|
247
|
-
],
|
|
248
|
-
webhookUrl: 'https://your-app.com/webhook', // Optional: get notified when complete
|
|
311
|
+
await stack0.mail.events.track({
|
|
312
|
+
eventName: 'purchase_completed',
|
|
313
|
+
contactEmail: 'user@example.com',
|
|
314
|
+
properties: { orderId: '12345', amount: 99.99 },
|
|
249
315
|
});
|
|
250
316
|
|
|
251
|
-
|
|
317
|
+
// Batch tracking
|
|
318
|
+
await stack0.mail.events.trackBatch({
|
|
319
|
+
events: [
|
|
320
|
+
{ eventName: 'page_viewed', contactEmail: 'user1@example.com', properties: { page: '/pricing' } },
|
|
321
|
+
{ eventName: 'page_viewed', contactEmail: 'user2@example.com', properties: { page: '/features' } },
|
|
322
|
+
],
|
|
323
|
+
});
|
|
252
324
|
```
|
|
253
325
|
|
|
254
|
-
###
|
|
326
|
+
### Mail Method Reference
|
|
327
|
+
|
|
328
|
+
| Method | Description |
|
|
329
|
+
|--------|-------------|
|
|
330
|
+
| `mail.send(req)` | Send a single email |
|
|
331
|
+
| `mail.sendBatch(req)` | Send up to 100 emails in a batch |
|
|
332
|
+
| `mail.sendBroadcast(req)` | Broadcast to up to 1000 recipients |
|
|
333
|
+
| `mail.get(id)` | Get email by ID |
|
|
334
|
+
| `mail.list(req?)` | List emails with filters |
|
|
335
|
+
| `mail.resend(id)` | Resend an email |
|
|
336
|
+
| `mail.cancel(id)` | Cancel a scheduled email |
|
|
337
|
+
| `mail.getAnalytics()` | Get overall email analytics |
|
|
338
|
+
| `mail.getTimeSeriesAnalytics(req?)` | Get daily analytics breakdown |
|
|
339
|
+
| `mail.getHourlyAnalytics()` | Get hourly analytics |
|
|
340
|
+
| `mail.listSenders(req?)` | List unique senders with stats |
|
|
341
|
+
| `mail.domains.*` | Domain management (list, add, verify, delete, setDefault, getDNSRecords) |
|
|
342
|
+
| `mail.templates.*` | Template CRUD + preview and getBySlug |
|
|
343
|
+
| `mail.audiences.*` | Audience CRUD + addContacts, removeContacts, listContacts |
|
|
344
|
+
| `mail.contacts.*` | Contact CRUD + import |
|
|
345
|
+
| `mail.campaigns.*` | Campaign CRUD + send, pause, cancel, duplicate, getStats |
|
|
346
|
+
| `mail.sequences.*` | Sequence CRUD + node/connection management + publish/pause/resume/archive |
|
|
347
|
+
| `mail.events.*` | Event CRUD + track, trackBatch, listOccurrences, getAnalytics |
|
|
348
|
+
|
|
349
|
+
---
|
|
350
|
+
|
|
351
|
+
## CDN
|
|
352
|
+
|
|
353
|
+
Upload, manage, and transform files. Supports images, video transcoding, private files, download bundles, and S3 imports.
|
|
354
|
+
|
|
355
|
+
### Uploading Files
|
|
255
356
|
|
|
256
357
|
```typescript
|
|
257
|
-
//
|
|
258
|
-
const
|
|
259
|
-
|
|
358
|
+
// Simple upload (handles presigned URL flow automatically)
|
|
359
|
+
const asset = await stack0.cdn.upload({
|
|
360
|
+
projectSlug: 'my-project',
|
|
361
|
+
file: fileBuffer, // Blob, Buffer, or ArrayBuffer
|
|
362
|
+
filename: 'photo.jpg',
|
|
363
|
+
mimeType: 'image/jpeg',
|
|
364
|
+
folder: '/images/avatars',
|
|
365
|
+
metadata: { userId: 'user_123' },
|
|
366
|
+
});
|
|
260
367
|
|
|
261
|
-
//
|
|
262
|
-
|
|
368
|
+
console.log(asset.id); // Asset ID
|
|
369
|
+
console.log(asset.cdnUrl); // Public CDN URL
|
|
370
|
+
|
|
371
|
+
// Manual presigned URL flow for more control
|
|
372
|
+
const { uploadUrl, assetId } = await stack0.cdn.getUploadUrl({
|
|
263
373
|
projectSlug: 'my-project',
|
|
264
|
-
|
|
265
|
-
|
|
374
|
+
filename: 'document.pdf',
|
|
375
|
+
mimeType: 'application/pdf',
|
|
376
|
+
size: file.size,
|
|
266
377
|
});
|
|
267
378
|
|
|
268
|
-
|
|
269
|
-
await stack0.cdn.
|
|
379
|
+
await fetch(uploadUrl, { method: 'PUT', body: file, headers: { 'Content-Type': 'application/pdf' } });
|
|
380
|
+
const confirmed = await stack0.cdn.confirmUpload(assetId);
|
|
270
381
|
```
|
|
271
382
|
|
|
272
|
-
###
|
|
383
|
+
### Asset Management
|
|
273
384
|
|
|
274
385
|
```typescript
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
386
|
+
// Get, update, delete
|
|
387
|
+
const asset = await stack0.cdn.get('asset-id');
|
|
388
|
+
await stack0.cdn.update({ id: 'asset-id', alt: 'Sunset photo', tags: ['nature'] });
|
|
389
|
+
await stack0.cdn.delete('asset-id');
|
|
390
|
+
await stack0.cdn.deleteMany(['asset-1', 'asset-2']);
|
|
279
391
|
|
|
280
|
-
//
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
392
|
+
// List with filters
|
|
393
|
+
const { assets, total, hasMore } = await stack0.cdn.list({
|
|
394
|
+
projectSlug: 'my-project',
|
|
395
|
+
type: 'image',
|
|
396
|
+
folder: '/images',
|
|
397
|
+
search: 'avatar',
|
|
398
|
+
sortBy: 'createdAt',
|
|
399
|
+
sortOrder: 'desc',
|
|
400
|
+
limit: 20,
|
|
401
|
+
});
|
|
284
402
|
|
|
285
|
-
//
|
|
286
|
-
|
|
287
|
-
console.log(`Thumbnail at ${thumb.timestamp}s: ${thumb.url}`);
|
|
288
|
-
}
|
|
403
|
+
// Move assets between folders
|
|
404
|
+
await stack0.cdn.move({ assetIds: ['asset-1', 'asset-2'], folder: '/archive' });
|
|
289
405
|
```
|
|
290
406
|
|
|
291
|
-
###
|
|
407
|
+
### Image Transformations
|
|
408
|
+
|
|
409
|
+
Generate optimized image URLs client-side (no API call required):
|
|
292
410
|
|
|
293
411
|
```typescript
|
|
294
|
-
const
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
format: 'webp',
|
|
412
|
+
const url = stack0.cdn.getTransformUrl(asset.cdnUrl, {
|
|
413
|
+
width: 800,
|
|
414
|
+
height: 600,
|
|
415
|
+
fit: 'cover',
|
|
416
|
+
format: 'webp',
|
|
417
|
+
quality: 80,
|
|
299
418
|
});
|
|
300
419
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
420
|
+
// Advanced transforms
|
|
421
|
+
const advancedUrl = stack0.cdn.getTransformUrl(asset.cdnUrl, {
|
|
422
|
+
width: 400,
|
|
423
|
+
crop: 'attention', // Smart crop: 'attention', 'entropy', 'center'
|
|
424
|
+
blur: 5,
|
|
425
|
+
sharpen: 1.5,
|
|
426
|
+
brightness: 10,
|
|
427
|
+
saturation: -50,
|
|
428
|
+
grayscale: true,
|
|
429
|
+
rotate: 90,
|
|
430
|
+
flip: true,
|
|
431
|
+
flop: true,
|
|
312
432
|
});
|
|
313
433
|
```
|
|
314
434
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
435
|
+
| Transform | Type | Description |
|
|
436
|
+
|-----------|------|-------------|
|
|
437
|
+
| `width` | `number` | Target width (snapped to nearest allowed width for caching) |
|
|
438
|
+
| `height` | `number` | Target height |
|
|
439
|
+
| `fit` | `string` | `cover`, `contain`, `fill`, `inside`, `outside` |
|
|
440
|
+
| `format` | `string` | `webp`, `jpeg`, `png`, `avif`, `auto` |
|
|
441
|
+
| `quality` | `number` | Output quality (1-100) |
|
|
442
|
+
| `crop` | `string` | Smart crop strategy: `attention`, `entropy`, `center` |
|
|
443
|
+
| `blur` | `number` | Gaussian blur sigma (0.3-100) |
|
|
444
|
+
| `sharpen` | `number` | Sharpen sigma |
|
|
445
|
+
| `brightness` | `number` | -100 to 100 |
|
|
446
|
+
| `saturation` | `number` | -100 to 100 |
|
|
447
|
+
| `grayscale` | `boolean` | Convert to grayscale |
|
|
448
|
+
| `rotate` | `number` | 0, 90, 180, 270 |
|
|
449
|
+
| `flip` | `boolean` | Flip vertically |
|
|
450
|
+
| `flop` | `boolean` | Flip horizontally |
|
|
319
451
|
|
|
320
|
-
|
|
452
|
+
### Folders
|
|
321
453
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
video.src = urls.hlsUrl;
|
|
330
|
-
}
|
|
454
|
+
```typescript
|
|
455
|
+
const tree = await stack0.cdn.getFolderTree({ projectSlug: 'my-project', maxDepth: 3 });
|
|
456
|
+
const folder = await stack0.cdn.createFolder({ projectSlug: 'my-project', name: 'avatars' });
|
|
457
|
+
const byPath = await stack0.cdn.getFolderByPath('/images/avatars');
|
|
458
|
+
await stack0.cdn.updateFolder({ id: folder.id, name: 'profile-pictures' });
|
|
459
|
+
await stack0.cdn.moveFolder({ id: folder.id, newParentId: 'other-folder-id' });
|
|
460
|
+
await stack0.cdn.deleteFolder(folder.id, true); // true = delete contents
|
|
331
461
|
```
|
|
332
462
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
The Mail API is compatible with the Resend API for easy migration.
|
|
336
|
-
|
|
337
|
-
### Send Email
|
|
463
|
+
### Video Transcoding
|
|
338
464
|
|
|
339
465
|
```typescript
|
|
340
|
-
//
|
|
341
|
-
await stack0.
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
from: 'hello@example.com',
|
|
351
|
-
to: ['user1@example.com', 'user2@example.com'],
|
|
352
|
-
subject: 'Newsletter',
|
|
353
|
-
html: '<p>Monthly update</p>',
|
|
354
|
-
});
|
|
355
|
-
|
|
356
|
-
// With name and email
|
|
357
|
-
await stack0.mail.send({
|
|
358
|
-
from: { name: 'Acme Inc', email: 'noreply@acme.com' },
|
|
359
|
-
to: { name: 'John Doe', email: 'john@example.com' },
|
|
360
|
-
subject: 'Important Update',
|
|
361
|
-
html: '<p>Your account has been updated</p>',
|
|
362
|
-
});
|
|
363
|
-
|
|
364
|
-
// With CC and BCC
|
|
365
|
-
await stack0.mail.send({
|
|
366
|
-
from: 'hello@example.com',
|
|
367
|
-
to: 'user@example.com',
|
|
368
|
-
cc: 'manager@example.com',
|
|
369
|
-
bcc: ['audit@example.com', 'compliance@example.com'],
|
|
370
|
-
subject: 'Team Update',
|
|
371
|
-
html: '<p>Quarterly results</p>',
|
|
466
|
+
// Start a transcoding job
|
|
467
|
+
const job = await stack0.cdn.transcode({
|
|
468
|
+
projectSlug: 'my-project',
|
|
469
|
+
assetId: 'video-asset-id',
|
|
470
|
+
outputFormat: 'hls',
|
|
471
|
+
variants: [
|
|
472
|
+
{ quality: '720p', codec: 'h264' },
|
|
473
|
+
{ quality: '1080p', codec: 'h264' },
|
|
474
|
+
],
|
|
475
|
+
webhookUrl: 'https://your-app.com/webhook',
|
|
372
476
|
});
|
|
373
477
|
|
|
374
|
-
//
|
|
375
|
-
await stack0.
|
|
376
|
-
|
|
377
|
-
to: 'user@example.com',
|
|
378
|
-
subject: 'Hello',
|
|
379
|
-
html: '<h1>Welcome!</h1><p>Thanks for joining</p>',
|
|
380
|
-
text: 'Welcome! Thanks for joining',
|
|
381
|
-
});
|
|
478
|
+
// Check job status
|
|
479
|
+
const status = await stack0.cdn.getJob(job.id);
|
|
480
|
+
console.log(`Progress: ${status.progress}%`);
|
|
382
481
|
|
|
383
|
-
//
|
|
384
|
-
await stack0.
|
|
385
|
-
|
|
386
|
-
to: 'user@example.com',
|
|
387
|
-
subject: 'Order Confirmation',
|
|
388
|
-
html: '<p>Your order #12345 is confirmed</p>',
|
|
389
|
-
tags: ['transactional', 'order'],
|
|
390
|
-
metadata: {
|
|
391
|
-
orderId: '12345',
|
|
392
|
-
customerId: 'cus_abc123',
|
|
393
|
-
},
|
|
394
|
-
});
|
|
482
|
+
// Get streaming URLs for playback
|
|
483
|
+
const urls = await stack0.cdn.getStreamingUrls('asset-id');
|
|
484
|
+
console.log(urls.hlsUrl);
|
|
395
485
|
|
|
396
|
-
//
|
|
397
|
-
await stack0.
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
templateVariables: {
|
|
403
|
-
name: 'John',
|
|
404
|
-
activationUrl: 'https://example.com/activate?token=...',
|
|
405
|
-
},
|
|
486
|
+
// Generate thumbnails
|
|
487
|
+
const thumb = await stack0.cdn.getThumbnail({
|
|
488
|
+
assetId: 'video-asset-id',
|
|
489
|
+
timestamp: 10.5,
|
|
490
|
+
width: 320,
|
|
491
|
+
format: 'webp',
|
|
406
492
|
});
|
|
407
493
|
|
|
408
|
-
//
|
|
409
|
-
await stack0.
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
attachments: [
|
|
415
|
-
{
|
|
416
|
-
filename: 'invoice.pdf',
|
|
417
|
-
content: 'base64-encoded-content',
|
|
418
|
-
contentType: 'application/pdf',
|
|
419
|
-
},
|
|
420
|
-
],
|
|
494
|
+
// Extract audio
|
|
495
|
+
const { jobId } = await stack0.cdn.extractAudio({
|
|
496
|
+
projectSlug: 'my-project',
|
|
497
|
+
assetId: 'video-asset-id',
|
|
498
|
+
format: 'mp3',
|
|
499
|
+
bitrate: 192,
|
|
421
500
|
});
|
|
422
501
|
```
|
|
423
502
|
|
|
424
|
-
###
|
|
425
|
-
|
|
426
|
-
Retrieve details about a sent email:
|
|
503
|
+
### GIF Generation
|
|
427
504
|
|
|
428
505
|
```typescript
|
|
429
|
-
const
|
|
506
|
+
const gif = await stack0.cdn.generateGif({
|
|
507
|
+
projectSlug: 'my-project',
|
|
508
|
+
assetId: 'video-asset-id',
|
|
509
|
+
startTime: 5,
|
|
510
|
+
duration: 3,
|
|
511
|
+
width: 480,
|
|
512
|
+
fps: 10,
|
|
513
|
+
});
|
|
430
514
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
515
|
+
// Poll for completion
|
|
516
|
+
let result = await stack0.cdn.getGif(gif.id);
|
|
517
|
+
while (result?.status === 'pending' || result?.status === 'processing') {
|
|
518
|
+
await new Promise(r => setTimeout(r, 1000));
|
|
519
|
+
result = await stack0.cdn.getGif(gif.id);
|
|
520
|
+
}
|
|
521
|
+
console.log(result?.url);
|
|
434
522
|
```
|
|
435
523
|
|
|
436
|
-
|
|
524
|
+
### Video Merge
|
|
437
525
|
|
|
438
526
|
```typescript
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
} else if (error.statusCode === 403) {
|
|
450
|
-
console.error('Insufficient permissions');
|
|
451
|
-
} else if (error.statusCode === 400) {
|
|
452
|
-
console.error('Validation error:', error.message);
|
|
453
|
-
} else {
|
|
454
|
-
console.error('Error:', error.message);
|
|
455
|
-
}
|
|
456
|
-
}
|
|
527
|
+
const mergeJob = await stack0.cdn.createMergeJob({
|
|
528
|
+
projectSlug: 'my-project',
|
|
529
|
+
inputs: [
|
|
530
|
+
{ assetId: 'intro-video-id' },
|
|
531
|
+
{ assetId: 'image-id', duration: 5 },
|
|
532
|
+
{ assetId: 'main-video-id', startTime: 10, endTime: 60 },
|
|
533
|
+
],
|
|
534
|
+
audioTrack: { assetId: 'music-id', loop: true, fadeIn: 2, fadeOut: 3 },
|
|
535
|
+
output: { format: 'mp4', quality: '1080p', filename: 'final.mp4' },
|
|
536
|
+
});
|
|
457
537
|
```
|
|
458
538
|
|
|
459
|
-
|
|
539
|
+
### Private Files
|
|
460
540
|
|
|
461
|
-
|
|
541
|
+
Private files are stored securely and can only be accessed through authorized, time-limited download URLs.
|
|
462
542
|
|
|
463
543
|
```typescript
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
ListAssetsRequest,
|
|
472
|
-
TransformOptions,
|
|
473
|
-
} from '@stack0/sdk';
|
|
474
|
-
```
|
|
544
|
+
// Upload a private file
|
|
545
|
+
const privateFile = await stack0.cdn.uploadPrivate({
|
|
546
|
+
projectSlug: 'my-project',
|
|
547
|
+
file: fileBuffer,
|
|
548
|
+
filename: 'confidential.pdf',
|
|
549
|
+
mimeType: 'application/pdf',
|
|
550
|
+
});
|
|
475
551
|
|
|
476
|
-
|
|
552
|
+
// Generate a time-limited download URL
|
|
553
|
+
const { downloadUrl, expiresAt } = await stack0.cdn.getPrivateDownloadUrl({
|
|
554
|
+
fileId: privateFile.id,
|
|
555
|
+
expiresIn: 86400, // 24 hours in seconds
|
|
556
|
+
});
|
|
557
|
+
```
|
|
477
558
|
|
|
478
|
-
|
|
559
|
+
### Download Bundles
|
|
479
560
|
|
|
480
561
|
```typescript
|
|
481
|
-
const
|
|
482
|
-
|
|
562
|
+
const { bundle } = await stack0.cdn.createBundle({
|
|
563
|
+
projectSlug: 'my-project',
|
|
564
|
+
name: 'December Assets',
|
|
565
|
+
assetIds: ['asset-1', 'asset-2'],
|
|
566
|
+
privateFileIds: ['file-1'],
|
|
567
|
+
expiresIn: 86400,
|
|
483
568
|
});
|
|
484
|
-
```
|
|
485
569
|
|
|
486
|
-
|
|
570
|
+
const { downloadUrl } = await stack0.cdn.getBundleDownloadUrl({
|
|
571
|
+
bundleId: bundle.id,
|
|
572
|
+
expiresIn: 3600,
|
|
573
|
+
});
|
|
574
|
+
```
|
|
487
575
|
|
|
488
|
-
|
|
576
|
+
### Usage Tracking
|
|
489
577
|
|
|
490
578
|
```typescript
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
const resend = new Resend('re_...');
|
|
494
|
-
await resend.emails.send({ ... });
|
|
579
|
+
const usage = await stack0.cdn.getUsage({ projectSlug: 'my-project' });
|
|
580
|
+
console.log(`Bandwidth: ${usage.bandwidthFormatted}`);
|
|
495
581
|
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
const stack0 = new Stack0({ apiKey: 'stack0_...' });
|
|
499
|
-
await stack0.mail.send({ ... });
|
|
582
|
+
const history = await stack0.cdn.getUsageHistory({ projectSlug: 'my-project', days: 30 });
|
|
583
|
+
const breakdown = await stack0.cdn.getStorageBreakdown({ projectSlug: 'my-project', groupBy: 'type' });
|
|
500
584
|
```
|
|
501
585
|
|
|
502
|
-
|
|
586
|
+
---
|
|
503
587
|
|
|
504
|
-
|
|
588
|
+
## Screenshots
|
|
505
589
|
|
|
506
|
-
|
|
590
|
+
Capture high-quality screenshots of any webpage with options for format, viewport, device emulation, and more.
|
|
591
|
+
|
|
592
|
+
### Basic Capture
|
|
507
593
|
|
|
508
594
|
```typescript
|
|
509
|
-
// Capture
|
|
595
|
+
// Capture and wait for the result (recommended for most use cases)
|
|
510
596
|
const screenshot = await stack0.screenshots.captureAndWait({
|
|
511
597
|
url: 'https://example.com',
|
|
512
598
|
format: 'png',
|
|
513
599
|
fullPage: true,
|
|
514
600
|
blockAds: true,
|
|
601
|
+
blockCookieBanners: true,
|
|
515
602
|
});
|
|
516
603
|
|
|
517
604
|
console.log(screenshot.imageUrl);
|
|
518
605
|
console.log(screenshot.imageWidth, screenshot.imageHeight);
|
|
519
606
|
```
|
|
520
607
|
|
|
521
|
-
###
|
|
608
|
+
### Capture Options
|
|
522
609
|
|
|
523
610
|
```typescript
|
|
524
611
|
const screenshot = await stack0.screenshots.captureAndWait({
|
|
525
612
|
url: 'https://example.com',
|
|
526
|
-
format: 'png',
|
|
527
|
-
quality: 90,
|
|
528
|
-
fullPage: true,
|
|
529
|
-
deviceType: 'desktop',
|
|
613
|
+
format: 'png', // 'png' | 'jpeg' | 'webp' | 'pdf'
|
|
614
|
+
quality: 90, // JPEG/WebP quality (1-100)
|
|
615
|
+
fullPage: true, // Capture entire scrollable page
|
|
616
|
+
deviceType: 'desktop', // 'desktop' | 'tablet' | 'mobile'
|
|
530
617
|
viewportWidth: 1280,
|
|
531
618
|
viewportHeight: 720,
|
|
532
|
-
// Block unwanted elements
|
|
533
619
|
blockAds: true,
|
|
534
620
|
blockCookieBanners: true,
|
|
535
621
|
blockChatWidgets: true,
|
|
536
|
-
// Wait for content
|
|
537
622
|
waitForSelector: '.main-content',
|
|
538
|
-
waitForTimeout: 2000,
|
|
539
|
-
|
|
623
|
+
waitForTimeout: 2000, // Wait ms after page load
|
|
624
|
+
darkMode: true,
|
|
625
|
+
hideSelectors: ['.popup', '.banner'],
|
|
626
|
+
customCss: 'body { background: white; }',
|
|
627
|
+
customJs: 'document.querySelector(".modal")?.remove()',
|
|
540
628
|
headers: { 'Authorization': 'Bearer token' },
|
|
541
629
|
cookies: [{ name: 'session', value: 'abc123' }],
|
|
630
|
+
clip: { x: 0, y: 0, width: 800, height: 600 },
|
|
631
|
+
cacheKey: 'homepage-v2',
|
|
632
|
+
cacheTtl: 3600,
|
|
633
|
+
webhookUrl: 'https://your-app.com/webhook',
|
|
634
|
+
webhookSecret: 'secret',
|
|
542
635
|
});
|
|
543
636
|
```
|
|
544
637
|
|
|
638
|
+
### Async Capture (Manual Polling)
|
|
639
|
+
|
|
640
|
+
```typescript
|
|
641
|
+
const { id } = await stack0.screenshots.capture({
|
|
642
|
+
url: 'https://example.com',
|
|
643
|
+
format: 'png',
|
|
644
|
+
});
|
|
645
|
+
|
|
646
|
+
// Poll for completion
|
|
647
|
+
const screenshot = await stack0.screenshots.get({ id });
|
|
648
|
+
if (screenshot.status === 'completed') {
|
|
649
|
+
console.log(screenshot.imageUrl);
|
|
650
|
+
}
|
|
651
|
+
```
|
|
652
|
+
|
|
545
653
|
### Batch Screenshots
|
|
546
654
|
|
|
547
655
|
```typescript
|
|
656
|
+
// Capture multiple URLs and wait for all to complete
|
|
548
657
|
const job = await stack0.screenshots.batchAndWait({
|
|
549
658
|
urls: ['https://example.com', 'https://example.org'],
|
|
550
659
|
config: { format: 'png', fullPage: true },
|
|
551
660
|
});
|
|
552
661
|
|
|
553
|
-
console.log(`
|
|
662
|
+
console.log(`Processed: ${job.processedUrls}/${job.totalUrls}`);
|
|
663
|
+
```
|
|
664
|
+
|
|
665
|
+
### Scheduled Screenshots
|
|
666
|
+
|
|
667
|
+
```typescript
|
|
668
|
+
const { id } = await stack0.screenshots.createSchedule({
|
|
669
|
+
name: 'Daily homepage capture',
|
|
670
|
+
url: 'https://example.com',
|
|
671
|
+
frequency: 'daily',
|
|
672
|
+
config: { format: 'png', fullPage: true },
|
|
673
|
+
});
|
|
674
|
+
|
|
675
|
+
await stack0.screenshots.toggleSchedule({ id }); // Toggle on/off
|
|
554
676
|
```
|
|
555
677
|
|
|
556
|
-
|
|
678
|
+
### Screenshots Method Reference
|
|
679
|
+
|
|
680
|
+
| Method | Description |
|
|
681
|
+
|--------|-------------|
|
|
682
|
+
| `capture(req)` | Start a screenshot capture |
|
|
683
|
+
| `get(req)` | Get screenshot by ID |
|
|
684
|
+
| `list(req?)` | List screenshots with filters |
|
|
685
|
+
| `delete(req)` | Delete a screenshot |
|
|
686
|
+
| `captureAndWait(req, opts?)` | Capture and poll until complete |
|
|
687
|
+
| `batch(req)` | Start a batch capture job |
|
|
688
|
+
| `getBatchJob(req)` | Get batch job status |
|
|
689
|
+
| `listBatchJobs(req?)` | List batch jobs |
|
|
690
|
+
| `cancelBatchJob(req)` | Cancel a batch job |
|
|
691
|
+
| `batchAndWait(req, opts?)` | Batch capture and poll until complete |
|
|
692
|
+
| `createSchedule(req)` | Create a recurring capture schedule |
|
|
693
|
+
| `updateSchedule(req)` | Update a schedule |
|
|
694
|
+
| `getSchedule(req)` | Get a schedule |
|
|
695
|
+
| `listSchedules(req?)` | List schedules |
|
|
696
|
+
| `deleteSchedule(req)` | Delete a schedule |
|
|
697
|
+
| `toggleSchedule(req)` | Toggle a schedule on/off |
|
|
698
|
+
|
|
699
|
+
---
|
|
700
|
+
|
|
701
|
+
## Extraction
|
|
557
702
|
|
|
558
|
-
Extract structured data from any webpage using AI.
|
|
703
|
+
Extract structured data from any webpage using AI. Supports markdown, JSON schema, and raw HTML extraction modes.
|
|
559
704
|
|
|
560
|
-
###
|
|
705
|
+
### Markdown Extraction
|
|
561
706
|
|
|
562
707
|
```typescript
|
|
563
|
-
|
|
564
|
-
const extraction = await stack0.extraction.extractAndWait({
|
|
708
|
+
const result = await stack0.extraction.extractAndWait({
|
|
565
709
|
url: 'https://example.com/article',
|
|
566
710
|
mode: 'markdown',
|
|
567
711
|
includeMetadata: true,
|
|
568
712
|
});
|
|
569
713
|
|
|
570
|
-
console.log(
|
|
571
|
-
console.log(
|
|
714
|
+
console.log(result.markdown);
|
|
715
|
+
console.log(result.pageMetadata?.title);
|
|
716
|
+
console.log(result.pageMetadata?.description);
|
|
572
717
|
```
|
|
573
718
|
|
|
574
719
|
### Schema-Based Extraction
|
|
575
720
|
|
|
721
|
+
Extract structured data that matches a JSON schema:
|
|
722
|
+
|
|
576
723
|
```typescript
|
|
577
|
-
|
|
578
|
-
const extraction = await stack0.extraction.extractAndWait({
|
|
724
|
+
const result = await stack0.extraction.extractAndWait({
|
|
579
725
|
url: 'https://example.com/product',
|
|
580
726
|
mode: 'schema',
|
|
581
727
|
schema: {
|
|
@@ -584,28 +730,539 @@ const extraction = await stack0.extraction.extractAndWait({
|
|
|
584
730
|
name: { type: 'string' },
|
|
585
731
|
price: { type: 'number' },
|
|
586
732
|
description: { type: 'string' },
|
|
733
|
+
inStock: { type: 'boolean' },
|
|
587
734
|
},
|
|
588
735
|
required: ['name', 'price'],
|
|
589
736
|
},
|
|
590
737
|
});
|
|
591
738
|
|
|
592
|
-
console.log(
|
|
593
|
-
// { name: 'Product
|
|
739
|
+
console.log(result.extractedData);
|
|
740
|
+
// { name: 'Product X', price: 29.99, description: '...', inStock: true }
|
|
594
741
|
```
|
|
595
742
|
|
|
596
743
|
### Extraction Modes
|
|
597
744
|
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
745
|
+
| Mode | Description |
|
|
746
|
+
|------|-------------|
|
|
747
|
+
| `markdown` | AI-cleaned markdown content |
|
|
748
|
+
| `schema` | Structured data matching a provided JSON schema |
|
|
749
|
+
| `auto` | AI determines the best extraction strategy |
|
|
750
|
+
| `raw` | Raw HTML content |
|
|
751
|
+
|
|
752
|
+
### Batch Extraction
|
|
753
|
+
|
|
754
|
+
```typescript
|
|
755
|
+
const job = await stack0.extraction.batchAndWait({
|
|
756
|
+
urls: [
|
|
757
|
+
'https://example.com/article-1',
|
|
758
|
+
'https://example.com/article-2',
|
|
759
|
+
],
|
|
760
|
+
config: { mode: 'markdown' },
|
|
761
|
+
});
|
|
762
|
+
```
|
|
763
|
+
|
|
764
|
+
### Scheduled Extraction
|
|
765
|
+
|
|
766
|
+
```typescript
|
|
767
|
+
const { id } = await stack0.extraction.createSchedule({
|
|
768
|
+
name: 'Daily price monitor',
|
|
769
|
+
url: 'https://competitor.com/pricing',
|
|
770
|
+
frequency: 'daily',
|
|
771
|
+
config: { mode: 'schema', schema: { /* ... */ } },
|
|
772
|
+
});
|
|
773
|
+
```
|
|
774
|
+
|
|
775
|
+
### Usage Stats
|
|
776
|
+
|
|
777
|
+
```typescript
|
|
778
|
+
const usage = await stack0.extraction.getUsage({
|
|
779
|
+
periodStart: '2024-01-01T00:00:00Z',
|
|
780
|
+
periodEnd: '2024-01-31T23:59:59Z',
|
|
781
|
+
});
|
|
782
|
+
console.log(`Extractions: ${usage.extractionsTotal}`);
|
|
783
|
+
console.log(`Tokens used: ${usage.extractionTokensUsed}`);
|
|
784
|
+
|
|
785
|
+
const daily = await stack0.extraction.getUsageDaily({ /* ... */ });
|
|
786
|
+
```
|
|
787
|
+
|
|
788
|
+
### Extraction Method Reference
|
|
789
|
+
|
|
790
|
+
| Method | Description |
|
|
791
|
+
|--------|-------------|
|
|
792
|
+
| `extract(req)` | Start an extraction |
|
|
793
|
+
| `get(req)` | Get extraction by ID |
|
|
794
|
+
| `list(req?)` | List extractions |
|
|
795
|
+
| `delete(req)` | Delete an extraction |
|
|
796
|
+
| `extractAndWait(req, opts?)` | Extract and poll until complete |
|
|
797
|
+
| `batch(req)` | Start a batch extraction |
|
|
798
|
+
| `getBatchJob(req)` | Get batch job status |
|
|
799
|
+
| `listBatchJobs(req?)` | List batch jobs |
|
|
800
|
+
| `cancelBatchJob(req)` | Cancel a batch job |
|
|
801
|
+
| `batchAndWait(req, opts?)` | Batch extract and poll until complete |
|
|
802
|
+
| `createSchedule(req)` | Create a recurring schedule |
|
|
803
|
+
| `updateSchedule(req)` | Update a schedule |
|
|
804
|
+
| `getSchedule(req)` | Get a schedule |
|
|
805
|
+
| `listSchedules(req?)` | List schedules |
|
|
806
|
+
| `deleteSchedule(req)` | Delete a schedule |
|
|
807
|
+
| `toggleSchedule(req)` | Toggle a schedule on/off |
|
|
808
|
+
| `getUsage(req?)` | Get usage statistics |
|
|
809
|
+
| `getUsageDaily(req?)` | Get daily usage breakdown |
|
|
810
|
+
|
|
811
|
+
---
|
|
812
|
+
|
|
813
|
+
## Integrations
|
|
814
|
+
|
|
815
|
+
Unified API for connecting to third-party services. Manage OAuth connections and interact with CRM, storage, communication, and productivity platforms through a single interface.
|
|
816
|
+
|
|
817
|
+
### Managing Connections
|
|
818
|
+
|
|
819
|
+
```typescript
|
|
820
|
+
// Initiate OAuth flow
|
|
821
|
+
const { authUrl, connectionId } = await stack0.integrations.initiateOAuth({
|
|
822
|
+
connectorSlug: 'hubspot',
|
|
823
|
+
redirectUrl: 'https://yourapp.com/oauth/callback',
|
|
824
|
+
name: 'My HubSpot',
|
|
825
|
+
});
|
|
826
|
+
// Redirect user to authUrl...
|
|
827
|
+
|
|
828
|
+
// Complete OAuth after callback
|
|
829
|
+
await stack0.integrations.completeOAuth({
|
|
830
|
+
code: 'auth_code_from_callback',
|
|
831
|
+
state: 'state_from_initiate',
|
|
832
|
+
redirectUrl: 'https://yourapp.com/oauth/callback',
|
|
833
|
+
});
|
|
834
|
+
|
|
835
|
+
// List and manage connections
|
|
836
|
+
const { connections } = await stack0.integrations.listConnections({
|
|
837
|
+
status: 'connected',
|
|
838
|
+
});
|
|
839
|
+
|
|
840
|
+
const stats = await stack0.integrations.getStats({ environment: 'production' });
|
|
841
|
+
console.log(`Active connections: ${stats.activeConnections}`);
|
|
842
|
+
```
|
|
843
|
+
|
|
844
|
+
### CRM
|
|
845
|
+
|
|
846
|
+
```typescript
|
|
847
|
+
// Contacts
|
|
848
|
+
const contacts = await stack0.integrations.crm.listContacts('conn_123');
|
|
849
|
+
const contact = await stack0.integrations.crm.createContact('conn_123', {
|
|
850
|
+
firstName: 'Jane',
|
|
851
|
+
lastName: 'Doe',
|
|
852
|
+
email: 'jane@example.com',
|
|
853
|
+
});
|
|
854
|
+
|
|
855
|
+
// Companies
|
|
856
|
+
const companies = await stack0.integrations.crm.listCompanies('conn_123');
|
|
857
|
+
|
|
858
|
+
// Deals
|
|
859
|
+
const deals = await stack0.integrations.crm.listDeals('conn_123');
|
|
860
|
+
const deal = await stack0.integrations.crm.createDeal('conn_123', {
|
|
861
|
+
name: 'Enterprise Contract',
|
|
862
|
+
amount: 50000,
|
|
863
|
+
stage: 'negotiation',
|
|
864
|
+
});
|
|
865
|
+
```
|
|
866
|
+
|
|
867
|
+
### Storage
|
|
868
|
+
|
|
869
|
+
```typescript
|
|
870
|
+
// Files
|
|
871
|
+
const files = await stack0.integrations.storage.listFiles('conn_456', 'folder-id');
|
|
872
|
+
await stack0.integrations.storage.uploadFile('conn_456', {
|
|
873
|
+
name: 'report.pdf',
|
|
874
|
+
mimeType: 'application/pdf',
|
|
875
|
+
data: fileBuffer,
|
|
876
|
+
folderId: 'folder-id',
|
|
877
|
+
});
|
|
878
|
+
const { data, filename } = await stack0.integrations.storage.downloadFile('conn_456', 'file-id');
|
|
879
|
+
|
|
880
|
+
// Folders
|
|
881
|
+
const folders = await stack0.integrations.storage.listFolders('conn_456');
|
|
882
|
+
await stack0.integrations.storage.createFolder('conn_456', { name: 'Reports', parentId: 'root' });
|
|
883
|
+
```
|
|
884
|
+
|
|
885
|
+
### Communication
|
|
886
|
+
|
|
887
|
+
```typescript
|
|
888
|
+
// Send a message via Slack, Teams, etc.
|
|
889
|
+
await stack0.integrations.communication.sendMessage('conn_789', {
|
|
890
|
+
channelId: 'C123',
|
|
891
|
+
content: 'Hello from Stack0!',
|
|
892
|
+
});
|
|
893
|
+
|
|
894
|
+
const channels = await stack0.integrations.communication.listChannels('conn_789');
|
|
895
|
+
const messages = await stack0.integrations.communication.listMessages('conn_789', 'C123');
|
|
896
|
+
const users = await stack0.integrations.communication.listUsers('conn_789');
|
|
897
|
+
```
|
|
898
|
+
|
|
899
|
+
### Productivity
|
|
900
|
+
|
|
901
|
+
```typescript
|
|
902
|
+
// Documents (Notion, Google Docs, etc.)
|
|
903
|
+
const docs = await stack0.integrations.productivity.listDocuments('conn_abc');
|
|
904
|
+
await stack0.integrations.productivity.createDocument('conn_abc', {
|
|
905
|
+
title: 'Meeting Notes',
|
|
906
|
+
content: 'Agenda: ...',
|
|
907
|
+
});
|
|
908
|
+
|
|
909
|
+
// Tables (Airtable, Google Sheets, etc.)
|
|
910
|
+
const tables = await stack0.integrations.productivity.listTables('conn_abc');
|
|
911
|
+
const rows = await stack0.integrations.productivity.listTableRows('conn_abc', 'table-id');
|
|
912
|
+
await stack0.integrations.productivity.createTableRow('conn_abc', 'table-id', {
|
|
913
|
+
fields: { Name: 'Alice', Email: 'alice@example.com' },
|
|
914
|
+
});
|
|
915
|
+
```
|
|
916
|
+
|
|
917
|
+
---
|
|
918
|
+
|
|
919
|
+
## Marketing
|
|
920
|
+
|
|
921
|
+
AI-powered trend discovery, content opportunity generation, script creation, and content calendar management.
|
|
922
|
+
|
|
923
|
+
### Discovering Trends
|
|
924
|
+
|
|
925
|
+
```typescript
|
|
926
|
+
const { trendsDiscovered, trends } = await stack0.marketing.discoverTrends({
|
|
927
|
+
projectSlug: 'my-project',
|
|
928
|
+
environment: 'production',
|
|
929
|
+
});
|
|
930
|
+
console.log(`Discovered ${trendsDiscovered} new trends`);
|
|
931
|
+
|
|
932
|
+
const allTrends = await stack0.marketing.listTrends({
|
|
933
|
+
projectSlug: 'my-project',
|
|
934
|
+
environment: 'production',
|
|
935
|
+
status: 'active',
|
|
936
|
+
});
|
|
937
|
+
```
|
|
938
|
+
|
|
939
|
+
### Generating Content Opportunities
|
|
940
|
+
|
|
941
|
+
```typescript
|
|
942
|
+
const { opportunitiesGenerated } = await stack0.marketing.generateOpportunities({
|
|
943
|
+
projectSlug: 'my-project',
|
|
944
|
+
environment: 'production',
|
|
945
|
+
});
|
|
946
|
+
|
|
947
|
+
const opportunities = await stack0.marketing.listOpportunities({
|
|
948
|
+
projectSlug: 'my-project',
|
|
949
|
+
environment: 'production',
|
|
950
|
+
status: 'pending',
|
|
951
|
+
});
|
|
952
|
+
```
|
|
953
|
+
|
|
954
|
+
### Content and Scripts
|
|
955
|
+
|
|
956
|
+
```typescript
|
|
957
|
+
// Create content from an opportunity
|
|
958
|
+
const content = await stack0.marketing.createContent({
|
|
959
|
+
projectSlug: 'my-project',
|
|
960
|
+
environment: 'production',
|
|
961
|
+
contentType: 'tiktok_slideshow',
|
|
962
|
+
title: 'How AI is Changing Marketing',
|
|
963
|
+
opportunityId: 'opp-id',
|
|
964
|
+
});
|
|
965
|
+
|
|
966
|
+
// Review workflow
|
|
967
|
+
await stack0.marketing.approveContent({ contentId: content.id, reviewNotes: 'Looks great!' });
|
|
968
|
+
// or
|
|
969
|
+
await stack0.marketing.rejectContent({ contentId: content.id, reviewNotes: 'Needs revisions' });
|
|
970
|
+
|
|
971
|
+
// Create and version scripts
|
|
972
|
+
const script = await stack0.marketing.createScript({
|
|
973
|
+
projectSlug: 'my-project',
|
|
974
|
+
environment: 'production',
|
|
975
|
+
hook: 'Are you ready to see the future?',
|
|
976
|
+
slides: [{ order: 0, text: 'AI is changing everything', voiceoverText: '...', duration: 3 }],
|
|
977
|
+
cta: 'Follow for more!',
|
|
978
|
+
});
|
|
979
|
+
```
|
|
980
|
+
|
|
981
|
+
### Content Calendar
|
|
982
|
+
|
|
983
|
+
```typescript
|
|
984
|
+
const entry = await stack0.marketing.scheduleContent({
|
|
985
|
+
projectSlug: 'my-project',
|
|
986
|
+
contentId: 'content-id',
|
|
987
|
+
scheduledFor: new Date('2024-12-25T10:00:00Z'),
|
|
988
|
+
autoPublish: true,
|
|
989
|
+
});
|
|
990
|
+
|
|
991
|
+
const entries = await stack0.marketing.listCalendarEntries({
|
|
992
|
+
projectSlug: 'my-project',
|
|
993
|
+
startDate: new Date('2024-12-01'),
|
|
994
|
+
endDate: new Date('2024-12-31'),
|
|
995
|
+
});
|
|
996
|
+
```
|
|
997
|
+
|
|
998
|
+
### Analytics and Usage
|
|
999
|
+
|
|
1000
|
+
```typescript
|
|
1001
|
+
const overview = await stack0.marketing.getAnalyticsOverview({
|
|
1002
|
+
projectSlug: 'my-project',
|
|
1003
|
+
environment: 'production',
|
|
1004
|
+
});
|
|
1005
|
+
|
|
1006
|
+
const performance = await stack0.marketing.getContentPerformance({
|
|
1007
|
+
projectSlug: 'my-project',
|
|
1008
|
+
environment: 'production',
|
|
1009
|
+
contentType: 'tiktok_slideshow',
|
|
1010
|
+
});
|
|
1011
|
+
|
|
1012
|
+
const usage = await stack0.marketing.getCurrentUsage({
|
|
1013
|
+
projectSlug: 'my-project',
|
|
1014
|
+
environment: 'production',
|
|
1015
|
+
});
|
|
1016
|
+
```
|
|
1017
|
+
|
|
1018
|
+
---
|
|
1019
|
+
|
|
1020
|
+
## Workflows
|
|
1021
|
+
|
|
1022
|
+
Create and execute AI-powered workflows with step-by-step orchestration.
|
|
1023
|
+
|
|
1024
|
+
### Creating Workflows
|
|
1025
|
+
|
|
1026
|
+
```typescript
|
|
1027
|
+
const workflow = await stack0.workflows.create({
|
|
1028
|
+
slug: 'content-pipeline',
|
|
1029
|
+
name: 'Content Generation Pipeline',
|
|
1030
|
+
steps: [
|
|
1031
|
+
{
|
|
1032
|
+
id: 'generate',
|
|
1033
|
+
name: 'Generate Content',
|
|
1034
|
+
type: 'llm',
|
|
1035
|
+
provider: 'anthropic',
|
|
1036
|
+
model: 'claude-sonnet-4-20250514',
|
|
1037
|
+
config: {
|
|
1038
|
+
prompt: 'Write a blog post about {{topic}}',
|
|
1039
|
+
maxTokens: 2000,
|
|
1040
|
+
},
|
|
1041
|
+
},
|
|
1042
|
+
],
|
|
1043
|
+
variables: [
|
|
1044
|
+
{ name: 'topic', type: 'string', required: true },
|
|
1045
|
+
],
|
|
1046
|
+
});
|
|
1047
|
+
```
|
|
1048
|
+
|
|
1049
|
+
### Running Workflows
|
|
1050
|
+
|
|
1051
|
+
```typescript
|
|
1052
|
+
// Run and wait for the result (simplest approach)
|
|
1053
|
+
const run = await stack0.workflows.runAndWait({
|
|
1054
|
+
workflowSlug: 'content-pipeline',
|
|
1055
|
+
variables: { topic: 'artificial intelligence' },
|
|
1056
|
+
}, { timeout: 120000 }); // 2 minute timeout
|
|
1057
|
+
|
|
1058
|
+
console.log(run.output);
|
|
1059
|
+
|
|
1060
|
+
// Async run with webhook notification
|
|
1061
|
+
const { id } = await stack0.workflows.run({
|
|
1062
|
+
workflowSlug: 'content-pipeline',
|
|
1063
|
+
variables: { topic: 'machine learning' },
|
|
1064
|
+
webhook: {
|
|
1065
|
+
url: 'https://your-app.com/webhook',
|
|
1066
|
+
secret: 'webhook-secret',
|
|
1067
|
+
},
|
|
1068
|
+
});
|
|
1069
|
+
|
|
1070
|
+
// Check run status
|
|
1071
|
+
const status = await stack0.workflows.getRun({ id });
|
|
1072
|
+
console.log(status.status); // 'pending' | 'running' | 'completed' | 'failed' | 'cancelled'
|
|
1073
|
+
```
|
|
1074
|
+
|
|
1075
|
+
### Managing Workflows
|
|
1076
|
+
|
|
1077
|
+
```typescript
|
|
1078
|
+
const { items } = await stack0.workflows.list({ environment: 'production', isActive: true });
|
|
1079
|
+
await stack0.workflows.update({ id: 'workflow-id', name: 'Updated Pipeline', isActive: false });
|
|
1080
|
+
await stack0.workflows.delete({ id: 'workflow-id' });
|
|
1081
|
+
await stack0.workflows.cancelRun({ id: 'run-id' });
|
|
1082
|
+
```
|
|
1083
|
+
|
|
1084
|
+
### Workflows Method Reference
|
|
1085
|
+
|
|
1086
|
+
| Method | Description |
|
|
1087
|
+
|--------|-------------|
|
|
1088
|
+
| `create(req)` | Create a new workflow |
|
|
1089
|
+
| `get(req)` | Get workflow by ID or slug |
|
|
1090
|
+
| `list(req?)` | List workflows |
|
|
1091
|
+
| `update(req)` | Update a workflow |
|
|
1092
|
+
| `delete(req)` | Delete a workflow |
|
|
1093
|
+
| `run(req)` | Start a workflow run |
|
|
1094
|
+
| `getRun(req)` | Get run status and output |
|
|
1095
|
+
| `listRuns(req?)` | List workflow runs |
|
|
1096
|
+
| `cancelRun(req)` | Cancel a running workflow |
|
|
1097
|
+
| `runAndWait(req, opts?)` | Run and poll until complete |
|
|
1098
|
+
|
|
1099
|
+
---
|
|
1100
|
+
|
|
1101
|
+
## Error Handling
|
|
1102
|
+
|
|
1103
|
+
All SDK methods throw errors with additional properties for API errors:
|
|
1104
|
+
|
|
1105
|
+
```typescript
|
|
1106
|
+
import { Stack0 } from '@stack0/sdk';
|
|
1107
|
+
import type { Stack0Error } from '@stack0/sdk';
|
|
1108
|
+
|
|
1109
|
+
try {
|
|
1110
|
+
await stack0.mail.send({
|
|
1111
|
+
from: 'hello@yourapp.com',
|
|
1112
|
+
to: 'user@example.com',
|
|
1113
|
+
subject: 'Test',
|
|
1114
|
+
html: '<p>Hello</p>',
|
|
1115
|
+
});
|
|
1116
|
+
} catch (err) {
|
|
1117
|
+
const error = err as Stack0Error;
|
|
1118
|
+
|
|
1119
|
+
if (error.statusCode === 401) {
|
|
1120
|
+
console.error('Invalid API key');
|
|
1121
|
+
} else if (error.statusCode === 403) {
|
|
1122
|
+
console.error('Insufficient permissions');
|
|
1123
|
+
} else if (error.statusCode === 400) {
|
|
1124
|
+
console.error('Validation error:', error.message);
|
|
1125
|
+
} else if (error.statusCode === 429) {
|
|
1126
|
+
console.error('Rate limit exceeded');
|
|
1127
|
+
} else {
|
|
1128
|
+
console.error('Unexpected error:', error.message);
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
// Full error response from the API
|
|
1132
|
+
console.error(error.response);
|
|
1133
|
+
console.error(error.code);
|
|
1134
|
+
}
|
|
1135
|
+
```
|
|
1136
|
+
|
|
1137
|
+
The `Stack0Error` interface extends `Error` with:
|
|
1138
|
+
|
|
1139
|
+
| Property | Type | Description |
|
|
1140
|
+
|----------|------|-------------|
|
|
1141
|
+
| `statusCode` | `number` | HTTP status code |
|
|
1142
|
+
| `code` | `string` | Machine-readable error code |
|
|
1143
|
+
| `response` | `unknown` | Full error response body from the API |
|
|
1144
|
+
|
|
1145
|
+
---
|
|
1146
|
+
|
|
1147
|
+
## TypeScript Types
|
|
1148
|
+
|
|
1149
|
+
The SDK exports all request/response types for each module. Import them directly:
|
|
1150
|
+
|
|
1151
|
+
```typescript
|
|
1152
|
+
import type {
|
|
1153
|
+
// Shared
|
|
1154
|
+
Environment,
|
|
1155
|
+
Stack0Error,
|
|
1156
|
+
BatchJobStatus,
|
|
1157
|
+
ScheduleFrequency,
|
|
1158
|
+
PaginatedRequest,
|
|
1159
|
+
PaginatedResponse,
|
|
1160
|
+
|
|
1161
|
+
// Mail
|
|
1162
|
+
SendEmailRequest,
|
|
1163
|
+
SendEmailResponse,
|
|
1164
|
+
EmailStatus,
|
|
1165
|
+
ListEmailsRequest,
|
|
1166
|
+
Template,
|
|
1167
|
+
Campaign,
|
|
1168
|
+
Sequence,
|
|
1169
|
+
MailContact,
|
|
1170
|
+
|
|
1171
|
+
// CDN
|
|
1172
|
+
Asset,
|
|
1173
|
+
TransformOptions,
|
|
1174
|
+
UploadUrlRequest,
|
|
1175
|
+
ListAssetsRequest,
|
|
1176
|
+
TranscodeVideoRequest,
|
|
1177
|
+
PrivateFile,
|
|
1178
|
+
|
|
1179
|
+
// Screenshots
|
|
1180
|
+
Screenshot,
|
|
1181
|
+
CreateScreenshotRequest,
|
|
1182
|
+
|
|
1183
|
+
// Extraction
|
|
1184
|
+
ExtractionResult,
|
|
1185
|
+
CreateExtractionRequest,
|
|
1186
|
+
|
|
1187
|
+
// Integrations
|
|
1188
|
+
Connection,
|
|
1189
|
+
Contact,
|
|
1190
|
+
Company,
|
|
1191
|
+
Deal,
|
|
1192
|
+
|
|
1193
|
+
// Marketing
|
|
1194
|
+
Trend,
|
|
1195
|
+
Opportunity,
|
|
1196
|
+
Content,
|
|
1197
|
+
Script,
|
|
1198
|
+
CalendarEntry,
|
|
1199
|
+
|
|
1200
|
+
// Workflows
|
|
1201
|
+
Workflow,
|
|
1202
|
+
WorkflowRun,
|
|
1203
|
+
CreateWorkflowRequest,
|
|
1204
|
+
RunWorkflowRequest,
|
|
1205
|
+
} from '@stack0/sdk';
|
|
1206
|
+
```
|
|
1207
|
+
|
|
1208
|
+
You can also import types from individual module entry points:
|
|
1209
|
+
|
|
1210
|
+
```typescript
|
|
1211
|
+
import type { SendEmailRequest } from '@stack0/sdk/mail';
|
|
1212
|
+
import type { Asset, TransformOptions } from '@stack0/sdk/cdn';
|
|
1213
|
+
import type { Screenshot } from '@stack0/sdk/screenshots';
|
|
1214
|
+
import type { ExtractionResult } from '@stack0/sdk/extraction';
|
|
1215
|
+
```
|
|
1216
|
+
|
|
1217
|
+
---
|
|
1218
|
+
|
|
1219
|
+
## Polling Helpers
|
|
1220
|
+
|
|
1221
|
+
Several methods include built-in polling for async operations:
|
|
1222
|
+
|
|
1223
|
+
| Method | Default Poll Interval | Default Timeout |
|
|
1224
|
+
|--------|----------------------|-----------------|
|
|
1225
|
+
| `screenshots.captureAndWait()` | 1s | 60s |
|
|
1226
|
+
| `screenshots.batchAndWait()` | 2s | 5min |
|
|
1227
|
+
| `extraction.extractAndWait()` | 1s | 60s |
|
|
1228
|
+
| `extraction.batchAndWait()` | 2s | 5min |
|
|
1229
|
+
| `workflows.runAndWait()` | 2s | 10min |
|
|
1230
|
+
|
|
1231
|
+
Override defaults via the options parameter:
|
|
1232
|
+
|
|
1233
|
+
```typescript
|
|
1234
|
+
const result = await stack0.screenshots.captureAndWait(
|
|
1235
|
+
{ url: 'https://example.com', format: 'png' },
|
|
1236
|
+
{ pollInterval: 500, timeout: 30000 }, // 500ms interval, 30s timeout
|
|
1237
|
+
);
|
|
1238
|
+
```
|
|
1239
|
+
|
|
1240
|
+
---
|
|
1241
|
+
|
|
1242
|
+
## Migration from Resend
|
|
1243
|
+
|
|
1244
|
+
The Stack0 Mail API is designed for compatibility with the Resend API:
|
|
1245
|
+
|
|
1246
|
+
```typescript
|
|
1247
|
+
// Before (Resend)
|
|
1248
|
+
import { Resend } from 'resend';
|
|
1249
|
+
const resend = new Resend('re_...');
|
|
1250
|
+
await resend.emails.send({ from: '...', to: '...', subject: '...', html: '...' });
|
|
1251
|
+
|
|
1252
|
+
// After (Stack0)
|
|
1253
|
+
import { Stack0 } from '@stack0/sdk';
|
|
1254
|
+
const stack0 = new Stack0({ apiKey: 'stack0_...' });
|
|
1255
|
+
await stack0.mail.send({ from: '...', to: '...', subject: '...', html: '...' });
|
|
1256
|
+
```
|
|
1257
|
+
|
|
1258
|
+
---
|
|
602
1259
|
|
|
603
|
-
##
|
|
1260
|
+
## Links
|
|
604
1261
|
|
|
605
|
-
- Documentation
|
|
606
|
-
-
|
|
607
|
-
-
|
|
1262
|
+
- [Documentation](https://stack0.dev/docs)
|
|
1263
|
+
- [GitHub](https://github.com/stack0dev/sdk)
|
|
1264
|
+
- [Changelog](https://github.com/stack0dev/sdk/releases)
|
|
608
1265
|
|
|
609
1266
|
## License
|
|
610
1267
|
|
|
611
|
-
MIT
|
|
1268
|
+
[MIT](https://opensource.org/licenses/MIT)
|