Server API
The Server API lets your backend read requests, close them, pull reports, change who sees the bot and list published releases. Every call uses your secret key.
The full specification is in openapi.yaml (OpenAPI 3.1). Import it into Postman or Insomnia, or generate a client from it. The server SDKs wrap every endpoint below.
Authentication
Base URL: https://api.kavchat.com. Send the secret key as a bearer token. Keep it on your backend and never ship it to a browser or an app. You can rotate it on the Install page in the dashboard.
curl https://api.kavchat.com/v1/server/workspace \
-H "Authorization: Bearer $KAVCHAT_SECRET_KEY" \
-H "Accept-Language: en"Responses and errors
Every response is JSON with a success field. Errors have a stable code that your code can check, and a message in the language of the Accept-Language header (he, en, ar, ru, es, fr, de, pt, it, tr).
// success
{ "success": true, "data": { ... } }
// error
{ "success": false, "code": "ticket_not_found", "message": "Request not found" }| Status | Example codes | Meaning |
|---|---|---|
| 400 | ticket_status_invalid | A parameter or body field is not valid. |
| 401 | invalid_key | The key is missing, wrong or rotated. |
| 404 | ticket_not_found | No such request in this workspace. |
Endpoints
| Method and path | Description |
|---|---|
GET /v1/server/workspace | Workspace details and public key |
GET /v1/server/tickets | List requests, newest first |
GET /v1/server/tickets/{id} | One request with its transcript |
PUT /v1/server/tickets/{id}/status | Close or reopen a request |
GET /v1/server/reports | Sessions, outcomes, drop-off and unanswered questions |
GET /v1/server/settings | Bot settings and audience |
PUT /v1/server/settings | Change who sees the bot |
GET /v1/server/releases | Published releases |
Workspace
GET /v1/server/workspace
Workspace details, the public key and the last 4 characters of the secret key (secretKeyHint). Handy as a first call to check the key works.
curl https://api.kavchat.com/v1/server/workspace \
-H "Authorization: Bearer $KAVCHAT_SECRET_KEY" \
-H "Accept-Language: en"{
"success": true,
"data": {
"id": "6718e9a1f0c2a4b5c6d7e8f9",
"name": "Example Store",
"locale": "en",
"defaultCountry": "IL",
"plan": "premium",
"publicKey": "pk_live_Ab12Cd34Ef56Gh78Ij90Kl",
"secretKeyHint": "a1b2",
"webhookUrl": "https://example.com/kavchat/webhook"
}
}List requests
GET /v1/server/tickets
Requests opened from the chat, newest first.
| Query parameter | Description |
|---|---|
status | open or closed. Omit for both. |
limit | 1 to 200, default 50. |
before | For paging: the number of the last request you already have. |
curl "https://api.kavchat.com/v1/server/tickets?status=open&limit=50" \
-H "Authorization: Bearer $KAVCHAT_SECRET_KEY"
# next page: pass the number of the last ticket you received
curl "https://api.kavchat.com/v1/server/tickets?status=open&limit=50&before=120" \
-H "Authorization: Bearer $KAVCHAT_SECRET_KEY"{
"success": true,
"data": [
{
"id": "6718f0c2a4b5c6d7e8f90123",
"number": 128,
"status": "open",
"name": "Dana",
"email": "dana@example.com",
"phone": "+972501234567",
"userId": "user_123",
"topicId": "billing",
"topicName": "Billing",
"scriptId": "returns",
"sessionId": "k3J9xQ2mP7aB",
"message": "My refund has not arrived yet.",
"transcript": [
{ "from": "bot", "text": "Hi! How can I help?", "stepId": "start", "at": "2026-10-25T09:14:02.000Z" }
],
"createdAt": "2026-10-25T09:15:40.000Z",
"updatedAt": "2026-10-25T09:15:40.000Z"
}
]
}userId is the sub claim of the identity token when the user was identified. phone is in E.164 format.
Get a request
GET /v1/server/tickets/{id}
One request with its full transcript, in the same shape as an item of the list. Answers 404 with ticket_not_found when the id is unknown.
curl https://api.kavchat.com/v1/server/tickets/6718f0c2a4b5c6d7e8f90123 \
-H "Authorization: Bearer $KAVCHAT_SECRET_KEY"Close or reopen a request
PUT /v1/server/tickets/{id}/status
Body: { "status": "closed" } or { "status": "open" }. Returns the updated request.
curl -X PUT https://api.kavchat.com/v1/server/tickets/6718f0c2a4b5c6d7e8f90123/status \
-H "Authorization: Bearer $KAVCHAT_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{ "status": "closed" }'Reports
GET /v1/server/reports
Sessions and their outcomes (open, answered, contact, abandoned), totals per script, the steps where users leave, and questions the bot could not answer, grouped. from and to are ISO 8601 date-times. The default is the last 30 days.
curl "https://api.kavchat.com/v1/server/reports?from=2026-10-01T00:00:00Z&to=2026-10-25T00:00:00Z" \
-H "Authorization: Bearer $KAVCHAT_SECRET_KEY"{
"success": true,
"data": {
"from": "2026-10-01T00:00:00.000Z",
"to": "2026-10-25T00:00:00.000Z",
"totals": { "sessions": 940, "open": 12, "answered": 801, "contact": 64, "abandoned": 63 },
"byScript": [
{ "scriptId": "returns", "name": "Returns", "sessions": 310, "open": 3, "answered": 270, "contact": 22, "abandoned": 15 }
],
"dropOff": [
{ "scriptId": "returns", "scriptName": "Returns", "stepId": "label", "stepText": "Print the return label from...", "count": 9 }
],
"unmatched": [
{ "groupKey": "gift card balance", "sample": "how do I check my gift card balance", "count": 17, "lastAt": "2026-10-24T18:02:11.000Z" }
]
}
}Bot settings
GET /v1/server/settings
The audience, the testers list and the appearance of the bot.
curl https://api.kavchat.com/v1/server/settings \
-H "Authorization: Bearer $KAVCHAT_SECRET_KEY"{
"success": true,
"data": {
"enabled": true,
"audience": "testers",
"testerPhones": ["+972501234567"],
"botName": "Support",
"avatarText": "S",
"avatarImageUrl": null,
"subtitle": "Replies instantly",
"primaryColor": "#2563eb",
"position": "end",
"contactTopics": [{ "id": "billing", "name": "Billing" }]
}
}Change who sees the bot
PUT /v1/server/settings
Only audience (off, testers or all) and testerPhones (up to 50) can be changed here. Other settings are edited in the dashboard. Returns the settings, as in GET.
curl -X PUT https://api.kavchat.com/v1/server/settings \
-H "Authorization: Bearer $KAVCHAT_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{ "audience": "testers", "testerPhones": ["+972501234567"] }'
# open the bot to everyone
curl -X PUT https://api.kavchat.com/v1/server/settings \
-H "Authorization: Bearer $KAVCHAT_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{ "audience": "all" }'Releases
GET /v1/server/releases
The last 50 published releases, newest first. restoredFrom is set when a release restored an earlier version.
curl https://api.kavchat.com/v1/server/releases \
-H "Authorization: Bearer $KAVCHAT_SECRET_KEY"{
"success": true,
"data": [
{
"version": 7,
"note": "New returns flow",
"publishedAt": "2026-10-20T11:30:00.000Z",
"scripts": [
{ "id": "opening", "kind": "opening", "name": "Welcome" },
{ "id": "returns", "kind": "topic", "name": "Returns" }
]
},
{
"version": 6,
"note": "Restore",
"restoredFrom": 4,
"publishedAt": "2026-10-12T08:05:00.000Z",
"scripts": [{ "id": "opening", "kind": "opening", "name": "Welcome" }]
}
]
}With a server SDK
The same calls in each SDK. Errors are thrown as an API error with status, code, message and details. status is 0 when no response arrived (timeout or network_error).
await kavchat.getWorkspace();
await kavchat.listTickets({ status: 'open', limit: 50, before: 120 });
await kavchat.getTicket(id);
await kavchat.setTicketStatus(id, 'closed');
await kavchat.getReports({ from: new Date('2026-01-01'), to: new Date() });
await kavchat.getSettings();
await kavchat.updateSettings({ audience: 'testers', testerPhones: ['+972501234567'] });
await kavchat.listReleases();kavchat.get_workspace()
kavchat.list_tickets(status="open", limit=50, before=120)
kavchat.get_ticket(ticket_id)
kavchat.set_ticket_status(ticket_id, "closed")
kavchat.get_reports(from_=datetime(2026, 1, 1, tzinfo=timezone.utc), to=datetime.now(timezone.utc))
kavchat.get_settings()
kavchat.update_settings(audience="testers", tester_phones=["+972501234567"])
kavchat.list_releases()$kavchat->getWorkspace();
$kavchat->listTickets(status: 'open', limit: 50, before: 120);
$kavchat->getTicket($id);
$kavchat->setTicketStatus($id, 'closed');
$kavchat->getReports(new DateTimeImmutable('2026-01-01'), new DateTimeImmutable());
$kavchat->getSettings();
$kavchat->updateSettings(audience: 'testers', testerPhones: ['+972501234567']);
$kavchat->listReleases();kavchat.getWorkspace();
kavchat.listTickets(ListTicketsParams.create().status(TicketStatus.OPEN).limit(50).before(120));
kavchat.getTicket(id);
kavchat.setTicketStatus(id, TicketStatus.CLOSED);
kavchat.getReports(Instant.parse("2026-01-01T00:00:00Z"), Instant.now());
kavchat.getSettings();
kavchat.updateSettings(UpdateSettingsParams.create().audience(Audience.TESTERS).testerPhones(List.of("+972501234567")));
kavchat.listReleases();await kavchat.GetWorkspaceAsync();
await kavchat.ListTicketsAsync(new ListTicketsOptions { Status = TicketStatus.Open, Limit = 50, Before = 120 });
await kavchat.GetTicketAsync(id);
await kavchat.SetTicketStatusAsync(id, TicketStatus.Closed);
await kavchat.GetReportsAsync(DateTimeOffset.UtcNow.AddDays(-7), DateTimeOffset.UtcNow);
await kavchat.GetSettingsAsync();
await kavchat.UpdateSettingsAsync(new UpdateSettingsRequest { Audience = Audience.Testers, TesterPhones = ["+972501234567"] });
await kavchat.ListReleasesAsync();client.GetWorkspace(ctx)
client.ListTickets(ctx, kavchat.ListTicketsParams{Status: kavchat.TicketOpen, Limit: 50, Before: 120})
client.GetTicket(ctx, id)
client.SetTicketStatus(ctx, id, kavchat.TicketClosed)
client.GetReports(ctx, kavchat.ReportParams{From: time.Now().AddDate(0, 0, -7)})
client.GetSettings(ctx)
client.UpdateSettings(ctx, kavchat.UpdateSettingsParams{Audience: kavchat.AudienceTesters, TesterPhones: []string{"+972501234567"}})
client.ListReleases(ctx)kavchat.get_workspace
kavchat.list_tickets(status: "open", limit: 50, before: 120)
kavchat.get_ticket(id)
kavchat.set_ticket_status(id, "closed")
kavchat.get_reports(from: Time.now - 7 * 86_400, to: Time.now)
kavchat.get_settings
kavchat.update_settings(audience: "testers", tester_phones: ["+972501234567"])
kavchat.list_releasesClient API
The widget and the mobile SDKs use a second, public surface under /v1/client/{publicKey}: it returns the published release, records the conversation path and opens requests from the contact form. It is open to guests and needs only the public key. You do not need to call it yourself. It is described in openapi.yaml for completeness.