TypeScript SDK

@enprompta/sdk

Full-featured TypeScript SDK with middleware support, automatic retries, and comprehensive type definitions.

Type Safe

Full TypeScript support with comprehensive type definitions.

Auto Retry

Built-in retry strategies with exponential backoff.

Middleware

Extensible middleware system for custom logic.

Free to start

Creating an API key is free, and the SDK records observability traces on any plan (5,000/month on Free). Serving prompts and the rest of the REST API require a Pro or Enterprise plan — editor seats read/write, viewer seats read-only. View pricing →

Installation

npm
npm install @enprompta/sdk
yarn
yarn add @enprompta/sdk
pnpm
pnpm add @enprompta/sdk

Quick Start

TypeScript
import { Enprompta } from '@enprompta/sdk'

// Initialize with API key
const client = new Enprompta({
  apiKey: process.env.ENPROMPTA_API_KEY
})

// Or with OAuth2 credentials
const client = new Enprompta({
  clientId: process.env.ENPROMPTA_CLIENT_ID,
  clientSecret: process.env.ENPROMPTA_CLIENT_SECRET
})

// List all prompts
const { data: prompts } = await client.prompts.list()
console.log(prompts)

// Create a new prompt
const prompt = await client.prompts.create({
  title: 'Email Assistant',
  content: 'Write a professional email about {{topic}} to {{recipient}}',
  visibility: 'PRIVATE',
  variables: [
    { name: 'topic', type: 'text', required: true },
    { name: 'recipient', type: 'text', required: true }
  ]
})

// Execute the prompt
const result = await client.prompts.execute(prompt.id, {
  variables: {
    topic: 'project deadline extension',
    recipient: 'the project manager'
  },
  provider: 'openai',
  model: 'gpt-4'
})

console.log(result.output)

Authentication

The SDK supports both API key and OAuth2 client credentials authentication.

API Key Authentication

const client = new Enprompta({
  apiKey: 'ep_your_api_key',
  // Optional: custom base URL
  baseUrl: 'https://enprompta.com/api/v1'
})

OAuth2 Client Credentials

Create a client in your dashboard under API Keys → OAuth Clients to get a client_id and client_secret (the secret is shown once).

const client = new Enprompta({
  clientId: 'your_client_id',
  clientSecret: 'your_client_secret',
  scopes: ['prompts:read', 'prompts:write', 'executions:write']
})

// Tokens are automatically managed and refreshed

Working with Prompts

List Prompts

// List with pagination
const { data, pagination } = await client.prompts.list({
  limit: 20,
  visibility: 'PRIVATE',
  search: 'email'
})

// Iterate through all pages
for await (const prompt of client.prompts.listAll()) {
  console.log(prompt.title)
}

Create Prompt

const prompt = await client.prompts.create({
  title: 'Code Reviewer',
  content: `Review the following {{language}} code and provide feedback:

\`\`\`{{language}}
{{code}}
\`\`\`

Focus on: {{focus_areas}}`,
  description: 'AI-powered code review assistant',
  category: 'development',
  tags: ['code', 'review', 'development'],
  visibility: 'TEAM',
  teamId: 'team_abc123',
  variables: [
    { name: 'language', type: 'select', options: ['javascript', 'python', 'go'] },
    { name: 'code', type: 'text', required: true },
    { name: 'focus_areas', type: 'text', defaultValue: 'best practices, performance' }
  ]
})

Update Prompt

const updated = await client.prompts.update('prompt_abc123', {
  title: 'Updated Title',
  content: 'New prompt content with {{variable}}',
  visibility: 'PUBLIC'
})

Delete Prompt

await client.prompts.delete('prompt_abc123')

Executions

Execute a Prompt

const result = await client.prompts.execute('prompt_abc123', {
  variables: {
    topic: 'quarterly review',
    tone: 'professional'
  },
  provider: 'openai',
  model: 'gpt-4o-mini',
  temperature: 0.7,
  maxTokens: 1000
})

console.log(result.output)          // Generated text
console.log(result.tokensUsed)      // Token count
console.log(result.cost)            // Estimated cost
console.log(result.latencyMs)       // Response time

List Executions

// Get execution history
const { data: executions } = await client.executions.list({
  promptId: 'prompt_abc123',
  provider: 'openai',
  startDate: '2025-01-01',
  endDate: '2025-01-31'
})

// Get aggregated statistics
const stats = await client.executions.getStats({
  groupBy: 'day'
})

Auto-Instrumentation

The fastest way to get traces: init() patches your provider SDKs so every OpenAI, Anthropic, and Gemini call is traced automatically — no call-site changes. Pass the client you imported via modules so it works under ESM and bundlers. It captures provider, model, prompt, response, tokens, latency, cost, errors, and — as of @enprompta/sdk 1.1.1 — tool-call turns, where the model's tool call is recorded as the output instead of a blank (streaming and non-streaming).

TypeScript
import { init } from '@enprompta/sdk'
import OpenAI from 'openai'
import Anthropic from '@anthropic-ai/sdk'

