Integration

Official SDK

A small TypeScript client for one job. Use an agent you already configured, and render its answers in a chat interface you build yourself. It cannot change anything about the agent.

Install

terminal
npm install @wicarax/sdk

Requires Node.js 18 or later, or any modern browser or bundler. The package ships as CommonJS with TypeScript declarations, has no runtime dependencies, and works with both require and ESM named imports.

Note

The package page is @wicarax/sdk on npm.

What each key can do

Capability is per method, not per client. A publishable key is a real credential. It is simply not a chat credential.

SDK capabilityPublishable keySecret key
chat.sendNoYes
agent.getAppearanceYesYes
models.listYesYes

Calling chat.send on a client built with only a publishable key throws before any network request, with code: "secret_key_required" and no status. It is a configuration mistake rather than a refusal, so isAuthError is false and isCapabilityError is true.

Heads up

The two checks are independent and both real. The SDK refuseschat.send in your own process, with no network request; the server refuses a wcx_pk_ on /v1/chat/completions with 403 access_denied. The server-side refusal is the boundary that matters, because curl, a copied fetch snippet and any OpenAI client never run the SDK's check. The SDK's version exists so you find out at your desk rather than in production.

Send a message

Chat runs on a server, with a secret key. If you are building a browser UI, put this call behind your own endpoint and let your frontend talk to that.

server.ts
import { Wicarax } from '@wicarax/sdk';

const wicarax = new Wicarax({ secretKey: process.env.WICARAX_SECRET_KEY });

const result = await wicarax.chat.send({
  message: 'What is your refund policy?',
});

console.log(result.reply);

In a browser, a publishable key still reads the agent's public appearance and the model list, which is what you need to theme a custom interface:

browser.ts
import { Wicarax } from '@wicarax/sdk';

const wicarax = new Wicarax({ publishableKey: 'wcx_pk_...' });

const { name, customization } = await wicarax.agent.getAppearance();
const { data: models } = await wicarax.models.list();

// wicarax.chat.send(...) would throw here: chat needs a secret key.
Result fieldWhat it holds
replyThe answer, ready to render
modelThe public model id that produced it
usageInput, output and total token counts for this exchange
idCompletion id, useful in your own logs
Heads up

The client refuses a secret key in a browser before any network call, and the check reads the key's prefix rather than which option carried it, so passing a wcx_sk_ value as publishableKey is still refused. A publishable key requires a browser origin, so using one from a server is refused even though the key is valid. Keep secret keys on your server.

Conversation history

The client is stateless and keeps nothing between calls. Hold the transcript in your own state and pass it back on each turn, oldest first.

history.ts
const history: ChatMessage[] = [];

const first = await wicarax.chat.send({ message: 'Do you offer annual plans?' });
history.push({ role: 'user', content: 'Do you offer annual plans?' });
history.push({ role: 'assistant', content: first.reply });

const second = await wicarax.chat.send({
  message: 'What is the refund window for those?',
  history,
});
  • Only user and assistant turns are used, and the SDK filters history to those two roles before sending, so a system turn never leaves your process. A raw API request that includes a system, developer, tool or function turn is refused with 400: instructions come from the dashboard and cannot be overridden by a request.
  • Keep failures out of the transcript. If a call throws, do not push an assistant turn, or the model will read your error message back as something it said.

An optional sessionIdgroups exchanges in the owner's chat log. It is limited to 64 characters by the API. The SDK does not truncate a longer value; choose a stable value within the limit or handle the validation error.

Which model answers

You never choose one. There is exactly one model, and the SDK sends its id, wicarax-zenith, on every chat.send for you, because the API requires the field and refuses any other value with 400 model_not_found. When you need the id in your own code, read it from the API rather than assuming it.

model.ts
// Before the first message.
const { data } = await wicarax.models.list();
console.log(data[0].id);

// Or after any answer, echoed by the API.
console.log(result.model);

Theming your interface

Your UI is yours, but you can read the same display values the hosted widget uses, so a custom interface stays consistent with what the agent owner configured.

appearance.ts
const appearance = await wicarax.agent.getAppearance();

appearance.name;                          // agent display name
appearance.customization.welcomeMessage;  // first message to show
appearance.customization.placeholderText; // input placeholder

This call is read only and returns cosmetic fields only. It is most useful when whoever builds the interface is not whoever configured the agent, for example an agency shipping a frontend for a client. If you own the agent and are designing your own interface anyway, skip it and use chat.send alone.

Timeouts, cancellation and errors

Timeouts and cancellation

Requests time out after 60 seconds by default. Pass timeoutMs to change it, or 0 to disable it. Cancel an in-flight request with an AbortSignal, for example when the user edits their question or navigates away.

cancel.ts
const controller = new AbortController();
const promise = wicarax.chat.send({ message, signal: controller.signal });
controller.abort();

Errors

Every failure throws WicaraxError, which carries enough to decide what to do without parsing a message string.

errors.ts
import { WicaraxError } from '@wicarax/sdk';

try {
  await wicarax.chat.send({ message });
} catch (error) {
  if (error instanceof WicaraxError) {
    if (error.isRateLimited) {
      await wait((error.retryAfter ?? 5) * 1000);
    } else if (error.isCapabilityError) {
      // This key cannot do this. Today: chat.send with a publishable key.
      // Nothing was sent, so there is no status to inspect.
    } else if (error.isAuthError) {
      // Key, agent status or origin.
    } else if (error.isTimeout) {
      // Exceeded timeoutMs.
    }
  }
}
PropertyMeaning
statusHTTP status, when the failure came from the API. Absent for local errors, which never reached the network
typeError family, for example authentication_error
codeStable machine code, for example access_denied
paramThe request field the API blamed, when it named one, for example model or messages[1].name
retryAfterSeconds to wait, taken from the Retry-After header on 429

The full list of statuses and codes is on Errors.

What the SDK does not do

  • It does not create, edit or delete agents, and it cannot change any agent setting.
  • It does not manage keys, domains, plans or billing.
  • It does not stream, and it returns no citation or source data.
  • It ships no interface. That is the hosted widget’s job.

If you would rather not build an interface at all, use the widget. If you are working in a language the SDK does not cover, call the REST API directly.

Tip

Put a small endpoint of your own in front of the secret key. Your frontend calls your server, your server calls Wicarax, and the key never reaches a browser or a bundle.

Disclaimer

This client consumes an agent and nothing more. It cannot create or reconfigure anything, it does not stream, and it returns no citation or source data. If a future version adds capability, it will be additive rather than a change to what is documented here.