v0.5.0 Open source · early development

Support where the AI agent finishes the job

Requests come in by email or over an API. The agent reads the customer's data in your backend, drafts a reply and proposes actions such as crediting points or issuing a coupon. An operator approves them, and your backend executes them.

A ticket in Kvitok: the customer's message, the agent's analysis and two proposed actions waiting for the operator A ticket in Kvitok: the customer's message, the agent's analysis and two proposed actions waiting for the operator

A guest got neither points nor a promo cup for a purchase. Kvitok found her in the backend and filled in her tier and balance, the agent found the receipt, worked out what was missing and proposed two actions. Each one waits for the operator.

Why Kvitok

A full support loop, not a one-shot script

AI in support often means a job that sends a request to a model once and posts its guess to a chat. An operator then repeats the actions by hand and the customer hears nothing. Kvitok covers the whole path from the request to an executed action and a reply.

The whole loop

The agent works on the ticket while it is open. Approved actions run in your backend and the reply goes back through the channel the request came from.

Any backend

Kvitok knows nothing about your business. A backend describes its data and actions through a small signed HTTP protocol, one workspace per project.

A chat with the agent

The ticket works like a chat. The operator writes "offer a coupon instead" and the agent changes only what the message affects.

The agent only proposes

A person approves every action. The backend validates it when proposed and again before executing it, once per idempotency key.

Your model, known cost

Any OpenAI-compatible endpoint with tool calling. Every run has a cost, broken down by ticket, department, model and day.

Rules around the agent

Deterministic routing before and after the agent. Low-confidence tickets go to triage; a department set by a person stays fixed.

Measured, not guessed

pnpm eval runs test scenarios against a real model and reports which passed and what they cost.

Set up by your agent

An MCP server lets Claude Code, Cursor or any other MCP client connect your project and tune the support agent.

How it works

From the first email to points on the card

Rules route the request, the agent reads your backend and proposes a reply and actions, and the operator corrects it in a chat and approves. Color shows who does what.

the agent a person Kvitok or your backend
A request in six steps across four lanes. The customer writes in; Kvitok finds the customer in your backend and applies routing rules; the agent calls read tools and proposes a draft and actions that the backend validates; the operator reviews the ticket in a chat with the agent, which revises the draft; the operator approves, the backend executes the actions and the customer gets the reply A request in six steps across four lanes. The customer writes in; Kvitok finds the customer in your backend and applies routing rules; the agent calls read tools and proposes a draft and actions that the backend validates; the operator reviews the ticket in a chat with the agent, which revises the draft; the operator approves, the backend executes the actions and the customer gets the reply

An example: 44 missing points

A guest writes through the app form that the points for a 640-ruble receipt never came.

The agent finds the guest by phone, fetches the receipts and sees that receipt 4112 earned nothing. It proposes crediting 44 points, the guest's 7% tier, the backend validates the action, and the agent drafts a reply.

"The points are credited. Update the reply: say they are already on his card."

The operator approves, the backend credits the points, and after one more message the agent rewrites only the draft. The reply goes out by email and the ticket is resolved.

The operator's message to the agent about the phone call and the agent's answer: the numbers match the proposal, only the draft changed The action approved by the operator and executed by the backend, the operator's request to update the reply, the agent's answer and the reply sent to the guest by email

The queue after the agent's first pass

Each ticket gets a department and a confidence score. Where the agent is not sure enough, the ticket waits in triage for a person to decide.

The ticket queue with departments set by the agent The ticket queue with departments set by the agent

Onboarding

Your coding agent connects your project

Kvitok serves an MCP endpoint. Open your backend's repository in an agent that speaks MCP, connect it to Kvitok, and ask it to set Kvitok up for the project. It works in both places at once: it writes the protocol module in your code and configures the workspace in Kvitok.

Claude Code Cursor Claude Desktop any MCP client with HTTP headers
Your backend repository Existing support flow and data Loaders and action services + Protocol module, signed + Manifest and playbooks (.md) Kvitok New workspace Backend connected and checked Departments, fields, rules, policy Test tickets and agent runs You review the plan and the result Your coding agent Claude Code, Cursor and others code MCP Kvitok calls the new endpoints over signed HTTP
  1. Study the project

    Where requests come from, how customers are identified, what operators look at, what actions support performs.

    In your repository
  2. Show you a plan

    What goes into the backend manifest, what into the support policy, which departments, fields and rules to create.

    Nothing changes before you agree
  3. Implement the protocol

    A module with signed endpoints that reuses your loaders and services, its own tests, then the conformance suite.

    In your repository
  4. Configure Kvitok

    A workspace for the project, the integration, departments with examples, fields, routing rules and the policy.

    Over MCP
  5. Check on test data

    Writes test requests, including vague ones and prompt injections, sends them in and reads how the agent handled them.

    Over MCP, on staging customers
  6. Hand over to you

    You add the model key yourself, so it never passes through the agent, then hand out channel secrets and turn on real traffic.

    In the Kvitok interface

Then keep tuning it