init({
  apiKey: process.env.ENPROMPTA_API_KEY,
  modules: { openai: OpenAI, anthropic: Anthropic }, // also: { google: GenerativeModel }
})

// Every call on these clients is now traced — nothing else to change.
const openai = new OpenAI()
await openai.chat.completions.create({
  model: 'gpt-4o',
  messages: [{ role: 'user', content: 'Hello' }],
})

Why modules? In ESM apps and bundlers, the SDK can't reliably reach into your provider package on its own — it may patch a different copy of the module than the one you imported, so traces silently never appear. Passing the client you imported removes that ambiguity. If init() instruments nothing, it logs a warning telling you to use this form.

Framework instrumentation (LangChain, agents)

To capture the whole agent/RAG trace — retrievals, tool calls, and sub-agent steps with their nesting — install the Enprompta instrumentor for your stack and call instrumentFrameworks().

Install
npm install @enprompta/instrument-langchain
# or your stack's instrumentor: @enprompta/instrument-openai
TypeScript
import { instrumentFrameworks } from '@enprompta/sdk'

instrumentFrameworks({
  apiKey: process.env.ENPROMPTA_API_KEY,
  frameworks: ['langchain'], // omit to auto-detect installed instrumentors
})

// Your LangChain runs now appear as typed, nested spans in Observability.

Sending Traces (manual)

Prefer full control, or calling a provider the auto-instrumenter doesn't patch? Send traces yourself. The simplest flow: fetch a prompt, call your LLM provider, then POST the result to Enprompta.

Agent span trees & sessions (recommended for agents)

For agents and tool loops, record a span tree instead of one flat trace per call: a root span per request with typed child spans (LLM, TOOL, RETRIEVAL, …), grouped by a sessionId so a multi-turn conversation reads as one session. Works in any runtime — no framework instrumentor required, and bundler-safe. Requires @enprompta/sdk 1.1.0+.

TypeScript
import { Enprompta } from '@enprompta/sdk'

const enprompta = new Enprompta({ apiKey: process.env.ENPROMPTA_API_KEY })

// One root span per user question, grouped by session.
const trace = enprompta.trace({
  name: 'support-agent',
  type: 'AGENT',
  sessionId: conversationId, // groups a multi-turn chat into one session
  input: question,
})

// Each model call → a typed LLM child span.
const llm = trace.span({ type: 'LLM', name: 'plan', model: 'gpt-4o', input: messages })
const completion = await openai.chat.completions.create({ /* … */ })
llm.end({
  output: completion.choices[0].message,
  inputTokens: completion.usage?.prompt_tokens,
  outputTokens: completion.usage?.completion_tokens,
})

// Each tool run → a TOOL child span (now visible in the trace tree).
const tool = trace.span({ type: 'TOOL', name: 'get_weather', input: args })
tool.end({ output: result })

await trace.end({ output: answer }) // flushes the whole tree

The result is a nested, typed tree in Observability — end-to-end latency per request, which tool was slow, how many turns it took — plus a Sessions view that groups the whole conversation. Spans are typed (LLM / TOOL / RETRIEVAL / AGENT / CHAIN / RERANKER / GUARDRAIL / EMBEDDING / EVALUATOR), following the OpenInference convention.

Fetch prompt → call LLM → send trace

import OpenAI from 'openai'

const openai = new OpenAI()

// 1. Fetch the prompt at runtime (no redeploy needed to change it)
const res = await fetch('https://enprompta.com/api/sdk/prompts/my-prompt?label=prod', {
  headers: { Authorization: `Bearer ${process.env.ENPROMPTA_API_KEY}` }
})
const { content, _meta } = await res.json()
// _meta.promptId  — link the trace to this prompt
// _meta.promptVersionId  — track which version ran

// 2. Call your LLM provider directly
const start = Date.now()
const completion = await openai.chat.completions.create({
  model: 'gpt-4o',
  messages: [{ role: 'user', content }]
})
const latencyMs = Date.now() - start
const output = completion.choices[0].message.content ?? ''

// 3. Send the trace — fire and forget
fetch('https://enprompta.com/api/ingest/traces', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.ENPROMPTA_API_KEY}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    spans: [{
      traceId: crypto.randomUUID(),
      spanId: crypto.randomUUID(),
      name: 'my-prompt',
      spanType: 'LLM',
      startTime: new Date(Date.now() - latencyMs).toISOString(),
      endTime: new Date().toISOString(),
      status: 'SUCCESS',
      attributes: {
        'llm.provider': 'openai',
        'llm.model': 'gpt-4o',
        'llm.input.messages': content,
        'llm.output.messages': output,
        'llm.token_count.prompt': completion.usage?.prompt_tokens,
        'llm.token_count.completion': completion.usage?.completion_tokens,
        'llm.latency_ms': latencyMs,
        'enprompta.prompt_id': _meta.promptId,
        'enprompta.prompt_version_id': _meta.promptVersionId,
        'enprompta.environment': 'production'
      }
    }]
  })
}).catch(() => {}) // never block your critical path

Alternative: single-trace endpoint

