Beta

Chat API

Connect your own website, app or back-office software to one of your virtual employees. Your server sends a visitor's message and gets the employee's reply back as plain text, so you can show it in your own chat widget.

Getting started

  1. Hire a chat-enabled employee from the employees page.
  2. Open the employee's settings and fill in "This employee's own prompt": what the employee should talk about and which facts it may share (services, prices, opening hours, page links). It takes effect once it has been approved.
  3. In the same settings page, under "API keys", create a key. Copy it right away; it is shown only once.
  4. From your server, call the endpoint below with the key. That's it.

Keep your key secret

The key lets anyone who has it chat as your employee and use up your quota. Call the API from your own server (back end), never from JavaScript running in a visitor's browser, and keep the key in an environment variable. If a key leaks, revoke it in the employee's settings and create a new one; revoked keys stop working immediately.

Request

POST https://virtualemployees.nl/api/v1/external/chat
Authorization: Bearer ve_live_...
Content-Type: application/json

{
  "message": "string",    // required, up to 2000 characters
  "sessionId": "string",  // optional, up to 128 characters
  "locale": "en"          // optional language hint
}
message
The visitor's message. Required, up to 2000 characters.
sessionId
Your own id for one visitor's conversation (for example a random id you store in the visitor's session). When you send the same sessionId again, the employee remembers the earlier messages of the last 24 hours, so you only ever send the newest message. Omit it for one-off questions.
locale
Language of your website, such as "en" or "nl". The employee answers in the language the visitor writes in and falls back to this one.

Response

200 OK
{ "reply": "We're open Monday to Friday, 9:00 to 17:00." }

Examples

cURL

curl -X POST https://virtualemployees.nl/api/v1/external/chat \
  -H "Authorization: Bearer $VE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "message": "What are your opening hours?", "sessionId": "visitor-8f3a21", "locale": "en" }'

JavaScript (Node.js)

// Node.js 18+ (server side)
const response = await fetch("https://virtualemployees.nl/api/v1/external/chat", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.VE_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    message: "What are your opening hours?",
    sessionId: "visitor-8f3a21",
    locale: "en",
  }),
});

if (!response.ok) {
  const problem = await response.json(); // { title: "external_chat.invalid_key", detail: "..." }
  throw new Error(problem.title ?? `HTTP ${response.status}`);
}

const { reply } = await response.json();

Python

import os, requests

response = requests.post(
    "https://virtualemployees.nl/api/v1/external/chat",
    headers={"Authorization": f"Bearer {os.environ['VE_API_KEY']}"},
    json={"message": "What are your opening hours?", "sessionId": "visitor-8f3a21", "locale": "en"},
    timeout=60,
)
response.raise_for_status()
reply = response.json()["reply"]

PHP

<?php
$ch = curl_init("https://virtualemployees.nl/api/v1/external/chat");
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 60,
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer " . getenv("VE_API_KEY"),
        "Content-Type: application/json",
    ],
    CURLOPT_POSTFIELDS => json_encode([
        "message" => "What are your opening hours?",
        "sessionId" => "visitor-8f3a21",
        "locale" => "en",
    ]),
]);
$reply = json_decode(curl_exec($ch), true)["reply"];

C# (.NET)

// .NET 8+ (server side)
using var http = new HttpClient { Timeout = TimeSpan.FromSeconds(60) };
http.DefaultRequestHeaders.Authorization =
    new("Bearer", Environment.GetEnvironmentVariable("VE_API_KEY"));

var response = await http.PostAsJsonAsync("https://virtualemployees.nl/api/v1/external/chat",
    new { message = "What are your opening hours?", sessionId = "visitor-8f3a21", locale = "en" });

response.EnsureSuccessStatusCode();
var reply = (await response.Content.ReadFromJsonAsync<ChatReply>())!.Reply;

record ChatReply(string Reply);

Errors

Failures return a JSON problem object: the title field holds a stable error code you can match on, and detail is a human-readable explanation. Replies can take several seconds, so use a timeout of about 60 seconds.

401 Unauthorized
{ "title": "external_chat.invalid_key", "detail": "The API key is missing, invalid or revoked.", "status": 401 }
HTTPtitleMeaning
400(validation)The body is invalid, for example an empty or too long message.
401external_chat.invalid_keyThe key is missing, wrong or revoked.
403external_chat.unavailableThis employee is not available for chat right now (for example the account is suspended or the employment ended).
409external_chat.not_configuredThis employee has not been set up for chat yet.
429(rate limit)Too many requests for this key. Wait a moment and retry.
5xx(server)A temporary problem on our side. Show your visitor a friendly fallback message and try again later.

Telegram

Besides the API, a chat-enabled employee can answer in Telegram. There are two ways to use it, and both are set up in the employee's settings under "Telegram".

Chat with the employee yourself

Create a connection code and send it to the employee's own Telegram bot (or open the link we show you). That chat is then connected to your hire of the employee. You can connect several chats, such as your team, and disconnect them from the panel or by sending /unlink to the bot. The code works once and expires after 10 minutes.

Your own bot for your customers

Create a bot with @BotFather and paste its token into the employee's settings. Everyone who messages your bot then chats with the employee, with no code. Each person is limited to a number of messages per hour, and so is the bot as a whole. The token is stored encrypted. Make sure no webhook is set on your bot, because we receive its messages by polling.

Good to know

  • Every key is bound to one employee, so you never name the employee in the request. Use a separate key per website or app so you can revoke them independently.
  • The employee only talks about what you wrote in its prompt. If it can't answer something, add that information to the prompt.
  • Add the addresses of your pages to the prompt (for example "For prices, point people to /pricing") and the employee will mention them in its reply text.
  • Requests are limited per key. If you expect heavy traffic, contact us.