back to documentation
sdk reference · api v1

Kirtonic SDK

Protocol reference and integration guide for the TypeScript, Python, and Go clients.

typescript v0.1.0python v0.1.0go v0.1.012 sectionsendpoint referenceerror model
section 01

Architecture

The Kirtonic SDK is a thin client over the Kirtonic public API (/api/v1/classify and /api/v1/outcomes). It performs no model inference locally; the classifier runs in the Kirtonic control plane and the SDK is responsible for marshalling requests, propagating verdicts to the caller, and recording outcomes.

The library is distributed in three implementations (TypeScript, Python, and Go) that converge on a single API contract. Where language idioms diverge (synchronous calls in Python, idiomatichttp.RoundTrippermiddleware in Go) the SDK preserves the host platform's conventions rather than imposing a uniform shape.

┌─────────────────┐    classify    ┌────────────────────┐
│  Your service   │ ─────────────▶ │  Kirtonic control  │
│  (SDK client)   │ ◀───────────── │  plane             │
└─────────────────┘   verdict      │  · classifier      │
       │                           │  · policy engine   │
       │  allow                    │  · audit log       │
       ▼                           │  · webhook fanout  │
┌─────────────────┐                └────────────────────┘
│  AI provider    │                          ▲
│  (OpenAI /      │     outcome              │
│  Anthropic /    │ ────────────────────────-┘
│  Bedrock / …)   │
└─────────────────┘

The SDK is provider-agnostic. Two integration shapes are offered:

  1. Thin classifier. The caller invokes classify()before its own AI request and logOutcome() after. Request lifecycle (streaming, retries, error handling) remains the responsibility of the caller. Use this when integrating into an existing AI client or when the host language has no first-class provider library.
  2. Wrapped client. The SDK provides drop-in shims for the official OpenAI and Anthropic libraries (TypeScript and Python) or an http.RoundTripper middleware (Go). The shim classifies the input, forwards on allow, and records the outcome on success. block and reviewverdicts are raised as typed errors so the caller can map them directly to HTTP responses at the boundary.
section 02

Authentication

All API requests authenticate with a workspace-scoped API key carried in the Authorization header as a bearer token. Keys are issued from the workspace settings page in the dashboard; each key is tied to a single workspace and can be rotated or revoked at any time without redeploying the SDK.

Authorization: Bearer <KIRTONIC_API_KEY>
Content-Type:  application/json
User-Agent:    kirtonic-sdk-<lang>/<version>

The SDK reads the key from the constructor argument first, then falls back to the KIRTONIC_API_KEY environment variable in the Python and Go clients. The TypeScript client requires explicit construction and does not consult environment variables, to remain consistent with conventional Node.js library patterns.

Keys must not be embedded in client-side JavaScript bundles or mobile binaries. The SDK is intended for server-side integration; classifying end-user input from a browser requires a server-side proxy or the Kirtonic browser extension, which uses a different token scheme.

section 03

Endpoint reference

3.1 POST /api/v1/classify

Submits a prompt for classification. Returns the verdict synchronously. Latency depends on the classifier configured for the workspace and the deployment topology. Self-hosted deployments depend on local classifier provisioning.

POST /api/v1/classify
{
  "prompt":   "<string, required>",
  "category": "<string, optional>",
  "user_id":  "<string, optional>",
  "metadata": { "<string>": <any>, ... }    // optional
}

Fields.

  • prompt: The text about to be sent to an AI provider. The SDK enforces a non-empty string; the control plane enforces a maximum length of 32 KiB.
  • category: A free-form hint describing the workload (chat, rag, agent,embedding, etc.). Used by the policy engine to scope rule matching and by the dashboard for filtering.
  • user_id: A stable identifier for the end-user. Drives per-user audit aggregation and rate limit accounting. Hashed at rest if the workspace has pseudonymisation enabled.
  • metadata: An arbitrary JSON object persisted alongside the decision. Common keys: provider, model,session_id, trace_id. The wrappers auto-populate provider and model.
200 OK
{
  "decision_id":     "dec_01J9X…",
  "action":          "allow" | "review" | "block",
  "severity":        "high" | "medium" | "low" | "clean",
  "category":        "<classified-category>",
  "reason":          "<human-readable rationale>",
  "model_reasoning": "<full reasoning trace>",      // paid plans only
  "classified_at":   "2026-05-23T17:42:11.318Z"
}

3.2 POST /api/v1/outcomes

Records the AI provider's response against an earlier decision. Optional but recommended: this is the call that populates the audit log with the end-to-end request and response trace required for regulatory evidence.

POST /api/v1/outcomes
{
  "decision_id":   "<string, required>",
  "response":      "<string, required>",
  "finish_reason": "<string, optional>",      // OpenAI / Anthropic finish_reason
  "latency_ms":    <integer, optional>
}
204 No Content

The outcome endpoint is idempotent on decision_id: re-submitting an outcome overwrites the previous record. This avoids orphaned decisions when client-side retries fire after a partial response.

section 04

Verdict object

The verdict is the contract between the SDK and the caller. Three actions are defined; client behaviour is fixed and documented here so that integrations behave identically across languages.

action          caller behaviour
─────────────   ───────────────────────────────────────────────
allow           Forward to the AI provider. Record outcome on
                response.
review          Do NOT forward. Return decision_id to the
                caller (HTTP 202 + Location header is the
                recommended pattern). The reviewer's decision
                arrives via webhook (configured in dashboard).
block           Do NOT forward. Surface verdict.reason to the
                end-user (HTTP 403 is the recommended status).

4.1 Severity vs action

Severity is the classifier's assessment of inherent risk; action is the policy engine's decision about what to do about it. The two are not the same. A high severity prompt under a permissive policy may yield action: allow with the severity recorded for retrospective analysis; a medium severity prompt under a strict policy may yield action: block. The action is authoritative for runtime control flow; severity is informational.

section 05

Error model

The SDK distinguishes three error classes. The wrappers raise typed errors so callers can map directly to HTTP status codes at the application boundary; the thin client surfaces all three as ordinary runtime errors.

Error class           Trigger                      Suggested HTTP
─────────────────     ──────────────────────────   ───────────────
BlockedError          verdict.action === 'block'   403 Forbidden
ReviewError           verdict.action === 'review'  202 Accepted
                                                   (return decision_id)
RuntimeError          Network / 4xx / 5xx          502 Bad Gateway
                                                   (per fallback policy)

5.1 Network failure semantics

The SDK does not implement implicit retries. Transient network failures and 5xx responses surface as plain exceptions so the caller can apply its own retry budget. This is deliberate: in regulated deployments, the appropriate fallback policy depends on the workload class, and a default retry loop in the SDK would mask policy intentions. See §11 for guidance on fallback policy.

5.2 Per-error fields

BlockedError and ReviewError exposedecision_id, severity, andcategory. The full verdict is also available on.verdict (Python and Go) or via the constructor argument (TypeScript) for callers that need access to model reasoning or to forward the decision into their own observability stack.

section 06

TypeScript / JavaScript API

Distributed as @kirtonic/sdk. Node.js 18+. Browser usage is unsupported; the SDK depends on a server-side API key.

npm install @kirtonic/sdk
# wrappers require peer dependencies:
npm install openai @anthropic-ai/sdk

6.1 Constructor

new Kirtonic({
  apiKey:    string                  // required
  endpoint?: string                  // default 'https://app.kirtonic.io'
  timeoutMs?: number                 // default 8000
  fetch?:    typeof fetch            // default globalThis.fetch
})

6.2 Methods

k.classify(input: ClassifyInput) → Promise<Verdict>
k.logOutcome(input: LogOutcomeInput) → Promise<void>

type ClassifyInput = {
  prompt:     string
  category?:  string
  userId?:    string
  metadata?:  Record<string, unknown>
}

type Verdict = {
  decisionId:      string
  action:          'allow' | 'review' | 'block'
  severity:        'high' | 'medium' | 'low' | 'clean'
  category:        string
  reason:          string
  modelReasoning?: string
  classifiedAt:    string            // ISO 8601
}

type LogOutcomeInput = {
  decisionId:    string
  response:      string
  finishReason?: string
  latencyMs?:    number
}

6.3 Errors

KirtonicBlockedError and KirtonicReviewErrorare the typed errors raised by the wrappers. Both extend the standardError class and expose the constructor verdict via public fields.

6.4 Thread safety

The client is safe to share across concurrent invocations. Internally each request runs in an isolated AbortController; no state is shared between calls beyond the immutable configuration.

section 07

Python API

Distributed as kirtonic. Python 3.9+. Built onhttpx for HTTP transport; the OpenAI and Anthropic wrappers are installable as optional extras.

pip install kirtonic
# wrappers:
pip install "kirtonic[openai]"
pip install "kirtonic[anthropic]"
pip install "kirtonic[all]"

7.1 Constructor

Kirtonic(
    api_key:   str | None = None,    # falls back to KIRTONIC_API_KEY env var
    endpoint:  str        = "https://app.kirtonic.io",
    timeout_s: float      = 8.0,
    client:    httpx.Client | None = None,
)

7.2 Methods

k.classify(
    prompt:   str,
    category: str | None = None,
    user_id:  str | None = None,
    metadata: Mapping[str, Any] | None = None,
) → Verdict

k.log_outcome(
    decision_id:   str,
    response:      str,
    finish_reason: str | None = None,
    latency_ms:    int | None = None,
) → None

k.close()      # close the underlying httpx.Client

7.3 Async usage

The current release exposes a synchronous client only. Async support (an AsyncKirtonic variant built on httpx.AsyncClient) is on the roadmap. In the interim, asynchronous callers should delegate classify() to a thread pool viaasyncio.to_thread.

7.4 Thread safety

The default constructor instantiates a private httpx.Clientwhich is thread-safe under standard CPython concurrency models. Pass a pre-configured httpx.Client via the clientargument to share a connection pool with the rest of the application.

section 08

Go API

Distributed under github.com/MK11TON/CloudWhisper/sdk/go. Go 1.21+. The standard library supplies all transport dependencies; no external modules are required for the base client.

go get github.com/MK11TON/CloudWhisper/sdk/go

8.1 Constructor

func New(cfg Config) (*Client, error)

type Config struct {
    APIKey     string         // required
    Endpoint   string         // default "https://app.kirtonic.io"
    Timeout    time.Duration  // default 8 * time.Second
    HTTPClient *http.Client   // optional, bring your own
}

8.2 Methods

func (c *Client) Classify(ctx context.Context, in ClassifyInput) (*Verdict, error)
func (c *Client) LogOutcome(ctx context.Context, in LogOutcomeInput) error

type ClassifyInput struct {
    Prompt   string
    Category string
    UserID   string
    Metadata map[string]interface{}
}

type Verdict struct {
    DecisionID     string
    Action         Action     // "allow" | "review" | "block"
    Severity       Severity   // "high" | "medium" | "low" | "clean"
    Category       string
    Reason         string
    ModelReasoning string     // omitempty
    ClassifiedAt   string
}

8.3 Wrapper pattern

The Go wrappers are implemented as http.RoundTrippermiddleware (kopenai.GuardedTransport,kanthropic.GuardedTransport) rather than as wrapping types. Drop the transport into the http.Client that your OpenAI or Anthropic library consumes, and every request to the relevant API path is classified before egress. This decouples the wrapper from any specific Go client library and avoids type-system coupling to upstream interfaces.

client := &http.Client{
    Transport: &kopenai.GuardedTransport{
        Kirtonic: k,
        Category: "chat",
    },
}
// Pass to whichever OpenAI Go library you use.

8.4 Error handling

Blocked and review verdicts are surfaced as *kirtonic.BlockedErrorand *kirtonic.ReviewError. Use errors.Asfor type-switched handling:

var blocked *kirtonic.BlockedError
if errors.As(err, &blocked) {
    return ctx.JSON(403, gin.H{
        "error":       blocked.Verdict.Reason,
        "decision_id": blocked.Verdict.DecisionID,
    })
}
section 09

Wrapper integration patterns

The wrapped clients are convenience layers over the thin client. They enforce three behaviours:

  1. Pre-classification. The prompt is assembled from the request body and submitted to classify() before any outbound call to the AI provider.
  2. Verdict enforcement. On block orreview the upstream call is suppressed and the typed error is raised. On allow the original request is forwarded unchanged.
  3. Fire-and-forget outcome logging. On a successful upstream response, logOutcome() is invoked asynchronously (background promise in TypeScript, swallowed exception in Python, goroutine in Go). Failures to log do not propagate to the caller; the user-visible response is never blocked by an audit-log write.

