New:Socket for Asana Is Now Available.Learn more
Get Started

@hlix/sdk

Package Overview
Dependencies
Maintainers
1
Versions
1
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@hlix/sdk

Typed client for the hlix API — auth, organization scoping, typed errors, SSE streaming, and bounded retry on idempotent verbs only.

latest
Source
npmnpm
Version
0.2.0
Version published
Maintainers
1
Created
Source

@hlix/sdk

The authenticated hlix client. Typed end to end from the published API contract, with the things a caller should not have to reimplement: credentials, workspace selection, typed errors, Server-Sent Events, and bounded retry.

Install

npm install @hlix/sdk

Requires Node.js 20 or newer. The only runtime dependency is openapi-fetch.

Quick start

import { createHlix, apiKeyCredential, HlixNotFoundError } from "@hlix/sdk"

const hlix = createHlix({
  baseUrl: "https://server.hlix.ai",
  credential: apiKeyCredential(process.env.HLIX_API_KEY!),
  organizationId: process.env.HLIX_WORKSPACE_ID,
})

const comments = await hlix.tasks.listComments(taskId)

try {
  await hlix.tasks.get("does-not-exist")
} catch (error) {
  if (error instanceof HlixNotFoundError) {
    // 404 also means "shared with someone else" — see below.
  }
}

Where the types come from

Nothing in this package restates a request or response shape. Types are read out of the generated paths in @hlix/api-client, which is generated from apps/backend/openapi.json, which is emitted from the Zod validators the routes actually run. One schema, from request validation to your editor.

A consequence worth knowing: responses the contract has not yet pinned come back as unknown rather than as an invented interface. tasks.listComments() is fully typed; projects.list() is not, because the contract does not yet describe that body. That is deliberate — a fabricated type would be wrong the first time a column changed.

Authentication

Two credential kinds:

apiKeyCredential("hlix_…")     // x-api-key
sessionCredential("session=…") // the better-auth session cookie

API keys are verified by the backend's API middleware and do not become browser sessions. They also do not carry a default workspace, so pass organizationId for every programmatic client.

organizationId selects the workspace (X-Organization-Id). Omit it and the server falls back to the session's active workspace — fine for a browser, a trap for a script that assumes it knows where it just wrote. The SDK never invents one.

Errors

Every failure throws a typed error carrying status, the parsed body, and the requestId when the server sent one.

ClassStatus
HlixValidationError400 with a schema failure — exposes decoded issues
HlixBadRequestError400 otherwise (e.g. no active workspace)
HlixAuthenticationError401
HlixPermissionError403
HlixNotFoundError404
HlixConflictError409
HlixRateLimitError429 — exposes retryAfter
HlixNotImplementedError501
HlixServerError5xx
HlixTransportErrorno response at all

HlixValidationError.issues matters: the server sends its issue list as a JSON string inside error.message. Printing that raw is how you end up with an error message containing an escaped array. The SDK decodes it for you.

HlixNotFoundError does not mean "does not exist". The API answers 404 for a resource the caller was never granted, so existence is not revealed.

Retry

Bounded exponential backoff with full jitter, honouring Retry-After.

A non-idempotent request is never retried. Not on 503, not on 429, not when the transport failed and the SDK cannot know whether the server acted. POST and PATCH are absent from the allowlist, and the allowlist is an allowlist — a verb nobody considered is not retried. A retried task dispatch is a duplicated coder run, which is exactly the hazard durable attempt identity exists to eliminate.

createHlix({ …, retry: { attempts: 1 } })            // off
createHlix({ …, retry: { attempts: 5, baseDelayMs: 100 } })

Retried statuses are 429, 500, 502, 503 and 504. 501 is excluded: the contract documents it as a permanent refusal, so a retry only buys latency.

Streaming

const controller = new AbortController()
for await (const event of hlix.tasks.stream(taskId, { signal: controller.signal })) {
  console.log(event.event, JSON.parse(event.data))
}

stream(path) consumes any text/event-stream route. It is not routed through the typed client — that parses a body, and a stream must be read as it arrives — and retry does not apply, because a stream that drops has already delivered events and restarting it would replay them.

The parser follows the WHATWG spec, which means an event whose data buffer is empty is not dispatched. The backend's event: keepalive frames carry no data, so they never surface. They still keep the connection warm; if you need a liveness signal, use a timeout rather than waiting for a keepalive.

Escape hatch

hlix.raw is the untouched @hlix/api-client transport for anything this facade does not wrap — same types, no retry, no throwing, { data, error }.

Build

bun run --filter '@hlix/sdk' build   # dist/index.js + a self-contained dist/index.d.ts
bun run --filter '@hlix/sdk' test

@hlix/api-client is a private workspace package, so it is bundled into both outputs. The only external dependency in the shipped bundle is openapi-fetch.

Keywords

hlix

FAQs

Package last updated on 13 Aug 2026

Related posts