API Documentation
REST API to integrate AI assistants into your platform. Base URL: configurable per environment.
All requests must include the header Content-Type: application/json unless stated otherwise.
Authentication
Three authentication methods available depending on the integration type.
API Key
For server-to-server integrations. Create your keys in the admin panel. Keys have the ak_ prefix and are only shown once when created.
Header: X-API-Key: ak_your_key_here
# o alternativamente
Header: Authorization: Bearer ak_your_key_hereEmbed Token
For integration via embedded widget. The token resolves the assistant's configuration from the backend.
Header: X-Embed-Token: your_embed_tokenJWT (Pre-Auth)
For authenticated integrations. The JWT must be signed with the shared secret configured in the panel.
Header: X-Embed-Token: your_embed_token
Header: Authorization: Bearer eyJhbG...Chat API
Send message (no streaming)
// Request Body
{
"message": "string",
"assistantId?": "string",
"conversationId?": "string"
}
// Response
{
"conversationId": "string",
"message": "string"
}Send message (SSE streaming)
Response of type text/event-stream. Possible events:
// Request Body
{
"message": "string",
"assistantId?": "string",
"conversationId?": "string"
}
// Event types
data: {"token":"hello","conversationId":"..."} // text chunk
data: {"type":"thinking","content":"..."} // tool processing
data: {"type":"action_preview","preview":{...}} // action preview
data: {"type":"token","content":"..."} // text chunk (new format)Confirm action
{
"conversationId": "string",
"confirmAction": "string",
"confirmPayload": {}
}List conversations
// Response
[
{
"id": "string",
"createdAt": "string",
"updatedAt": "string"
}
]Get messages
// Response
[
{
"id": "string",
"role": "user|assistant",
"content": "string"
}
]Delete conversation
Get available actions
// Response
[
{
"name": "string",
"description": "string",
"parameters": [...]
}
]Widget Integration
Embed the assistant on any website with a simple script tag.
Floating widget
<script src="https://your-api.com/widget/ai-assistant-widget.js"
data-api-url="https://your-api.com/api"
data-embed-token="your_token"
data-mode="floating">
</script>Inline widget
<div style="height: 600px;">
<script src="https://your-api.com/widget/ai-assistant-widget.js"
data-api-url="https://your-api.com/api"
data-embed-token="your_token"
data-mode="inline">
</script>
</div>Pre-authenticated
Pass a signed JWT to identify the user without requiring registration.
<script src="https://your-api.com/widget/ai-assistant-widget.js"
data-embed-token="your_token"
data-token="eyJhbG..."
data-api-url="https://your-api.com/api">
</script>PRE_AUTH integration (external authentication)
If your users are already authenticated on your platform, you can use PRE_AUTH mode so the widget identifies them automatically without additional registration.
Your backend signs a JWT with a secret shared between your system and the AI platform. The widget receives this JWT via data-token and sends it on every request.
Requirements
1. A shared secret configured on both systems (your backend and the assistant's admin panel).
2. A PRE_AUTH type embed created in the admin panel.
3. An endpoint on your backend that generates and signs the JWT for authenticated users.
Step 1: Generate the JWT on your backend
The JWT must contain the fields: sub (user ID), email, name, and tenantSlug.
Node.js / Express
const jwt = require('jsonwebtoken');
const PRE_AUTH_SECRET = process.env.AI_ASSISTANT_PRE_AUTH_SECRET;
app.get('/api/ai-assistant/token', authMiddleware, async (req, res) => {
// Verify user has active subscription/access
const user = req.user;
const token = jwt.sign({
sub: String(user.id),
email: user.email,
name: user.name,
tenantSlug: 'your-tenant-slug'
}, PRE_AUTH_SECRET, { expiresIn: '60m' });
res.json({ token });
});Python / Django
import jwt
from django.conf import settings
@login_required
def ai_assistant_token(request):
token = jwt.encode({
'sub': str(request.user.id),
'email': request.user.email,
'name': request.user.get_full_name(),
'tenantSlug': 'your-tenant-slug',
'exp': datetime.utcnow() + timedelta(hours=1)
}, settings.AI_ASSISTANT_PRE_AUTH_SECRET, algorithm='HS256')
return JsonResponse({'token': token})PHP / Laravel
Route::middleware('auth')->get('/api/ai-assistant/token', function (Request $request) {
$token = JWT::encode([
'sub' => (string) $request->user()->id,
'email' => $request->user()->email,
'name' => $request->user()->name,
'tenantSlug' => 'your-tenant-slug',
'exp' => time() + 3600
], config('services.ai_assistant.secret'), 'HS256');
return response()->json(['token' => $token]);
});Step 2: Inject the widget with the token
From your frontend, get the token from your backend and pass it to the widget via data-token.
<!-- 1. Fetch the token from YOUR backend -->
<script>
fetch('/api/ai-assistant/token', {
headers: { 'Authorization': 'Bearer ' + yourSessionToken }
})
.then(r => r.json())
.then(data => {
const script = document.createElement('script');
script.src = 'https://your-assistant.com/widget/ai-assistant-widget.js';
script.setAttribute('data-api-url', 'https://your-assistant.com/api');
script.setAttribute('data-embed-token', 'your-embed-token');
script.setAttribute('data-token', data.token);
document.body.appendChild(script);
});
</script>Customization (Branding)
Configure the widget's appearance from the admin panel or via the API.
primaryColorstringWidget's primary color (hex)avatarUrlstringURL of the assistant's avatarheaderColorstringHeader background colorheaderTitlestringTitle shown in the headerwelcomeMessagestringWelcome message when opening the chatfontFamilystringWidget font familyborderRadiusstringBorder radius (e.g. "12px")positionstringFloating widget position ("bottom-right", "bottom-left")hideBrandingbooleanHide Paidio branding (Professional+ plans){
"primaryColor": "#d4a853",
"avatarUrl": "https://example.com/bot-avatar.png",
"headerColor": "#0f172a",
"headerTitle": "Soporte IA",
"welcomeMessage": "Hola, en que puedo ayudarte?",
"fontFamily": "Inter, sans-serif",
"borderRadius": "12px",
"position": "bottom-right",
"hideBranding": false
}Code examples
JavaScript (fetch)
const response = await fetch('https://your-api.com/api/v1/chat/stream', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': 'ak_your_key',
},
body: JSON.stringify({ message: 'Hola' }),
});
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value);
// Parse SSE lines...
}Python
import requests
response = requests.post(
'https://your-api.com/api/v1/chat/message',
headers={'X-API-Key': 'ak_your_key'},
json={'message': 'Hola'},
)
print(response.json())cURL
curl -X POST https://your-api.com/api/v1/chat/message \
-H "Content-Type: application/json" \
-H "X-API-Key: ak_your_key" \
-d '{"message": "Hola"}'Rate Limits
The API has a limit of 100 requests per minute per IP. If you exceed the limit, you'll get a 429 Too Many Requests error.
Additionally, your Stripe plan determines the monthly token consumption limit. You can check your current usage in the admin panel.
Errors
The API uses standard HTTP status codes:
200OKSuccessful request201CreatedResource created successfully400Bad RequestInvalid or missing parameters401UnauthorizedCredentials not provided or invalid403ForbiddenNo permission for this resource404Not FoundResource not found429Too Many RequestsRate limit exceeded500Internal Server ErrorInternal server errorError format
{
"statusCode": 401,
"message": "Invalid API key",
"error": "Unauthorized"
}