Ask the same agent why the support agent got some tickets wrong. It reads where operators corrected it, changes the instructions and replays past tickets with the new configuration, using the backend data those tickets saw. Every change lands in the change log under the token that made it.

quality_report what operators corrected Find the cause tickets and agent runs Change instructions policy, playbooks, manifest replay rerun past tickets repeat

Changes over MCP apply at once and every one is recorded with values before and after. Customer messages reach the external agent marked as data, never as instructions. For analyzing production tickets, give a token only the read scope. Details in docs/mcp.md.

Configuration

Each team owns its part of the agent

Every change creates a new version, and each agent run records the version it used.

Backend team

Publishes a manifest with read tools, actions, playbooks, departments and domain knowledge. It lives in git next to their code, and the agent picks up a new version without a change in Kvitok.

Support team

Writes the policy in the interface: tone, what not to promise, when to hand over to a human. Sets up departments, ticket and customer fields and routing rules.

Administrator

Picks the provider, the model and its prices per workspace: OpenAI, OpenRouter, a LiteLLM proxy, vLLM or Ollama. Connects mailboxes and API channels.

Rules before and after the agent

Rules match the channel, the sender and their domain, the subject and body, source metadata, fields, and the hour and weekday. After the agent they also see its department, its confidence and whether it asked for a human.

Routing rules before and after the agent

Support Action Protocol

Six signed endpoints in your backend

Kvitok calls your backend on behalf of the agent and the operator. Every request is signed with HMAC-SHA256 over the timestamp, method, path and body. The protocol package and the reference backend are MIT licensed, so you can copy their code into a closed backend.

Connect or sync A ticket arrives The agent works on the ticket The operator approves Outcome unclear after a timeout Kvitok Your backend GET /manifest tools, actions, playbooks, departments · ETag POST /customers/resolve found · not_found · ambiguous POST /context the customer card POST /tools/{name} data the agent reads, with refs POST /actions/{name}/validate a preview, or errors the agent fixes POST /actions/{name}/execute · Idempotency-Key validated again, executed once per key GET /actions/executions/{key} executed · failed · pending · unknown

The core supplies the customer

customer_ref comes from the ticket, and the model cannot replace it. Your backend checks that a requested receipt or order belongs to this customer and answers for anyone else's as if it did not exist. A prompt injection in an email cannot reach another customer's data.

Check before you connect

packages/protocol has the schemas, request signing, a client and a conformance suite to run against your staging backend. The coffee shop simulator in apps/coffee-sim is a complete reference implementation.

Read the protocol specification →

A connected backend: its tools, actions, knowledge for the agent and suggested departments, all from the manifest

Safety

The agent never acts on its own

It proposes actions, a person approves them, and the backend validates the parameters again before executing.

The life of an action: the agent proposes it; the backend validates it and returns a preview, or an error the agent corrects; the operator approves it; the backend validates it again against the current state and executes it once by idempotency key The life of an action: the agent proposes it; the backend validates it and returns a preview, or an error the agent corrects; the operator approves it; the backend validates it again against the current state and executes it once by idempotency key

Customer text is data

Customer messages and backend data reach the model wrapped in tags and marked as untrusted. Here a guest's message poses as an operator instruction to credit 500 points. The agent proposes nothing and asks the operator whether such an approval exists.

Mailbox passwords, webhook secrets and model keys are encrypted at rest.

A customer message posing as a system instruction to credit 500 points; the agent proposes nothing and asks the operator

Architecture

One image, two processes, Postgres and Redis

The API serves the web interface, the REST API and the MCP endpoint. The worker runs the agent, receives and sends mail and delivers webhooks. Both run from one Docker image, next to PostgreSQL and Redis.

Customers email, app forms External agent any MCP client Operators in the browser Kvitok API REST · ingest · /api/mcp Web UI Nuxt, English or Russian Worker mail in and out · webhooks Agent runs with tool calls PostgreSQL tickets, config, runs Redis BullMQ queues one Docker image: api · worker · migrate · cli Your app or form ingest · webhooks Mailbox IMAP · SMTP LLM provider OpenAI-compatible API Your backend Support Action Protocol MCP signed

Built with NestJS on Fastify, Prisma with PostgreSQL, BullMQ with Redis, Nuxt 4 with Nuxt UI, and zod schemas shared between the API, the interface and the protocol. Images for amd64 and arm64 are published to ghcr.io/rogue85/kvitok.

Get started

Try it on a coffee shop, then connect yours

Kvitok is in early development and has not run in production yet. Start with synthetic or staging data, and read the changelog before updating: until 1.0 a minor version may break compatibility.

You need Docker. The development stack includes the coffee shop simulator and a fake LLM that answers by simple rules, so no model key is required.

git clone https://github.com/Rogue85/kvitok.git && cd kvitok
cp apps/api/.env.example apps/api/.env
docker compose up -d
docker compose exec api pnpm --filter @kvitok/api db:seed

Open http://localhost:3000 and sign in as admin@example.com / admin12345. Then add the fake LLM or a real provider, connect the simulator at http://coffee-sim:3100 and load its scenario tickets, as described in the README.