For simple single-turn calls, use the flat POST /api/v1/traces endpoint instead of the spans batch format.

fetch('https://enprompta.com/api/v1/traces', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.ENPROMPTA_API_KEY}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    provider: 'openai',
    model: 'gpt-4o',
    input: content,
    output,
    inputTokens: completion.usage?.prompt_tokens,
    outputTokens: completion.usage?.completion_tokens,
    latencyMs,
    environment: 'production',
    promptId: _meta.promptId,
    metadata: { feature: 'summariser', user_tier: 'pro' }
  })
}).catch(() => {})

Safety Auto-Scan: When enprompta.prompt_id is set and you have Safety Auto-Scan enabled on the prompt, PII Leakage is evaluated inline (no latency added) and Bias & Prompt Injection run via a 15-minute background cron. Enable toggles under Dashboard > Prompts > [prompt] > Settings.

Teams

// List teams
const { data: teams } = await client.teams.list()

// Create a team
const team = await client.teams.create({
  name: 'Engineering',
  description: 'Engineering team prompts'
})

// Get team details
const teamDetails = await client.teams.get('team_abc123')

// Update team
await client.teams.update('team_abc123', {
  name: 'Engineering Team'
})

Webhooks

// Create a webhook
const webhook = await client.webhooks.create({
  name: 'Slack Notifications',
  url: 'https://your-server.com/webhooks/enprompta',
  events: ['prompt.created', 'prompt.updated', 'execution.completed'],
  secret: 'whsec_your_webhook_secret'
})

// List webhooks
const { data: webhooks } = await client.webhooks.list()

// Get delivery history
const deliveries = await client.webhooks.getDeliveries('webhook_abc123')

Available Events

prompt.createdprompt.updatedprompt.deletedexecution.startedexecution.completedexecution.failedteam.member_added

Middleware

The SDK includes a powerful middleware system for request/response processing.

Built-in Middleware

import { Enprompta, LoggingMiddleware, CacheMiddleware, RetryMiddleware } from '@enprompta/sdk'

const client = new Enprompta({
  apiKey: 'ep_your_api_key',
  middleware: [
    // Log all requests and responses
    new LoggingMiddleware({ level: 'debug' }),

    // Cache GET requests for 5 minutes
    new CacheMiddleware({ ttl: 300000 }),

    // Retry failed requests
    new RetryMiddleware({ maxRetries: 3 })
  ]
})

Custom Middleware

import { Middleware, RequestContext, NextFunction } from '@enprompta/sdk'

class TimingMiddleware implements Middleware {
  name = 'timing'
  priority = 100

  async handle(ctx: RequestContext, next: NextFunction) {
    const start = Date.now()

    const response = await next(ctx)

    const duration = Date.now() - start
    console.log(`Request took ${duration}ms`)

    return response
  }
}

const client = new Enprompta({
  apiKey: 'ep_your_api_key',
  middleware: [new TimingMiddleware()]
})

Error Handling

The SDK provides typed error classes for different failure scenarios.

import {
  EnpromptaError,
  AuthenticationError,
  RateLimitError,
  ValidationError,
  NotFoundError,
  NetworkError,
  TimeoutError
} from '@enprompta/sdk'

try {
  await client.prompts.get('invalid_id')
} catch (error) {
  if (error instanceof NotFoundError) {
    console.log('Prompt not found')
  } else if (error instanceof AuthenticationError) {
    console.log('Invalid API key')
  } else if (error instanceof RateLimitError) {
    console.log(`Rate limited. Retry after ${error.retryAfter}s`)
  } else if (error instanceof ValidationError) {
    console.log('Validation errors:', error.errors)
  } else if (error instanceof NetworkError) {
    console.log('Network error:', error.message)
  } else if (error instanceof TimeoutError) {
    console.log('Request timed out')
  } else if (error instanceof EnpromptaError) {
    console.log(`API error ${error.code}: ${error.message}`)
  }
}

Retry Strategies

Built-in retry strategies for handling transient failures.

import { Enprompta, RetryStrategies } from '@enprompta/sdk'

const client = new Enprompta({
  apiKey: 'ep_your_api_key',
  retry: {
    // Built-in strategies
    strategy: RetryStrategies.exponential,  // 1s, 2s, 4s, 8s...
    // strategy: RetryStrategies.linear,    // 1s, 2s, 3s, 4s...
    // strategy: RetryStrategies.fixed,     // Always same delay
    // strategy: RetryStrategies.aggressive, // Quick retries
    // strategy: RetryStrategies.conservative, // Long delays
    // strategy: RetryStrategies.rateLimitAware, // Respects 429

    maxRetries: 3,
    baseDelay: 1000,
    maxDelay: 30000
  }
})
StrategyDelaysUse Case
exponential1s, 2s, 4s, 8s...General purpose (default)
linear1s, 2s, 3s, 4s...Predictable backoff
fixed1s, 1s, 1s...Consistent retry timing
aggressive100ms, 200ms...Quick recovery
conservative5s, 10s...Resource-constrained
rateLimitAwareUses Retry-After headerRate limit handling
TypeScript SDK - Enprompta