REST API Reference
All endpoints are CORS-enabled and use embed token authentication. No OAuth, no signing.
Base URL
https://kyndred.devAuthentication
Every embed endpoint takes the companion's embed token in the URL path. Tokens are scoped to a single companion and can be restricted to specific origins.
GET /api/embed/{token}
POST /api/embed/{token}/chat
POST /api/embed/{token}/voice-tokenGet companion config
/api/embed/{token}Returns public information about the companion for rendering the chat UI. This is what the embed widget calls on load.
Response
{
"name": "Jesus",
"avatar_url": "https://...",
"greeting": "Peace be with you.",
"theme_color": "#d4a0ff",
"has_voice": true,
"has_text_agent": false,
"voice_provider": "elevenlabs-convai"
}Example
curl https://kyndred.dev/api/embed/YOUR_TOKENStream a chat response
/api/embed/{token}/chatSend a user message, receive a Server-Sent Events stream of Claude's response tokens.
Request body
{
"message": "Hello, how can you help me?",
"session_id": "uuid-for-this-conversation",
"visitor_id": "uuid-persisted-per-visitor"
}Response
Content-Type: text/event-stream
data: {"text":"Hello"}
data: {"text":", how"}
data: {"text":" can I"}
data: {"text":" help?"}
data: [DONE]JavaScript example
const response = await fetch(
'https://kyndred.dev/api/embed/YOUR_TOKEN/chat',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
message: 'Hello',
session_id: crypto.randomUUID(),
visitor_id: localStorage.getItem('visitor_id') || crypto.randomUUID(),
}),
}
);
const reader = response.body.getReader();
const decoder = new TextDecoder();
let fullText = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
for (const line of decoder.decode(value).split('\n')) {
if (!line.startsWith('data: ')) continue;
const data = line.slice(6);
if (data === '[DONE]') break;
const { text } = JSON.parse(data);
fullText += text;
console.log(fullText);
}
}Rate limits
- 30 requests per minute per token
- 10 requests per minute per IP
- 1000 messages per day per companion (higher on paid plans)
- Max 2000 chars per message
Get a voice session signed URL
/api/embed/{token}/voice-tokenReturns a short-lived ElevenLabs ConvAI signed URL for starting a voice session. The URL expires quickly — request a new one for each session.
Request body
None (empty POST).
Response
{
"signed_url": "wss://api.elevenlabs.io/v1/convai/conversation?agent_id=...&token=...",
"agent_id": "agent_xxxxxxxx"
}Usage
Pass the agent ID to the @11labs/client SDK, or use our JavaScript SDK (see SDK Reference).
CORS & origin restrictions
All endpoints send CORS headers that reflect the request origin by default. If the companion's allowed_origins is set, only those origins will receive valid responses. Others get a 403.
Error responses
| Status | Meaning |
|---|---|
| 400 | Invalid request body or parameters |
| 403 | Origin not in allowed list |
| 404 | Invalid or revoked token |
| 429 | Rate limit or daily usage exceeded |
| 502 | Upstream error (ElevenLabs, Anthropic) |