Integration
REST API
Plain HTTP using the OpenAI Chat Completions shape. Server integrations use Chat Completions and read-only model and appearance endpoints. Hosted-widget session endpoints are managed by the widget.
Base URL and authentication
https://api.wicarax.appThis is the intended production API origin. For local development, use your configured local API origin, commonly http://localhost:4000.
Every request carries the agent key as a bearer token. The key identifies the agent, so no request ever names one.
Authorization: Bearer wcx_sk_YOUR_SECRET_KEY| Key | Where it may be used | Origin requirement |
|---|---|---|
wcx_sk_ | Servers, scripts, automations | None. Never expose it in a browser |
wcx_pk_ | Browsers, for public reads and widget bootstrap. It cannot call chat completions | Required. The request must come from an origin you allowed, so a publishable key will not work from a terminal |
Core integration endpoints
| Method | Path | Purpose |
|---|---|---|
POST | /v1/chat/completions | Ask a question and receive the answer. See Chat Completions |
GET | /v1/models | Read the public model id. See Models |
GET | /v1/agent/config | Read the agent display name and cosmetic settings, for theming your own interface. Read only |
GET | /v1/widget.js | The hosted widget loader. No authentication, since the embedding page supplies the key |
The hosted widget also uses session and message endpoints internally. The table is the core integration surface, not an exhaustive count of public routes. Agents, material, keys and plans are managed in the dashboard.
Your first request
curl https://api.wicarax.app/v1/chat/completions \
-H "Authorization: Bearer $WICARAX_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "wicarax-zenith",
"messages": [
{ "role": "user", "content": "What is your refund policy?" }
]
}'The response is a complete JSON body:
{
"id": "chatcmpl-...",
"object": "chat.completion",
"created": 1769500000,
"model": "wicarax-zenith",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "Example answer based on your sources."
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 1254,
"completion_tokens": 20,
"total_tokens": 1274
}
}Using an OpenAI client
The request and response shapes follow the OpenAI chat completions format, so libraries and tools that already speak it need two changes: the base URL and the key. What they must not do is send parameters this API has not implemented: those are refused, not ignored, so a client configured with its usual defaults will get a 400 until they are removed.
import OpenAI from 'openai';
const client = new OpenAI({
apiKey: process.env.WICARAX_SECRET_KEY,
baseURL: 'https://api.wicarax.app/v1',
});
const completion = await client.chat.completions.create({
model: 'wicarax-zenith',
messages: [{ role: 'user', content: 'What is your refund policy?' }],
});- The
modelfield is required and must be the public model saved on the agent. Unknown ids returnmodel_not_found; known unavailable ids returnmodel_unavailable; an available but unassigned id returnsmodel_not_assigned. The request cannot override the agent's configured model. - Default parameters are the common first failure. An OpenAI client or framework that sends
temperature,max_tokens,stoportoolsby default will be refused with400 unsupported_parameter, andparamwill name the offending field. - Streaming is not supported. Sending
stream: trueis refused with400and the codestreaming_not_supported, rather than being silently ignored, so a client cannot believe it opted in and then mis-parse the body. systemmessages are refused, not dropped. They return400 unsupported_parameter, because silently discarding a system prompt lets a caller believe they steered the assistant. Behaviour comes from the dashboard, not from the request.
Text-only requests
Product chat is text-only. content is normally a string; an array of text parts is also accepted for OpenAI-compatible clients. Any file or media part — including image_url, file, input_image or input_file — is refused with 400 attachments_not_supported before inference, quota reservation or conversation persistence. A multipart/form-data request to this route is rejected the same way; it has no attachment upload endpoint.
{
"model": "wicarax-zenith",
"messages": [{ "role": "user", "content": "What is your refund policy?" }]
}Answers are grounded in the agent's authorized Knowledge Base. To add material, upload it as a Knowledge Base source in the dashboard; that upload workflow is separate from product chat and remains available.
Limits to design around
| Limit | Value | What happens when exceeded |
|---|---|---|
| Request body size | 15 MB JSON on Chat Completions | 413 before the request is processed |
| Requests per minute across the account | Set by the plan, applied per account and shared by every agent you own | 429 with a Retry-After header |
| Requests per month | Set by the plan, applied to the account | The call still succeeds, and the answer is a short unavailability message with zero usage |
| Failed credential attempts | Budgeted per network | 429 after repeated refusals, with Retry-After |
The monthly quota does not produce an error. A caller sees a normal 200 with a polite unavailability message, so if your integration starts returning that for everything, check the account rather than your code.
Exact numbers per plan are on Rate Limits, and every status and code is on Errors.
Errors
Failures use the OpenAI error envelope:
{
"error": {
"message": "Access denied for this request.",
"type": "authentication_error",
"param": null,
"code": "access_denied"
}
}All four fields are always present. param names the request field at fault on a 400, and is null when no single field is to blame.
A malformed key, an unknown key, an inactive agent and a disallowed origin all answer with the same 403 access_denied. That is deliberate, so a scraped key cannot be confirmed by the response. Check the credential type, key state, agent state, and allowed origin before retrying.
Keep the key in an environment variable and pass it as a bearer token per request. Never place a secret key in a query string, where it ends up in server logs and browser history.
There is no endpoint that writes configuration, and there is not going to be one in this surface. An API key can consume an agent, never reconfigure it, which is why a leaked key cannot be used to change your setup even though it can spend your allowance.