9.1 Prompt extraction

The wrappers concatenate all system and usermessages with double-newline separators to construct the classification input. assistant messages are excluded (they have already been classified on prior turns). Tool-call payloads and image blocks are reduced to their text components.

9.2 Metadata propagation

The provider name and model id are auto-injected into the metadata object on every classify call:{ provider: 'openai', model: 'gpt-4o' }. Callers can merge additional metadata via the wrapper-specific options object without losing the auto-populated fields.

9.3 Streaming

The current wrappers cover non-streaming completion calls only. Streaming integrations should use the thin client directly: classify the input prompt, open the stream, accumulate the response, then calllogOutcome() on stream close.

section 10

Self-hosted deployment

The SDK is endpoint-agnostic. Self-hosted Kirtonic deployments configure the endpoint argument to point at an internal URL; no traffic leaves the customer network perimeter. The contract is identical to the managed control plane. The SDK does not differentiate between the two deployment models at the protocol layer.

// TypeScript
new Kirtonic({
  apiKey:   process.env.KIRTONIC_API_KEY!,
  endpoint: 'https://kirtonic.internal.example.com',
})

// Python
Kirtonic(api_key=..., endpoint="https://kirtonic.internal.example.com")

// Go
kirtonic.New(kirtonic.Config{
    APIKey:   os.Getenv("KIRTONIC_API_KEY"),
    Endpoint: "https://kirtonic.internal.example.com",
})

In air-gapped deployments the SDK's User-Agentheader (kirtonic-sdk-<lang>/<version>) is the only identifying string emitted; no telemetry is dispatched to any external endpoint by the SDK itself. The control plane is the only recipient of API traffic.

section 11

Operations: timeouts, retries, fallback

The SDK enforces a default per-request timeout of 8 seconds. This is conservative for an interactive AI use case where theclassify call sits on the critical path of every end-user response; production deployments should reduce the timeout to 1 or 2 seconds and pair it with an explicit fallback policy.

11.1 Fallback policy

Two fallback regimes are commonly correct depending on the workload class:

  • Fail-open. If the classifier is unreachable, forward the request to the AI provider with an audit-log marker indicating unclassified delivery. Appropriate for low-risk consumer workloads where uptime is more valuable than per-request governance.
  • Fail-closed. If the classifier is unreachable, refuse the request and surface a 503 Service Unavailable to the caller. Appropriate for regulated workloads (financial advice, healthcare, defence) where unclassified inference is a policy violation regardless of cause.

The SDK does not select between these policies. The caller must implement the chosen fallback explicitly in the catch block. This places the policy decision in the application layer, where it is visible to auditors and version-controlled alongside the rest of the application's safety logic.

11.2 Retries

Retries are the caller's responsibility. A bounded retry budget (e.g. one retry on connect failure, none on 4xx) is appropriate for most integrations. Do not retry on BlockedError orReviewError; both are deterministic responses from the policy engine and a retry will produce the same result.

11.3 Rate limits

The control plane enforces per-workspace rate limits returned via the standard X-RateLimit-* headers. The SDK does not inspect these headers automatically; callers running at high request rates should observe the headers and back off proactively. A429 Too Many Requests response surfaces as aRuntimeError.

section 12

Versioning

The SDK follows semantic versioning. The API protocol version is carried in the URL path (/api/v1/) and is independent of the SDK version. Compatibility commitments:

  • Patch. Bug fixes, dependency upgrades that do not change the public API. Safe to upgrade without reading the changelog.
  • Minor. Backwards-compatible additions to the SDK surface (new methods, new optional fields on existing methods).
  • Major. Breaking changes to the SDK surface. A migration guide is published with the release and the previous major version receives security patches for twelve months from the date of the breaking release.

A new API protocol version (/api/v2/) ships as an independent route. Existing v1 traffic continues to function for at least eighteen months after v2 general availability.

SDK source is MIT licensed. The full implementation across all three languages is available for download from the SDK page or for inspection in the sdk/ directory of the project repository.