Tela SDK for JavaScript
The Tela SDK for JavaScript provides a simple and powerful way to interact with the Tela API. This SDK allows you to create chat completions, handle file uploads, and manage various resources with ease.
Table of Contents
Installation
You can install the Tela SDK via npm, yarn, or pnpm:
npm install @meistrari/tela-sdk-js
yarn add @meistrari/tela-sdk-js
pnpm add @meistrari/tela-sdk-js
Usage
First, you need to import the SDK and initialize it with your API key:
import { TelaSDK } from '@meistrari/tela-sdk-js'
const tela = new TelaSDK({ apiKey: 'your-api-key' })
Examples
Canvas API
The Canvas API provides a type-safe way to execute prompts with schema validation and multiple execution modes.
Synchronous Execution
Execute a canvas and wait for the complete result:
import { TelaSDK } from '@meistrari/tela-sdk-js'
const tela = new TelaSDK({
apiKey: 'your-api-key'
})
const canvas = await tela.canvas.get({
id: 'canvas-id',
input: schema => schema.object({
query: schema.string()
}),
output: schema => schema.object({
result: schema.string()
}),
})
const result = await canvas.execute({ query: 'What is the capital of France?' }).result
console.log(result)
const execution = await canvas.execute({ query: 'What is the capital of France?' })
const result2 = await execution.result
Asynchronous Execution with Polling
For long-running operations, use async mode with automatic polling:
const canvas = await tela.canvas.get({
id: 'canvas-id',
input: schema => schema.object({
document: schema.file(),
query: schema.string()
}),
output: schema => schema.object({
summary: schema.string()
}),
})
const result = await canvas.execute(
{
document: TelaFile.create(documentBlob, { name: 'report.pdf' }),
query: 'Summarize this document'
},
{
async: true,
pollingInterval: 1000,
pollingTimeout: 60000,
}
).result
console.log(result.summary)
Streaming Execution
Stream results as they're generated:
const canvas = await tela.canvas.get({
id: 'canvas-id',
input: schema => schema.object({
prompt: schema.string()
}),
output: schema => schema.object({
response: schema.string()
}),
})
for await (const chunk of canvas.execute(
{ prompt: 'Write a story about a robot' },
{ stream: true }
).result) {
process.stdout.write(chunk.response || '')
}
Event-Driven Execution
Monitor execution lifecycle with events. Events are only useful with async executions - synchronous and streaming executions complete too quickly for event-driven monitoring to be practical.
const canvas = await tela.canvas.get({
id: 'canvas-id',
input: schema => schema.object({
query: schema.string()
}),
output: schema => schema.object({
result: schema.string()
}),
})
const execution = await canvas.execute(
{ query: 'What is the capital of France?' },
{
async: true,
pollingInterval: 1000,
pollingTimeout: 60000,
}
)
console.log(execution.status)
execution.on('statusChange', (status) => {
console.log(`Status: ${status}`)
})
execution.on('poll', (pollResult) => {
console.log(`Poll - Status: ${pollResult.status}, Request ID: ${pollResult.requestId}`)
})
execution.on('success', (result) => {
console.log('Success!', result)
console.log('Request ID:', result.requestId)
})
execution.on('error', (error) => {
console.error('Failed:', error.message)
})
execution.poll()
Why Async Only?
Events are designed for tracking progress over time. Synchronous executions complete immediately (blocking until done), so by the time you can access the execution object, all events have already fired. Streaming executions provide their own iteration mechanism via async generators, making events redundant.
Status Property:
The status property provides the current execution state:
- Async:
created → running → succeeded or failed
Status is set to succeeded only after successful validation. If validation fails, status will be failed even if the API request succeeded.
Available Events (Async Executions Only):
success - Emitted when execution completes successfully (after validation)
error - Emitted on failure (if listeners exist, errors won't throw)
statusChange - Emitted on status transitions
poll - Emitted during polling (initial response + each poll)
Error Handling:
- With error listener: Errors emitted as events, promise resolves to
undefined
- Without error listener: Errors throw and reject the promise
Important: You must call execution.poll() or await execution.result to start polling - events won't fire until polling begins.
See Event Examples for detailed usage patterns.
Accessing Request IDs
Every API request to Tela returns a unique requestId (from the x-request-id header) that can be used for debugging, tracking, and correlating with server logs. The SDK exposes request IDs in three ways:
const canvas = await tela.canvas.get({ id: 'canvas-id' })
const execution = await canvas.execute(
{ query: 'test' },
{ async: true }
)
const requestId = await execution.requestId
console.log('Request ID:', requestId)
execution.on('poll', (pollResult) => {
console.log('Request ID from this poll:', pollResult.requestId)
})
execution.on('success', (result) => {
console.log('Request ID from final request:', result.requestId)
})
execution.poll()
Important Notes:
execution.requestId - Request ID from the execution creation request (POST /v2/chat/completions)
pollResult.requestId - Request ID from each individual polling request (GET /v2/chat/completions/:id)
result.requestId - Request ID from the final successful polling request
Use Cases:
- Debugging: Identify specific API requests when troubleshooting issues
- Logging: Track individual requests across distributed systems
- Support: Reference specific requests in support tickets
- Monitoring: Correlate client requests with server logs and traces
See Request ID Example for a complete demonstration.
Schema Validation
The Canvas API validates your inputs and outputs against the server schema:
const canvas = await tela.canvas.get({
id: 'canvas-id',
input: schema => schema.object({
text: schema.string(),
file: schema.file(),
settings: schema.object({
temperature: schema.number(),
maxTokens: schema.number(),
}),
}),
output: schema => schema.object({
analysis: schema.string(),
confidence: schema.number(),
}),
skipSchemaValidation: false,
})
const result = await canvas.execute({
text: 'Analyze this document',
file: TelaFile.create(blob, { name: 'data.pdf' }),
settings: {
temperature: 0.7,
maxTokens: 1000,
}
}).result
const resultWithoutValidation = await canvas.execute(
{
text: 'Analyze this document',
file: TelaFile.create(blob, { name: 'data.pdf' }),
settings: {
temperature: 0.7,
maxTokens: 1000,
}
},
{
skipResultValidation: true
}
).result
Multiple Files
Process multiple files in a single canvas execution:
const canvas = await tela.canvas.get({
id: 'canvas-id',
input: schema => schema.object({
documents: schema.array(schema.file()),
instructions: schema.string()
}),
output: schema => schema.object({
comparison: schema.string()
}),
})
const result = await canvas.execute({
documents: [
TelaFile.create('https://example.com/doc1.pdf', { range: [0, 5] }),
TelaFile.create('https://example.com/doc2.pdf', { range: [0, 5] }),
TelaFile.create(localFileBlob, { name: 'doc3.pdf' })
],
instructions: 'Compare these documents and identify key differences'
}).result
console.log(result.comparison)
Webhook Notifications
Receive completion notifications via webhook for async executions:
const canvas = await tela.canvas.get({
id: 'canvas-id',
input: schema => schema.object({
document: schema.file(),
query: schema.string()
}),
output: schema => schema.object({
analysis: schema.string()
}),
})
const result = await canvas.execute(
{
document: TelaFile.create('https://example.com/large-report.pdf'),
query: 'Analyze this report'
},
{
async: true,
webhookUrl: 'https://your-server.com/webhook',
pollingTimeout: 300000,
}
).result
Execution Tags
Add tags to your canvas executions for filtering, categorization, and analytics:
const canvas = await tela.canvas.get({
id: 'canvas-id',
input: schema => schema.object({
query: schema.string()
}),
output: schema => schema.object({
result: schema.string()
}),
})
const result = await canvas.execute(
{ query: 'Analyze customer feedback' },
{
tags: ['production', 'analytics', 'customer-feedback']
}
).result
const asyncResult = await canvas.execute(
{ query: 'Process large dataset' },
{
async: true,
tags: ['batch-processing', 'analytics']
}
).result
for await (const chunk of canvas.execute(
{ query: 'Generate report' },
{
stream: true,
tags: ['streaming', 'reports']
}
).result) {
process.stdout.write(chunk.result || '')
}
Use Cases:
- Environment tracking: Tag executions by environment (
production, staging, development)
- Feature categorization: Organize executions by feature (
analytics, reporting, summarization)
- User segmentation: Track executions by user tier or department
- Cost allocation: Attribute API usage to specific projects or teams
- Performance monitoring: Filter and analyze execution metrics by tag
Tags are sent to the Tela API and can be used for filtering and analytics in your Tela dashboard.
Fetching Existing Executions
Retrieve and monitor async executions by their ID, useful for job queues, resuming workflows, or multi-user dashboards:
const execution = await canvas.execute(
{ query: 'Process this data' },
{ async: true }
)
const executionId = execution.id
const fetchedExecution = await canvas.getExecution(executionId, {
pollingInterval: 2000,
pollingTimeout: 120000
})
console.log(fetchedExecution.status)
fetchedExecution.on('statusChange', (status) => {
console.log(`Status: ${status}`)
})
fetchedExecution.on('success', (result) => {
console.log('Completed!', result)
})
fetchedExecution.on('error', (error) => {
console.error('Failed:', error)
})
fetchedExecution.poll()
console.log('Polling in background, doing other work...')
const result = await fetchedExecution.result
Alternative: Use poll() on newly started executions
You can also use event-driven polling immediately when starting an execution:
const execution = await canvas.execute(
{ query: 'Process this data' },
{ async: true }
)
execution.on('statusChange', status => console.log('Status:', status))
execution.on('success', result => console.log('Done!', result))
execution.on('error', error => console.error('Failed:', error))
execution.poll()
console.log('Execution polling in background')
Key Features:
-
canvas.getExecution(id, options?) - Fetch execution by UUID
- Only async executions have UUIDs and can be fetched
- Returns execution in current state (running, succeeded, or failed)
- Supports custom polling options
-
execution.poll() - Start event-driven polling
- Returns
void (non-blocking)
- Emits events:
statusChange, poll, success, error
- Perfect for UI updates, dashboards, and concurrent monitoring
Important Notes:
⚠️ Events require polling or awaiting .result: Events are only emitted when polling is active. You must either:
- Call
execution.poll() to start event-driven polling, OR
- Await
execution.result which starts polling automatically
const execution = await canvas.execute({ query: 'test' }, { async: true })
execution.on('success', result => console.log('Done'))
const execution = await canvas.execute({ query: 'test' }, { async: true })
execution.on('success', result => console.log('Done'))
execution.poll()
const execution = await canvas.execute({ query: 'test' }, { async: true })
execution.on('success', result => console.log('Done'))
await execution.result
⚠️ Already completed executions don't emit events: If you fetch an execution that has already finished (succeeded or failed), the success and error events won't fire because there's no polling needed. Instead, check the status and access the result directly:
const execution = await canvas.getExecution(executionId)
if (execution.status === 'succeeded') {
const result = await execution.result
console.log('Already completed:', result)
}
else if (execution.status === 'failed') {
try {
await execution.result
}
catch (error) {
console.error('Already failed:', error)
}
}
else {
execution.on('success', result => console.log('Completed!', result))
execution.on('error', error => console.error('Failed:', error))
execution.poll()
}
Use Cases:
- Job queue management systems
- Resuming monitoring after page refresh or server restart
- Multi-user dashboards showing execution status
- Background processing with real-time updates
- Retry mechanisms with persistent execution IDs
See Fetch and Poll Examples for detailed usage patterns.
File Handling
The TelaFile API provides flexible file handling with support for various input types:
import { TelaFile } from '@meistrari/tela-sdk-js'
const fileFromBlob = TelaFile.create(blob, { name: 'custom.pdf' })
const fileFromUrl = TelaFile.create('https://example.com/document.pdf')
const fileFromBytes = TelaFile.create(
new Uint8Array(['...']),
{
mimeType: 'application/pdf',
name: 'document.pdf'
}
)
const fileFromStream = TelaFile.create(
readableStream,
{
mimeType: 'image/jpeg',
name: 'photo.jpg'
}
)
const vaultFile = TelaFile.create('vault://file-id')
const fileWithRange = TelaFile.create(
'https://example.com/document.pdf',
{
range: [0, 5],
parserType: 'pdf'
}
)
Migration Guide from v1.x to v2
Version 2.0 of the Tela SDK introduces significant improvements to type safety, schema validation, and API design. This guide will help you migrate your code from v1.x to v2.
Breaking Changes
1. API Surface Changed: completions.create() → canvas.get() + execute()
The v1 API used a single completions.create() method. V2 introduces a two-step pattern: first retrieve a canvas, then execute it.
v1.x:
const tela = new TelaSDK({ apiKey: 'your-api-key' })
const completion = await tela.completions.create({
canvasId: 'canvas-id',
variables: {
query: 'What is the capital of France?'
}
})
console.log(completion.choices[0].message.content)
v2:
const tela = new TelaSDK({ apiKey: 'your-api-key' })
const canvas = await tela.canvas.get({
id: 'canvas-id'
})
const result = await canvas.execute({
query: 'What is the capital of France?'
}).result
console.log(result)
2. Schema Validation and Type Safety (Optional)
V2 introduces optional runtime and compile-time type safety with Zod schema validation. Schemas validate data at runtime (catching invalid data) and provide TypeScript types at compile-time. You can use the SDK without schemas if you prefer.
v1.x (no schema validation):
const completion = await tela.completions.create<
{ query: string },
{ result: string }
>({
canvasId: 'canvas-id',
variables: {
query: 'Hello'
}
})
v2 (without schemas - works just like v1.x):
const canvas = await tela.canvas.get({
id: 'canvas-id'
})
const result = await canvas.execute({ query: 'Hello' }).result
v2 (with optional schema validation for runtime + compile-time safety):
const canvas = await tela.canvas.get({
id: 'canvas-id',
input: schema => schema.object({
query: schema.string()
}),
output: schema => schema.object({
result: schema.string()
})
})
const result = await canvas.execute({ query: 'Hello' }).result
3. File Handling Changes
The TelaFile constructor now requires mimeType for Uint8Array and ReadableStream inputs (for compatibility with storage solutions).
v1.x:
import { TelaFile } from '@meistrari/tela-sdk-js'
const file = new TelaFile(blob, { range: [0, 5] })
const fileFromBytes = new TelaFile(new Uint8Array([]))
v2:
import { TelaFile } from '@meistrari/tela-sdk-js'
const file = new TelaFile(blob, { range: [0, 5] })
const fileFromBytes = new TelaFile(
new Uint8Array([]),
{
mimeType: 'application/pdf',
name: 'document.pdf'
}
)
const fileFromStream = tela.createFile(readableStream, { mimeType: 'image/jpeg' })
4. Streaming API Changes
Streaming now returns an async generator directly via the .result property.
v1.x:
const stream = await tela.completions.create({
canvasId: 'canvas-id',
stream: true,
variables: { query: 'Tell me a story' }
})
for await (const chunk of stream) {
process.stdout.write(chunk.message.content || '')
}
v2:
const canvas = await tela.canvas.get({
id: 'canvas-id',
input: schema => schema.object({
query: schema.string()
}),
output: schema => schema.object({
response: schema.string()
})
})
const execution = canvas.execute(
{ query: 'Tell me a story' },
{ stream: true }
)
for await (const chunk of execution.result) {
process.stdout.write(chunk.response || '')
}
5. Async Execution with Polling
V2 introduces async execution with automatic polling. V1.x only supported synchronous execution (the request would block until completion).
v1.x:
const response = await tela.completions.create({
canvasId: 'canvas-id',
variables: { query: 'Analyze this' }
})
console.log(response.choices[0].message.content)
v2:
const canvas = await tela.canvas.get({
id: 'canvas-id',
input: schema => schema.object({ query: schema.string() }),
output: schema => schema.object({ result: schema.string() })
})
const execution = canvas.execute(
{ query: 'Analyze this' },
{
async: true,
pollingInterval: 1000,
pollingTimeout: 60000,
webhookUrl: 'https://your-server.com/webhook'
}
)
const result = await execution.result
console.log(result.result)
6. Response Structure Changes
The response structure has been simplified.
v1.x:
const completion = await tela.completions.create({
canvasId: 'canvas-id',
variables: { query: 'Hello' }
})
console.log(completion.choices[0].message.content)
v2:
const canvas = await tela.canvas.get({
id: 'canvas-id',
output: schema => schema.object({
answer: schema.string()
})
})
const response = await canvas.execute({ query: 'Hello' }).result
console.log(response.answer)
Migration Checklist
New Features in v2
Event-Driven Execution Monitoring
V2 introduces a comprehensive event system for monitoring execution lifecycle across all execution modes (sync, async, streaming):
const execution = await canvas.execute(
{ query: 'Analyze this' },
{ async: true }
)
execution.on('statusChange', (status) => {
console.log(`Status: ${status}`)
})
execution.on('poll', (pollResult) => {
console.log(`Polling - ${pollResult.status}`)
})
execution.on('success', (result) => {
console.log('Completed!', result)
})
execution.on('error', (error) => {
console.error('Failed:', error.message)
})
const result = await execution.result
Available Events:
success - Completion with validated result
error - Failure notification (prevents throwing if listener exists)
statusChange - Status transitions
poll - Polling updates (async only)
See Event Examples for comprehensive patterns and use cases.
Promise-like Execution API
V2 introduces a flexible execution API that supports both direct result access and execution object retrieval:
const result = await canvas.execute({ query: 'Hello' }).result
const execution = await canvas.execute({ query: 'Hello' })
const result2 = await execution.result
Schema Validation Warnings
V2 validates your schemas against the server configuration and warns you about mismatches:
const canvas = await tela.canvas.get({
id: 'canvas-id',
input: schema => schema.object({
wrongField: schema.string()
}),
skipSchemaValidation: false
})
Vault File References
V2 supports vault file references:
const vaultFile = new TelaFile('vault://file-id')
Skip Result Validation
When you have schemas defined, you can skip runtime validation for better performance (you'll still get TypeScript types):
const canvas = await tela.canvas.get({
id: 'canvas-id',
input: schema => schema.object({
query: schema.string()
}),
output: schema => schema.object({
answer: schema.string()
})
})
const execution = canvas.execute(
{ query: 'Hello' },
{ skipResultValidation: true }
)
const result = await execution.result
Access Raw API Responses
V2 provides access to the raw, unprocessed API response alongside the parsed result:
const canvas = await tela.canvas.get({
id: 'canvas-id',
output: schema => schema.object({
answer: schema.string()
})
})
const execution = await canvas.execute({ query: 'Hello' })
const result = await execution.result
console.log(result.answer)
const rawResult = await execution.rawResult
console.log(rawResult)
const raw = await execution.rawResult
const parsed = await execution.result
This is useful when you need:
- Full API response metadata (execution IDs, timestamps, etc.)
- Debugging and logging complete responses
- Access to data not in your schema
Fetch Existing Executions and Event-Driven Polling
V2 introduces the ability to fetch existing async executions by ID and monitor them with event-driven polling:
const execution = await canvas.execute(
{ query: 'Process this data' },
{ async: true }
)
const executionId = execution.id
const fetchedExecution = await canvas.getExecution(executionId, {
pollingInterval: 2000,
pollingTimeout: 120000
})
fetchedExecution.on('statusChange', (status) => {
console.log(`Status changed: ${status}`)
})
fetchedExecution.on('success', (result) => {
console.log('Completed!', result)
})
fetchedExecution.on('error', (error) => {
console.error('Failed:', error)
})
fetchedExecution.poll()
console.log('Polling in background')
This enables powerful use cases:
- Job queue management: Start jobs, store IDs, fetch later to check status
- Resumable workflows: Continue monitoring after page refresh or server restart
- Multi-user dashboards: Monitor executions started by different users
- Real-time UI updates: Non-blocking polling with event listeners
- Background processing: Track multiple executions concurrently
See Fetch and Poll Examples for detailed patterns.
Execution Tags
V2 introduces support for tagging executions for filtering, categorization, and analytics:
const result = await canvas.execute(
{ query: 'Analyze customer feedback' },
{
tags: ['production', 'analytics', 'customer-feedback']
}
).result
const asyncResult = await canvas.execute(
{ query: 'Process large dataset' },
{
async: true,
tags: ['batch-processing', 'analytics']
}
).result
for await (const chunk of canvas.execute(
{ query: 'Generate report' },
{
stream: true,
tags: ['streaming', 'reports']
}
).result) {
process.stdout.write(chunk.result || '')
}
Use Cases:
- Environment tracking: Tag executions by environment (
production, staging, development)
- Feature categorization: Organize executions by feature (
analytics, reporting, summarization)
- User segmentation: Track executions by user tier or department
- Cost allocation: Attribute API usage to specific projects or teams
- Performance monitoring: Filter and analyze execution metrics by tag
Application Execution Labels
V2 adds support for setting workspace task titles when using deployed applications via applicationId:
const canvas = await tela.canvas.get({
applicationId: 'app-id'
})
const result = await canvas.execute(
{ query: 'Process request' },
{
label: 'Customer Dashboard Query'
}
).result
When you execute a canvas using applicationId, it creates a task in the application's workspace. The label field sets the title of that workspace task, making it easier to identify and track executions in your Tela workspace.
Note: The label field is only applicable when using applicationId. A warning will be logged if you provide a label without an applicationId.
Use Cases:
- Task identification: Give meaningful titles to workspace tasks for easier tracking
- Execution categorization: Organize tasks by feature or workflow in the workspace
- User-friendly naming: Display clear task names instead of generic execution IDs
- Dashboard clarity: Make workspace task lists more readable and searchable
Need Help?
If you encounter issues during migration, please: