Skip to content

TypeScript SDK

Use the typed hlix client for workspace-scoped automation, projects, tasks, repository access, and activity.

@hlix/sdk is the first-party TypeScript client. Its request and response types come from the published OpenAPI contract, and it adds credentials, explicit workspace selection, typed errors, bounded retry, repository access, and activity.

  • Node.js 20 or later
  • An hlix API key
  • A workspace ID
  • TypeScript is recommended, but the package also exposes ESM JavaScript
Terminal window
npm install @hlix/sdk

Pin the version in CI so a build never picks up a client you have not tested against:

Terminal window
npm install @hlix/sdk@3.0.0
import { createHlix, apiKeyCredential } from '@hlix/sdk'
const hlix = createHlix({
baseUrl: 'https://server.hlix.ai',
credential: apiKeyCredential(process.env.HLIX_API_KEY!),
organizationId: process.env.HLIX_WORKSPACE_ID!,
})

Set organizationId explicitly in server-side automation to select the Workspace for the authenticated user.

Set TASK_ID to an existing task ID, then list projects and inspect that task:

import {
createHlix,
apiKeyCredential,
HlixNotFoundError,
} from '@hlix/sdk'
const hlix = createHlix({
baseUrl: process.env.HLIX_BASE_URL ?? 'https://server.hlix.ai',
credential: apiKeyCredential(process.env.HLIX_API_KEY!),
organizationId: process.env.HLIX_WORKSPACE_ID!,
})
const projects = await hlix.projects.list()
console.log(projects)
const taskId = process.env.TASK_ID!
try {
const task = await hlix.tasks.get(taskId)
console.log(task)
} catch (error) {
if (error instanceof HlixNotFoundError) {
console.error('Task is absent or not visible to this caller')
} else {
throw error
}
}

Expected result: the project list is scoped to the selected workspace.

const task = await hlix.tasks.get(taskId)
for await (const watch of hlix.live.task(task.projectId, taskId)) {
console.log(watch.state, watch.task?.status)
}

watch.state is running until the task is over, then finished — or unmet, with a reason, when its cycle ended first. The loop ends on its own at that point.

hlix.live uses the same live protocol as the dashboard. Set projectId to an existing project ID for these examples. Events are typed: event.type narrows to the event’s fields.

// One message to the project's team, answered as it is written.
for await (const event of hlix.live.run({ threadId: `project:${projectId}`, text: 'What is left?' })) {
if (event.type === 'TEXT_MESSAGE_CONTENT') process.stdout.write(event.delta)
if (event.type === 'RUN_ERROR') console.error(event.message)
}
// A project's state as it changes: its cycles, tasks, agents and new decisions (the board, read again every five seconds).
for await (const { state } of hlix.live.project(projectId)) {
console.log(state.project.status, state.tasks.length)
}

A conversation is named by its chat: project:<id>, its :cycle:<id> and :task:<id> forms, or workspace:<orgId>:user:<userId> for a person’s own assistant. live.run sends one turn, then yields the chat’s events until that turn ends (CUSTOM hlix.outcome for its own messageId: answered, stopped or failed, with a RUN_ERROR before a failed one). The chat is shared, so other people’s turns can appear in the same events: read your own answer from the run whose id is your messageId. A stream joined after the answer began opens with a MESSAGES_SNAPSHOT that already holds the words written so far under the id live-<messageId>-<n>.

live.follow(threadId) reads a chat without sending anything. It stays open across runs, including a run the project manager starts on its own, and reconnects with backoff after a dropped connection, resuming from the last frame it yielded so none repeats. It ends only when you abort its signal. A turn is sent once and never retried automatically: pass the same messageId if you retry a message yourself, so the server recognizes the same turn. live.send posts a turn without reading it, hlix.live.stop(threadId) stops the run in flight, live.answer(threadId, requestId, { approved }) answers an approval the agent asked for (CUSTOM hlix.approval_requested; a request someone already answered is a HlixConflictError), and live.messages(threadId, { before }) pages the saved transcript.

hlix.chat is the same chat for a caller that cannot hold a stream open (an assistant on a phone): chat.send({ threadId, message }) stores a turn and answers at once with its operationId, chat.stop({ threadId }) stops the run, and chat.reply(threadId) answers { status, text } — running while a run is in flight, complete with the newest answer after the person’s last message, or delivered while nobody has answered it yet.

A project watch stays open until you leave the loop or abort its signal. It reads the project’s board again every five seconds and yields only when something changed; key repeated activity rows by id.

Class Meaning
HlixValidationError 400 schema failure; exposes decoded issues
HlixBadRequestError other 400 response
HlixAuthenticationError 401
HlixPermissionError 403
HlixNotFoundError 404, including not visible
HlixConflictError 409 compare-and-swap or state conflict
HlixRateLimitError 429; exposes retryAfter
HlixNotImplementedError 501 unavailable operation
HlixServerError other 5xx
HlixTransportError no HTTP response

Every HTTP error also carries the status, parsed body, and request ID when the server provides one.

The default uses bounded exponential backoff with jitter and honors Retry-After. Only idempotent verbs are eligible. POST and PATCH are never retried automatically because the client may not know whether the server already performed the mutation.

Disable retry:

const hlix = createHlix({
baseUrl,
credential,
organizationId,
retry: { attempts: 1 },
})

HLIX Repos stores committed project files. The SDK exposes scoped repository access and head reads:

  • hlix.repos.access(projectId, repoId, 'read') requests short-lived read access.
  • hlix.repos.head(projectId, repoId) reads the repository’s current commit.
  • hlix.repos.protectedFiles(projectId) reads protected files when your role permits it.

Keep access tokens and protected-file responses out of logs. For local-to-cloud import and synchronization, use the CLI, which handles scanning, secret separation, and conflict detection.

hlix.raw exposes a typed client for every published operation, including those the higher-level methods do not wrap:

const { data, error } = await hlix.raw.GET('/v1/api/projects')

The raw client returns { data, error }; it does not apply the SDK’s throwing error model.

Terminal window
HLIX_API_KEY='…' \
HLIX_WORKSPACE_ID='<workspace-id>' \
node dist/example.js

Use a secret manager in CI and avoid printing environment variables. Confirm the result against hlix projects list --json or the web app in the same workspace.

  • Cannot find module '@hlix/sdk' — the package is not installed in this project. Run npm install @hlix/sdk; Current versions lists the published version.
  • HlixAuthenticationError — the key is missing or rejected. It authenticates a user, so a valid key with no organizationId can still fail the next check.
  • HlixPermissionError — the user cannot act in the selected workspace. Confirm organizationId is the one you meant.
  • HlixNotFoundError on a resource you can see in the dashboard — check the workspace, resource ID, and your access. A missing or inaccessible resource uses this error.
  • HlixConflictError — the resource changed or an operation is already in progress. Read its current state before deciding whether to retry.
  • HlixTransportError — no HTTP response at all. Check baseUrl, DNS, and TLS before suspecting credentials.
  • A write returned no response — inspect the resource before submitting it again. The SDK does not automatically retry POST or PATCH, and a lost response does not prove the server rejected the request.
  • A stream disconnects — live.follow and live.run reconnect with backoff and resume from their last frame; live.project and live.task read the board on a clock and throw when a read fails. Handle the final error if reconnect attempts are exhausted.