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
npm install @wicarax/sdkRequires 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.
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 capability | Publishable key | Secret key |
|---|---|---|
chat.send | No | Yes |
agent.getAppearance | Yes | Yes |
models.list | Yes | Yes |
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.
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.
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:
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 field | What it holds |
|---|---|
reply | The answer, ready to render |
model | The public model id that produced it |
usage | Input, output and total token counts for this exchange |
id | Completion id, useful in your own logs |
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
Product chat is text-only. chat.send accepts a text message and text history; file and media attachments are refused by the API with 400 attachments_not_supported. The currently published SDK release still advertises an attachments option, so a new SDK release is required to remove it from the public types and examples. Do not send attachments in the meantime.
For material the agent should answer from, upload it to a Knowledge Base in the dashboard. Knowledge Base ingestion and custom avatar uploads are separate upload workflows and remain available.
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.
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
userandassistantturns are used, and the SDK filters history to those two roles before sending, so asystemturn never leaves your process. A raw API request that includes asystem,developer,toolorfunctionturn is refused with400: 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 sessionId groups 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
The SDK sends wicarax-zenith by default for existing callers. Pass model explicitly when the agent is configured for a different public model. The API rejects a known but unassigned id with model_not_assigned. Use the catalog when your interface needs to show available models; chat does not fetch it first.
// Optional discovery for a model picker.
const { data } = await wicarax.models.list();
console.log(data.map((model) => model.id));
// Or after any answer, echoed by the API.
console.log(result.model);Older clients send Zenith by default. If an agent is switched to another model, show the model_not_assigned error in your own chat interface and update the model passed to chat.send.
import { WicaraxError } from '@wicarax/sdk';
try {
const answer = await wicarax.chat.send({ message, model: 'wicarax-pulse' });
showAnswer(answer.reply);
} catch (error) {
if (error instanceof WicaraxError && error.code === 'model_not_assigned') {
showError(error.message);
} else {
throw error;
}
}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.
const appearance = await wicarax.agent.getAppearance();
appearance.name; // agent display name
appearance.customization.theme; // light or dark surface
appearance.customization.primaryColor; // widget color used by the hosted UI
appearance.customization.widgetLanguage; // en or id for interface text
appearance.customization.welcomeMessage; // first message to show
appearance.customization.placeholderText; // input placeholderThis call is read only and returns public display fields only. It never includes private Personalization instructions. 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.
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.
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.
}
}
}| Property | Meaning |
|---|---|
status | HTTP status, when the failure came from the API. Absent for local errors, which never reached the network |
type | Error family, for example authentication_error |
code | Stable machine code, for example access_denied |
param | The request field the API blamed, when it named one, for example model or messages[1].name |
retryAfter | Seconds 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.
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.
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.