TypeScript SDK
@lvng/sdk@2.0.0 is the official TypeScript client for the LVNG API. It runs on Node 18+ and modern browsers, ships dual ESM/CJS builds, and covers all 51 OpenAPI tags / 386 operations with full type definitions.
Install
npm install @lvng/sdk
# or
pnpm add @lvng/sdk
yarn add @lvng/sdkQuickstart
Construct a client with your API key and start calling resources. The example below sends a chat message and lists workflows.
import { LvngClient } from '@lvng/sdk'
const client = new LvngClient({
apiKey: process.env.LVNG_API_KEY!,
})
// Send a chat message
const reply = await client.chat.send({
message: 'Summarize the latest sales report',
platform: 'api',
})
console.log(reply)
// List workflows
const workflows = await client.workflows.list({ limit: 20 })
console.log(workflows.data)Authentication
The SDK authenticates every request with the x-api-key header. Generate a key at Settings → Developer and store it in the LVNG_API_KEY environment variable — never commit it to source control.
import { LvngClient } from '@lvng/sdk'
// Recommended — read from the environment
const client = new LvngClient({
apiKey: process.env.LVNG_API_KEY!,
})
// You can also pass a custom base URL (self-hosted / staging)
const staging = new LvngClient({
apiKey: process.env.LVNG_STAGING_API_KEY!,
baseUrl: 'https://staging.api.lvng.ai',
})Configuration
Every option besides apiKey is optional and has a sensible default.
apiKey—Required. Per-user API key.baseUrlhttps://api.lvng.aiOverride for staging or self-hosted deployments.timeout30000Per-request timeout in milliseconds.maxRetries3Retries on 429 / 503. POSTs without an Idempotency-Key are skipped.retryBaseDelayMs200Base for exponential backoff (200, 600, 1800 ms).fetchImplglobalThis.fetchInject a custom fetch (testing, polyfills).import { LvngClient } from '@lvng/sdk'
const client = new LvngClient({
apiKey: process.env.LVNG_API_KEY!,
baseUrl: 'https://api.lvng.ai', // default
timeout: 30000, // ms; default 30s
maxRetries: 3, // 429 / 503 retries; default 3
retryBaseDelayMs: 200, // exponential backoff base; default 200ms
})Streaming chat
client.chat.streamEvents() wraps the underlying SSE response and yields parsed events. Use stream() if you need the raw Response instead.
import { LvngClient } from '@lvng/sdk'
const client = new LvngClient({ apiKey: process.env.LVNG_API_KEY! })
// streamEvents() yields parsed SSE events as they arrive
for await (const ev of client.chat.streamEvents({
message: 'Walk me through the Q4 revenue numbers',
platform: 'api',
})) {
if (ev.event === 'text_delta') {
const data = ev.data as { delta?: string }
process.stdout.write(data.delta ?? '')
} else if (ev.event === 'tool_use') {
console.log('\n[tool]', ev.data)
} else if (ev.event === 'done') {
console.log('\n[done]')
}
}
// Need raw SSE bytes? Use stream() to get the underlying Response.
const response = await client.chat.stream({ message: 'hi' })
const reader = response.body!.getReader()
// ... read and parse manuallyError handling
The SDK throws a typed error hierarchy rooted at LvngError. Match on the subclass for actionable recovery.
LvngErroranyBase class. Has status, code, details.AuthenticationError401API key is missing, expired, or revoked.RateLimitError429Throttled. Inspect retryAfter (seconds).NotFoundError404Resource does not exist or is not visible to your workspace.import {
LvngClient,
LvngError,
AuthenticationError,
RateLimitError,
NotFoundError,
} from '@lvng/sdk'
const client = new LvngClient({ apiKey: process.env.LVNG_API_KEY! })
try {
const workflow = await client.workflows.get('wf_does_not_exist')
console.log(workflow)
} catch (err) {
if (err instanceof AuthenticationError) {
console.error('API key is invalid or revoked — generate a new one.')
} else if (err instanceof RateLimitError) {
console.error(`Rate limited. Retry after ${err.retryAfter}s.`)
} else if (err instanceof NotFoundError) {
console.error('Workflow not found.')
} else if (err instanceof LvngError) {
console.error(`LVNG ${err.status} ${err.code}: ${err.message}`)
} else {
throw err
}
}Pagination
List endpoints come in two flavours: cursor-based (audit log, conversations, developer events) and offset-based (workflows, agents, channels, ...). Both keep their server-side wire shape so the SDK doesn't paper over the difference.
import { LvngClient } from '@lvng/sdk'
const client = new LvngClient({ apiKey: process.env.LVNG_API_KEY! })
// Cursor-based pagination (audit log, conversations, developer events, ...)
let cursor: string | undefined
do {
const page = await client.auditLog.list({ limit: 100, cursor }) as {
data: unknown[]
nextCursor?: string
}
for (const entry of page.data) {
console.log(entry)
}
cursor = page.nextCursor
} while (cursor)
// Offset-based pagination (workflows, agents, channels, ...)
const first = await client.workflows.list({ limit: 50, offset: 0 })
const next = await client.workflows.list({ limit: 50, offset: 50 })Idempotent retries
The SDK retries 429 / 503 on idempotent verbs automatically. To make a POST safe to retry, pass an idempotencyKey per request — the API treats two requests with the same key as one.
import { randomUUID } from 'node:crypto'
import { LvngClient } from '@lvng/sdk'
const client = new LvngClient({ apiKey: process.env.LVNG_API_KEY! })
// Pass an idempotency key to make POSTs safe to retry on 429 / 503
const key = randomUUID()
await client.chat.send(
{ message: 'create one report', platform: 'api' },
{ idempotencyKey: key },
)Resources reference
Every OpenAPI tag is exposed as a resource on the client. The hand-tuned resources (chat, workflows, agents, knowledge, apiKeys) extend their generated counterparts with stronger types and convenience helpers.