Widget API Documentation
Everything you need to integrate the NexonTech chat widget into your application.
Overview
The Widget API lets you initialize chat conversations and exchange messages with your AI agent programmatically. All endpoints accept and return JSON. CORS is fully open, so you can call these endpoints from any domain.
https://nexontech.org/api/widgetQuick Start
Get a conversation running in two API calls:
1. Initialize a conversation
POST /api/widget/init
Content-Type: application/json
{
"key": "wk_your_widget_key"
}2. Send a message
POST /api/widget/chat
Content-Type: application/json
{
"key": "wk_your_widget_key",
"conversation_id": "conv_id_from_step_1",
"text": "Hello, I need help!"
}Authentication
Every widget request identifies itself with your widget key, sent in the request body. Keys start with wk_ and are tied to one agent. Treat the key as a public identifier, not a secret: it ships inside the embed snippet on your own page, so anyone viewing your site can read it. These endpoints carry no cookies or Authorization header by design and answer any origin. The key says which agent to answer as — it does not prove who is asking, so never rely on it to protect anything.
Where to find your key
- Go to Dashboard > Agents
- Select or create an agent
- Copy the key from the Embed Code section
Initialize Conversation
/api/widget/initCreates a new conversation and returns the conversation ID. Call this once per chat session.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
key | string | Yes | Your widget API key (starts with wk_) |
visitor_id | string | No | Optional visitor identifier for tracking returning users |
page_url | string | No | URL of the page where the chat was started |
Response 200 OK
{
"conversation_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"visitor_id": "v_abc123",
"chatbot_name": "Support Bot",
"config": {}
}| Field | Type | Description |
|---|---|---|
conversation_id | string | Unique conversation identifier — use this in all subsequent chat requests |
visitor_id | string | Visitor ID (generated if not provided in the request) |
chatbot_name | string | Display name of the agent |
config | object | Agent configuration metadata |
Send Message
/api/widget/chatSends a user message and returns the AI agent's response. Messages are persisted and visible in your dashboard.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
key | string | Yes | Your widget API key |
conversation_id | string | Yes | Conversation ID from the init response |
text | string | Yes | User message (max 2,000 characters) |
visitor_id | string | No | Visitor identifier |
lang | string | No | Language code: en, ro, or ru (default: en) |
Response 200 OK
{
"response": "Hi! I'd be happy to help. What can I do for you today?",
"user_reply_options": ["Check order status", "Talk to a human", "Browse products"]
}| Field | Type | Description |
|---|---|---|
response | string | The AI agent's reply text |
user_reply_options | string[] | Optional quick-reply suggestions (may be absent) |
When a human has taken over 200 OK
{
"handoff": "human",
"wait": true
}| Field | Type | Description |
|---|---|---|
handoff | string | Always the string "human". Present only while an operator is handling this conversation. |
wait | boolean | true — stop expecting AI replies and poll for the operator's messages instead. |
If an operator has taken over the conversation, this endpoint returns a different 200 body — no response text at all. Your message is still stored, but the AI does not answer it. Handle this shape or your integration will read an empty reply as a failure. The operator's replies arrive via GET /api/widget/poll, which returns only human-sent messages after a cursor; poll it every few seconds while wait is true.
Rate Limiting
All widget endpoints are rate-limited to 30 requests per minute per IP address, counted in a fixed 60-second window across every widget route combined. Exceeding it returns 429 with no limit_reached field; the window clears on its own, so waiting a few seconds is the whole fix. This is separate from the account's conversation allowance, which also returns 429 but carries limit_reached: true and is not cleared by waiting — see Error Codes.
Error Codes
All errors return a JSON object with an error field describing the issue.
| Status | Description |
|---|---|
| 400 Bad Request | Missing or invalid required fields in the request body. |
| 403 Forbidden | Invalid or inactive widget key, or invalid conversation. |
| 429 Too Many Requests | Two conditions share this code — read the body. No limit_reached field means the per-IP rate limit: wait and retry. limit_reached: true means the account itself is blocked, with reason "quota" (monthly conversation allowance used up) or reason "spend_cap" (metered overage budget reached). Retrying clears neither. |
| 502 Bad Gateway | The AI agent backend is temporarily unavailable. |
| 504 Gateway Timeout | The request timed out. Try again. |
Error response format
{
"error": "Invalid or inactive widget key"
}Embed Snippet
The easiest way to add the chat widget to your website is with our drop-in script tag. No API calls needed — it handles everything automatically.
<script src="https://nexontech.org/widget.js" data-key="wk_your_widget_key"></script>Replace wk_your_widget_key with your actual key from the dashboard. The script loads asynchronously and won't block your page.
Ready to integrate?