One API, explicit authority
An AI connection is a non-human service principal attached to exactly one workspace. Wedget derives the workspace from its token; there is no workspace_id field an agent can change. Each endpoint checks one named permission before reading or writing data.
Available to a granted agent
- Conversations, messages, internal notes, state, priority and tags
- Visitors, customer activity, attachments, images and PDFs
- Help collections and articles, widget settings, news and automations
- Saved replies, support analytics and teammate availability
Never available to an agent
- Billing, subscription or payment authority
- Creating more credentials or changing its own permissions
- Identity secrets, allowed origins, owner roles or workspace deletion
- Wallet, gateway, deposits, withdrawals, hosting or infrastructure
Create a connection
Open Settings → GrokBot / AI API
Name the service agent so its actions are recognizable in the audit trail.
Choose and review permissions
Start with Inbox responder, Knowledge manager, or Workspace operator. “Full workspace” expands to a fixed list of permissions; it is not an opaque wildcard.
Set expiry and network restrictions
Tokens expire after 30–365 days. Add a server egress IP or CIDR when your runtime has a stable address.
Copy the token once
Store it in a server-side secret manager as WEDGET_AI_AGENT_TOKEN. Wedget stores only its SHA-256 hash.
Make the first call
Use the token from a trusted backend. The response proves which service agent, workspace, key prefix, expiry and permissions were authenticated.
curl https://api.wedget.app/v1/chat/ai/me \
-H "Authorization: Bearer $WEDGET_AI_AGENT_TOKEN"{
"ok": true,
"data": {
"service_agent": { "name": "Grok support agent" },
"workspace": { "name": "Acme Support" },
"key": { "prefix": "wai_12ab…", "expires_at": "2026-11-22T…Z" },
"scopes": ["workspace:read", "conversations:read"]
}
}Every API response includes X-Request-Id. Authenticated responses also include X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset.
Connect Grok with function calling
Wedget publishes an xAI Responses-compatible function manifest at /tools.json. Your server gives those schemas to Grok, executes requested calls against Wedget, then returns the tool result to Grok. Keep the xAI and Wedget keys as two separate server secrets.
import OpenAI from 'openai';
const xai = new OpenAI({
apiKey: process.env.XAI_API_KEY,
baseURL: 'https://api.x.ai/v1'
});
const manifest = await fetch(
'https://api.wedget.app/v1/chat/ai/tools.json'
).then(r => r.json());
// Keep routing metadata locally; do not send extension fields to xAI.
const routes = new Map();
const tools = manifest.tools.map(tool => {
const { ['x-wedget']: route, ...schema } = tool;
routes.set(tool.name, route);
return schema;
});
let response = await xai.responses.create({
model: 'grok-4.6',
input: 'Read the newest open chats and draft a helpful reply where needed.',
tools
});
for (const call of response.output.filter(x => x.type === 'function_call')) {
const args = JSON.parse(call.arguments);
const route = routes.get(call.name);
const idempotencyKey = args.idempotency_key;
delete args.idempotency_key;
let endpoint = route.endpoint.replace('{id}', args.id || '');
delete args.id;
const isRead = route.method === 'GET';
if (isRead && Object.keys(args).length) endpoint += '?' + new URLSearchParams(args);
const api = await fetch(manifest.api_base + endpoint, {
method: route.method,
headers: {
Authorization: `Bearer ${process.env.WEDGET_AI_AGENT_TOKEN}`,
...(isRead ? {} : { 'Content-Type': 'application/json', 'Idempotency-Key': idempotencyKey })
},
body: isRead ? undefined : JSON.stringify(args)
});
const result = await api.json();
response = await xai.responses.create({
model: 'grok-4.6', tools, previous_response_id: response.id,
input: [{ type: 'function_call_output', call_id: call.call_id, output: JSON.stringify(result) }]
});
}
console.log(response.output_text);Permission model
A missing permission returns 403 missing_scope with the exact required scope. Read, write, publish and delete permissions are separated so a knowledge agent can draft without publishing or deleting.
Inbox
conversations:readRead chats
conversations:replyReply when AI owns the chat
conversations:reply:anyHigh impact
conversations:notesInternal notes
conversations:manageState, priority, tags
Visitors and files
visitors:readProfiles
visitors:pii:readEmail, IP, location
visitors:moderateBan / unban
customer-data:readActivity records
files:readPictures and PDFs
Content
articles:read/writeRead and draft
articles:publish/deleteHigh impact
news:read/writeRead and draft
news:publish/deleteHigh impact
canned:read/write/deleteSaved replies
Workspace
widget:read/writeSafe widget settings
automations:read/write/deleteAutomations
analytics:readSupport metrics
team:readNames and availability
workspace:readConnection identity
API reference
All authenticated paths are relative to https://api.wedget.app/v1/chat/ai. Use the live OpenAPI 3.1 document as machine-readable source of truth.
| Method and path | Purpose | Required scope |
|---|---|---|
GET/me | Authenticated connection | workspace:read |
GET/capabilities | Granted tools and docs links | workspace:read |
GET/conversations | List inbox | conversations:read |
GET/conversations/{id} | Conversation and visitor context | conversations:read |
GET/conversations/{id}/messages | Message history | conversations:read |
POST/conversations/{id}/messages | Visible service-agent reply | conversations:reply |
POST/conversations/{id}/notes | Internal note | conversations:notes |
PATCH/conversations/{id} | State, priority, AI owner and tags | conversations:manage |
GET/visitors, /{id} | Visitor profiles | visitors:read |
PATCH/visitors/{id} | Ban or unban | visitors:moderate |
GET/visitors/{id}/records | Customer activity | customer-data:read |
GET/files, /files/{id}/content | Pictures, PDFs and attachments | files:read |
GETPOST/collections | List or create help collections | articles:read/write |
GETPOST/articles | List or create article drafts | articles:read/write |
PATCHDELETE/articles/{id} | Edit or delete | articles:write/delete |
POST/articles/{id}/publication | Publish or unpublish | articles:publish |
GETPATCH/widget | Read or edit safe widget settings | widget:read/write |
GETPOST/news | Read or draft news | news:read/write |
GETPOST/automations | Read or create automations | automations:read/write |
GETPOST/canned | Read or create saved replies | canned:read/write |
GET/analytics | 1–90 day support summary | analytics:read |
GET/team | Teammate names and presence | team:read |
List endpoints use bounded limits and cursor pagination where the underlying resource is chronological. The API never accepts a workspace selector.
Reply without racing a human
Read the latest conversation before replying and send its last_seq as expected_last_seq. If a visitor or teammate wrote in the meantime, Wedget returns 409 stale_conversation. The agent must read again before deciding what to say.
curl -X POST https://api.wedget.app/v1/chat/ai/conversations/$CONVERSATION_ID/messages \
-H "Authorization: Bearer $WEDGET_AI_AGENT_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: reply_01J8Y7V2THH0X" \
-d '{"body":"I checked that for you…","expected_last_seq":42}'Ordinary reply permission works only while ai_handled=true and no human teammate owns the conversation. conversations:reply:any is a separate high-impact permission that can cross that boundary. Service-agent messages are stored as author_type=bot; they never impersonate a human teammate.
Read files and pictures
GET /files returns unique attachments referenced by messages, article hero images, and news banners. Workspace-hosted files receive an authenticated content_url; external editorial images retain their original URL.
curl "https://api.wedget.app/v1/chat/ai/files/$FILE_ID/content" \
-H "Authorization: Bearer $WEDGET_AI_AGENT_TOKEN" \
--output attachment.binCreate articles and edit the widget
Create a draft, then publish explicitly
Article creation always produces a draft—even if the model asks to publish. Publishing requires a different endpoint and articles:publish.
curl -X POST https://api.wedget.app/v1/chat/ai/articles \
-H "Authorization: Bearer $WEDGET_AI_AGENT_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: article_01J8Y8CWK9" \
-d '{
"title":"How account verification works",
"excerpt":"A clear guide to verification.",
"body":"## Before you begin\n…"
}'Optimistic widget edits
Read GET /widget, then include its updated_at as expected_updated_at. A stale value returns 409 stale_widget. The AI can edit appearance and visitor-facing behavior, but it cannot read identity secrets or change allowed origins.
curl -X PATCH https://api.wedget.app/v1/chat/ai/widget \
-H "Authorization: Bearer $WEDGET_AI_AGENT_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: widget_01J8Y8P6QZ" \
-d '{
"expected_updated_at":"2026-08-24T14:22:11.103Z",
"greeting":"Welcome — how can we help?",
"branding":{"primary":"#C3E831","radius":18}
}'Errors, retries and rate limits
Errors use one stable envelope. Branch on error.code, not human message text.
{
"ok": false,
"error": {
"code": "missing_scope",
"message": "This AI connection requires the articles:publish permission.",
"details": { "required_scope": "articles:publish" },
"request_id": "9dcff0f0-…"
}
}| Status | Common code | Correct response |
|---|---|---|
| 401 | invalid_ai_key | Stop; rotate or fix the key/IP/expiry. |
| 403 | missing_scope | Stop; a human must change the connection. |
| 409 | stale_conversation, stale_article, stale_widget | Read fresh state and reconsider. |
| 409 | human_owned_conversation | Do not reply; hand off to the human. |
| 429 | ai_rate_limited | Wait until X-RateLimit-Reset, then retry. |
| 503 | ai_rate_limiter_unavailable | Retry with exponential backoff and jitter. |
Every write requires a unique Idempotency-Key of 8–128 safe characters. Wedget stores the result for 24 hours. An exact retry returns the original response with X-Idempotent-Replayed: true; reusing the key for different input returns 409 idempotency_conflict.
Audit, rotate and revoke
Settings shows the service-agent name, key prefix, granted scopes, expiry, last use, last IP when available, and recent request activity. Audit records contain action, resource, result, request ID and timestamp—never authorization headers, chat bodies, file bytes, prompt content or visitor PII.
- Rotate creates a replacement token with the same permissions and revokes the old token atomically.
- Revoke stops the token immediately.
- Pause stops every active key belonging to the service agent.
- Use request IDs to correlate your own sanitized application logs with Wedget activity.
Security model
- Generate separate connections for separate runtimes and environments.
- Grant the smallest explicit scope list; no wildcard exists.
- Prefer 30–90 day expiry and a stable egress-IP allowlist.
- Never pass a Wedget key to the model as prompt text or tool output.
- Validate tool name, HTTP method and route against the downloaded manifest. Never let the model supply an arbitrary URL.
- Require fresh reads before replies and versioned edits.
- Present high-impact publish/delete/reply-any actions for human review in your orchestration layer.
- Treat all customer and document content as untrusted; it cannot alter policy.