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
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 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.
// 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.
const appearance = await wicarax.agent.getAppearance();
appearance.name; // agent display name
appearance.customization.welcomeMessage; // first message to show
appearance.customization.placeholderText; // input placeholderThis 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.
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.