CoworkkitDocs
Sign inCreate account
© 2026 CoworkkitTerms of ServiceRefund & Cancellation PolicyPrivacy PolicyCookie PolicyData & Security

Start here

  • Getting started
  • How it works

Declaring your app

  • Actions, surfaces & elements
  • Hand mode
  • Control & confirmation
  • Patterns & best practices

Integrate

  • Backends
  • Usage, quotas & webhooks
  • Connect your coding agent

Polish & operate

  • Appearance
  • Languages
  • The Watch panel

Reference

  • Advanced
  • Configuration
  • Browser & framework support
  • Network requirements
  • Troubleshooting & FAQ
  • Error codes
All docs

Docs

Usage, quotas & webhooks

After every session your backend learns what it consumed, for which user, and why it ended — by the sessions API (pull) and signed webhooks (push), on every plan. Plus Quotas: fixed on Free, adjustable on Starter, programmable per session on Growth.

After every voice session your backend can learn what it consumed — how many minutes, for which of your users, and how it ended — two ways: pull (a sessions API you call when you like) and push (a signed webhook we send the moment a session ends). Both ship on every plan, Free included — you wire and test the integration before you pay. You are billed in minutes, so you get minutes: no tokens, no provider names, no per-minute cost.

The session id

The mint response now carries a sessionId — an opaque ses_… handle. Store it against your user; it is the one identifier for the session everywhere you can see it (the mint response, the record, the API, the webhook).

the mint response
{ token, serverUrl, cloudUrl, language, supportedLanguages,
  sessionId: "ses_01J9…",              // store this
  session: { budgetMinutes: 7, closing: "cut", region: "europe" } }  // what actually applied

The record

Both the pull API and the push webhook return the same object:

the session record
{ sessionId, userId, startedAt, endedAt, billedMinutes, turns,
  endReason: "complete" | "user_away" | "out_of_credit"
           | "session_limit" | "daily_limit" | "budget_limit" | "error",
  region, status: "recorded" | "unrecorded",
  session: { budgetMinutes?, closing?, region? } }

billedMinutes is null (never 0) for a session that appears in the transport log but never wrote a record (status: "unrecorded").

Pull: the sessions API

Server-to-server, on the same secret x-api-key as /session (never from the browser). Fetch one session, or list them:

GET /sessions
GET {base}/sessions/{sessionId}       → the record above
GET {base}/sessions?userId=&from=&to=&cursor=&limit=
    → { sessions: [ …records… ], nextCursor: "…" | null }
      newest first · limit 1–100 (default 50) · from/to are ISO-8601 instants on startedAt

Push: webhooks

Configure one endpoint under Settings → Webhooks (every plan). We POST a signed event the moment a session ends (session.ended, data = the record) and when your balance runs low (credits.low). Failed deliveries retry with backoff (1 min, 5 min, 30 min, 2 h, 12 h); five consecutive failed events auto-disable the endpoint (we email you, re-enable in the portal). Verify the signature with verifyWebhook — read the raw body:

app/api/coworkkit/webhook/route.ts
import { verifyWebhook } from "@coworkkit/server";

export async function POST(req: Request) {
  const raw = await req.text(); // the RAW bytes — a re-serialised body won't match
  const event = verifyWebhook(raw, req.headers.get("coworkkit-signature"), process.env.COWORKKIT_WEBHOOK_SECRET!);
  if (event.type === "session.ended") {
    // idempotent on sessionId — deliveries are at-least-once
    await db.deductMinutes(event.data.userId, event.data.billedMinutes, event.data.sessionId);
  }
  return new Response("ok");
}

Quotas — keep one user from draining your balance

A quota is a standing per-user wall; a budget is what your code hands one session. Three depths, one per tier:

  • Fixed (Free) — 30 min per session, 60 min per user per day. We protect; you can't change them.
  • Adjustable (Starter) — tune both walls (or switch them off) in the coworker's Quotas card. One allowance for every user.
  • Programmable (Growth) — pass session.budgetMinutes at mint from your own code, so different users get different allowances.

The layers stack; the tightest wins. When a wall is hit the user hears a short goodbye (or a clean cut, per session.closing) — never "out of credit"; the record's endReason says which wall it was, and a refused mint returns reason: "daily_limit".

The session container echoes what applied

Read the response's session to know what will actually govern the session: budgetMinutes is the ceiling that will bind (absent when unbounded), closing is "goodbye" (default) or "cut", and region is the basin honoured (present only when a preference was). On Free and Starter the plan's wall is echoed whatever you requested — nothing is refused, the response just tells you what applies.

PreviousBackendsNextConnect your coding agent

Machine-readable: this page as Markdown · all docs (llms.txt) · everything in one file (llms-full.txt)