Docs menu

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
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).

JSON
// success
{ "success": true, "data": { ... } }

// error
{ "success": false, "code": "ticket_not_found", "message": "Request not found" }
StatusExample codesMeaning
400ticket_status_invalidA parameter or body field is not valid.
401invalid_keyThe key is missing, wrong or rotated.
404ticket_not_foundNo such request in this workspace.

Endpoints

Method and pathDescription
GET /v1/server/workspaceWorkspace details and public key
GET /v1/server/ticketsList requests, newest first
GET /v1/server/tickets/{id}One request with its transcript
PUT /v1/server/tickets/{id}/statusClose or reopen a request
GET /v1/server/reportsSessions, outcomes, drop-off and unanswered questions
GET /v1/server/settingsBot settings and audience
PUT /v1/server/settingsChange who sees the bot
GET /v1/server/releasesPublished 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
curl https://api.kavchat.com/v1/server/workspace \
  -H "Authorization: Bearer $KAVCHAT_SECRET_KEY" \
  -H "Accept-Language: en"
Response
{
  "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 parameterDescription
statusopen or closed. Omit for both.
limit1 to 200, default 50.
beforeFor paging: the number of the last request you already have.
curl
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"
Response
{
  "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
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
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
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"
Response
{
  "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
curl https://api.kavchat.com/v1/server/settings \
  -H "Authorization: Bearer $KAVCHAT_SECRET_KEY"
Response
{
  "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
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
curl https://api.kavchat.com/v1/server/releases \
  -H "Authorization: Bearer $KAVCHAT_SECRET_KEY"
Response
{
  "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).

Node.js
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();
Python
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()
PHP
$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();
Java
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();
.NET
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();
Go
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)
Ruby
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_releases

Client 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.