Public endpoints
The keyless /api/public surface for building a custom chat frontend — every endpoint, the binding token, the domain allowlist, and the per-endpoint rate limits.
If the embedded widget is not the interface you want, build your own against the public chatbot endpoints. This is the supported path for a custom chat frontend.
https://app.evoriqa.com/api/public/chatbots/{publicId}/…Three things define this surface:
- No API key. These endpoints are called from a visitor's browser, so they
carry no secret. They are scoped by the chatbot's
publicId. - The domain allowlist applies. Every request is checked against the
chatbot's allowed domains via its
Origin. A disallowed origin getsDOMAIN_NOT_ALLOWED. See Domain allowlist. - CORS is handled. Each endpoint answers preflight requests, so a browser
call from an allowed origin works directly.
They share the { "success": …, "data" | "error" } envelope.
The binding token
Creating a conversation returns a binding token alongside the conversation id. Every endpoint that reads or changes an existing conversation requires it, passed as a ?bt= query parameter.
The token proves this client owns that conversation. Without it — or with a guessed conversation id — the request is a 404, which is what stops a visitor from reading someone else's thread. Store it with the conversation id for the session and replay it on every subsequent call.
The endpoints
GET /config
The chatbot's public configuration: branding, greeting, suggested questions, lead-capture mode, AI disclosure, and the online flag with any away message. Cached for up to a minute.
POST /conversations
Starts a conversation. Body fields are all optional: visitorId (≤ 100 chars) and pageUrl (≤ 2,000 chars). An empty body is fine.
{
"success": true,
"data": {
"conversationId": "b1d2e3f4-5a6b-4c7d-8e9f-0a1b2c3d4e5f",
"bindingToken": "…"
}
}Keep both values. Limit: 20 per chatbot per IP per minute.
POST /chat
Sends a visitor message and streams the answer back.
| Field | Notes |
|---|---|
message | Required, 1–4,000 characters |
conversationId | Optional — omit or null to start fresh |
visitorId | Optional, ≤ 100 chars |
pageUrl | Optional, ≤ 2,000 chars |
Limit: 20 per chatbot per IP per minute.
When the workspace is out of budget the turn still proceeds — the visitor's message is recorded, a handoff request still escalates, and you still get a real conversation id and binding token. Only the paid AI work is withheld, and the static "assistant is unavailable" reply is returned instead. Handle it as an ordinary reply, not as an error.
GET /conversations/{id}/stream?bt=…
A server-sent event stream of agent replies and handoff state during a live takeover. Limit: 60 stream opens per chatbot per IP per minute, with a cap on concurrently held-open streams.
GET /conversations/{id}/poll?bt=…
The polling alternative to the stream: current status, whether a human is live, the agent's display name, and recent messages. This is what the shipped widget uses. Limit: 90 per minute.
Prefer polling unless you specifically need the stream — it is simpler, it survives flaky networks, and it is what the reference implementation exercises.
POST /conversations/{id}/resume?bt=…
Opts the visitor back into AI answers after they asked for a human — useful when nobody is available. Flips the conversation back to open and unassigns it. Limit: 10 per minute.
POST /conversations/{id}/close?bt=…
Ends the conversation from the visitor's side. Limit: 10 per minute.
POST /leads
Submits a lead. name (≤ 200), email, phone, company (≤ 200), message (≤ 2,000), pageUrl, conversationId, visitorId — all optional, though a lead with no contact detail is not much use. Limit: 5 per chatbot per minute.
Emits the `lead.created` webhook.
POST /csat
Submits a satisfaction rating: conversationId, rating (an integer 1–5), an optional comment (≤ 2,000), and the binding token. Limit: 10 per minute. The first rating for a conversation emits the csat.received webhook.
POST /feedback
Per-message thumbs: messageId, rating of -1, 0, or 1, an optional comment, and the binding token. Limit: 30 per minute.
GET /conversations/{id}/attachments/{attachmentId}?bt=…
Fetches a file an agent attached to a reply.
Building against it
A minimal client is: create a conversation, keep the id and binding token, send messages to /chat, and poll /poll so agent replies appear during a handoff. Everything else — CSAT, feedback, leads, resume, close — is optional polish.
What you take on by building your own frontend is everything the embedded widget does for free: persistence across reloads, streaming, accessibility, the away state, retry on failure, and transcript download. See Widget embed before deciding.
Where to go next
Last updated