# Run Canvas Source: https://docs.tela.com/api-reference/completion/create POST /v2/chat/completions Executes a canvas ## Files The Tela SDKs allows you to easily process files, such as PDFs, by creating a completion. You can use various file sources including public URLs, vault URLs from uploaded files, or pass multiple files in a single variable. When passing file URLs directly (without using the SDK's `.createFile()` method), you must wrap them in an object with a `file_url` property. The `.createFile()` method handles this formatting automatically. ### Single File Processing Below is an example of processing a single PDF document: ```typescript typescript theme={null} import { createTelaClient } from '@meistrari/tela-sdk-js' import type { TelaFile } from '@meistrari/tela-sdk-js' const tela = createTelaClient({ apiKey: process.env.TELA_API_KEY, }) const completion = await tela.completions.create<{ document: TelaFile }, { fileSummary: string }>({ canvasId: process.env.TELA_CANVAS_ID, variables: { document: tela.createFile('https://www.wmaccess.com/downloads/sample-invoice.pdf'), // It is possible to pass a URL, Buffer, Blob, etc. }, }) ``` ```javascript javascript theme={null} import { createTelaClient } from '@meistrari/tela-sdk-js' import type { TelaFile } from '@meistrari/tela-sdk-js' const tela = createTelaClient({ apiKey: process.env.TELA_API_KEY, }) const completion = await tela.completions.create({ canvasId: process.env.TELA_CANVAS_ID, variables: { document: tela.createFile('https://www.wmaccess.com/downloads/sample-invoice.pdf'), // It is possible to pass a URL, Buffer, Blob, etc. }, }) ``` ```python python theme={null} import os from tela import create_tela_client TELA_CANVAS_ID = os.getenv("TELA_CANVAS_ID") TELA_API_KEY = os.getenv("TELA_API_KEY") client = create_tela_client( api_key=TELA_API_KEY, ) completion = client.completions.create( canvas_id=TELA_CANVAS_ID, variables={ "document": client.create_file("https://www.wmaccess.com/downloads/sample-invoice.pdf"), }, ) ``` ### Using Vault URLs After uploading a file using the `/v3/files` endpoint, you can use the vault URL in your completions. When not using the SDK's `.createFile()` method, you need to wrap the URL in an object with a `file_url` property: ```typescript typescript theme={null} // First, upload a file and get the file ID const fileId = '3fa85f64-5717-4562-b3fc-2c963f66afa6' // UUID from file upload const completion = await tela.completions.create({ canvasId: process.env.TELA_CANVAS_ID, variables: { document: { file_url: `vault://${fileId}` }, // Using vault:// URL as object }, }) ``` ```javascript javascript theme={null} // First, upload a file and get the file ID const fileId = '3fa85f64-5717-4562-b3fc-2c963f66afa6' // UUID from file upload const completion = await tela.completions.create({ canvasId: process.env.TELA_CANVAS_ID, variables: { document: { file_url: `vault://${fileId}` }, // Using vault:// URL as object }, }) ``` ```python python theme={null} # First, upload a file and get the file ID file_id = '3fa85f64-5717-4562-b3fc-2c963f66afa6' # UUID from file upload completion = client.completions.create( canvas_id=TELA_CANVAS_ID, variables={ "document": {"file_url": f"vault://{file_id}"}, # Using vault:// URL as object }, ) ``` ### Multiple Files in a Single Variable You can pass multiple file URLs in a single variable as an array: ```typescript typescript theme={null} const completion = await tela.completions.create({ canvasId: process.env.TELA_CANVAS_ID, variables: { documents: [ { file_url: 'https://example.com/file1.pdf' }, { file_url: 'https://example.com/file2.pdf' }, { file_url: 'vault://550e8400-e29b-41d4-a716-446655440000' }, // Mix of public and vault URLs ], }, }) ``` ```javascript javascript theme={null} const completion = await tela.completions.create({ canvasId: process.env.TELA_CANVAS_ID, variables: { documents: [ { file_url: 'https://example.com/file1.pdf' }, { file_url: 'https://example.com/file2.pdf' }, { file_url: 'vault://550e8400-e29b-41d4-a716-446655440000' }, // Mix of public and vault URLs ], }, }) ``` ```python python theme={null} completion = client.completions.create( canvas_id=TELA_CANVAS_ID, variables={ "documents": [ {"file_url": "https://example.com/file1.pdf"}, {"file_url": "https://example.com/file2.pdf"}, {"file_url": "vault://550e8400-e29b-41d4-a716-446655440000"}, # Mix of public and vault URLs ], }, ) ``` ## Streaming The Tela SDKs provides a `stream` option, which allows you to consume the stream of the completion. Below is an example of consuming a stream of the completion. ```typescript typescript theme={null} import fs from 'node:fs' import { createTelaClient } from '@meistrari/tela-sdk-js' import type { TelaFile } from '@meistrari/tela-sdk-js' const tela = createTelaClient({ apiKey: process.env.TELA_API_KEY, }) const PATH = 'examples/stream/sample-invoice.pdf' const fileStream = fs.createReadStream(PATH) const completion = await tela.completions.create<{ document: TelaFile }, { fileSummary: string }>({ canvasId: process.env.TELA_CANVAS_ID, variables: { document: tela.createFile(fileStream), }, stream: true, }) for await (const chunk of completion) { console.log(JSON.stringify(chunk)) } ``` ```javascript javascript theme={null} import fs from 'node:fs' import { createTelaClient } from '@meistrari/tela-sdk-js' import type { TelaFile } from '@meistrari/tela-sdk-js' const tela = createTelaClient({ apiKey: process.env.TELA_API_KEY, }) const PATH = 'examples/stream/sample-invoice.pdf' const fileStream = fs.createReadStream(PATH) const completion = await tela.completions.create({ canvasId: process.env.TELA_CANVAS_ID, variables: { document: tela.createFile(fileStream), }, stream: true, }) for await (const chunk of completion) { console.log(JSON.stringify(chunk)) } ``` ```python python theme={null} import os from tela import create_tela_client TELA_CANVAS_ID = os.getenv("TELA_CANVAS_ID") TELA_API_KEY = os.getenv("TELA_API_KEY") client = create_tela_client( api_key=TELA_API_KEY, ) PATH = 'examples/stream/sample-invoice.pdf' with open(PATH, 'rb') as file: completion = client.completions.create( canvas_id=TELA_CANVAS_ID, variables={ "document": client.create_file(file), }, stream=True, ) for chunk in completion: print(chunk) ``` ## Webhook The Tela SDKs provides a `webhookUrl` option, which allows you to receive a webhook when the completion is finished. Below is an example of receiving a webhook when the completion is finished. ```typescript typescript theme={null} import { createTelaClient } from '@meistrari/tela-sdk-js' const tela = createTelaClient({ apiKey: process.env.TELA_API_KEY, }) const webhook = await tela.completions.create({ canvasId: process.env.TELA_CANVAS_ID, variables: { document: tela.createFile('https://www.wmaccess.com/downloads/sample-invoice.pdf'), }, webhookUrl: 'https://webhook.site/12345', }) ``` ```javascript javascript theme={null} import { createTelaClient } from '@meistrari/tela-sdk-js' const tela = createTelaClient({ apiKey: process.env.TELA_API_KEY, }) const webhook = await tela.completions.create({ canvasId: process.env.TELA_CANVAS_ID, variables: { document: tela.createFile('https://www.wmaccess.com/downloads/sample-invoice.pdf'), }, webhookUrl: 'https://webhook.site/12345', }) ``` ```python python theme={null} import os from tela import create_tela_client TELA_CANVAS_ID = os.getenv("TELA_CANVAS_ID") TELA_API_KEY = os.getenv("TELA_API_KEY") client = create_tela_client( api_key=TELA_API_KEY, ) webhook = client.completions.create( canvas_id=TELA_CANVAS_ID, variables={ "document": client.create_file("https://www.wmaccess.com/downloads/sample-invoice.pdf"), }, webhook_url="https://webhook.site/12345", ) print(webhook) ``` For detailed information about the webhook payload structure, see the [Webhook Payload Structure](/en/completion-api-guide#webhook-payload-structure) section in the completion API guide. # Retrieve Canvas Run Source: https://docs.tela.com/api-reference/completion/get GET /v2/chat/completions/{id} Retrieve the status and details of an asynchronous chat completion using its unique identifier, enabling efficient polling and status monitoring. # Upload a File Source: https://docs.tela.com/api-reference/file/create POST /v2/file Upload a file to the Tela API ## Uploading a File Using this endpoint you can request a temporary URL to upload a file to Tela's storage. Here's an code example on how to do it: ```typescript TypeScript theme={null} const { download_url, upload_url: uploadUrl } = await fetch( 'https://api.tela.com/v2/file', { method: 'POST' } ).then(res => res.json()) await fetch(uploadUrl, { method: 'PUT', body: file }) ``` ```python Python theme={null} import requests response = requests.post('https://api.tela.com/v2/file') upload_url = response.json().upload_url file = open('path/to/file', 'rb') requests.put(upload_url, data=file) ``` # Upload a File (v3) Source: https://docs.tela.com/api-reference/file/upload POST /v3/files Upload a file using the new v3 API endpoint ## File Upload Process The v3 file upload API provides a secure two-step process for uploading files to Tela's storage: 1. **Request an upload URL** - Call the `/v3/files` endpoint to get a temporary upload URL 2. **Upload your file** - Use the returned upload URL to upload your file content directly ## Step 1: Get Upload URL First, request a temporary upload URL by providing the filename: ```typescript TypeScript theme={null} const response = await fetch('https://api.tela.com/v3/files', { method: 'POST', headers: { 'Authorization': 'Bearer YOUR_API_TOKEN', 'x-compatibility-date': '2025-07-23', 'Content-Type': 'application/json' }, body: JSON.stringify({ fileName: 'test.txt' }) }) const { id, uploadUrl } = await response.json() ``` ```python Python theme={null} import requests response = requests.post( 'https://api.tela.com/v3/files', headers={ 'Authorization': 'Bearer YOUR_API_TOKEN', 'x-compatibility-date': '2025-07-23' }, json={ 'fileName': 'test.txt' } ) data = response.json() file_id = data['id'] upload_url = data['uploadUrl'] ``` ```bash cURL theme={null} curl -X POST https://api.tela.com/v3/files \ -H "Authorization: Bearer YOUR_API_TOKEN" \ -H "x-compatibility-date: 2025-07-23" \ -H "Content-Type: application/json" \ -d '{"fileName": "test.txt"}' ``` The response will include: * `id`: The unique identifier for your file * `uploadUrl`: A temporary URL to upload your file content ## Step 2: Upload File Content Use the `uploadUrl` from the previous step to upload your file content: ```typescript TypeScript theme={null} // For browser environments const file = new File(['Hello, world!'], 'test.txt', { type: 'text/plain' }) await fetch(uploadUrl, { method: 'PUT', headers: { 'Content-Type': file.type // Explicitly set Content-Type }, body: file }) // For Node.js environments const blob = new Blob(['Hello, world!'], { type: 'text/plain' }) await fetch(uploadUrl, { method: 'PUT', headers: { 'Content-Type': 'text/plain' }, body: blob }) ``` ```python Python theme={null} # For file upload with open('test.txt', 'rb') as file: requests.put( upload_url, headers={ 'Content-Type': 'text/plain' # Explicitly set Content-Type }, data=file ) # For in-memory content import io file_content = b'Hello, world!' file_like = io.BytesIO(file_content) requests.put( upload_url, headers={ 'Content-Type': 'text/plain' }, data=file_like ) ``` ```bash cURL theme={null} # Upload a file curl -X PUT "UPLOAD_URL_FROM_PREVIOUS_STEP" \ -H "Content-Type: text/plain" \ --data-binary @test.txt # Or upload binary content curl -X PUT "UPLOAD_URL_FROM_PREVIOUS_STEP" \ -H "Content-Type: application/pdf" \ --data-binary @document.pdf ``` The upload URL is temporary and will expire after a short period. Make sure to upload your file promptly after receiving the URL. ## Using the File ID After successfully uploading your file, you can use the UUID returned in Step 1 to reference the file in other API endpoints: ```typescript TypeScript theme={null} // Retrieve file metadata const fileData = await fetch(`https://api.tela.com/v3/files/${id}`, { headers: { 'Authorization': 'Bearer YOUR_API_TOKEN', 'x-compatibility-date': '2025-07-23' } }).then(res => res.json()) ``` ```python Python theme={null} # Retrieve file metadata response = requests.get( f'https://api.tela.com/v3/files/{file_id}', headers={ 'Authorization': 'Bearer YOUR_API_TOKEN', 'x-compatibility-date': '2025-07-23' } ) file_data = response.json() ``` ## Request Headers ### Required Headers * `Authorization`: Bearer token for authentication * `x-compatibility-date`: API version compatibility date (e.g., `2025-07-23`) ### Content Types When uploading the file content in Step 2, set the appropriate `Content-Type` header: * `text/plain` for text files * `application/json` for JSON files * `image/png`, `image/jpeg` for images * `application/pdf` for PDF files * And other standard MIME types as needed While some HTTP clients may attempt to infer the Content-Type from the file, it's best practice to explicitly set the Content-Type header to ensure proper file handling. This is especially important for binary files and when the file extension doesn't match the actual content type. ## Complete Example Here's a complete example showing the entire file upload process: ```typescript TypeScript theme={null} async function uploadFile(file: File) { // Step 1: Get upload URL const response = await fetch('https://api.tela.com/v3/files', { method: 'POST', headers: { 'Authorization': 'Bearer YOUR_API_TOKEN', 'x-compatibility-date': '2025-07-23', 'Content-Type': 'application/json' }, body: JSON.stringify({ fileName: file.name }) }) const { id, uploadUrl } = await response.json() console.log(`File ID: ${id}`) // e.g., 3fa85f64-5717-4562-b3fc-2c963f66afa6 // Step 2: Upload file content await fetch(uploadUrl, { method: 'PUT', headers: { 'Content-Type': file.type || 'application/octet-stream' }, body: file }) console.log('File uploaded successfully!') return id } // Usage - Browser const file = new File(['Hello, world!'], 'test.txt', { type: 'text/plain' }) const fileId = await uploadFile(file) // Usage - From file input const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement if (fileInput.files?.[0]) { const fileId = await uploadFile(fileInput.files[0]) } ``` ```python Python theme={null} import requests import mimetypes def upload_file(file_path): # Step 1: Get upload URL file_name = file_path.split('/')[-1] response = requests.post( 'https://api.tela.com/v3/files', headers={ 'Authorization': 'Bearer YOUR_API_TOKEN', 'x-compatibility-date': '2025-07-23' }, json={'fileName': file_name} ) data = response.json() file_id = data['id'] # e.g., 3fa85f64-5717-4562-b3fc-2c963f66afa6 upload_url = data['uploadUrl'] print(f'File ID: {file_id}') # Step 2: Upload file content content_type, _ = mimetypes.guess_type(file_path) if not content_type: content_type = 'application/octet-stream' with open(file_path, 'rb') as file: requests.put( upload_url, headers={'Content-Type': content_type}, data=file ) print('File uploaded successfully!') return file_id # Usage file_id = upload_file('test.txt') # Or with in-memory content import io def upload_bytes(file_name, content_bytes, content_type='application/octet-stream'): # ... (Step 1 same as above) requests.put( upload_url, headers={'Content-Type': content_type}, data=io.BytesIO(content_bytes) ) return file_id ``` ## Using Uploaded Files in Completions After uploading files, you can use them in your canvas completions with the `vault://` URL scheme: ```typescript TypeScript theme={null} // Using a single uploaded file const completion = await tela.completions.create({ canvasId: process.env.TELA_CANVAS_ID, variables: { document: { file_url: `vault://3fa85f64-5717-4562-b3fc-2c963f66afa6` } } }) // Using multiple uploaded files const completion2 = await tela.completions.create({ canvasId: process.env.TELA_CANVAS_ID, variables: { documents: [ { file_url: `vault://3fa85f64-5717-4562-b3fc-2c963f66afa6` }, { file_url: `vault://550e8400-e29b-41d4-a716-446655440000` }, { file_url: 'https://example.com/public-file.pdf' } // Mix vault and public URLs ] } }) ``` ```python Python theme={null} # Using a single uploaded file completion = client.completions.create( canvas_id=TELA_CANVAS_ID, variables={ "document": {"file_url": f"vault://3fa85f64-5717-4562-b3fc-2c963f66afa6"} } ) # Using multiple uploaded files completion2 = client.completions.create( canvas_id=TELA_CANVAS_ID, variables={ "documents": [ {"file_url": f"vault://3fa85f64-5717-4562-b3fc-2c963f66afa6"}, {"file_url": f"vault://550e8400-e29b-41d4-a716-446655440000"}, {"file_url": "https://example.com/public-file.pdf"} # Mix vault and public URLs ] } ) ``` The `vault://` URL scheme provides secure access to your uploaded files. These URLs can only be accessed within your workspace context and are ideal for processing sensitive documents. # Create Prompt Version Source: https://docs.tela.com/api-reference/prompt-version/create POST /prompt-version Creates a new version of a prompt ## Create a Prompt Version This endpoint creates a new version of a prompt with the specified configuration, variables, and content. ### Request Body The request body must include the following fields: | Parameter | Type | Description | | --------------- | ------------- | ---------------------------------------------------- | | `promptId` | string (UUID) | The ID of the prompt to create a version for | | `title` | string | The title of the prompt version (max 256 characters) | | `configuration` | object | Configuration settings for the prompt version | | `variables` | array | An array of variable objects used in the prompt | #### Optional Fields | Parameter | Type | Description | | ----------------- | ------------ | -------------------------------- | | `content` | string | Plain text content of the prompt | | `markdownContent` | string | Markdown content of the prompt | | `promoted` | boolean | Whether this version is promoted | | `draft` | boolean | Whether this version is a draft | | `messages` | array/object | Messages for chat-type prompts | ### Examples ```typescript typescript theme={null} const promptVersion = { promptId: 'prompt_uuid', title: 'Document Analysis v2', configuration: { model: 'claude-3-opus-20240229', temperature: 0.7, type: 'chat' }, variables: [ { name: 'document', type: 'file', required: true, description: 'The document to analyze' }, { name: 'query', type: 'text', required: true, description: 'The specific question about the document' } ], markdownContent: '# Document Analysis\n\nAnalyze the document and answer questions about it.', draft: true } const response = await fetch('https://api.tela.ai/prompt-version', { method: 'POST', headers: { 'Authorization': `Bearer ${process.env.TELA_API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify(promptVersion) }) const result = await response.json() console.log(result) ``` ```javascript javascript theme={null} const promptVersion = { promptId: 'prompt_uuid', title: 'Document Analysis v2', configuration: { model: 'claude-3-opus-20240229', temperature: 0.7, type: 'chat' }, variables: [ { name: 'document', type: 'file', required: true, description: 'The document to analyze' }, { name: 'query', type: 'text', required: true, description: 'The specific question about the document' } ], markdownContent: '# Document Analysis\n\nAnalyze the document and answer questions about it.', draft: true } const response = await fetch('https://api.tela.ai/prompt-version', { method: 'POST', headers: { 'Authorization': `Bearer ${process.env.TELA_API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify(promptVersion) }) const result = await response.json() console.log(result) ``` ```python python theme={null} import requests import os api_key = os.getenv("TELA_API_KEY") prompt_version = { "promptId": "prompt_uuid", "title": "Document Analysis v2", "configuration": { "model": "claude-3-opus-20240229", "temperature": 0.7, "type": "chat" }, "variables": [ { "name": "document", "type": "file", "required": True, "description": "The document to analyze" }, { "name": "query", "type": "text", "required": True, "description": "The specific question about the document" } ], "markdownContent": "# Document Analysis\n\nAnalyze the document and answer questions about it.", "draft": True } headers = { "Authorization": f"Bearer {api_key}", "Content-Type": "application/json" } response = requests.post("https://api.tela.ai/prompt-version", json=prompt_version, headers=headers) result = response.json() print(result) ``` ### Response The response includes the created prompt version object with its assigned ID and other details. ```json theme={null} { "id": "version_uuid", "promptId": "prompt_uuid", "title": "Document Analysis v2", "markdownContent": "# Document Analysis\n\nAnalyze the document and answer questions about it.", "configuration": { "model": "claude-3-opus-20240229", "temperature": 0.7, "type": "chat" }, "variables": [ { "name": "document", "type": "file", "required": true, "description": "The document to analyze" }, { "name": "query", "type": "text", "required": true, "description": "The specific question about the document" } ], "draft": true, "promoted": false, "createdAt": "2024-04-15T12:00:00.000Z", "updatedAt": "2024-04-15T12:00:00.000Z" } ``` ### Structured Output Example ```typescript typescript theme={null} const promptVersionWithStructuredOutput = { promptId: 'prompt_uuid', title: 'Document Analysis with Structured Output', configuration: { model: 'claude-3-opus-20240229', temperature: 0.7, type: 'chat', structuredOutput: { enabled: true, schema: { type: "object", title: "DocumentAnalysis", description: "Analysis of a document", properties: { summary: { type: "string", description: "Brief summary of the document" }, keyInsights: { type: "array", items: { type: "string" }, description: "Key insights from the document" }, sentiment: { type: "string", enum: ["positive", "neutral", "negative"], description: "Overall sentiment of the document" } }, required: ["summary", "keyInsights", "sentiment"] } } }, variables: [ { name: 'document', type: 'file', required: true, description: 'The document to analyze' } ] } const response = await fetch('https://api.tela.ai/prompt-version', { method: 'POST', headers: { 'Authorization': `Bearer ${process.env.TELA_API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify(promptVersionWithStructuredOutput) }) const result = await response.json() console.log(result) ``` ```javascript javascript theme={null} const promptVersionWithStructuredOutput = { promptId: 'prompt_uuid', title: 'Document Analysis with Structured Output', configuration: { model: 'claude-3-opus-20240229', temperature: 0.7, type: 'chat', structuredOutput: { enabled: true, schema: { type: "object", title: "DocumentAnalysis", description: "Analysis of a document", properties: { summary: { type: "string", description: "Brief summary of the document" }, keyInsights: { type: "array", items: { type: "string" }, description: "Key insights from the document" }, sentiment: { type: "string", enum: ["positive", "neutral", "negative"], description: "Overall sentiment of the document" } }, required: ["summary", "keyInsights", "sentiment"] } } }, variables: [ { name: 'document', type: 'file', required: true, description: 'The document to analyze' } ] } const response = await fetch('https://api.tela.ai/prompt-version', { method: 'POST', headers: { 'Authorization': `Bearer ${process.env.TELA_API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify(promptVersionWithStructuredOutput) }) const result = await response.json() console.log(result) ``` ```python python theme={null} import requests import os api_key = os.getenv("TELA_API_KEY") prompt_version_with_structured_output = { "promptId": "prompt_uuid", "title": "Document Analysis with Structured Output", "configuration": { "model": "claude-3-opus-20240229", "temperature": 0.7, "type": "chat", "structuredOutput": { "enabled": True, "schema": { "type": "object", "title": "DocumentAnalysis", "description": "Analysis of a document", "properties": { "summary": { "type": "string", "description": "Brief summary of the document" }, "keyInsights": { "type": "array", "items": { "type": "string" }, "description": "Key insights from the document" }, "sentiment": { "type": "string", "enum": ["positive", "neutral", "negative"], "description": "Overall sentiment of the document" } }, "required": ["summary", "keyInsights", "sentiment"] } } }, "variables": [ { "name": "document", "type": "file", "required": True, "description": "The document to analyze" } ] } headers = { "Authorization": f"Bearer {api_key}", "Content-Type": "application/json" } response = requests.post("https://api.tela.ai/prompt-version", json=prompt_version_with_structured_output, headers=headers) result = response.json() print(result) ``` # Delete Prompt Version Source: https://docs.tela.com/api-reference/prompt-version/delete DELETE /prompt-version/{id} Deletes a specific prompt version ## Delete a Prompt Version This endpoint deletes a specific prompt version by its ID. ### URL Parameters | Parameter | Type | Description | | --------- | ------ | -------------------------------------- | | `id` | string | The ID of the prompt version to delete | ### Examples ```typescript typescript theme={null} const promptVersionId = 'version_uuid' const response = await fetch(`https://api.tela.ai/prompt-version/${promptVersionId}`, { method: 'DELETE', headers: { 'Authorization': `Bearer ${process.env.TELA_API_KEY}`, 'Content-Type': 'application/json' } }) const result = await response.json() console.log(result) ``` ```javascript javascript theme={null} const promptVersionId = 'version_uuid' const response = await fetch(`https://api.tela.ai/prompt-version/${promptVersionId}`, { method: 'DELETE', headers: { 'Authorization': `Bearer ${process.env.TELA_API_KEY}`, 'Content-Type': 'application/json' } }) const result = await response.json() console.log(result) ``` ```python python theme={null} import requests import os api_key = os.getenv("TELA_API_KEY") prompt_version_id = "version_uuid" url = f"https://api.tela.ai/prompt-version/{prompt_version_id}" headers = { "Authorization": f"Bearer {api_key}", "Content-Type": "application/json" } response = requests.delete(url, headers=headers) result = response.json() print(result) ``` ### Response The response includes a confirmation of the deletion. ```json theme={null} { "id": "version_uuid", "deleted": true } ``` ### Notes * Deleting a promoted prompt version will remove it from being available for use in completions. * If a prompt has only one version, you may not be able to delete it without first deleting the entire prompt. * Deleted prompt versions cannot be recovered. Make sure you want to permanently remove the version before calling this endpoint. # List Prompt Versions Source: https://docs.tela.com/api-reference/prompt-version/get GET /prompt-version Returns a list of prompt versions for a specific prompt ## Retrieve Prompt Versions This endpoint retrieves all prompt versions associated with a specific prompt ID. ### Required Parameters | Parameter | Type | Description | | ---------- | ------------- | --------------------------------------------- | | `promptId` | string (UUID) | The ID of the prompt to retrieve versions for | ### Examples ```typescript typescript theme={null} const promptId = 'prompt_uuid' // Build URL with query parameters const url = new URL('https://api.tela.ai/prompt-version') url.searchParams.append('promptId', promptId) const response = await fetch(url, { method: 'GET', headers: { 'Authorization': `Bearer ${process.env.TELA_API_KEY}`, 'Content-Type': 'application/json' } }) const promptVersions = await response.json() console.log(promptVersions) ``` ```javascript javascript theme={null} const promptId = 'prompt_uuid' // Build URL with query parameters const url = new URL('https://api.tela.ai/prompt-version') url.searchParams.append('promptId', promptId) const response = await fetch(url, { method: 'GET', headers: { 'Authorization': `Bearer ${process.env.TELA_API_KEY}`, 'Content-Type': 'application/json' } }) const promptVersions = await response.json() console.log(promptVersions) ``` ```python python theme={null} import requests import os api_key = os.getenv("TELA_API_KEY") prompt_id = "prompt_uuid" url = "https://api.tela.ai/prompt-version" params = { "promptId": prompt_id } headers = { "Authorization": f"Bearer {api_key}", "Content-Type": "application/json" } response = requests.get(url, params=params, headers=headers) prompt_versions = response.json() print(prompt_versions) ``` ### Response The response includes an array of prompt version objects associated with the specified prompt ID. ```json theme={null} [ { "id": "version_uuid", "promptId": "prompt_uuid", "title": "My Prompt Version", "content": "Prompt content...", "markdownContent": "# Markdown content...", "configuration": { "model": "claude-3-opus-20240229", "temperature": 0.7, "type": "chat" }, "variables": [ { "name": "document", "type": "file", "required": true, "description": "The document to analyze" } ], "promoted": true, "draft": false, "createdAt": "2024-04-01T12:00:00.000Z", "updatedAt": "2024-04-01T12:30:00.000Z" } ] ``` # Get Prompt Version Source: https://docs.tela.com/api-reference/prompt-version/get-by-id GET /prompt-version/{id} Retrieves a specific prompt version by ID ## Retrieve a Prompt Version This endpoint retrieves a specific prompt version by its ID. ### URL Parameters | Parameter | Type | Description | | --------- | ------ | ---------------------------------------- | | `id` | string | The ID of the prompt version to retrieve | ### Examples ```typescript typescript theme={null} const promptVersionId = 'version_uuid' const response = await fetch(`https://api.tela.ai/prompt-version/${promptVersionId}`, { method: 'GET', headers: { 'Authorization': `Bearer ${process.env.TELA_API_KEY}`, 'Content-Type': 'application/json' } }) const promptVersion = await response.json() console.log(promptVersion) ``` ```javascript javascript theme={null} const promptVersionId = 'version_uuid' const response = await fetch(`https://api.tela.ai/prompt-version/${promptVersionId}`, { method: 'GET', headers: { 'Authorization': `Bearer ${process.env.TELA_API_KEY}`, 'Content-Type': 'application/json' } }) const promptVersion = await response.json() console.log(promptVersion) ``` ```python python theme={null} import requests import os api_key = os.getenv("TELA_API_KEY") prompt_version_id = "version_uuid" url = f"https://api.tela.ai/prompt-version/{prompt_version_id}" headers = { "Authorization": f"Bearer {api_key}", "Content-Type": "application/json" } response = requests.get(url, headers=headers) prompt_version = response.json() print(prompt_version) ``` ### Response The response includes the requested prompt version object with all its details. ```json theme={null} { "id": "version_uuid", "promptId": "prompt_uuid", "title": "Document Analysis v2", "content": "Analyze the following document and answer the question.", "markdownContent": "# Document Analysis\n\nAnalyze the following document and answer the question.", "configuration": { "model": "claude-3-opus-20240229", "temperature": 0.7, "type": "chat" }, "variables": [ { "name": "document", "type": "file", "required": true, "description": "The document to analyze" }, { "name": "query", "type": "text", "required": true, "description": "The specific question about the document" } ], "promoted": true, "draft": false, "messages": [ { "role": "system", "content": "You are a document analysis assistant." }, { "role": "user", "content": "Please analyze the document {{document}} and answer this question: {{query}}" } ], "createdAt": "2024-04-01T12:00:00.000Z", "updatedAt": "2024-04-01T12:30:00.000Z" } ``` # Update Prompt Version Source: https://docs.tela.com/api-reference/prompt-version/update PATCH /prompt-version/{id} Updates an existing prompt version ## Update a Prompt Version This endpoint updates an existing prompt version with new configuration, variables, or content. ### URL Parameters | Parameter | Type | Description | | --------- | ------ | -------------------------------------- | | `id` | string | The ID of the prompt version to update | ### Request Body The request body may include any of the following fields to update: | Parameter | Type | Description | | ----------------- | ------------ | ---------------------------------------------------- | | `title` | string | The title of the prompt version (max 256 characters) | | `content` | string | Plain text content of the prompt | | `markdownContent` | string | Markdown content of the prompt | | `configuration` | object | Configuration settings for the prompt version | | `variables` | array | An array of variable objects used in the prompt | | `promoted` | boolean | Whether this version is promoted | | `draft` | boolean | Whether this version is a draft | | `messages` | array/object | Messages for chat-type prompts | ### Examples ```typescript typescript theme={null} const promptVersionId = 'version_uuid' const updateData = { title: 'Document Analysis v2.1', configuration: { temperature: 0.5 // Updating just the temperature }, promoted: true, draft: false } const response = await fetch(`https://api.tela.ai/prompt-version/${promptVersionId}`, { method: 'PATCH', headers: { 'Authorization': `Bearer ${process.env.TELA_API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify(updateData) }) const updatedVersion = await response.json() console.log(updatedVersion) ``` ```javascript javascript theme={null} const promptVersionId = 'version_uuid' const updateData = { title: 'Document Analysis v2.1', configuration: { temperature: 0.5 // Updating just the temperature }, promoted: true, draft: false } const response = await fetch(`https://api.tela.ai/prompt-version/${promptVersionId}`, { method: 'PATCH', headers: { 'Authorization': `Bearer ${process.env.TELA_API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify(updateData) }) const updatedVersion = await response.json() console.log(updatedVersion) ``` ```python python theme={null} import requests import os api_key = os.getenv("TELA_API_KEY") prompt_version_id = "version_uuid" update_data = { "title": "Document Analysis v2.1", "configuration": { "temperature": 0.5 # Updating just the temperature }, "promoted": True, "draft": False } url = f"https://api.tela.ai/prompt-version/{prompt_version_id}" headers = { "Authorization": f"Bearer {api_key}", "Content-Type": "application/json" } response = requests.patch(url, json=update_data, headers=headers) updated_version = response.json() print(updated_version) ``` ### Response The response includes the updated prompt version object with the changes applied. ```json theme={null} { "id": "version_uuid", "promptId": "prompt_uuid", "title": "Document Analysis v2.1", "content": "Analyze the following document and answer the question.", "markdownContent": "# Document Analysis\n\nAnalyze the following document and answer the question.", "configuration": { "model": "claude-3-opus-20240229", "temperature": 0.5, "type": "chat" }, "variables": [ { "name": "document", "type": "file", "required": true, "description": "The document to analyze" }, { "name": "query", "type": "text", "required": true, "description": "The specific question about the document" } ], "promoted": true, "draft": false, "messages": [ { "role": "system", "content": "You are a document analysis assistant." }, { "role": "user", "content": "Please analyze the document {{document}} and answer this question: {{query}}" } ], "createdAt": "2024-04-01T12:00:00.000Z", "updatedAt": "2024-04-15T14:30:00.000Z" } ``` ### Update Variables Example ```typescript typescript theme={null} const promptVersionId = 'version_uuid' const updateData = { variables: [ { name: 'document', type: 'file', required: true, description: 'The document to analyze' }, { name: 'query', type: 'text', required: true, description: 'The specific question about the document' }, { name: 'format', type: 'text', required: false, description: 'Preferred format for the analysis (bullet points, paragraph, etc.)' } ] } const response = await fetch(`https://api.tela.ai/prompt-version/${promptVersionId}`, { method: 'PATCH', headers: { 'Authorization': `Bearer ${process.env.TELA_API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify(updateData) }) const updatedVersion = await response.json() console.log(updatedVersion) ``` ```javascript javascript theme={null} const promptVersionId = 'version_uuid' const updateData = { variables: [ { name: 'document', type: 'file', required: true, description: 'The document to analyze' }, { name: 'query', type: 'text', required: true, description: 'The specific question about the document' }, { name: 'format', type: 'text', required: false, description: 'Preferred format for the analysis (bullet points, paragraph, etc.)' } ] } const response = await fetch(`https://api.tela.ai/prompt-version/${promptVersionId}`, { method: 'PATCH', headers: { 'Authorization': `Bearer ${process.env.TELA_API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify(updateData) }) const updatedVersion = await response.json() console.log(updatedVersion) ``` ```python python theme={null} import requests import os api_key = os.getenv("TELA_API_KEY") prompt_version_id = "version_uuid" update_data = { "variables": [ { "name": "document", "type": "file", "required": True, "description": "The document to analyze" }, { "name": "query", "type": "text", "required": True, "description": "The specific question about the document" }, { "name": "format", "type": "text", "required": False, "description": "Preferred format for the analysis (bullet points, paragraph, etc.)" } ] } url = f"https://api.tela.ai/prompt-version/{prompt_version_id}" headers = { "Authorization": f"Bearer {api_key}", "Content-Type": "application/json" } response = requests.patch(url, json=update_data, headers=headers) updated_version = response.json() print(updated_version) ``` # Query Tasks Source: https://docs.tela.com/api-reference/task/query POST /task/query Query and filter tasks from a Workstation/App using advanced filters on metadata and extracted data Query tasks from a Workstation (App) with powerful filtering capabilities. This endpoint allows you to filter by metadata (status, dates, tags) and by extracted data fields using MongoDB-style operators. ## Basic Query ```typescript theme={null} const API_KEY = process.env.TELA_API_KEY; const response = await fetch('https://api.tela.com/task/query', { method: 'POST', headers: { 'Authorization': `Bearer ${API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ promptApplicationId: "your-app-uuid", limit: 20 }) }); const data = await response.json(); console.log(data); ``` ```python theme={null} import requests import os API_KEY = os.getenv('TELA_API_KEY') response = requests.post( 'https://api.tela.com/task/query', headers={ 'Authorization': f'Bearer {API_KEY}', 'Content-Type': 'application/json' }, json={ 'promptApplicationId': 'your-app-uuid', 'limit': 20 } ) print(response.json()) ``` ```javascript theme={null} const API_KEY = process.env.TELA_API_KEY; fetch('https://api.tela.com/task/query', { method: 'POST', headers: { 'Authorization': `Bearer ${API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ promptApplicationId: 'your-app-uuid', limit: 20 }) }) .then(response => response.json()) .then(data => console.log(data)) .catch(error => console.error('Error:', error)); ``` ## Query with Metadata Filters Filter tasks by status, creation date, approval date, tags, and more. ```typescript theme={null} const response = await fetch('https://api.tela.com/task/query', { method: 'POST', headers: { 'Authorization': `Bearer ${API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ promptApplicationId: "your-app-uuid", filters: { status: ["approved", "completed"], createdAtSince: "2025-01-01T00:00:00Z", createdAtUntil: "2025-01-31T23:59:59Z", approvedBy: ["user@company.com"], tags: ["priority", "reviewed"] }, orderBy: "createdAt", order: "desc", limit: 50 }) }); ``` ```python theme={null} response = requests.post( 'https://api.tela.com/task/query', headers={ 'Authorization': f'Bearer {API_KEY}', 'Content-Type': 'application/json' }, json={ 'promptApplicationId': 'your-app-uuid', 'filters': { 'status': ['approved', 'completed'], 'createdAtSince': '2025-01-01T00:00:00Z', 'createdAtUntil': '2025-01-31T23:59:59Z', 'approvedBy': ['user@company.com'], 'tags': ['priority', 'reviewed'] }, 'orderBy': 'createdAt', 'order': 'desc', 'limit': 50 } ) ``` ## Query by Extracted Data (outputQuery) The most powerful feature is filtering by the data extracted from your documents. Use MongoDB-style operators to query any field in your task's output. ### Simple Equality ```typescript theme={null} const response = await fetch('https://api.tela.com/task/query', { method: 'POST', headers: { 'Authorization': `Bearer ${API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ promptApplicationId: "your-app-uuid", outputQuery: { "cliente": "Acme Corporation", "status": "active" } }) }); ``` ```python theme={null} response = requests.post( 'https://api.tela.com/task/query', headers={ 'Authorization': f'Bearer {API_KEY}', 'Content-Type': 'application/json' }, json={ 'promptApplicationId': 'your-app-uuid', 'outputQuery': { 'cliente': 'Acme Corporation', 'status': 'active' } } ) ``` ### Comparison Operators ```typescript theme={null} const response = await fetch('https://api.tela.com/task/query', { method: 'POST', headers: { 'Authorization': `Bearer ${API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ promptApplicationId: "your-app-uuid", outputQuery: { // Numeric comparisons "valor": { "$gte": 100000, "$lte": 500000 }, // Date comparisons "dataVencimento": { "$lte": "2025-03-31" }, // Text search (case-insensitive) "descricao": { "$contains": "compliance" }, // Value in list "categoria": { "$in": ["financeiro", "juridico"] } } }) }); ``` ```python theme={null} response = requests.post( 'https://api.tela.com/task/query', headers={ 'Authorization': f'Bearer {API_KEY}', 'Content-Type': 'application/json' }, json={ 'promptApplicationId': 'your-app-uuid', 'outputQuery': { # Numeric comparisons 'valor': { '$gte': 100000, '$lte': 500000 }, # Date comparisons 'dataVencimento': { '$lte': '2025-03-31' }, # Text search (case-insensitive) 'descricao': { '$contains': 'compliance' }, # Value in list 'categoria': { '$in': ['financeiro', 'juridico'] } } } ) ``` ### Querying Nested Arrays with \$elemMatch When your extracted data contains arrays (e.g., line items, categories, participants), use `$elemMatch` to query elements within those arrays. `$elemMatch` supports nesting, so you can query deeply nested structures like `categories[].items[].price`. ```typescript theme={null} // Example: Find menus that have any item under $10 const response = await fetch('https://api.tela.com/task/query', { method: 'POST', headers: { 'Authorization': `Bearer ${API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ promptApplicationId: "your-app-uuid", outputQuery: { "categories": { "$elemMatch": { "items": { "$elemMatch": { "price": { "$lt": 10 } } } } } } }) }); ``` ```python theme={null} # Example: Find menus that have any item under $10 response = requests.post( 'https://api.tela.com/task/query', headers={ 'Authorization': f'Bearer {API_KEY}', 'Content-Type': 'application/json' }, json={ 'promptApplicationId': 'your-app-uuid', 'outputQuery': { 'categories': { '$elemMatch': { 'items': { '$elemMatch': { 'price': { '$lt': 10 } } } } } } } ) ``` #### More \$elemMatch Examples ```typescript theme={null} // Find invoices with any line item over $1000 { "outputQuery": { "lineItems": { "$elemMatch": { "amount": { "$gt": 1000 } } } } } // Find contracts with a specific signatory { "outputQuery": { "signatories": { "$elemMatch": { "name": { "$contains": "John" }, "role": "CEO" } } } } // Find menus with vegan options in the beverages category { "outputQuery": { "categories": { "$elemMatch": { "category_name": "BEVERAGES", "items": { "$elemMatch": { "dietary_tags": { "$contains": "Vegan" } } } } } } } ``` ## Select Specific Fields Reduce response size by selecting only the fields you need from the extracted data. ```typescript theme={null} const response = await fetch('https://api.tela.com/task/query', { method: 'POST', headers: { 'Authorization': `Bearer ${API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ promptApplicationId: "your-app-uuid", outputQuery: { "valor": { "$gt": 100000 } }, select: ["cliente", "valor", "dataVencimento", "status"] }) }); ``` ```python theme={null} response = requests.post( 'https://api.tela.com/task/query', headers={ 'Authorization': f'Bearer {API_KEY}', 'Content-Type': 'application/json' }, json={ 'promptApplicationId': 'your-app-uuid', 'outputQuery': { 'valor': { '$gt': 100000 } }, 'select': ['cliente', 'valor', 'dataVencimento', 'status'] } ) ``` ## Pagination Use `limit` and `offset` for paginating through large result sets. ```typescript theme={null} // First page const page1 = await fetch('https://api.tela.com/task/query', { method: 'POST', headers: { 'Authorization': `Bearer ${API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ promptApplicationId: "your-app-uuid", limit: 20, offset: 0 }) }); // Second page const page2 = await fetch('https://api.tela.com/task/query', { method: 'POST', headers: { 'Authorization': `Bearer ${API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ promptApplicationId: "your-app-uuid", limit: 20, offset: 20 }) }); ``` *** ## Request Body | Field | Type | Required | Description | | --------------------- | --------- | -------- | ------------------------------------------------------------- | | `promptApplicationId` | string | Yes | UUID of the Workstation/App to query | | `filters` | object | No | Metadata filters (see below) | | `outputQuery` | object | No | Filters on extracted data fields | | `select` | string\[] | No | Fields to return from output. If empty, returns all fields | | `limit` | number | No | Max results to return (1-100, default: 50) | | `offset` | number | No | Number of results to skip (default: 0) | | `orderBy` | string | No | Sort field: `createdAt`, `updatedAt`, `approvedAt`, or `name` | | `order` | string | No | Sort direction: `asc` or `desc` | ### Filters Object | Field | Type | Description | | ----------------- | --------- | --------------------------------------------------------- | | `status` | string\[] | Filter by task status (e.g., `["approved", "completed"]`) | | `createdAtSince` | string | Tasks created after this ISO datetime | | `createdAtUntil` | string | Tasks created before this ISO datetime | | `approvedAtSince` | string | Tasks approved after this ISO datetime | | `approvedAtUntil` | string | Tasks approved before this ISO datetime | | `approvedBy` | string\[] | Filter by approver email addresses | | `createdBy` | string\[] | Filter by creator email addresses | | `tags` | string\[] | Filter by task tags | ### Output Query Operators | Operator | Example | Description | | ------------ | ------------------------------------- | ---------------------------- | | (none) | `"field": "value"` | Exact equality match | | `$gt` | `{ "$gt": 100 }` | Greater than | | `$gte` | `{ "$gte": 100 }` | Greater than or equal | | `$lt` | `{ "$lt": 100 }` | Less than | | `$lte` | `{ "$lte": 100 }` | Less than or equal | | `$eq` | `{ "$eq": 100 }` | Explicit equality | | `$ne` | `{ "$ne": 100 }` | Not equal | | `$in` | `{ "$in": ["a", "b"] }` | Value in array | | `$contains` | `{ "$contains": "text" }` | Case-insensitive text search | | `$elemMatch` | `{ "$elemMatch": {...} }` | Match element in array | | `$type` | `{ "$gt": "100", "$type": "number" }` | Override type inference | ### Type Inference The system automatically infers data types from values: | Value Format | Inferred Type | SQL Cast | | ------------------------ | ------------- | ------------- | | `100`, `3.14` | number | `::numeric` | | `true`, `false` | boolean | `::boolean` | | `"2025-03-01"` | date | `::date` | | `"2025-03-01T10:00:00Z"` | timestamp | `::timestamp` | | Other strings | string | (none) | Use `$type` to override when needed: ```json theme={null} { "codigo": { "$gt": "100", "$type": "number" } } ``` *** ## Response ```json theme={null} { "data": [ { "taskId": "550e8400-e29b-41d4-a716-446655440000", "taskName": "Invoice-2025-001.pdf", "status": "approved", "createdAt": "2025-01-15T10:30:00Z", "approvedAt": "2025-01-15T11:00:00Z", "output": { "cliente": "Acme Corporation", "valor": 150000, "dataVencimento": "2025-03-31" } } ], "meta": { "totalCount": 142, "limit": 20, "offset": 0, "currentPage": 1, "totalPages": 8 } } ``` ### Response Fields | Field | Type | Description | | ------------------- | -------------- | ---------------------------------------------- | | `data` | array | Array of matching tasks | | `data[].taskId` | string | Unique task identifier | | `data[].taskName` | string | Task name (usually the document filename) | | `data[].status` | string | Current task status | | `data[].createdAt` | string | ISO datetime when task was created | | `data[].approvedAt` | string \| null | ISO datetime when task was approved | | `data[].output` | object | Extracted data (all fields or selected fields) | | `meta.totalCount` | number | Total number of matching tasks | | `meta.limit` | number | Results per page | | `meta.offset` | number | Number of results skipped | | `meta.currentPage` | number | Current page number | | `meta.totalPages` | number | Total number of pages | *** ## Error Responses | Status | Description | | ------ | ---------------------------------------------- | | `400` | Invalid request body or malformed query | | `401` | Missing or invalid data token | | `403` | Token doesn't have access to the specified app | | `404` | App not found | | `500` | Internal server error | # Create Test Cases Source: https://docs.tela.com/api-reference/test-case/create POST /test-case Create one or more test cases Create one or more test cases for a prompt. Test cases can be used to validate prompt performance and ensure consistent output across different scenarios. ## Create a Test Case ```typescript theme={null} const API_KEY = process.env.TELA_API_KEY; // Single test case const testCase = { title: "Customer Support Query Test", promptId: "prompt_uuid", messages: [ { role: "system", content: "You are a helpful customer support assistant." }, { role: "user", content: "My order hasn't arrived yet." } ], variables: { "customer_name": "John Doe", "order_id": "ORD-12345" }, variablesRichContent: {}, expectedOutput: "I'll help you track down your order, John. Let me look up order ORD-12345 for you.", metadata: { source: "craft" } }; const response = await fetch('https://api.tela.ai/test-case', { method: 'POST', headers: { 'Authorization': `Bearer ${API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify(testCase) }); const data = await response.json(); console.log(data); ``` ```python theme={null} import requests API_KEY = os.getenv('TELA_API_KEY') # Single test case test_case = { "title": "Customer Support Query Test", "promptId": "prompt_uuid", "messages": [ { "role": "system", "content": "You are a helpful customer support assistant." }, { "role": "user", "content": "My order hasn't arrived yet." } ], "variables": { "customer_name": "John Doe", "order_id": "ORD-12345" }, "variablesRichContent": {}, "expectedOutput": "I'll help you track down your order, John. Let me look up order ORD-12345 for you.", "metadata": { "source": "craft" } } response = requests.post( 'https://api.tela.ai/test-case', headers={ 'Authorization': f'Bearer {API_KEY}', 'Content-Type': 'application/json' }, json=test_case ) print(response.json()) ``` ```javascript theme={null} const API_KEY = process.env.TELA_API_KEY; // Single test case const testCase = { title: "Customer Support Query Test", promptId: "prompt_uuid", messages: [ { role: "system", content: "You are a helpful customer support assistant." }, { role: "user", content: "My order hasn't arrived yet." } ], variables: { "customer_name": "John Doe", "order_id": "ORD-12345" }, variablesRichContent: {}, expectedOutput: "I'll help you track down your order, John. Let me look up order ORD-12345 for you.", metadata: { source: "craft" } }; fetch('https://api.tela.ai/test-case', { method: 'POST', headers: { 'Authorization': `Bearer ${API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify(testCase) }) .then(response => response.json()) .then(data => { console.log(data); }) .catch(error => { console.error('Error:', error); }); ``` ## Create Multiple Test Cases ```typescript theme={null} const API_KEY = process.env.TELA_API_KEY; // Multiple test cases const testCases = [ { title: "Customer Support Query - Order Status", promptId: "prompt_uuid", messages: [ { role: "system", content: "You are a helpful customer support assistant." }, { role: "user", content: "My order hasn't arrived yet." } ], variables: { "customer_name": "John Doe", "order_id": "ORD-12345" }, variablesRichContent: {}, expectedOutput: "I'll help you track down your order, John. Let me look up order ORD-12345 for you.", metadata: { source: "craft" } }, { title: "Customer Support Query - Return Policy", promptId: "prompt_uuid", messages: [ { role: "system", content: "You are a helpful customer support assistant." }, { role: "user", content: "What's your return policy?" } ], variables: { "customer_name": "Jane Smith" }, variablesRichContent: {}, expectedOutput: "Our return policy allows returns within 30 days of purchase with a receipt.", metadata: { source: "craft" } } ]; const response = await fetch('https://api.tela.ai/test-case', { method: 'POST', headers: { 'Authorization': `Bearer ${API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify(testCases) }); const data = await response.json(); console.log(data); ``` ```python theme={null} import requests API_KEY = os.getenv('TELA_API_KEY') # Multiple test cases test_cases = [ { "title": "Customer Support Query - Order Status", "promptId": "prompt_uuid", "messages": [ { "role": "system", "content": "You are a helpful customer support assistant." }, { "role": "user", "content": "My order hasn't arrived yet." } ], "variables": { "customer_name": "John Doe", "order_id": "ORD-12345" }, "variablesRichContent": {}, "expectedOutput": "I'll help you track down your order, John. Let me look up order ORD-12345 for you.", "metadata": { "source": "craft" } }, { "title": "Customer Support Query - Return Policy", "promptId": "prompt_uuid", "messages": [ { "role": "system", "content": "You are a helpful customer support assistant." }, { "role": "user", "content": "What's your return policy?" } ], "variables": { "customer_name": "Jane Smith" }, "variablesRichContent": {}, "expectedOutput": "Our return policy allows returns within 30 days of purchase with a receipt.", "metadata": { "source": "craft" } } ] response = requests.post( 'https://api.tela.ai/test-case', headers={ 'Authorization': f'Bearer {API_KEY}', 'Content-Type': 'application/json' }, json=test_cases ) print(response.json()) ``` ```javascript theme={null} const API_KEY = process.env.TELA_API_KEY; // Multiple test cases const testCases = [ { title: "Customer Support Query - Order Status", promptId: "prompt_uuid", messages: [ { role: "system", content: "You are a helpful customer support assistant." }, { role: "user", content: "My order hasn't arrived yet." } ], variables: { "customer_name": "John Doe", "order_id": "ORD-12345" }, variablesRichContent: {}, expectedOutput: "I'll help you track down your order, John. Let me look up order ORD-12345 for you.", metadata: { source: "craft" } }, { title: "Customer Support Query - Return Policy", promptId: "prompt_uuid", messages: [ { role: "system", content: "You are a helpful customer support assistant." }, { role: "user", content: "What's your return policy?" } ], variables: { "customer_name": "Jane Smith" }, variablesRichContent: {}, expectedOutput: "Our return policy allows returns within 30 days of purchase with a receipt.", metadata: { source: "craft" } } ]; fetch('https://api.tela.ai/test-case', { method: 'POST', headers: { 'Authorization': `Bearer ${API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify(testCases) }) .then(response => response.json()) .then(data => { console.log(data); }) .catch(error => { console.error('Error:', error); }); ``` ### Request Body The request body can be either a single test case object or an array of test case objects. #### Test Case Object | Field | Type | Required | Description | | ---------------------- | ------ | -------- | -------------------------------------------------- | | `promptId` | string | Yes | UUID of the prompt to create test cases for | | `title` | string | No | Title of the test case | | `messages` | array | Yes | Array of message objects with role and content | | `variables` | object | No | Key-value pairs of variables used in the test case | | `variablesRichContent` | object | Yes | Key-value pairs of rich content variables | | `files` | array | No | Array of file objects to attach to the test case | | `expectedOutput` | string | No | Expected output for evaluation purposes | | `promptApplicationId` | string | No | UUID of the prompt application if applicable | | `metadata` | object | No | Metadata about the test case including source | #### Message Object | Field | Type | Required | Description | | --------- | ------ | -------- | ------------------------------------------------------------------------- | | `role` | string | Yes | Role of the message sender (user, system, assistant, function, developer) | | `content` | string | Yes | Content of the message | #### File Object | Field | Type | Required | Description | | -------------- | ------ | -------- | -------------------------------------- | | `index` | number | Yes | Index of the file in the array | | `name` | string | Yes | Name of the file | | `mimeType` | string | Yes | MIME type of the file | | `vaultUrl` | string | Yes | URL to the file in the vault | | `variableName` | string | Yes | Variable name associated with the file | | `url` | string | Yes | Public URL to the file | ### Response The response will contain the created test case(s) with all fields including the generated IDs and timestamps. # Delete Test Case Source: https://docs.tela.com/api-reference/test-case/delete DELETE /test-case/{id} Remove a test case Delete a test case by its unique identifier. ## Delete a Test Case ```typescript theme={null} const API_KEY = process.env.TELA_API_KEY; const testCaseId = 'test_case_uuid'; const response = await fetch(`https://api.tela.ai/test-case/${testCaseId}`, { method: 'DELETE', headers: { 'Authorization': `Bearer ${API_KEY}`, 'Content-Type': 'application/json' } }); const data = await response.json(); console.log(data); ``` ```python theme={null} import requests API_KEY = os.getenv('TELA_API_KEY') test_case_id = 'test_case_uuid' response = requests.delete( f'https://api.tela.ai/test-case/{test_case_id}', headers={ 'Authorization': f'Bearer {API_KEY}', 'Content-Type': 'application/json' } ) print(response.json()) ``` ```javascript theme={null} const API_KEY = process.env.TELA_API_KEY; const testCaseId = 'test_case_uuid'; fetch(`https://api.tela.ai/test-case/${testCaseId}`, { method: 'DELETE', headers: { 'Authorization': `Bearer ${API_KEY}`, 'Content-Type': 'application/json' } }) .then(response => response.json()) .then(data => { console.log(data); }) .catch(error => { console.error('Error:', error); }); ``` ### Parameters | Name | Type | Required | Description | | ---- | ------ | -------- | ----------------------------------- | | `id` | string | Yes | The UUID of the test case to delete | ### Response A successful deletion will return a 200 OK status. The test case will be marked as deleted but may still be retrievable with the deletedAt field populated. # Retrieve Test Cases Source: https://docs.tela.com/api-reference/test-case/get GET /test-case Get test cases for a prompt Retrieve test cases for a specific prompt. This endpoint returns test cases associated with the given prompt ID, with options to filter by prompt version and include generation data. ## Retrieve Test Cases ```typescript theme={null} const API_KEY = process.env.TELA_API_KEY; const promptId = 'prompt_uuid'; // Optional parameters const promptVersionIds = ['version_uuid1', 'version_uuid2']; // Optional const includeGenerations = true; // Optional // Build URL with query parameters const url = new URL('https://api.tela.ai/test-case'); url.searchParams.append('promptId', promptId); // Add optional parameters if provided if (promptVersionIds.length > 0) { promptVersionIds.forEach(id => url.searchParams.append('promptVersionIds', id)); } if (includeGenerations) { url.searchParams.append('includeGenerations', 'true'); } const response = await fetch(url, { method: 'GET', headers: { 'Authorization': `Bearer ${API_KEY}`, 'Content-Type': 'application/json' } }); const data = await response.json(); console.log(data); ``` ```python theme={null} import requests API_KEY = os.getenv('TELA_API_KEY') prompt_id = 'prompt_uuid' # Optional parameters prompt_version_ids = ['version_uuid1', 'version_uuid2'] # Optional include_generations = True # Optional params = { 'promptId': prompt_id } # Add optional parameters if provided if prompt_version_ids: params['promptVersionIds'] = prompt_version_ids if include_generations: params['includeGenerations'] = 'true' response = requests.get( 'https://api.tela.ai/test-case', headers={ 'Authorization': f'Bearer {API_KEY}', 'Content-Type': 'application/json' }, params=params ) print(response.json()) ``` ```javascript theme={null} const API_KEY = process.env.TELA_API_KEY; const promptId = 'prompt_uuid'; // Optional parameters const promptVersionIds = ['version_uuid1', 'version_uuid2']; // Optional const includeGenerations = true; // Optional // Build URL with query parameters const url = new URL('https://api.tela.ai/test-case'); url.searchParams.append('promptId', promptId); // Add optional parameters if provided if (promptVersionIds.length > 0) { promptVersionIds.forEach(id => url.searchParams.append('promptVersionIds', id)); } if (includeGenerations) { url.searchParams.append('includeGenerations', 'true'); } fetch(url, { method: 'GET', headers: { 'Authorization': `Bearer ${API_KEY}`, 'Content-Type': 'application/json' } }) .then(response => response.json()) .then(data => { console.log(data); }) .catch(error => { console.error('Error:', error); }); ``` ### Parameters | Name | Type | Required | Description | | -------------------- | --------- | -------- | -------------------------------------------------------------------------------- | | `promptId` | string | Yes | The UUID of the prompt to retrieve test cases for | | `promptVersionIds` | string\[] | No | Optional array of prompt version UUIDs to filter test cases by specific versions | | `includeGenerations` | boolean | No | Whether to include generation data in the response | ### Response The response returns an array of test case objects with the following properties: | Field | Type | Description | | ---------------------- | -------------- | --------------------------------------------------------------- | | `id` | string | Unique identifier for the test case (UUID) | | `title` | string | Title of the test case | | `messages` | array or null | Array of message objects with role and content | | `variables` | object or null | Key-value pairs of variables used in the test case | | `variablesRichContent` | object or null | Key-value pairs of rich content variables | | `files` | array or null | Array of file objects attached to the test case | | `promptId` | string | UUID of the prompt this test case belongs to | | `expectedOutput` | string or null | Expected output for evaluation purposes | | `answers` | object or null | Evaluation answers with good/bad results and evals | | `promptApplicationId` | string or null | UUID of the prompt application if applicable | | `metadata` | object or null | Metadata about the test case including source | | `createdAt` | string | Creation timestamp | | `updatedAt` | string | Last update timestamp | | `deletedAt` | string or null | Deletion timestamp if applicable | | `generations` | array | Array of generations (only included if includeGenerations=true) | #### Example Response ```json theme={null} [ { "id": "550e8400-e29b-41d4-a716-446655440000", "title": "Customer Support Query Test", "messages": [ { "role": "system", "content": "You are a helpful customer support assistant." }, { "role": "user", "content": "My order hasn't arrived yet." } ], "variables": { "customer_name": "John Doe", "order_id": "ORD-12345" }, "variablesRichContent": null, "files": null, "promptId": "550e8400-e29b-41d4-a716-446655440001", "expectedOutput": "I'll help you track down your order, John. Let me look up order ORD-12345 for you.", "answers": { "version1": { "good": [], "bad": [], "evals": [] } }, "promptApplicationId": null, "metadata": { "source": "craft" }, "createdAt": "2023-01-01T00:00:00.000Z", "updatedAt": "2023-01-02T00:00:00.000Z", "deletedAt": null, "generations": [] } ] ``` # Get Test Case by ID Source: https://docs.tela.com/api-reference/test-case/get-by-id GET /test-case/{id} Retrieve a specific test case Retrieve a specific test case by its unique identifier. ## Retrieve a Test Case ```typescript theme={null} const API_KEY = process.env.TELA_API_KEY; const testCaseId = 'test_case_uuid'; const response = await fetch(`https://api.tela.ai/test-case/${testCaseId}`, { method: 'GET', headers: { 'Authorization': `Bearer ${API_KEY}`, 'Content-Type': 'application/json' } }); const data = await response.json(); console.log(data); ``` ```python theme={null} import requests API_KEY = os.getenv('TELA_API_KEY') test_case_id = 'test_case_uuid' response = requests.get( f'https://api.tela.ai/test-case/{test_case_id}', headers={ 'Authorization': f'Bearer {API_KEY}', 'Content-Type': 'application/json' } ) print(response.json()) ``` ```javascript theme={null} const API_KEY = process.env.TELA_API_KEY; const testCaseId = 'test_case_uuid'; fetch(`https://api.tela.ai/test-case/${testCaseId}`, { method: 'GET', headers: { 'Authorization': `Bearer ${API_KEY}`, 'Content-Type': 'application/json' } }) .then(response => response.json()) .then(data => { console.log(data); }) .catch(error => { console.error('Error:', error); }); ``` ### Parameters | Name | Type | Required | Description | | ---- | ------ | -------- | ------------------------------------- | | `id` | string | Yes | The UUID of the test case to retrieve | ### Response The response returns a single test case object with the following properties: | Field | Type | Description | | ---------------------- | -------------- | -------------------------------------------------- | | `id` | string | Unique identifier for the test case (UUID) | | `title` | string | Title of the test case | | `messages` | array or null | Array of message objects with role and content | | `variables` | object or null | Key-value pairs of variables used in the test case | | `variablesRichContent` | object or null | Key-value pairs of rich content variables | | `files` | array or null | Array of file objects attached to the test case | | `promptId` | string | UUID of the prompt this test case belongs to | | `expectedOutput` | string or null | Expected output for evaluation purposes | | `answers` | object or null | Evaluation answers with good/bad results and evals | | `promptApplicationId` | string or null | UUID of the prompt application if applicable | | `metadata` | object or null | Metadata about the test case including source | | `createdAt` | string | Creation timestamp | | `updatedAt` | string | Last update timestamp | | `deletedAt` | string or null | Deletion timestamp if applicable | #### Example Response ```json theme={null} { "id": "550e8400-e29b-41d4-a716-446655440000", "title": "Customer Support Query Test", "messages": [ { "role": "system", "content": "You are a helpful customer support assistant." }, { "role": "user", "content": "My order hasn't arrived yet." } ], "variables": { "customer_name": "John Doe", "order_id": "ORD-12345" }, "variablesRichContent": null, "files": null, "promptId": "550e8400-e29b-41d4-a716-446655440001", "expectedOutput": "I'll help you track down your order, John. Let me look up order ORD-12345 for you.", "answers": { "version1": { "good": [], "bad": [], "evals": [] } }, "promptApplicationId": null, "metadata": { "source": "craft" }, "createdAt": "2023-01-01T00:00:00.000Z", "updatedAt": "2023-01-02T00:00:00.000Z", "deletedAt": null } ``` # Update Test Case Source: https://docs.tela.com/api-reference/test-case/update PATCH /test-case/{id} Update an existing test case Update an existing test case by its unique identifier. ## Update a Test Case ```typescript theme={null} const API_KEY = process.env.TELA_API_KEY; const testCaseId = 'test_case_uuid'; const updateData = { title: "Updated Customer Support Query Test", expectedOutput: "I'll help you track down your order right away, John. Let me check the status of order ORD-12345.", messages: [ { role: "system", content: "You are a helpful and efficient customer support assistant." }, { role: "user", content: "My order hasn't arrived yet." } ] }; const response = await fetch(`https://api.tela.ai/test-case/${testCaseId}`, { method: 'PATCH', headers: { 'Authorization': `Bearer ${API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify(updateData) }); const data = await response.json(); console.log(data); ``` ```python theme={null} import requests API_KEY = os.getenv('TELA_API_KEY') test_case_id = 'test_case_uuid' update_data = { "title": "Updated Customer Support Query Test", "expectedOutput": "I'll help you track down your order right away, John. Let me check the status of order ORD-12345.", "messages": [ { "role": "system", "content": "You are a helpful and efficient customer support assistant." }, { "role": "user", "content": "My order hasn't arrived yet." } ] } response = requests.patch( f'https://api.tela.ai/test-case/{test_case_id}', headers={ 'Authorization': f'Bearer {API_KEY}', 'Content-Type': 'application/json' }, json=update_data ) print(response.json()) ``` ```javascript theme={null} const API_KEY = process.env.TELA_API_KEY; const testCaseId = 'test_case_uuid'; const updateData = { title: "Updated Customer Support Query Test", expectedOutput: "I'll help you track down your order right away, John. Let me check the status of order ORD-12345.", messages: [ { role: "system", content: "You are a helpful and efficient customer support assistant." }, { role: "user", content: "My order hasn't arrived yet." } ] }; fetch(`https://api.tela.ai/test-case/${testCaseId}`, { method: 'PATCH', headers: { 'Authorization': `Bearer ${API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify(updateData) }) .then(response => response.json()) .then(data => { console.log(data); }) .catch(error => { console.error('Error:', error); }); ``` ### Parameters | Name | Type | Required | Description | | ---- | ------ | -------- | ----------------------------------- | | `id` | string | Yes | The UUID of the test case to update | ### Request Body The request body can include any of the following fields to update. Only include the fields you want to modify. | Field | Type | Description | | ---------------------- | ------ | -------------------------------------------------- | | `title` | string | Title of the test case | | `messages` | array | Array of message objects with role and content | | `variables` | object | Key-value pairs of variables used in the test case | | `variablesRichContent` | object | Key-value pairs of rich content variables | | `files` | array | Array of file objects to attach to the test case | | `promptId` | string | UUID of the prompt this test case belongs to | | `expectedOutput` | string | Expected output for evaluation purposes | | `answers` | object | Evaluation answers with good/bad results and evals | | `promptApplicationId` | string | UUID of the prompt application if applicable | | `metadata` | object | Metadata about the test case including source | ### Response The response will contain the updated test case with all fields reflecting the changes. # Features Source: https://docs.tela.com/en/changelog/features Keep track of Tela updates ## Dashboard Redesign We've completely reimagined how you interact with your workspace. The new dashboard features a fresh, modern design with improved navigation and better project visibility. You can now delete projects directly from the Project Management dialog (with confirmation), switch between workspaces seamlessly with our new modal interface, and favorite your most-used apps for quick access. The dashboard also now properly filters canvases by your selected project, making it easier to focus on what matters. New dashboard interface with improved navigation *** ## AI & Agent Capabilities Access the latest Claude Sonnet 4.5 and Haiku 4.5 models for enhanced performance in your workflows. We've added a performance mode selector for agents, letting you optimize for speed or thoroughness based on your needs. The prompt assistant now works seamlessly with agent modules, and agents properly recognize and handle file references (like Vault URLs) from step outputs. New AI models and agent performance modes *** ## Workstation Enhancements Configure custom headers for your webhook subscriptions, giving you more control when integrating with external services. We've also added skeleton loading states throughout the workstation to eliminate those frustrating white screens when opening tasks. Custom webhook headers and improved loading states *** ## Documentation & Localization Documentation links now automatically detect your language preference (Portuguese/English) and adapt accordingly. API documentation is context-aware too, showing workstation or workflow docs based on where you are in the app. We've also improved Portuguese (pt-BR) translations throughout the platform. *** ## Critical Fixes ### Workflows Running Infinitely Workflows were running indefinitely without stopping, even when encountering API errors like 401s. Some workflows ran for over 2 hours without terminating, consuming resources unnecessarily. *** ### App Crashes with Nested Objects The application would freeze and enter an infinite loop when working with lists of objects nested inside other objects, making certain workflows completely unusable. *** ### Access Control Bypass Users without proper permissions could access canvases and workflows by using direct URLs, bypassing team-based access controls - a significant security vulnerability. *** ### Test Case Streaming Streaming of attributes in test cases stopped working entirely, breaking real-time updates during test execution and making it difficult to monitor progress. *** ### Workflow Editor Issues Multiple critical editor problems were affecting productivity: variables would disappear when renamed, files couldn't be uploaded to AI/LLM modules, consecutive file nodes wouldn't render correctly, and modules would disappear when placed inside conditionals. *** ## Additional Improvements ### Other Fixes * Fixed API calls failing with optional snake\_case variables * Fixed workflow spec to graph conversion * Fixed agent actions incorrectly handling nested file references * Fixed conditional steps missing auto-generated names * Fixed ordered lists breaking with double-digit numbers * Fixed variable input clearing unexpectedly during editing * Fixed test cases showing from previous prompt * Fixed document variables display in workflow preview * Fixed PDF preview cutting off first page * Fixed admins unable to manage projects outside their teams * Fixed blue outline visual bug in agent module * Fixed model list ordering * Fixed Clojure API code generation * Fixed documentation URLs * Added validation for multimodal file types before task creation ### UX Enhancements * Added ability to cancel file parsing in test cases * Improved report issue tooltip *** ## Contributors Thanks to all the contributors who made this release possible: * Aluisio Amorim * Guilherme Oliveira * Gustavo * Ian Fireman * Jonatas Venancio * Lucas Siqueira * Matheus Vellone * Neto Barbosa * Rodolpho Bravo * Rodrigo Godinho * Roz * Thierry Santos * ThullyoCunha ## New Version History Layout You can now view your Canvas version history in a more organized and intuitive way. The new layout allows you to: * View all changes made in each version * Easily restore previous versions This update makes version control more efficient and facilitates team collaboration. New version history layout interface ## Canvas Multimodal Support Canvas multimodal support feature Variables can now be resolved in a multimodal way, allowing your files to be processed natively by the model, without going through our standard parsing process. It's important to note that each model has different capabilities for supporting various file formats. Demonstration of multimodal file processing Format compatibility matrix by model: | | Image | Video | Document | Audio | | --------------- | :---: | :---: | :------: | :---: | | Gemini 2.5/2 | ✓ | ✓ | ✓ | ✓ | | Openai 4 family | ✓ | - | ✓ | - | | Claude 3/4 | ✓ | - | - | - | ## Canvas ### Share Output Format Now it's possible to copy an Output Format structure and use it in another Canvas. To do this, click the "Copy" button and use the "Paste" tool to import the same structure into a second Canvas. Copying output format structure between Canvas ### Variables with Multiple Files In this new version, variable fields within a Canvas can receive multiple files. Just drag a set of files and drop them onto the corresponding field. Adding multiple files to a variable field ## Workstation ### List Editing As an editor of a Task within the Workstation, it's often necessary to edit a list of items generated by the model. Now you can! The new update includes options to add, update, or remove items of any type within a list. List editing options in Workstation tasks ### Create Test Cases Reviewed a Task and reached a satisfactory result? Why not create a Test Case and help improve your initial Canvas? Now Tela suggests creating a new Test Case whenever a new task is approved. Test case creation suggestion after task approval ### New Tasks via API Tela now supports creating Tasks from API calls. This facilitates the integration flow between the development team and the team responsible for reviewing results and exporting documents in Tela. Inside the Workstation, we have a new "Connect via API" section, which contains copy-and-paste code snippets, making external integration easier. Creating tasks via API integration # Integrating via API Source: https://docs.tela.com/en/completion-api-guide Requirements: * [You have a published canvas](/en/craft/publishing) ## Get your canvas data We need two things to start using our canvas: 1. API Key 2. The canvas ID We can find both in the same place. First click on deploy button at the right top corner of the application interface. This will open the deploy options. Deploy There you will find how to deploy apps and how to connect to the API, click on "Or connect directly via API" at the bottom of the modal. Deploy Here you will find all the necessary info to start integrating! Choose your preferred language to get started. Deploy ## Constructing the Canvas Call Body To achieve the desired behavior for your business and integration, your canvas call can include variables, historical messages, streaming, and webhook responses for asynchronous integrations. Check out [API Reference](/api-reference/completion/create) for further details on our SDK and API specifications. ### Using Variables [Variables](/en/craft/using-variables) are a key concept in prompt building with Tela. They are used to store and reuse data throughout your prompts and can be either a manual input or a file. For information on supported file formats, visit [Supported files](/en/supported-file-formats). When we have defined variables at our prompt the connect modal will generate the code snippets for us already filled with the variables keys. Deploy Fill the keys with the values you want, it can be a text, a file URL or a file base64 string. ```typescript Typescript Tela SDK theme={null} variables: { document: tela.createFile('https://www.wmaccess.com/downloads/sample-invoice.pdf'), // It is possible to pass a URL, Buffer, Blob, etc. }, ``` ```python Python Tela SDK theme={null} variables={ "document": client.create_file("https://www.wmaccess.com/downloads/sample-invoice.pdf"), } ``` ```typescript File Url theme={null} variables: { document: { file_url: 'https://www.wmaccess.com/downloads/sample-invoice.pdf', } } ``` ```typescript Base 64 theme={null} variables: { document: { file_url: 'data:application/pdf;base64,JVBERi0xLjUKJeLjz9MKNCAwIG9ia...', } } ``` ```typescript Text theme={null} variables: { document: "Document Content!" } ``` [Check out](/api-reference/completion/create#files) file usage details with our SDK ### Using Messages The Tela API allows you to include messages in your requests by extending the request body with the `messages` key. This feature enables you to provide context or additional information to the model, enhancing the interaction and output quality. ```typescript Typescript Tela SDK theme={null} const completion = await tela.completions.create<{ document: TelaFile }, { fileSummary: string }>({ canvasId: process.env.TELA_CANVAS_ID, variables: { document: tela.createFile('https://www.wmaccess.com/downloads/sample-invoice.pdf') }, messages: [ { role: 'assistant', content: { type: 'text', text: 'Hello, how can I assist you today?' } }, { role: 'user', content: { type: 'image_url', image_url: { url: 'https://example.com/image.png', detail: 'high' } } } ] }) ``` ```typescript Javascript Tela SDK theme={null} const completion = await tela.completions.create({ ..., messages: [ { role: 'assistant', content: { type: 'text', text: 'Hello, how can I assist you today?' } }, { role: 'user', content: { type: 'image_url', image_url: { url: 'https://example.com/image.png', detail: 'high' } } } ] }) ``` ```python Python Tela SDK theme={null} completion = client.completions.create( ..., messages=[ { "role": "assistant", "content": { "type": "text", "text": "Hello, how can I assist you today?" } }, { "role": "user", "content": { "type": "image_url", "image_url": { "url": "https://example.com/image.png", "detail": "high" } } } ] ) ``` ```sh Curl theme={null} curl -XPOST https://api.tela.com/v2/chat/completions \ -H 'Content-Type: application/json' \ -H 'Authorization: Bearer API-KEY' \ -d '{ ..., "messages": [ { "role": "assistant", "content": { "type": "text", "text": "Hello, how can I assist you today?" } }, { "role": "user", "content": { "type": "image_url", "image_url": { "url": "https://example.com/image.png", "detail": "high" } } } ], }' ``` The sequence of messages in the array is crucial as it represents the chronological flow of the conversation. Each cycle begins with a user's message and is followed by the assistant's response, maintaining the dialogue's context and coherence. Check out Running a Canvas [API Reference](/api-reference/completion/create) body for further details on the message schema. ### Streaming To enable streaming for the canvas call, simply include the `stream` key set to `true` in the request body. ```typescript Typescript Tela SDK theme={null} const completion = await tela.completions.create<{ document: TelaFile }, { fileSummary: string }>({ canvasId: process.env.TELA_CANVAS_ID, variables: { document: tela.createFile('https://www.wmaccess.com/downloads/sample-invoice.pdf') }, stream: true }) ``` ```typescript Javascript Tela SDK theme={null} const completion = await tela.completions.create({ canvasId: process.env.TELA_CANVAS_ID, variables: { document: tela.createFile('https://www.wmaccess.com/downloads/sample-invoice.pdf') }, stream: true }) ``` ```python Python Tela SDK theme={null} completion = client.completions.create( canvas_id=TELA_CANVAS_ID, variables={ "document": client.create_file("https://www.wmaccess.com/downloads/sample-invoice.pdf"), }, stream=True, ) ``` ```sh Curl theme={null} curl -XPOST https://api.tela.com/v2/chat/completions \ -H 'Content-Type: application/json' \ -H 'Authorization: Bearer API-KEY' \ -d '{ "canvas_id": "95cec438-6080-44fe-8275-28b16bd40178", "variables": { "document": "data:application/pdf;base64,JVBERi0xLjUKJeLjz9MKNCAwIG9ia..." }, "stream": true }' ``` See [Streaming API Reference](/api-reference/completion/create#streaming) for more details consuming the stream. ### Async To achieve asynchronous behavior, you have two options: * Using the `async` flag * Using a webhook #### Using Async flag Extending the body with an `async` key set to `true` is the easiest way to achieve asynchronous behavior, allowing you to continue with other tasks while waiting for the completion. An `id` will be returned so you can monitor the status of the call. ```sh Curl theme={null} curl -XPOST https://api.tela.com/v2/chat/completions \ -H 'Content-Type: application/json' \ -H 'Authorization: Bearer API-KEY' \ -d '{ ..., "async": true }' ``` See [Get completion API Reference](/api-reference/completion/get) for how to consume the completion with the received `id` #### Using Webhook You can pass a webhook URL to our API extending the body with an `webhook_url` key, this will make the completion asynchronous, and you will receive the response when the completion is finished with a POST request on the URL you provided. ```typescript Typescript Tela SDK theme={null} const completion = await tela.completions.create<{ document: TelaFile }, { fileSummary: string }>({ canvasId: process.env.TELA_CANVAS_ID, variables: { document: tela.createFile('https://www.wmaccess.com/downloads/sample-invoice.pdf') }, webhook_url: "https://example.com/webhook" }) ``` ```typescript Javascript Tela SDK theme={null} const completion = await tela.completions.create({ ..., webhook_url: "https://example.com/webhook" }) ``` ```python Python Tela SDK theme={null} completion = client.completions.create( ..., webhook_url="https://example.com/webhook" ) ``` ```sh Curl theme={null} curl -XPOST https://api.tela.com/v2/chat/completions \ -H 'Content-Type: application/json' \ -H 'Authorization: Bearer API-KEY' \ -d '{ ..., "webhook_url": "https://example.com/webhook" }' ``` When the `webhook_url` key is used, the call becomes asynchronous automatically, eliminating the need to include the async flag. # Webhook Payload Structure When your completion finishes, Tela will send a POST request to your webhook URL with the following structure: **Headers:** * `x-completion-run-id`: The unique identifier of the completion run * `Content-Type`: `application/json` **Body:** The webhook payload contains the raw completion result: ``` { "usage": { "total_tokens": , "prompt_tokens": , "completion_tokens": }, "object": "chat.completion", "choices": [ { "message": { "role": "assistant", "content": <... your completion content> } } ], "created": } ``` You can use the `x-completion-run-id` header to correlate the webhook response with your original request and track completion status in your application. # Editing Prompts Source: https://docs.tela.com/en/craft/editing-prompts The left side of the Craft experience is your Prompt Editor. This is the space to write and refine your Prompt. A **Prompt** is a piece of text or an instruction that you provide to a natural language model in order to generate a specific response or output You should approach writing your Prompt as you would writing a guide for a new colleague that is capable, but has little context of your reality. A few principles that can help are: 1. **Understand What You Want from the LLM -** Before writing your prompt, it's essential to know what you're trying to achieve. 2. **Start Simple -** Begin with a straightforward sentence or question 3. **Be Specific and Clear -** The more specific your prompt, the more accurate the response. Avoid vague or overly broad questions. 4. **Give Context or Background When Needed -** If you want a response to focus on a particular area, include some context or background information. This can help guide the LLM toward a more useful answer. 5. **Use Constraints for More Focused Output -** If you want the answer in a particular format or within certain constraints, include that in your prompt. You can also use [Output Format](/en/craft/output-format) to clarify what you need as a response. 6. **Use Step-by-Step Instructions for Complex Queries -** For more complicated requests, break down your prompt into steps. This can help the LLM follow your logic and respond more effectively. 7. **Use Markdown Formatting** - You can leverage formatting to help the LLM understand the Prompt structure or focus on key parts of the text, as they are sensitive to markdown. **Finally, keep experimenting and iterating** - You don't need to get the prompt perfect on the first try. Often, the best results come from iterating based on the responses you get. And don't worry, we versionate the prompt and keep everything saved, so you can always go back to a previous version, if needed. Feel free to tweak and adjust your prompt. That's why we built **Tela**. # Output Format Source: https://docs.tela.com/en/craft/output-format ## What is Output Format? Tela allows you to define a structured Output Format for your prompts. **Output Format** is a way to communicate to your Canvas how you want to receive the response, providing guidelines that will help the model extract information with precision. Structuring an **Output Format** is useful when: * You want your output to consistently be generated within the same framework * You need to integrate your Canvas with other processes, that require structured data * You want to organize the output from your Canvas in lists or tables ## Setting up your Output Format To add an **Output Format** to your Canvas you can to click on the `Output Format +` button on the bottom left of the Craft experience. Output Format + This will open the Output Format interface, where you can click `Add Attribute +` to create your first attribute. ### Creating attributes Attributes are the guidelines that will tell your Canvas what to respond and how to structure the content. Each attribute has a label, a description and a type. * **Label**: a relevant name for the attribute, similar to naming a variable when programming or a column header on a table. We recommend using only letters, numbers and underscores for your labels. * **Description**: a short text describing what is the desired output and how it should be formatted, if applicable. This can complement and reinforce the instructions for your Prompt. * **Type** - The type of attribute, which can be: * **Text** - textual or string content * **Number** - numeric content * **Boolean** - Either TRUE or FALSE * **List** - a list of Attributes of variable length * **Object** - a group of Attributes representing an object * **Table** - a table of Attributes of variable length Lists and objects are powerful types of attributes that can be used together in several applications. Since they are more complex, the next section will provide more details about how to use them. #### Diving deeper into Lists **Lists** are useful when you can have more than one valid response for an attribute. Using lists, you can tell the Canvas to bring you everything that can be a valid response for that attribute. When using a list, you will notice that it requires a new Type, which will be the Type of the elements inside your list. Supposing you have a Canvas that presents a story and your objective is to extract the name of every character in the story. You can create an Output Format with the attribute `listOfCharacters` having the `Text` type since the names are strings of text: Output Format interface with listOfCharacters attribute With this list attribute, your Canvas will know to look for more than one character in your story and the response will contain a list of all the characters found! #### Diving deeper into Objects **Objects** are often described as a digital representation of something in the real world, they can have any number of **attributes** that will provide information related to it. You will be asked to provide a **type** to the objects you create. It can be any one of Tela's attribute types, even other Objects and Lists, allowing you to create complex structures if you need. Supposing you have a Canvas about cars and you need to extract all the relevant characteristics of a car from a document. You can create an Output Format with an Object `car` containing the following attributes: Output Format interface with object 'car' You can note that many types have been used in this example, even a list of objects to represent the previous owners. # Publishing a version Source: https://docs.tela.com/en/craft/publishing To utilize a Canvas through the API or within an App, it is required to publish a version of your Canvas. To publish a version, navigate to the top right corner of the application interface. Publish Version Publishing a new version will overwrite the current published version. ## Going back to a previous version If you need to go back to a previous version of your Canvas, you can click the version selector on the top left corner of your Canvas page. If you have already published a version, this version will be marked with the tag `Production`. Canvas version selector Clicking on this component will open a list with all your Canvas' versions. Feel free to click other versions in order to see how your Canvas evolved through time. Every version is saved and you can always go back to your published version or publish a different version if needed! # Selecting Models Source: https://docs.tela.com/en/craft/selecting-models A different way of understanding **Tela** is as a natural language development layer above Large Language Models (LLMs). **Large Language Model (LLM)** is a type of artificial intelligence model designed to process and generate human-like text by understanding the relationships between words, phrases, and context. At **Tela**, we do all the hard work of integrating and doing business with LLM providers, so you don't have to. When using our tool you will: * Access state-of-the-art LLMs, such as OpenAI's ChatGPT and Google's Gemini * Seamlessly test and use different LLMs, finding the one that best works for your application * Benefit from high usage limits from our enterprise license * Receive centralized billing for all your applications, including those using different LLM providers To select the model you want to use, simply go to the bottom right of the Prompt Editor pane and browse the list of available LLMs. Language Model selector button You will notice some models have a date in their names. This is a specific version of the model released on that date. We keep it available to allow users to *pin* their versions, avoiding possible inconsistencies in newer versions of the same model. ## Available Models Here is a list with links to the documentation of the LLMs supported by **Tela** at the moment: * **OpenAI** * [GPT 4o](https://platform.openai.com/docs/models#gpt-4o) * [GPT 4o mini](https://platform.openai.com/docs/models#gpt-4o-mini) * [GPT 4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4) * **Anthropic** * [Claude 3.5 Sonnet](https://www.anthropic.com/claude/sonnet) * [Claude 3 Opus](https://docs.anthropic.com/en/docs/intro-to-claude#claude-3-family) * [Claude 3 Sonnet](https://docs.anthropic.com/en/docs/intro-to-claude#claude-3-family) * [Claude 3 Haiku](https://docs.anthropic.com/en/docs/intro-to-claude#claude-3-family) * **Google** * [Gemini 1.5 Pro](https://deepmind.google/technologies/gemini/pro/) * [Gemini 1.5 Flash](https://deepmind.google/technologies/gemini/flash/) ## Temperature You can configure the Temperature of the LLM by clicking on the gear icon next to the LLM name. We recommend a Temperature of zero when working with repeatable and deterministic processes. In the LLM context, **temperature** is the amount of *randomness* the model is allowed to use. Generally, lower temperatures tend to generate more assertive responses while higher temperatures are more unexpected, allowing the model to be "creative". # Using Variables Source: https://docs.tela.com/en/craft/using-variables ## What is a Variable? Tela allows you to include Variables within your Prompt. **Variables** indicate a place in your prompt that can have it's content changed dynamically. You can vary it's content as you need without having to modify your Canvas directly. **Variables** are useful when you want to: * Have a place to dynamically provide content to your Canvas, depending on your need at runtime * Test different possible scenarios without having to edit the core of the Prompt itself, just changing the Variables * Use a Canvas with a specific context to understand and process the content of a variable and return some result ## Creating a variable You can create a Variable from anywhere in the Prompt Editor by simply typing the Variable name within curly brackets, such as **\{variableName}**. Please keep your variable names simple, we recommend using only letters, numbers and underscores (`_`). There's no limitation to how many variables you can create, but some of our features work best with a single variable per canvas. As soon as you create a Variable, you will see a place to input a value for the Variable on the Preview pane, on the right side of the page. ## Providing the content of a variable In the preview pane, you will see a field for each one of your variables. This is where you can provide the content of your variables and edit it for different runs without having to edit your Canvas directly. You can provide the content of a variable in the following ways: * Writing some text on the field. * Dragging a file from your computer into the variable field. * Pressing `/` to open our commands and selecting the "Add file" option. For information on supported file formats, visit [Supported files](/en/supported-file-formats). Check out [Integrating via API](/en/completion-api-guide#using-variables) for further details on how to use variables with our API # Creating new tasks Source: https://docs.tela.com/en/creating-tasks Within your Workstation App you can create tasks and review their output to check if the answers are correct. ## Creating tasks via API Creating a new task is similar to making a call to your Canvas, except that instead of passing your `canvas_id`, you're going to pass your `application_id`. Head to your Workstation App page and follow these steps: At the top bar, click on the three dots on the right of your app's title and select "Connect via API". Connect via API Here you will find all the necessary info to start creating tasks with our API. Choose your preferred language and use one of the snippets provided to get started! Connect modal By default, all tasks are created as if they were async completions. You'll receive a `success` response and the task will have a `running` status. You have two options for consuming the result of the task: * Polling during execution * Passing a webhook URL ### Polling tasks during execution You can do a simple polling until the status of the task changes to either `completed` or `failed`. To poll the status of a task after creating it we can make a request to our API `/task` endpoint. ```typescript TypeScript Tela SDK theme={null} let task = await tela.completions.create<{ document: TelaFile }, { fileSummary: string }>({ applicationId: process.env.TELA_APPLICATION_ID, variables: { document: tela.createFile('https://example.com/document.pdf') }, }) let isTaskCompleted = false while (!isTaskCompleted) { const response = await fetch(`https://api.tela.com/task/${task.id}`) task = await response.json() isTaskCompleted = task.status === 'completed' || task.status === 'failed' } ``` ```javascript JavaScript Tela SDK theme={null} let task = await tela.completions.create({ applicationId: process.env.TELA_APPLICATION_ID, variables: { document: tela.createFile('https://example.com/document.pdf') }, }) let isTaskCompleted = false while (!isTaskCompleted) { const response = await fetch(`https://api.tela.com/task/${task.id}`) task = await response.json() isTaskCompleted = task.status === 'completed' || task.status === 'failed' } ``` ```python Python Tela SDK theme={null} task = tela.completions.create( application_id=os.getenv('TELA_APPLICATION_ID'), variables={ "document": tela.create_file("https://example.com/document.pdf") } ) is_task_completed = False while not is_task_completed: response = requests.get(f'https://api.tela.com/task/{task.id}') task = response.json() is_task_completed = task.status in ['completed', 'failed'] ``` ### Passing a webhook URL You can pass a `webhook_url` to your API call. This way you'll receive a `POST` request in your endpoint when the task finishes. #### Webhook Response Structure When a task completes, your webhook endpoint will receive a POST request with the following structure: ```json theme={null} { "id": "b83b1558-de5b-4e45-b517-7bececbdd154", "status": "succeeded", "input_content": { "variables": { "document": { "file_url": "https://example.com/document.pdf" } }, "messages": [] }, "output_content": { "role": "assistant", "structured_content": false, "content": { "fileSummary": "This document contains quarterly financial reports showing a 15% revenue increase compared to last quarter, with key highlights in operational efficiency improvements and market expansion in the APAC region." } }, "raw_input": { "application_id": "95cec438-6080-44fe-8275-28b16bd40178", "variables": { "document": { "file_url": "https://example.com/document.pdf" } }, "webhook_url": "https://example.com/webhook" }, "raw_output": { "fileSummary": "This document contains quarterly financial reports showing a 15% revenue increase compared to last quarter, with key highlights in operational efficiency improvements and market expansion in the APAC region." }, "compatibility_date": "2024-10-14", "prompt_version_id": "76065caa-c519-441a-8fe2-cc43e29664ab", "prompt_application_id": "95cec438-6080-44fe-8275-28b16bd40178", "workspace_id": "3f55cfd9-4427-4887-a181-d9ad39967802", "created_at": "2024-10-14T19:00:53.664Z", "updated_at": "2024-10-14T19:01:15.892Z" } ``` Key fields in the webhook response: * `status`: Can be "created", "running", "succeeded", or "failed" * `input_content`: Contains the variables and messages sent in the request * `output_content`: Contains the structured output from your Canvas, matching your defined output schema * `raw_output`: The raw output data in the format defined by your Canvas outputs ```typescript Typescript Tela SDK theme={null} const task = await tela.completions.create<{ document: TelaFile }, { fileSummary: string }>({ applicationId: process.env.TELA_APPLICATION_ID, variables: { document: tela.createFile('https://example.com/document.pdf') }, webhook_url: "https://example.com/webhook" }) ``` ```javascript Javascript Tela SDK theme={null} const task = await tela.completions.create({ applicationId: process.env.TELA_APPLICATION_ID, variables: { document: tela.createFile('https://example.com/document.pdf') }, webhook_url: "https://example.com/webhook" }) ``` ```python Python Tela SDK theme={null} task = tela.completions.create( application_id=os.getenv('TELA_APPLICATION_ID'), variables={ "document": tela.create_file("https://example.com/document.pdf") } webhook_url="https://example.com/webhook" ) ``` ```sh cURL theme={null} curl -XPOST https://api.tela.com/v2/chat/completions \ -H 'Content-Type: application/json' \ -H 'Authorization: Bearer $TELA_API_KEY' \ -d '{ "application_id": $TELA_APPLICATION_ID, "variables": { "document": { file_url: "https://example.com/document.pdf" } }, "webhook_url": "https://example.com/webhook" }' ``` ## Fetching tasks To retrieve tasks from an app, you can send a `GET` request to the `/task` endpoint. This endpoint supports various optional parameters for filtering and pagination: ### Pagination parameters * `limit`: Number of results per response (default: 10, range: 1-100) * `offset`: Starting position of results (default: 0) ### Filtering parameters * `status`: Array of task statuses (options: "created", "running", "validating", "completed", "failed") * `since`: Start date for filtering tasks (format: timestamp or ISO 8601) * `until`: End date for filtering tasks (format: timestamp or ISO 8601) * `approvedBy`: Email of the user who approved the task * `revisionProcess`: Type of revision received (options: "approvedWithRevisions", "approvedDirectly") * `promptApplicationId`: ID of the application where the task was created * `promptVersionId`: ID of the Canvas version used to create the task * `ids`: Array of specific task IDs to retrieve ### Sorting parameters * `orderBy`: Field to sort by (options: "name", "reference", "approvedAt", "createdAt", "updatedAt", "id", "status", "approvedBy", "createdBy") * `order`: Sort direction (options: "asc" or "desc") ### Example request Here's an example of fetching 50 completed tasks created after November 1st, 2024, sorted by name in ascending order: ```javascript JavaScript theme={null} const url = new URL('https://api.tela.com/task') url.searchParams.append('limit', '50') url.searchParams.append('promptApplicationId', '{YOUR_APPLICATION_ID}') url.searchParams.append('status', JSON.stringify(['completed'])) url.searchParams.append('since', '2024-11-01T00:00:00Z') url.searchParams.append('orderBy', 'name') url.searchParams.append('order', 'asc') const response = await fetch(url, { method: 'GET', headers: { 'Authorization': `Bearer ${process.env.TELA_API_KEY}`, 'Content-Type': 'application/json', }, }); const tasks = await response.json() ``` ```python Python theme={null} import requests import os response = requests.get( 'https://api.tela.com/task', headers={ 'Authorization': f'Bearer {os.getenv("TELA_API_KEY")}', 'Content-Type': 'application/json', }, params={ 'limit': 50, 'promptApplicationId': os.getenv('TELA_APPLICATION_ID'), 'status': '["completed"]', 'since': '2024-11-01T00:00:00Z', 'orderBy': 'name', 'order': 'asc', } ) tasks = response.json() ``` ```sh cURL theme={null} curl -X GET "https://api.tela.com/task?limit=50&promptApplicationId=$TELA_APPLICATION_ID&status=%5B%22completed%22%5D&since=2024-11-01T00%3A00%3A00Z&orderBy=name&order=asc" \ -H "Authorization: Bearer $TELA_API_KEY" \ -H "Content-Type: application/json" ``` # Creating your first Canvas Source: https://docs.tela.com/en/creating-your-first-canvas ## Accessing your Project All **Canvases** in Tela are related to a **Project**. If you don't have any Projects, you need to create a new one before creating a new **Canvas**. You can create a new project by clicking on **Project +** in the "Projects" section of your *dashboard* To access your **Project's page**, click it's name on the left sidebar. ## Creating a Canvas Inside the Project page, following the next steps will create your first Canvas: 1. Click on **Create canvas +** 2. Name your new Canvas Alternatively, if your Project already has at least one Canvas, you can create a new **Canvas** directly from the project page by clicking the sign on the top right corner. When a new **Canvas** is created, you will be automatically redirected to it's page. ## Understanding the Canvas page Every Canvas has three different experiences, comprising the entire life cycle of your natural language applications. You can navigate through them using the top center icons. These experiences are: * **Craft**: The space to write and refine your prompts through quick iterations and no-code platform * **Test**: The space to test your application accuracy through structured repeatable tests * **Evaluate**: The space to set up evaluation criteria to assess your production inferences There are other very useful functions on the top menu, which we will explore later in this tutorial. * Top left: Versioning * Top right: Collaboration tools (Share, Deploy and Publish) # Credentials Source: https://docs.tela.com/en/credentials Securely store and manage sensitive API keys, tokens, and secrets for use in your workflows ## What are Credentials? Credentials are encrypted key-value pairs that allow you to securely store sensitive information like API keys, tokens, and secrets. Instead of hardcoding sensitive values in your workflow code, you can reference them by name and the system will securely inject them at runtime. ### Why Use Credentials? | Without Credentials | With Credentials | | ------------------------------------- | ------------------------------------------- | | API keys hardcoded in code | Values stored encrypted, referenced by name | | Risk of accidentally exposing secrets | Secrets never visible in logs or UI | | Difficult to rotate keys | Easy rotation without changing code | | Same key repeated across workflows | Single source of truth for all workflows | ## Managing Credentials ### Accessing Credentials Settings Navigate to **Workspace Settings** → **Credentials** to manage your workspace credentials. Credentials settings page in Workspace Settings ### Creating a Credential 1. Click **Add Credential** 2. Enter a **Key** (identifier you'll use in code, e.g., `SLACK_BOT_TOKEN`) 3. Enter the **Value** (the actual secret) 4. Click **Save** The credential value is encrypted before storage. Once saved, you cannot view the value again — you can only update or delete it. ### Credential Key Naming Choose descriptive, consistent names for your credential keys: ``` SLACK_BOT_TOKEN OPENAI_API_KEY DATABASE_PASSWORD GITHUB_ACCESS_TOKEN STRIPE_SECRET_KEY ``` Use uppercase with underscores for consistency, similar to environment variables. This makes it easy to identify credentials in your code. ### Updating a Credential (Rotation) To rotate a credential: 1. Find the credential in the list 2. Click **Edit** 3. Enter the new value 4. Click **Save** The new value takes effect immediately for any new workflow executions. ### Deleting a Credential Deleting a credential will cause any workflow referencing it to fail with a `missing_credential` error. Make sure no active workflows depend on the credential before deleting. ## Using Credentials in Code Execution In **Code Execution** steps, use the `credential()` function to retrieve secret values at runtime. ### JavaScript/TypeScript ```javascript theme={null} // Get a credential value const apiKey = credential("OPENAI_API_KEY"); // Use it in your code const response = await fetch("https://api.openai.com/v1/chat/completions", { headers: { "Authorization": `Bearer ${apiKey}`, "Content-Type": "application/json" }, // ... }); ``` ## Using Credentials in Agents When a workflow executes an **Agent step**, all workspace credentials are automatically available to the agent. You can instruct the agent to use a specific credential by referencing its name in your prompt or agent configuration. For example, you can tell the agent: *"Use the SLACK\_BOT\_TOKEN credential to send a message to the #general channel."* Credentials are inherited from the workflow's scope. The agent receives the same credentials available to the workflow that triggered it. ## How Credentials Work at Runtime When a workflow step executes: 1. The system resolves all available credentials for the workspace 2. Credentials are decrypted and injected into the step's runtime environment 3. The `credential()` function reads from this secure context 4. Credential values are kept in memory only — never persisted in logs, outputs, or state The system uses best-effort masking to prevent credential values from appearing in logs. However, if your code explicitly prints a credential value, it may still be visible. ## Credential Scopes Credentials can be defined at two levels: * **Workspace level**: Available to all workflows in the workspace (default) * **Workflow level**: Specific to a single workflow, overriding workspace credentials with the same key ### Workspace Credentials Workspace credentials are the default scope. They're available to all workflows and serve as the fallback when no workflow-scoped credential exists. ### Workflow-Scoped Credentials You can create credentials that are specific to a single workflow. This is useful when: * A workflow needs a different API key than the workspace default * You want to isolate credentials for security purposes * Different workflows connect to different environments (staging vs production) To manage workflow-scoped credentials: 1. Open the workflow you want to configure 2. Click the **Credentials** button in the workflow header 3. Add credentials specific to this workflow Workflow-scoped credentials modal Workflow-scoped credentials with the same key as workspace credentials will **override** the workspace value for that specific workflow only. ## Credentials Allowlist For additional security control, you can define an **allowlist** of credentials for each workflow. When an allowlist is configured, only the specified credentials are accessible during workflow execution. ### Why Use an Allowlist? | Scenario | Benefit | | ---------------------------- | ------------------------------------------------------- | | Principle of least privilege | Workflows only access credentials they need | | Multi-tenant workflows | Prevent accidental access to other clients' keys | | Security audits | Clear documentation of which secrets each workflow uses | | Onboarding new team members | Reduced risk from misconfigured workflows | ### Configuring the Allowlist 1. Open the workflow you want to configure 2. Click the **Credentials** button in the workflow header 3. Go to the **Usage Permissions** tab 4. Toggle on the credentials this workflow should have access to Credentials allowlist configuration If no allowlist is defined, the workflow has access to all workspace credentials (backwards compatible behavior). When using an allowlist, make sure to include all credentials your workflow needs. Missing credentials will cause the workflow to fail at runtime. ## Security Considerations ### Encryption All credential values are encrypted using **AES-256-GCM** before storage. Values are only decrypted at the moment of execution and kept in memory for the minimum time necessary. ### Access Control * Only **workspace administrators** can create, update, or delete credentials * Users with workflow edit/execute permissions can **reference** credentials by key, but cannot view values ## Error Handling ### Missing Credential If your code references a credential that doesn't exist: ```javascript theme={null} // This will throw an error if MY_KEY doesn't exist const value = credential("MY_KEY"); // Error: missing_credential - Credential 'MY_KEY' not found ``` **Solution:** Create the credential in Workspace Settings before running the workflow. ### Best Practice: Validate Early Check for required credentials at the start of your code: ```javascript theme={null} // Validate all required credentials upfront const slackToken = credential("SLACK_BOT_TOKEN"); const openaiKey = credential("OPENAI_API_KEY"); if (!slackToken || !openaiKey) { throw new Error("Missing required credentials"); } // Continue with your logic... ``` ## Best Practices * Use descriptive, consistent key names * Rotate credentials periodically * Use separate credentials for different services * Test workflows after credential rotation * Document what each credential is used for * Hardcode secrets in workflow code * Log or print credential values * Share credential values outside the system * Use the same credential for multiple purposes * Delete credentials without checking dependencies ## Frequently Asked Questions No. For security reasons, credential values cannot be retrieved after creation. You can only update (overwrite) or delete them. Each step resolves credentials at execution time. If a credential is rotated mid-run, subsequent steps will use the new value. Already-executing steps continue with the value they retrieved. Credentials are available in **Code Execution** steps and **Agent** steps via the `credential()` function. They cannot be used in workflow configuration fields or template strings. There's no strict limit, but we recommend keeping credentials organized and removing unused ones to maintain clarity. No. Credentials are scoped to a single workspace. Each workspace has its own isolated set of credentials. Workflow-scoped credentials take priority. If a workflow has a Workflow-scoped credential with key `API_KEY` and the workspace also has `API_KEY`, the workflow will use its own workflow-scoped value. Other workflows without a workflow-scoped `API_KEY` will continue using the workspace value. The workflow will fail with a `missing_credential` error when it tries to access the credential not in the allowlist. Make sure to test your workflows after configuring an allowlist. Yes. The allowlist controls which credential keys are accessible, and workflow-scoped credentials provide the values. If a key is in the allowlist, the system will first check for a workflow-scoped value, then fall back to workspace credentials. Credential values can be up to 64KB. For larger secrets, consider storing them in a dedicated secret manager and using a credential to store the access token. ## Summary | Feature | Description | | -------------- | ------------------------------------------------------------------------- | | **Storage** | Encrypted key-value pairs in workspace settings | | **Access** | Via `credential("KEY")` function in Code Execution and Agents | | **Scope** | Workspace-level or workflow-level (with override capability) | | **Allowlist** | Optional list of permitted credentials per workflow | | **Security** | AES-256-GCM encryption, never logged or exposed | | **Management** | Create, update (rotate), delete via Workspace Settings or Workflow header | Credentials provide a secure, centralized way to manage sensitive values across your workflows, eliminating the need for hardcoded secrets and simplifying key rotation. With workflow-scoped credentials and allowlists, you have fine-grained control over which secrets each workflow can access. # How to Build .docx Templates Source: https://docs.tela.com/en/how-to-build-docx-templates Learn how to create Word document templates with placeholders for automated document generation. ## Overview Tela's Document Templater allows you to generate documents from `.docx` templates by replacing placeholders with dynamic values from your workflow. You create a template once, and Tela fills in the data every time the workflow runs. ## How Placeholders Work Placeholders are markers inside your `.docx` template that tell Tela where to insert values. They use curly braces with the placeholder name: ``` {placeholder_name} ``` When the document is generated, each `{placeholder_name}` is replaced with the actual value you mapped in the workflow. ### Example A template with: ``` Dear {client_name}, Your invoice number is {invoice_number}, with a total of {total_amount}. Best regards, {company_name} ``` Will produce: ``` Dear John Smith, Your invoice number is INV-2026-001, with a total of $1,500.00. Best regards, Acme Corp ``` ## Creating a Template Open Microsoft Word, Google Docs (export as .docx), or any editor that supports the `.docx` format. Write the document content as you normally would — formatting, tables, headers, images, and styles are all preserved. Where you want dynamic values, type the placeholder name wrapped in curly braces: `{placeholder_name}`. Use descriptive names like `{client_name}`, `{contract_date}`, or `{total_value}`. Save the file in `.docx` format. Other formats (`.doc`, `.pdf`, `.odt`) are not supported. ## Rules for Placeholders | Rule | Example | Notes | | ----------------------- | ------------------------------------------------ | --------------------------------- | | Use curly braces | `{name}` | Single curly braces only | | No spaces inside braces | `{client_name}` | Use underscores instead of spaces | | Case sensitive | `{Name}` and `{name}` are different placeholders | Be consistent | | Can repeat | Use `{company}` multiple times | Same value is inserted everywhere | | Works in tables | Place `{value}` inside table cells | Formatting is preserved | ## Using Templates in Workflows 1. Add a **Generate Document** node to your workflow 2. Upload your `.docx` template 3. Tela automatically extracts all placeholders from the template 4. Map each placeholder to a workflow variable or a previous step's output 5. Set the output filename 6. Run the workflow — the generated document is available as output ## Tips Use short, descriptive names. Avoid special characters other than underscores. Before using in production, run the workflow with test values to verify formatting. Placeholders inherit the formatting (bold, italic, font size) of the surrounding text. Place placeholders inside table cells for invoices, reports, or forms. # Key Concepts Source: https://docs.tela.com/en/key-concepts To quickly get you started with Tela, let's begin with some key concepts. ### Prompt Natural language applications are programs that are written in plain language, similar to the way you would instruct a capable co-worker. These instructions are known as **Prompts.** A **Prompt** is a piece of text or an instruction that you provide to a natural language model in order to generate a specific response or output ### Canvas On Tela, each **Prompt** you create is related to a **Canvas**. The **Canvas** is the most important concept on Tela, as it allows you to manage the full life cycle of your natural language application. **Canvas** is the fundamental unit of Tela, a place to craft, test, evaluate, and monitor your **Prompts** and natural language applications ### Projects All **Canvases** in Tela are related to a **Project**. **Projects** work like folders that hold related **Canvases** together in a logical organization. ### Workspace **Projects** live in your team's **Workspace**, the exclusive environment of your organization where you will be able to manage and collaborate on initiatives ### Dashboard The first thing you will see when opening Tela, is your **Dashboard.** The **Dashboard** contains the essential information on all the **Projects** and **Canvases** that you have access to within your **Workspace.** We suggest the easiest way to get started is by simply [creating your first Canvas](/en/creating-your-first-canvas). This allows you to get familiar with Tela and its core functionalities. You can also follow this Tutorial (coming soon) for a guided approach to building your application. # Supported File Formats Source: https://docs.tela.com/en/supported-file-formats * **PDF** * `application/pdf`: `.pdf` * **Audio** * `audio/mpeg`: `.mp3` * `audio/ogg`: `.ogg` * `audio/wav`: `.wav` * **Images** * `image/jpeg`: `.jpg`, `.jpeg`, `.JPG`, `.JPEG` * `image/png`: `.png`, `.PNG` * **Documents** * `application/vnd.openxmlformats-officedocument.wordprocessingml.document`: `.docx`, `.DOCX` * `application/vnd.openxmlformats-officedocument.presentationml.presentation`: `.pptx`, `.PPTX` * `application/vnd.openxmlformats-officedocument.spreadsheetml.sheet`: `.xlsx`, `.XLSX` * `application/msword`: `.doc`, `.DOC` * `application/vnd.ms-excel`: `.xls`, `.XLS` * `application/vnd.ms-powerpoint`: `.ppt`, `.PPT` * `text/csv`: `.csv`, `.CSV` * **Video** * `video/mp4`: `.mp4`, `.MP4` * **Plain text** * `text/plain`: `.txt`, `.TXT` # Tags Source: https://docs.tela.com/en/tags Categorize and organize your Canvas executions with static or dynamic labels based on AI responses ## What are Tags? Tags are labels you can add to your Canvas executions to organize them. For example, if you have a Canvas that analyzes documents, you might want to label each execution with: * The type of document analyzed * The analysis status * The content category There are two types of tags: | Type | Description | Example | | ---------------- | ----------------------------------------------- | -------------------------------------- | | **Static Tags** | Fixed values defined by you | `support`, `urgent`, `vip-client` | | **Dynamic Tags** | Values automatically extracted from AI response | `$output.category`, `$output.priority` | ## How Dynamic Tags Work Dynamic tags use the special variable `$output` to access values from the AI response. When the execution completes, the system automatically replaces the variable with the actual value. ### Practical Example Imagine your Canvas analyzes support tickets and returns the following response: ```json theme={null} { "category": "billing", "priority": "high", "department": "finance" } ``` If you configured the following dynamic tags: * `sector-$output.category` * `priority-$output.priority` After execution, the registered tags will be: * `sector-billing` * `priority-high` ## Configuring Tags in Canvas ### Step 1: Access Canvas Settings In the Canvas editing screen, locate the **Custom Tags** section in the settings. Custom Tags menu in Canvas settings ### Step 2: Add Your Tags You can add two types of tags: Custom Tags modal Type the tag name directly: ``` my-tag fixed-category automated-process ``` Use the `$output.` prefix followed by the value path in the response: ``` $output.type $output.data.category $output.result.status ``` ### Step 3: Save the Canvas Tags will be automatically applied to all future executions. ## Dynamic Tag Syntax ### Basic Format ``` $output.field ``` Where `field` is the name of the value you want to extract from the response. ### Accessing Nested Values If the response has a more complex structure, use dots to navigate: ``` $output.data.client.name $output.analysis.result.score ``` ### Accessing List Items To access a specific item from a list, use brackets with the position number (starting at 0): ``` $output.items[0].name // first item $output.items[1].category // second item ``` ### Combining with Fixed Text You can combine fixed text with dynamic values: ``` client-$output.client_id status-$output.result type-$output.category-$output.priority ``` ## Sending Tags via API In addition to tags configured in the Canvas, you can also send additional tags directly through the API call. These tags will be **merged** with the Canvas tags. ### Request Example ```json theme={null} { "messages": [...], "tags": [ "origin-api", "client-123", "type-$output.category" ] } ``` ### How Merging Works | Source | Tags | | ---------------- | ------------------------------------------------------------ | | Canvas | `automated-process`, `$output.status` | | API | `origin-api`, `client-123` | | **Final Result** | `automated-process`, `completed`, `origin-api`, `client-123` | If the same tag appears in both Canvas and API, it will be registered only once (no duplication). ## Viewing Tags in Usage All tags (static and already processed dynamic ones) are registered in the Canvas **Usage** section. ### Where to Find 1. Access the desired Canvas 2. Go to the **Usage** or **Execution History** tab 3. Each execution will show its associated tags ### Filtering by Tags You can filter executions by specific tags to: * Analyze usage by category * Identify behavior patterns * Generate segmented reports * Monitor specific types of requests ## Common Use Cases **Scenario:** Canvas that analyzes customer messages **Configured tags:** * `support` (static) * `sentiment-$output.sentiment` * `subject-$output.subject` **Result:** Allows filtering support tickets by sentiment (positive, negative, neutral) and subject. **Scenario:** Canvas that analyzes contracts **Configured tags:** * `document` (static) * `type-$output.contract_type` * `value-$output.value_range` * `risk-$output.risk_level` **Result:** Makes it easy to find contracts by type, value range, or risk level. **Scenario:** Canvas that qualifies leads **Configured tags:** * `lead` (static) * `score-$output.qualification` * `origin-$output.channel` * `product-$output.interest` **Result:** Allows segmenting leads by qualification, origin channel, and product interest. ## Best Practices * Use descriptive and standardized tag names * Combine static tags for fixed context with dynamic tags for variable data * Configure your Canvas Output Format to return the fields you want to use as tags * Test tags in development environment before going to production * Create very long tags (256 character limit) * Use spaces in tag names (use hyphen or underscore) * Depend on fields that may not exist in the response (the tag will be ignored if the field doesn't exist) * Create too many different tags that make organization difficult ## Special Case Behavior | Situation | Behavior | | -------------------------------- | ------------------------------------------- | | Field doesn't exist in response | Dynamic tag is ignored (no error) | | Field returns empty value | Tag is ignored | | Value too long (+256 characters) | Value is truncated | | Error processing tag | Tag is ignored, other tags continue working | ## Frequently Asked Questions No. Tags are only for organization and tracking. They don't alter Canvas execution. No. Tags are registered at execution time and remain permanently associated with that record. There's no strict limit, but we recommend using between 3 to 10 tags per execution to maintain organization. Yes, as long as the response is valid JSON. The system can navigate through nested objects and arrays. No. Tags are merged. API tags are added to Canvas tags, without replacement. ## Summary | Feature | Description | | ---------------- | ---------------------------------------------------- | | **Static Tags** | Fixed values defined in Canvas | | **Dynamic Tags** | Values extracted from response using `$output.field` | | **Via API** | Additional tags sent in the request | | **Registration** | All tags are stored in Canvas Usage | | **Usage** | Filter, organize, and analyze executions | Tags are a tool to transform AI response data into organized and searchable information, facilitating the monitoring and analysis of your Canvas usage. # Creating Apps Source: https://docs.tela.com/en/workstation/creating-apps Requirements: * [You have a canvas with Output Format](/en/craft/output-format) * [Your canvas is published](/en/craft/publishing) ## What are apps? Apps are one of **Tela**'s main features. They are useful when you already have a reliable Canvas and need to give different users a way to create multiple *runs* of your Canvas at the same time, providing files to populate a [Variable](/en/craft/using-variables) and downloading the result of all the runs in a spreadsheet. Our **apps** are related to a Canvas, but you can share the app link with any person with access to your workspace and they will be able to use the app without needing to interact with the Canvas. ## Creating an App After publishing a version of your Canvas, you can click the `Deploy` button in the top right corner of your canvas page: Deploy button When starting the *Deploy* process, you will be prompted to select one of the 2 available templates. Tela currently supports 2 app Templates, the simpler **[Data Extraction Tool](/en/workstation/data-extraction-tool)** and the more robust **[Workstation](/en/workstation/workstation)**. We will explain each one in details in a while. Regardless of the selected template, clicking `Continue` will take you to the app configuration dialog, where you can add a name and a simple description of your app. You can also select the *Document Variable* which will be populated with the input files when running your new app. We currently support only one variable per app, but we are working on experiences with multiple variables After configuring your app, you can copy and share your app link or click `Open Web App` to see your app in action! Now that you have configured and opened your app's page, we can dive deeper into each one of the templates. * [Data Extraction Tool](/en/workstation/data-extraction-tool) * [Workstation](/en/workstation/workstation) ## Updating your App If your app is not getting the results you expect, maybe you need to [edit your prompt](/en/craft/editing-prompts) or change something in your **Canvas**. You can go directly to the Canvas associated with your app by clicking the `…` button in the top left corner of your App's page. Your app is associated with the **Published** version of your Canvas, so please remember to [publish your canvas](/en/craft/publishing) to see the changes in your app. # Data Extraction Tool Source: https://docs.tela.com/en/workstation/data-extraction-tool After selecting the **Data Extraction Tool** template in the *Deploy* section of your Canvas and providing a name and description for your app, you will be redirected to the newly created **Data Extraction App** page, where you will see a table prompting you to drag some files. ### Creating Runs To start a new run, you have some options: * Click the `Upload or drag your files` button and choose the files you want; * Simply drag the files you want from your file explorer into the table in the page; After this, your runs will be created with your selected files as the content of your Canvas' variable. The table will show the result of your runs as soon as they are completed! Note that your runs' results will not be persisted, so refreshing the page **will delete** everything. If you need to keep the results, we recommend using the [Workstation](/en/workstation/workstation) template. After your runs are completed, you can click the `Download` button on the bottom right corner to export your results as a `.csv` file! ## Downloading and Exporting When clicking on the `Download` button of your app, you will be granted 2 options to choose the structure of the downloaded sheet: * **Consolidate values inside a single cell**: It's better to integrate with other softwares, but it is harder to read. This will take every list in the result and represent it in a JSON format inside the cell. * **Display list items one per line**: More readable, but the structure can be confusing if you need to pass this result to other software. This will break down every list in many lines, where each line has one element of the list. The downloaded file will be a `.csv` file, but we also provide different separators that can be selected. ## Updating your App If your app is not getting the results you expect, maybe you need to [edit your prompt](/en/craft/editing-prompts) or change something in your **Canvas**. You can go directly to the Canvas associated with your app by clicking the `…` button in the top left corner. Your app is associated with the **Published** version of your Canvas, so please remember to [publish your canvas](/en/craft/publishing) to see the changes in your app. # Downloading Results Source: https://docs.tela.com/en/workstation/downloading-results When clicking on the `Download` button of your app, you will be granted 2 options to choose the structure of the downloaded sheet: * **Consolidate values inside a single cell**: It's better to integrate with other softwares, but it is harder to read. This will take every list in the result and represent it in a JSON format inside the cell. * **Display list items one per line**: More readable, but the structure can be confusing if you need to pass this result to other software. This will break down every list in many lines, where each line has one element of the list. The downloaded file will be a `.csv` file, but we also provide different separators that can be selected. Exclusively in [Workstation](/en/workstation/workstation) apps, you can select if you want every selected Task to be shown in the same file or if you want a `.zip` folder containing one `.csv` file for each task. # Authentication and Headers Source: https://docs.tela.com/en/workstation/events/authentication Secure your webhooks with custom headers Security is fundamental in integrations. That's why we offer **full control** over headers sent in webhooks, supporting any authentication method your system uses. ## Custom Headers Overview Workstation supports two types of custom headers, each optimized for different scenarios: Configured once in the interface, automatically sent with each notification Passed per request via API, maximum flexibility for multi-tenant scenarios ## Fixed Headers - For Permanent Configurations **Configure once, works forever** When creating a subscription via the interface, you can add custom headers that will be automatically sent in all notifications: Access the app settings in Workstation Access the "Events" tab Add or edit a webhook and in the headers table, add your key-value pairs Done! All webhooks will include these headers ### Use For * 🔐 Static authentication tokens (Bearer tokens, API keys) * 🏷️ Environment identifiers (staging, production) * 📋 Fixed integration metadata * 🔑 Any header that doesn't change between requests ### Example **Interface Configuration:** ``` Headers: authorization: Bearer fixed-company-token my-client-id: tela-production my-environment: prod ``` **Each webhook will include:** ```http theme={null} POST https://your-endpoint.com/webhook authorization: Bearer fixed-company-token my-client-id: tela-production my-environment: prod Content-Type: application/json {...payload...} ``` ## Dynamic Headers - Maximum Flexibility **Full control per request** For integrations that need maximum flexibility, pass different headers in each call using the `x-tela-forward-*` prefix: ### How It Works Any header you send with the `x-tela-forward-` prefix will be forwarded to your webhook endpoint **without the prefix**: ```bash theme={null} POST /api/v1/tasks Content-Type: application/json x-tela-forward-authorization: Bearer token123 x-tela-forward-my-client-id: request-xyz x-tela-forward-my-environment: production { "webhook_url": "https://example.com/webhook", // ... other task data } ``` **The webhook will receive:** ```http theme={null} POST https://example.com/webhook authorization: Bearer token123 my-client-id: request-xyz my-environment: production Content-Type: application/json {...payload...} ``` Note how `x-tela-forward-authorization` becomes just `authorization` in the webhook! ### Perfect For * 🎯 Dynamic tokens that change per client * 🔗 Unique correlation IDs per request * 👤 User/session specific contexts * 🏢 Multi-tenant SaaS with per-client credentials ### Multi-Tenant Example ```bash theme={null} # Client A Request curl -X POST https://api.tela.com/v1/tasks \ -H "x-tela-forward-authorization: Bearer client-a-token" \ -H "x-tela-forward-tenant-id: client-a" \ -d '{ "webhook_url": "https://client-a.example.com/webhook", "prompt": "Process document" }' # Client B Request curl -X POST https://api.tela.com/v1/tasks \ -H "x-tela-forward-authorization: Bearer client-b-token" \ -H "x-tela-forward-tenant-id: client-b" \ -d '{ "webhook_url": "https://client-b.example.com/webhook", "prompt": "Process document" }' ``` Each client receives webhooks with their own credentials! ## Security Best Practices Always use HTTPS URLs - we protect your data in transit Use Bearer tokens or API keys in custom headers Always validate that the webhook came from Workstation Regularly rotate your authentication tokens ### Validation Example ```javascript theme={null} // Validate webhook signature (example) function validateWebhook(req) { const providedToken = req.headers['authorization']; const expectedToken = process.env.TELA_WEBHOOK_TOKEN; if (providedToken !== expectedToken) { throw new Error('Unauthorized webhook request'); } } ``` ## Common Authentication Methods Most common method for API authentication: **Fixed (interface configuration):** ``` authorization: Bearer your-secret-token ``` **Dynamic (per request):** ```bash theme={null} -H "x-tela-forward-authorization: Bearer client-specific-token" ``` Simple API key in custom header: **Fixed (interface configuration):** ``` x-api-key: your-api-key-here ``` **Dynamic (per request):** ```bash theme={null} -H "x-tela-forward-x-api-key: client-api-key" ``` HTTP Basic authentication: **Fixed (interface configuration):** ``` authorization: Basic base64(username:password) ``` **Dynamic (per request):** ```bash theme={null} -H "x-tela-forward-authorization: Basic $(echo -n 'user:pass' | base64)" ``` Any custom authentication your system requires: **Fixed (interface configuration):** ``` x-custom-auth: your-value x-secret-key: secret-123 ``` **Dynamic (per request):** ```bash theme={null} -H "x-tela-forward-x-custom-auth: dynamic-value" -H "x-tela-forward-x-secret-key: dynamic-secret" ``` ## Next Steps Understand webhook payloads # Configuration Source: https://docs.tela.com/en/workstation/events/configuration Learn how to configure webhooks via interface or API ## Configuration Methods Workstation offers **two flexible ways** to configure webhooks, each optimized for different use cases: ### Permanent Configuration via Interface **Ideal for**: Long-term integrations, production environments, multiple teams Configure your webhooks visually through the Workstation interface in just a few clicks and receive events automatically: * **Intuitive interface**: Configure everything visually without writing code * **Centralized management**: View and manage all your integrations in one place * **Fixed headers**: Configure authentication tokens and headers that will be sent automatically * **Integrated testing**: Test button allows you to validate your configuration before activating ### How to Configure Access the app settings in Workstation Access the "Events" tab Enable Events in the app * **URL**: Your endpoint that will receive notifications * **Headers**: Add custom headers Click "Test Request" to validate your configuration Save and start receiving notifications! ### Dynamic Webhooks via API **Ideal for**: Multi-tenant SaaS integrations, dynamic endpoints, temporary integrations Send notifications to different endpoints with each call, without creating permanent subscriptions: * **Total flexibility**: Each call can notify a different endpoint * **Zero prior configuration**: Start using immediately without setup * **Dynamic headers**: Pass different credentials for each client/context * **Native multi-tenant**: Perfect for SaaS with multiple clients * **Temporary webhooks**: Useful for tests, demos, or one-time integrations ### How to Use Simply add `webhook_url` to your API request body and use `x-tela-forward-*` headers for authentication: ```bash theme={null} curl -X POST https://api.tela.com/v1/tasks \ -H "Content-Type: application/json" \ -H "x-tela-forward-authorization: Bearer client-123-token" \ -H "x-tela-forward-custom-id: request-xyz" \ -d '{ "webhook_url": "https://example.com/webhook", "prompt": "Analyze this document", "files": [] }' ``` **When the task completes**, the webhook will be sent to `https://example.com/webhook` with: * Header `authorization: Bearer client-123-token` * Header `custom-id: request-xyz` ## Next Steps Learn about custom headers Understand the webhook payload # Events Overview Source: https://docs.tela.com/en/workstation/events/overview Connect Workstation with any system through real-time notifications **Events** is Workstation's real-time integration solution that automatically connects your tasks with any external system. Through **webhooks**, you receive instant notifications about important events in your tasks' **lifecycle**, enabling you to build **automated and integrated workflows**. Trigger actions in other systems without manual intervention Connect Workstation with your existing systems Receive immediate updates about task status Create complex workflows across multiple platforms ## Available Events You can choose which events you want to receive notifications for. Select only the events relevant to your workflow to keep your integration focused and efficient. Triggered when a task run finishes successfully Triggered when a task run ends with a failure Triggered when a task is reviewed and approved Triggered when an approved task returns to validating ## How Does It Work? The Events system was designed to be simple to configure, yet extremely reliable in production: ### Simple Configuration Provide the URL that will receive notifications Add custom headers to authenticate your requests Validate your configuration with real payloads before going to production ## Two Flexible Approaches We offer **two flexible ways** to work with webhooks, allowing you to choose the best approach for each use case: ### Permanent Configuration via Interface **Ideal for**: Long-term integrations, production environments, multiple teams Configure your webhooks visually through the Workstation interface in just a few clicks and receive events automatically: * ✅ **Intuitive interface**: Configure everything visually without writing code * ✅ **Centralized management**: View and manage all your integrations in one place * ✅ **Fixed headers**: Configure authentication tokens and headers that will be sent automatically * ✅ **Integrated testing**: Test button allows you to validate your configuration before activating **How to access**: App Settings → "Events" Tab → Enable Events ### Dynamic Webhooks via API **Ideal for**: Multi-tenant SaaS integrations, dynamic endpoints, temporary integrations Send notifications to different endpoints with each call, without creating permanent subscriptions: * ⚡ **Total flexibility**: Each call can notify a different endpoint * ⚡ **Zero prior configuration**: Start using immediately without setup * ⚡ **Dynamic headers**: Pass different credentials for each client/context * ⚡ **Native multi-tenant**: Perfect for SaaS with multiple clients * ⚡ **Temporary webhooks**: Useful for tests, demos, or one-time integrations **How to use**: Add `webhook_url` in your call body + `x-tela-forward-*` headers for authentication ## Next Steps Learn how to configure webhooks Secure your integrations Understand the webhook payload # Payload Reference Source: https://docs.tela.com/en/workstation/events/payload-reference Complete webhook payload structure and delivery guarantees ## Webhook Payload Structure Each webhook delivers a **complete and structured payload** with all relevant task information, eliminating the need for additional API calls. ### Complete Payload Example ```json theme={null} { "id": "unique-webhook-id", "event": "task.completed", "timestamp": "2025-10-16T12:00:00Z", "task": { "id": "task-uuid", "title": "Contract Analysis", "description": "Review of contractual clauses", "status": "completed", "reference": 1234, // ... complete task metadata }, "inputContent": { "files": [ { "filename": "contract.pdf", "content": "processed and extracted content", "type": "application/pdf" } ] }, "outputContent": "Complete analysis with all identified clauses..." } ``` ### Field Reference Unique notification identifier (useful for deduplication) Which event triggered this webhook. Possible values: * `task.completed` - Task completed and ready for review * `task.approved` - Task approved Exact moment when the event occurred (ISO 8601 format) Complete task data including: * `id` (string) - Unique task identifier * `title` (string) - Task title * `description` (string) - Task description * `status` (string) - Current task status * `reference` (number) - Readable task reference number * Additional metadata All input content that was processed: * `files` (array) - Array of processed files with: * `filename` (string) - Original file name * `content` (string) - Processed and extracted content * `type` (string) - File MIME type * Additional input data The complete result generated by the task ## Next Steps Learn about Events and webhooks Configure webhooks in your app Secure your webhook integrations # Workstation Source: https://docs.tela.com/en/workstation/workstation After selecting the **Workstation** template in the *Deploy* section of your Canvas and providing a name and description for your app, you will be redirected to the newly created **Workstation** page, where you will see your **App Library** and a button to create `+ New Tasks`. ### Creating Tasks Every task is a execution of your Canvas and depends on a file that will be used as the content of your Canvas' variable. You can create tasks by: * Clicking the `New Task` button and choosing the files you want; * Dragging the files you want into the "Drop files here" section in the upper right corner; * Following our [guide](/en/creating-tasks) to create tasks via API; After selecting the files, your tasks will be created and start running. ### Reviewing Tasks When a task finishes running, it's content will be available for review in your **Workstation**. Clicking the name of your file or the `Review` button will open the *Review dialog* of this task. In this dialog you can: * See the result of the run on the left side; * Edit the content of any attribute if you think it can be better; * See the content of your file that was provided to your Canvas on the left side; * Click the `…` button on the top right to see more options; After being approved, your tasks will be available in the **App Library**. ### App Library In the **App Library** you can: * See the tasks that have been reviewed; * Click your task's name for details of the results and see if any attribute was edited; * Click your task's name and click the `…` button on the top right to `Create Test Case` or delete this task; * Select one or multiple tasks to `Download` as a `.csv` spreadsheet; The **App Library** will persist every task run in your Workstation, so you will not lose your tasks if you refresh or close the page.