TypeScript SDK
@syntic/sdk is the official JavaScript and TypeScript library for the Syntic API. It runs on Node.js, Deno, Bun, Cloudflare Workers, Vercel Edge and other runtimes with a standard fetch, and gives you full type definitions, retries with back-off, streaming helpers, automatic pagination, a tool-loop runner and access to every Syntic endpoint, including agents, sessions and the Admin API.
Today there is no official package. Use an OpenAI-compatible client with Syntic’s base URL, as shown in OpenAI-compatible clients. That keeps working after the SDK ships.
Install
npm install @syntic/sdk
# or: pnpm add @syntic/sdk / yarn add @syntic/sdk / bun add @syntic/sdk
# Deno: import Syntic from 'npm:@syntic/sdk'| Requirement | Value |
|---|---|
| Node.js | 18 or newer |
| TypeScript | 4.9 or newer (optional) |
| Module formats | ES modules and CommonJS |
| Dependencies | None at runtime |
Create a client
import Syntic from '@syntic/sdk'
const client = new Syntic() // reads SYNTIC_API_KEY where process.env exists| Option | Default | Notes |
|---|---|---|
apiKey | process.env.SYNTIC_API_KEY | Required in runtimes without process.env. |
baseURL | process.env.SYNTIC_BASE_URL ?? 'https://app.syntic.ai/v1' | |
version | The version the release was built for | Sent as syntic-version. |
betas | [] | Sent as syntic-beta. Methods that need a flag add it automatically. |
maxRetries | 2 | Retries rate_limit, overloaded, api_error and network errors, honouring retry-after. |
timeout | 600_000 ms | Per request. |
fetch | globalThis.fetch | Supply your own for proxies or tests. |
dangerouslyAllowBrowser | false | The client refuses to run in a browser unless set, because a key in a page is readable by anyone. See Calling from a browser. |
A first request
import Syntic from '@syntic/sdk'
const client = new Syntic()
const reply = await client.chat.completions.create({
model: 'Amara S1.2',
messages: [{ role: 'user', content: 'Give me three names for a bakery.' }],
max_tokens: 200,
})
console.log(reply.choices[0].message.content)
console.log(reply.usage.total_tokens, reply.request_id)Streaming
const stream = client.chat.completions.stream({
model: 'Amara S1.2',
messages: [{ role: 'user', content: 'Write a haiku about rain.' }],
})
for await (const text of stream.textDeltas()) process.stdout.write(text)
const final = await stream.finalCompletion() // message, joined tool calls, usageCancel with stream.abort() or an AbortSignal passed as { signal } in the second argument. To forward a stream from your server to a browser, stream.toReadableStream() returns a web ReadableStream of server-sent events.
Tool loop
import Syntic, { defineTool } from '@syntic/sdk'
import { z } from 'zod'
const client = new Syntic()
const getOrder = defineTool({
name: 'get_order',
description: 'Look up an order by its id.',
parameters: z.object({ order_id: z.string() }),
run: async ({ order_id }) => ({ order_id, status: 'shipped', carrier: 'DHL' }),
})
const result = await client.tools.run({
model: 'Amara S1.2',
messages: [{ role: 'user', content: 'Where is order 88123?' }],
tools: [getOrder],
maxRounds: 8,
})
console.log(result.finalMessage.content)parameters accepts a Zod schema or a plain JSON Schema object. Arguments are validated before run is called; invalid arguments are returned to the model as an error result.
Errors
API errors are thrown as subclasses of Syntic.APIStatusError, named after the envelope type: InvalidRequestError, AuthenticationError, PermissionDeniedError, NotFoundError, QuotaExhaustedError, RateLimitError, OverloadedError and InternalServerError. Network failures throw APIConnectionError or APITimeoutError. Each API error has status, type, code, message, param, details and requestId.
try {
await client.chat.completions.create({ model: 'Amara S1.2', messages: [] })
} catch (err) {
if (err instanceof Syntic.QuotaExhaustedError) {
console.log('Allowance resets at', err.details?.resets_at)
} else if (err instanceof Syntic.APIStatusError) {
console.log(err.code, err.param, err.requestId)
} else throw err
}Pagination
for await (const agent of client.agents.list({ limit: 50 })) {
console.log(agent.id, agent.name)
}Files, batches, agents and admin
import fs from 'node:fs'
import Syntic, { SynticAdmin } from '@syntic/sdk'
const client = new Syntic()
const file = await client.files.upload({ file: fs.createReadStream('contract.pdf'), purpose: 'document' })
const session = await client.sessions.create({
agent_id: 'agt_01JC4R8W6Q2N9X3T5V7Y',
input: [{ type: 'user.message', content: 'Refund order 88123' }],
})
for await (const event of client.sessions.stream(session.id)) {
if (event.type === 'message.completed') console.log(event.content)
}
const admin = new SynticAdmin() // reads SYNTIC_ADMIN_KEY
const org = await admin.organization.retrieve()
console.log(org.plan.name)Raw responses and headers
const { data, response } = await client.chat.completions
.create({ model: 'Amara Quick', messages: [{ role: 'user', content: 'ping' }] })
.withResponse()
console.log(response.headers.get('x-syntic-request-id'), response.headers.get('x-syntic-billing-source'))Build notes for the platform team
- Same generator and helper set as the Python SDK; release both together.
- Zero runtime dependencies; Zod is an optional peer dependency used only by
defineTool. - Test every release on Node 18, 20 and 22, Deno, Bun and an edge runtime in CI.
- Publish to npm with provenance from CI under the
@synticscope.