Node.js SDK
@seenn/node — Full TypeScript support with fluent API
#Installation
npm install @seenn/node
Or with other package managers:
# yarn
yarn add @seenn/node
# pnpm
pnpm add @seenn/node
#SeennClient
The main client class for interacting with Seenn.
Creates a new Seenn client instance.
sk_live_xxx or sk_test_xxx)required
https://api.seenn.io)
30000)
3)
false)
import { SeennClient } from '@seenn/node';
const seenn = new SeennClient({
apiKey: process.env.SEENN_SECRET_KEY,
debug: process.env.NODE_ENV === 'development',
});
#seenn.jobs.start()
Start a new job. Returns a Job instance for fluent updates.
'video-generation')required
const job = await seenn.jobs.start({
userId: 'user_123',
jobType: 'video-generation',
title: 'Creating your video...',
metadata: {
prompt: 'A cat playing piano',
quality: 'high',
},
stage: {
name: 'initializing',
current: 1,
total: 3,
},
});
console.log(job.id); // 'job_abc123'
#seenn.jobs.get()
Get a job by ID. Useful for resuming updates in a different process.
// In a worker process
const job = await seenn.jobs.get('job_abc123');
console.log(job.status); // 'running'
console.log(job.progress); // 25
#seenn.jobs.list()
List jobs for a user with pagination.
20, max: 100)
const { jobs, nextCursor } = await seenn.jobs.list('user_123', {
limit: 10,
});
for (const job of jobs) {
console.log(job.title, job.status);
}
// Fetch next page
if (nextCursor) {
const nextPage = await seenn.jobs.list('user_123', { cursor: nextCursor });
}
#Parent-Child Jobs
For batch processing workflows, you can create parent jobs that contain multiple child jobs. The parent's progress is automatically calculated from its children.
#seenn.jobs.createParent()
Create a parent job that will contain child jobs.
'average')
const parent = await seenn.jobs.createParent({
userId: 'user_123',
jobType: 'batch-processing',
title: 'Christmas Image Pack (5 images)',
childCount: 5,
childProgressMode: 'average',
});
console.log(parent.isParent); // true
console.log(parent.children); // { total: 5, completed: 0, ... }
#seenn.jobs.createChild()
Create a child job under a parent.
const child = await seenn.jobs.createChild({
parentJobId: parent.id,
childIndex: 0,
userId: 'user_123',
jobType: 'image-generation',
title: 'Snowflake Image',
});
console.log(child.isChild); // true
console.log(child.parent); // { parentJobId: '...', childIndex: 0 }
// Update child progress - parent auto-updates!
await child.setProgress(50);
await child.complete();
#seenn.jobs.createBatch()
Create a parent job and all children in one call. Best for batch processing workflows.
'average')
Complete Example: Glow Image Pack
This example shows a complete batch processing workflow for generating a 5-image Christmas pack:
// 1. Create batch job (parent + 5 children)
const { parent, children } = await seenn.jobs.createBatch({
userId: 'user_123',
jobType: 'image-pack',
parentTitle: 'Christmas Pack (5 images)',
childTitles: [
'Snowflake',
'Christmas Tree',
'Santa Claus',
'Reindeer',
'Gift Box',
],
childProgressMode: 'average',
metadata: { packId: 'christmas-2026', style: 'watercolor' },
});
console.log('Parent ID:', parent.id); // '01HXY...'
console.log('Children:', children.length); // 5
console.log('Parent status:', parent.status); // 'pending'
// 2. Process children in parallel with your AI model
await Promise.all(children.map(async (child, index) => {
try {
// Update progress as AI processes
await child.setProgress(10, { message: 'Starting generation...' });
// Call your AI model
const imageUrl = await generateImage(child.title, {
onProgress: (pct) => child.setProgress(pct),
});
// Complete with result URL
await child.complete({
result: { type: 'image', url: imageUrl },
message: 'Image ready!',
});
} catch (error) {
// Fail individual child - parent continues!
await child.fail({
error: { code: 'GENERATION_FAILED', message: error.message },
retryable: true,
});
}
}));
// 3. Check final results
await parent.refresh();
console.log('Parent status:', parent.status); // 'completed' or 'failed'
console.log('Completed:', parent.children.completed); // 4
console.log('Failed:', parent.children.failed); // 1
#seenn.jobs.getWithChildren()
Get a parent job with all its children.
const { parent, children } = await seenn.jobs.getWithChildren(parentId);
console.log(parent.progress); // 60 (average of children)
console.log(parent.childProgress); // { completed: 3, running: 1, pending: 1, ... }
for (const child of children) {
console.log(`Child ${child.childIndex}: ${child.status} (${child.progress}%)`);
}
#childProgressMode
The childProgressMode option determines how the parent job's progress is calculated from its children:
| Mode | Calculation | Best For |
|---|---|---|
'average' |
Sum of all child progress / total children | Equal-weight tasks (e.g., batch image generation) |
'sequential' |
(Completed children / total) × 100 | Pipeline stages where partial progress doesn't matter |
'weighted' |
Same as average (custom weights coming soon) | Variable-size tasks (future feature) |
// Example: 5 children, 3 at 100%, 1 at 50%, 1 at 0%
// Total progress: 100 + 100 + 100 + 50 + 0 = 350
// 'average' mode: 350 / 5 = 70%
// 'sequential' mode: 3 completed / 5 total = 60%
#Failed Child Behavior
Seenn allows partial success - not every child needs to complete for the parent to succeed.
Parent Status Rules
| Scenario | Parent Status | Example |
|---|---|---|
| All children completed | completed |
5/5 completed, 0 failed |
| Some children failed, some completed | completed |
4/5 completed, 1 failed (partial success) |
| ALL children failed | failed |
0/5 completed, 5 failed |
| Still processing | running |
2 completed, 1 running, 2 pending |
completed === 0 AND all children are in terminal state. If even ONE child completes, the parent completes (with partial results).
Progress Calculation with Failed Children
- Failed children count toward progress - Their last progress value is used
- Example: 3 at 100%, 1 at 50% (failed at 50%), 1 at 0% (failed immediately) = (100+100+100+50+0)/5 = 70%
- Use
parent.children.failedto check how many failed
Handling Partial Failures
// After batch processing completes
const { parent, children } = await seenn.jobs.getWithChildren(parentId);
const { completed, failed, total } = parent.children;
if (parent.status === 'completed' && failed > 0) {
// Partial success - some children failed
console.log(`Pack completed with ${completed}/${total} images`);
// Get successful results
const successfulImages = children
.filter(c => c.status === 'completed')
.map(c => c.result?.url);
// Get failed children for retry
const failedChildren = children.filter(c => c.status === 'failed');
for (const child of failedChildren) {
console.log(`"${child.title}" failed: ${child.error?.message}`);
// Optionally offer retry to user
}
} else if (parent.status === 'failed') {
// Total failure - ALL children failed
console.log('Pack generation completely failed');
}
#job.setProgress()
Update job progress. Automatically sets status to running if pending.
{ name, current, total, description? }
{ position, total?, queueName? }
// Simple progress update
await job.setProgress(50);
// With message
await job.setProgress(50, { message: 'Processing frames...' });
// With stage info
await job.setProgress(66, {
message: 'Rendering video...',
stage: {
name: 'rendering',
current: 2,
total: 3,
description: 'Combining frames into video',
},
});
// Fluent chaining
await job
.setProgress(25, { message: 'Step 1...' })
.then(j => j.setProgress(50, { message: 'Step 2...' }))
.then(j => j.setProgress(75, { message: 'Step 3...' }));
#job.complete()
Mark job as completed with optional result data.
{ type?, url?, data? } (max 100KB)
// Simple completion
await job.complete();
// With result URL
await job.complete({
result: {
type: 'video',
url: 'https://cdn.example.com/output.mp4',
},
});
// With custom data
await job.complete({
result: {
type: 'analysis',
data: {
sentiment: 'positive',
confidence: 0.95,
keywords: ['happy', 'success'],
},
},
message: 'Analysis complete!',
});
#job.fail()
Mark job as failed with error details.
{ code, message, details? }required
false)
try {
// Processing...
} catch (err) {
await job.fail({
error: {
code: 'PROCESSING_ERROR',
message: 'Failed to generate video',
details: { reason: err.message },
},
retryable: true,
});
}
#Self-Hosted Backend
Using your own backend instead of Seenn Cloud? Configure the SDK to point to your API:
const seenn = new SeennClient({
apiKey: process.env.SEENN_SECRET_KEY,
// Point to your own backend
baseUrl: 'https://api.yourcompany.com',
});
#Backend Requirements
Your self-hosted backend must implement these endpoints:
| Endpoint | Description |
|---|---|
POST /v1/jobs |
Create a job (supports parent-child params) |
GET /v1/jobs/:id |
Get job by ID |
GET /v1/jobs/:parentId/children |
Get parent with all children |
POST /v1/jobs/:id/progress |
Update job progress |
POST /v1/jobs/:id/complete |
Mark job as completed |
POST /v1/jobs/:id/fail |
Mark job as failed |
#Error Handling
The SDK throws typed errors for different scenarios:
import {
SeennError,
ValidationError,
NotFoundError,
RateLimitError,
} from '@seenn/node';
try {
await seenn.jobs.get('nonexistent');
} catch (err) {
if (err instanceof NotFoundError) {
console.log('Job not found');
} else if (err instanceof RateLimitError) {
console.log('Rate limited, retry after:', err.retryAfter);
} else if (err instanceof ValidationError) {
console.log('Invalid input:', err.message);
}
}