Kyndreddocs

REST API Reference

All endpoints are CORS-enabled and use embed token authentication. No OAuth, no signing.

Base URL

https://kyndred.dev
url

Authentication

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-token
http

Get companion config

GET/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"
}
json

Example

curl https://kyndred.dev/api/embed/YOUR_TOKEN
bash

Stream a chat response

POST/api/embed/{token}/chat

Send 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"
}
json

Response

Content-Type: text/event-stream

data: {"text":"Hello"}

data: {"text":", how"}

data: {"text":" can I"}

data: {"text":" help?"}

data: [DONE]
stream

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);
  }
}
js

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

POST/api/embed/{token}/voice-token

Returns 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"
}
json

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

StatusMeaning
400Invalid request body or parameters
403Origin not in allowed list
404Invalid or revoked token
429Rate limit or daily usage exceeded
502Upstream error (ElevenLabs, Anthropic)