O SDK oficial TypeScript/JavaScript para ZippyVendas fornece segurança de tipo completa e suporta ambientes Node.js e navegador, permitindo que você execute workflows programaticamente de suas aplicações Node.js, aplicações web e outros ambientes JavaScript.
O SDK TypeScript fornece segurança de tipo completa, suporte a execução assíncrona, limitação de taxa automática com backoff exponencial e rastreamento de uso.
Instalação
Instale o SDK usando seu gerenciador de pacotes preferido:
npm install simstudio-ts-sdkyarn add simstudio-ts-sdkbun add simstudio-ts-sdkInício Rápido
Aqui está um exemplo simples para começar:
import { SimStudioClient } from 'simstudio-ts-sdk';
// Initialize the client
const client = new SimStudioClient({
apiKey: 'your-api-key-here',
baseUrl: 'https://zippyvendas.com' // optional, defaults to https://zippyvendas.com
});
// Execute a workflow
try {
const result = await client.executeWorkflow('workflow-id');
console.log('Workflow executed successfully:', result);
} catch (error) {
console.error('Workflow execution failed:', error);
}Referência da API
SimStudioClient
Construtor
new SimStudioClient(config: SimStudioConfig)Configuração:
config.apiKey(string): Sua chave de API ZippyVendasconfig.baseUrl(string, opcional): URL base para a API ZippyVendas (padrão:https://zippyvendas.com)
Métodos
executeWorkflow()
Executa um workflow com dados de entrada opcionais.
const result = await client.executeWorkflow('workflow-id', {
input: { message: 'Hello, world!' },
timeout: 30000 // 30 seconds
});Parâmetros:
workflowId(string): O ID do workflow a ser executadooptions(ExecutionOptions, opcional):input(any): Dados de entrada para passar ao workflowtimeout(number): Tempo limite em milissegundos (padrão: 30000)stream(boolean): Habilita respostas em streaming (padrão: false)selectedOutputs(string[]): Saídas de blocos para transmitir no formatoblockName.attribute(ex:["agent1.content"])async(boolean): Executa de forma assíncrona (padrão: false)
Retorna: Promise<WorkflowExecutionResult | AsyncExecutionResult>
Quando async: true, retorna imediatamente com um ID de tarefa para polling. Caso contrário, aguarda a conclusão.
getWorkflowStatus()
Obtém o status de um workflow (status de deployment, etc.).
const status = await client.getWorkflowStatus('workflow-id');
console.log('Is deployed:', status.isDeployed);Parâmetros:
workflowId(string): O ID do workflow
Retorna: Promise<WorkflowStatus>
validateWorkflow()
Valida se um workflow está pronto para execução.
const isReady = await client.validateWorkflow('workflow-id');
if (isReady) {
// Workflow is deployed and ready
}Parâmetros:
workflowId(string): O ID do workflow
Retorna: Promise<boolean>
getJobStatus()
Obtém o status de uma execução de trabalho assíncrono.
const status = await client.getJobStatus('task-id-from-async-execution');
console.log('Status:', status.status); // 'queued', 'processing', 'completed', 'failed'
if (status.status === 'completed') {
console.log('Output:', status.output);
}Parâmetros:
taskId(string): O ID da tarefa retornado da execução assíncrona
Retorna: Promise<JobStatus>
Campos da resposta:
success(boolean): Se a requisição foi bem-sucedidataskId(string): O ID da tarefastatus(string): Um de'queued','processing','completed','failed','cancelled'metadata(object): ContémstartedAt,completedAt, edurationoutput(any, opcional): A saída do workflow (quando concluído)error(any, opcional): Detalhes do erro (quando falha)estimatedDuration(number, opcional): Duração estimada em milissegundos (quando processando/enfileirado)
executeWithRetry()
Executa um workflow com tentativa automática em erros de limite de taxa usando backoff exponencial.
const result = await client.executeWithRetry('workflow-id', {
input: { message: 'Hello' },
timeout: 30000
}, {
maxRetries: 3, // Maximum number of retries
initialDelay: 1000, // Initial delay in ms (1 second)
maxDelay: 30000, // Maximum delay in ms (30 seconds)
backoffMultiplier: 2 // Exponential backoff multiplier
});Parâmetros:
workflowId(string): O ID do workflow a ser executadooptions(ExecutionOptions, opcional): Igual aexecuteWorkflow()retryOptions(RetryOptions, opcional):maxRetries(number): Número máximo de tentativas (padrão: 3)initialDelay(number): Atraso inicial em ms (padrão: 1000)maxDelay(number): Atraso máximo em ms (padrão: 30000)backoffMultiplier(number): Multiplicador de backoff (padrão: 2)
Retorna: Promise<WorkflowExecutionResult | AsyncExecutionResult>
A lógica de tentativa usa backoff exponencial (1s → 2s → 4s → 8s...) com ±25% de jitter para prevenir thundering herd. Se a API fornecer um cabeçalho retry-after, ele será usado em vez disso.
getRateLimitInfo()
Obtém as informações atuais de limite de taxa da última resposta da API.
const rateLimitInfo = client.getRateLimitInfo();
if (rateLimitInfo) {
console.log('Limit:', rateLimitInfo.limit);
console.log('Remaining:', rateLimitInfo.remaining);
console.log('Reset:', new Date(rateLimitInfo.reset * 1000));
}Retorna: RateLimitInfo | null
getUsageLimits()
Obtém os limites de uso atuais e informações de cota para sua conta.
const limits = await client.getUsageLimits();
console.log('Sync requests remaining:', limits.rateLimit.sync.remaining);
console.log('Async requests remaining:', limits.rateLimit.async.remaining);
console.log('Current period cost:', limits.usage.currentPeriodCost);
console.log('Plan:', limits.usage.plan);Retorna: Promise<UsageLimits>
Estrutura da resposta:
{
success: boolean
rateLimit: {
sync: {
isLimited: boolean
limit: number
remaining: number
resetAt: string
}
async: {
isLimited: boolean
limit: number
remaining: number
resetAt: string
}
authType: string // 'api' or 'manual'
}
usage: {
currentPeriodCost: number
limit: number
plan: string // e.g., 'free', 'pro'
}
}setApiKey()
Atualiza a chave de API.
client.setApiKey('new-api-key');setBaseUrl()
Atualiza a URL base.
client.setBaseUrl('https://my-custom-domain.com');Tipos
WorkflowExecutionResult
interface WorkflowExecutionResult {
success: boolean;
output?: any;
error?: string;
logs?: any[];
metadata?: {
duration?: number;
executionId?: string;
[key: string]: any;
};
traceSpans?: any[];
totalDuration?: number;
}AsyncExecutionResult
interface AsyncExecutionResult {
success: boolean;
taskId: string;
status: 'queued';
createdAt: string;
links: {
status: string; // e.g., "/api/jobs/{taskId}"
};
}WorkflowStatus
interface WorkflowStatus {
isDeployed: boolean;
deployedAt?: string;
needsRedeployment: boolean;
}RateLimitInfo
interface RateLimitInfo {
limit: number;
remaining: number;
reset: number;
retryAfter?: number;
}UsageLimits
interface UsageLimits {
success: boolean;
rateLimit: {
sync: {
isLimited: boolean;
limit: number;
remaining: number;
resetAt: string;
};
async: {
isLimited: boolean;
limit: number;
remaining: number;
resetAt: string;
};
authType: string;
};
usage: {
currentPeriodCost: number;
limit: number;
plan: string;
};
}SimStudioError
class SimStudioError extends Error {
code?: string;
status?: number;
}Códigos de erro comuns:
UNAUTHORIZED: Chave de API inválidaTIMEOUT: Requisição expirouRATE_LIMIT_EXCEEDED: Limite de taxa excedidoUSAGE_LIMIT_EXCEEDED: Limite de uso excedidoEXECUTION_ERROR: Execução do workflow falhou
Exemplos
Execução Básica de Workflow
Configure o SimStudioClient com sua chave de API.
Verifique se o workflow está implantado e pronto para execução.
Execute o workflow com seus dados de entrada.
Processe o resultado da execução e trate quaisquer erros.
import { SimStudioClient } from 'simstudio-ts-sdk';
const client = new SimStudioClient({
apiKey: process.env.SIM_API_KEY!
});
async function runWorkflow() {
try {
// Check if workflow is ready
const isReady = await client.validateWorkflow('my-workflow-id');
if (!isReady) {
throw new Error('Workflow is not deployed or ready');
}
// Execute the workflow
const result = await client.executeWorkflow('my-workflow-id', {
input: {
message: 'Process this data',
userId: '12345'
}
});
if (result.success) {
console.log('Output:', result.output);
console.log('Duration:', result.metadata?.duration);
} else {
console.error('Workflow failed:', result.error);
}
} catch (error) {
console.error('Error:', error);
}
}
runWorkflow();Tratamento de Erros
Trate diferentes tipos de erros que podem ocorrer durante a execução do workflow:
import { SimStudioClient, SimStudioError } from 'simstudio-ts-sdk';
const client = new SimStudioClient({
apiKey: process.env.SIM_API_KEY!
});
async function executeWithErrorHandling() {
try {
const result = await client.executeWorkflow('workflow-id');
return result;
} catch (error) {
if (error instanceof SimStudioError) {
switch (error.code) {
case 'UNAUTHORIZED':
console.error('Invalid API key');
break;
case 'TIMEOUT':
console.error('Workflow execution timed out');
break;
case 'USAGE_LIMIT_EXCEEDED':
console.error('Usage limit exceeded');
break;
case 'INVALID_JSON':
console.error('Invalid JSON in request body');
break;
default:
console.error('Workflow error:', error.message);
}
} else {
console.error('Unexpected error:', error);
}
throw error;
}
}Configuração de Ambiente
Configure o cliente usando variáveis de ambiente:
import { SimStudioClient } from 'simstudio-ts-sdk';
// Development configuration
const apiKey = process.env.SIM_API_KEY;
if (!apiKey) {
throw new Error('SIM_API_KEY environment variable is required');
}
const client = new SimStudioClient({
apiKey,
baseUrl: process.env.SIM_BASE_URL // optional
});import { SimStudioClient } from 'simstudio-ts-sdk';
// Production configuration with validation
const apiKey = process.env.SIM_API_KEY;
if (!apiKey) {
throw new Error('SIM_API_KEY environment variable is required');
}
const client = new SimStudioClient({
apiKey,
baseUrl: process.env.SIM_BASE_URL || 'https://zippyvendas.com'
});Integração com Node.js Express
Integre com um servidor Express.js:
import express from 'express';
import { SimStudioClient } from 'simstudio-ts-sdk';
const app = express();
const client = new SimStudioClient({
apiKey: process.env.SIM_API_KEY!
});
app.use(express.json());
app.post('/execute-workflow', async (req, res) => {
try {
const { workflowId, input } = req.body;
const result = await client.executeWorkflow(workflowId, {
input,
timeout: 60000
});
res.json({
success: true,
data: result
});
} catch (error) {
console.error('Workflow execution error:', error);
res.status(500).json({
success: false,
error: error instanceof Error ? error.message : 'Unknown error'
});
}
});
app.listen(3000, () => {
console.log('Server running on port 3000');
});Rota de API Next.js
Use com rotas de API Next.js:
// pages/api/workflow.ts or app/api/workflow/route.ts
import { NextApiRequest, NextApiResponse } from 'next';
import { SimStudioClient } from 'simstudio-ts-sdk';
const client = new SimStudioClient({
apiKey: process.env.SIM_API_KEY!
});
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
if (req.method !== 'POST') {
return res.status(405).json({ error: 'Method not allowed' });
}
try {
const { workflowId, input } = req.body;
const result = await client.executeWorkflow(workflowId, {
input,
timeout: 30000
});
res.status(200).json(result);
} catch (error) {
console.error('Error executing workflow:', error);
res.status(500).json({
error: 'Failed to execute workflow'
});
}
}Uso no Navegador
Use no navegador (com configuração CORS adequada):
import { SimStudioClient } from 'simstudio-ts-sdk';
// Note: In production, use a proxy server to avoid exposing API keys
const client = new SimStudioClient({
apiKey: 'your-public-api-key', // Use with caution in browser
baseUrl: 'https://zippyvendas.com'
});
async function executeClientSideWorkflow() {
try {
const result = await client.executeWorkflow('workflow-id', {
input: {
userInput: 'Hello from browser'
}
});
console.log('Workflow result:', result);
// Update UI with result
document.getElementById('result')!.textContent =
JSON.stringify(result.output, null, 2);
} catch (error) {
console.error('Error:', error);
}
}Upload de Arquivos
Objetos File são automaticamente detectados e convertidos para formato base64. Inclua-os em sua entrada sob o nome do campo correspondente ao formato de entrada do gatilho de API do seu workflow.
O SDK converte objetos File para este formato:
{
type: 'file',
data: 'data:mime/type;base64,base64data',
name: 'filename',
mime: 'mime/type'
}Alternativamente, você pode fornecer arquivos manualmente usando o formato URL:
{
type: 'url',
data: 'https://example.com/file.pdf',
name: 'file.pdf',
mime: 'application/pdf'
}import { SimStudioClient } from 'simstudio-ts-sdk';
const client = new SimStudioClient({
apiKey: process.env.NEXT_PUBLIC_SIM_API_KEY!
});
// From file input
async function handleFileUpload(event: Event) {
const input = event.target as HTMLInputElement;
const files = Array.from(input.files || []);
// Include files under the field name from your API trigger's input format
const result = await client.executeWorkflow('workflow-id', {
input: {
documents: files, // Must match your workflow's "files" field name
instructions: 'Analyze these documents'
}
});
console.log('Result:', result);
}import { SimStudioClient } from 'simstudio-ts-sdk';
import fs from 'fs';
const client = new SimStudioClient({
apiKey: process.env.SIM_API_KEY!
});
// Read file and create File object
const fileBuffer = fs.readFileSync('./document.pdf');
const file = new File([fileBuffer], 'document.pdf', {
type: 'application/pdf'
});
// Include files under the field name from your API trigger's input format
const result = await client.executeWorkflow('workflow-id', {
input: {
documents: [file], // Must match your workflow's "files" field name
query: 'Summarize this document'
}
});Ao usar o SDK no navegador, tenha cuidado para não expor chaves de API sensíveis. Considere usar um proxy backend ou chaves de API públicas com permissões limitadas.
Exemplo de Hook React
Crie um hook React customizado para execução de workflow:
import { useState, useCallback } from 'react';
import { SimStudioClient, WorkflowExecutionResult } from 'simstudio-ts-sdk';
const client = new SimStudioClient({
apiKey: process.env.SIM_API_KEY!
});
interface UseWorkflowResult {
result: WorkflowExecutionResult | null;
loading: boolean;
error: Error | null;
executeWorkflow: (workflowId: string, input?: any) => Promise<void>;
}
export function useWorkflow(): UseWorkflowResult {
const [result, setResult] = useState<WorkflowExecutionResult | null>(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<Error | null>(null);
const executeWorkflow = useCallback(async (workflowId: string, input?: any) => {
setLoading(true);
setError(null);
setResult(null);
try {
const workflowResult = await client.executeWorkflow(workflowId, {
input,
timeout: 30000
});
setResult(workflowResult);
} catch (err) {
setError(err instanceof Error ? err : new Error('Unknown error'));
} finally {
setLoading(false);
}
}, []);
return {
result,
loading,
error,
executeWorkflow
};
}
// Usage in component
function WorkflowComponent() {
const { result, loading, error, executeWorkflow } = useWorkflow();
const handleExecute = () => {
executeWorkflow('my-workflow-id', {
message: 'Hello from React!'
});
};
return (
<div>
<button onClick={handleExecute} disabled={loading}>
{loading ? 'Executing...' : 'Execute Workflow'}
</button>
{error && <div>Error: {error.message}</div>}
{result && (
<div>
<h3>Result:</h3>
<pre>{JSON.stringify(result, null, 2)}</pre>
</div>
)}
</div>
);
}Execução Assíncrona de Workflow
Execute workflows de forma assíncrona para tarefas de longa duração:
import { SimStudioClient, AsyncExecutionResult } from 'simstudio-ts-sdk';
const client = new SimStudioClient({
apiKey: process.env.SIM_API_KEY!
});
async function executeAsync() {
try {
// Start async execution
const result = await client.executeWorkflow('workflow-id', {
input: { data: 'large dataset' },
async: true // Execute asynchronously
});
// Check if result is an async execution
if ('taskId' in result) {
console.log('Task ID:', result.taskId);
console.log('Status endpoint:', result.links.status);
// Poll for completion
let status = await client.getJobStatus(result.taskId);
while (status.status === 'queued' || status.status === 'processing') {
console.log('Current status:', status.status);
await new Promise(resolve => setTimeout(resolve, 2000)); // Wait 2 seconds
status = await client.getJobStatus(result.taskId);
}
if (status.status === 'completed') {
console.log('Workflow completed!');
console.log('Output:', status.output);
console.log('Duration:', status.metadata.duration);
} else {
console.error('Workflow failed:', status.error);
}
}
} catch (error) {
console.error('Error:', error);
}
}
executeAsync();Limitação de Taxa e Tentativas
Trate limites de taxa automaticamente com backoff exponencial:
import { SimStudioClient, SimStudioError } from 'simstudio-ts-sdk';
const client = new SimStudioClient({
apiKey: process.env.SIM_API_KEY!
});
async function executeWithRetryHandling() {
try {
// Automatically retries on rate limit
const result = await client.executeWithRetry('workflow-id', {
input: { message: 'Process this' }
}, {
maxRetries: 5,
initialDelay: 1000,
maxDelay: 60000,
backoffMultiplier: 2
});
console.log('Success:', result);
} catch (error) {
if (error instanceof SimStudioError && error.code === 'RATE_LIMIT_EXCEEDED') {
console.error('Rate limit exceeded after all retries');
// Check rate limit info
const rateLimitInfo = client.getRateLimitInfo();
if (rateLimitInfo) {
console.log('Rate limit resets at:', new Date(rateLimitInfo.reset * 1000));
}
}
}
}Monitoramento de Uso
Monitore o uso e limites da sua conta:
import { SimStudioClient } from 'simstudio-ts-sdk';
const client = new SimStudioClient({
apiKey: process.env.SIM_API_KEY!
});
async function checkUsage() {
try {
const limits = await client.getUsageLimits();
console.log('=== Rate Limits ===');
console.log('Sync requests:');
console.log(' Limit:', limits.rateLimit.sync.limit);
console.log(' Remaining:', limits.rateLimit.sync.remaining);
console.log(' Resets at:', limits.rateLimit.sync.resetAt);
console.log(' Is limited:', limits.rateLimit.sync.isLimited);
console.log('\nAsync requests:');
console.log(' Limit:', limits.rateLimit.async.limit);
console.log(' Remaining:', limits.rateLimit.async.remaining);
console.log(' Resets at:', limits.rateLimit.async.resetAt);
console.log(' Is limited:', limits.rateLimit.async.isLimited);
console.log('\n=== Usage ===');
console.log('Current period cost: $' + limits.usage.currentPeriodCost.toFixed(2));
console.log('Limit: $' + limits.usage.limit.toFixed(2));
console.log('Plan:', limits.usage.plan);
const percentUsed = (limits.usage.currentPeriodCost / limits.usage.limit) * 100;
console.log('Usage: ' + percentUsed.toFixed(1) + '%');
if (percentUsed > 80) {
console.warn('⚠️ Warning: You are approaching your usage limit!');
}
} catch (error) {
console.error('Error checking usage:', error);
}
}
checkUsage();Execução de Workflow com Streaming
Execute workflows com respostas em streaming em tempo real:
import { SimStudioClient } from 'simstudio-ts-sdk';
const client = new SimStudioClient({
apiKey: process.env.SIM_API_KEY!
});
async function executeWithStreaming() {
try {
// Enable streaming for specific block outputs
const result = await client.executeWorkflow('workflow-id', {
input: { message: 'Count to five' },
stream: true,
selectedOutputs: ['agent1.content'] // Use blockName.attribute format
});
console.log('Workflow result:', result);
} catch (error) {
console.error('Error:', error);
}
}A resposta em streaming segue o formato Server-Sent Events (SSE):
data: {"blockId":"7b7735b9-19e5-4bd6-818b-46aae2596e9f","chunk":"One"}
data: {"blockId":"7b7735b9-19e5-4bd6-818b-46aae2596e9f","chunk":", two"}
data: {"event":"done","success":true,"output":{},"metadata":{"duration":610}}
data: [DONE]Exemplo de Streaming com React:
import { useState, useEffect } from 'react';
function StreamingWorkflow() {
const [output, setOutput] = useState('');
const [loading, setLoading] = useState(false);
const executeStreaming = async () => {
setLoading(true);
setOutput('');
// IMPORTANT: Make this API call from your backend server, not the browser
// Never expose your API key in client-side code
const response = await fetch('https://zippyvendas.com/api/workflows/WORKFLOW_ID/execute', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': process.env.SIM_API_KEY! // Server-side environment variable only
},
body: JSON.stringify({
message: 'Generate a story',
stream: true,
selectedOutputs: ['agent1.content']
})
});
const reader = response.body?.getReader();
const decoder = new TextDecoder();
while (reader) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value);
const lines = chunk.split('\n\n');
for (const line of lines) {
if (line.startsWith('data: ')) {
const data = line.slice(6);
if (data === '[DONE]') {
setLoading(false);
break;
}
try {
const parsed = JSON.parse(data);
if (parsed.chunk) {
setOutput(prev => prev + parsed.chunk);
} else if (parsed.event === 'done') {
console.log('Execution complete:', parsed.metadata);
}
} catch (e) {
// Skip invalid JSON
}
}
}
}
};
return (
<div>
<button onClick={executeStreaming} disabled={loading}>
{loading ? 'Generating...' : 'Start Streaming'}
</button>
<div style={{ whiteSpace: 'pre-wrap' }}>{output}</div>
</div>
);
}Obtendo Sua Chave de API
Navegue até ZippyVendas e faça login na sua conta.
Navegue até o workflow que você deseja executar programaticamente.
Clique em "Deploy" para implantar seu workflow se ele ainda não foi implantado.
Durante o processo de implantação, selecione ou crie uma chave de API.
Copie a chave de API para usar em sua aplicação TypeScript/JavaScript.
Requisitos
- Node.js 16+
- TypeScript 5.0+ (para projetos TypeScript)
Licença
Apache-2.0