Skip to content

About

Official Node.js and TypeScript SDK for Klaracase: German case law and statutes API (Rechtsprechung, Gesetze)

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Klaracase for TypeScript and Node.js

The official client for the Klaracase REST API. Klaracase holds German court decisions and German statutes, linked by a citation graph.

Klaracase covers German law only. It is a research tool. It gives no legal advice and does not replace a lawyer's review. A not_found verdict means the citation was not found in the corpus; it is not a claim that the citation is fabricated.

This repository has two packages:

Package What it is
@klaracase/sdk The API client. No runtime dependencies. Node 22 or newer.
@klaracase/ai-sdk Tools for the Vercel AI SDK, built on the client.

The client calls the REST API. It does not speak MCP. If your agent has an MCP client, connect it to https://klaracase.de/mcp with the same token.

Install

npm install @klaracase/sdk

The package ships ESM and CommonJS builds with type files.

// ESM
import { Klaracase } from "@klaracase/sdk";
// CommonJS
const { Klaracase } = require("@klaracase/sdk");

Requirements and runtimes

The client needs a global fetch, FormData, Blob and AbortSignal.any. It has no runtime dependencies and imports no Node module.

Runtime Version Tested in CI
Node.js 22+ 22, 24 and 26
Bun 1.x the whole suite
Vercel Edge current the whole suite, in the Edge sandbox
Deno 2+ not in CI yet, see the changelog
Cloudflare Workers current not in CI yet, see the changelog
Browsers current not supported without your own backend

The User-Agent names the runtime it detects: node/22.12.0, bun/1.4.4, deno/2.1.0, vercel-edge/unknown, cloudflare-workers/unknown, browser/unknown or unknown/unknown. The detector never throws, so the client builds on a host that has no process object.

Browser and proxy pattern

The token is an organization secret. Do not ship it to a browser. The constructor throws a ConfigurationError with the code client_validation when it finds window and document and you did not pass dangerouslyAllowBrowser: true.

Keep the token on your server. Give the browser your own route, and point the fetch option at it. The client then sends its requests to your route, and your route adds the token.

// In the browser. No token is needed here, but the constructor wants a placeholder.
import { Klaracase } from "@klaracase/sdk";

const client = new Klaracase({
  token: "proxied",
  baseUrl: "https://app.example.com/klaracase",
  dangerouslyAllowBrowser: true,
  fetch: (input, init) => fetch(input, { ...init, credentials: "include" }),
});
// On your server (a route handler). It adds the real token and forwards the call.
export async function GET(request: Request): Promise<Response> {
  const url = new URL(request.url);
  const path = url.pathname.replace(/^\/klaracase/, "");
  return fetch(`https://klaracase.de/api/v1${path}${url.search}`, {
    headers: {
      Authorization: `Bearer ${process.env.KLARACASE_API_TOKEN}`,
      Accept: "application/json",
    },
  });
}

Check the signed-in user and rate-limit the route yourself. Every call it forwards costs credits.

Get a token

Create a token at https://klaracase.de/settings/api-tokens. You need to be signed in with a verified email.

  • A token belongs to the organization that is open when you create it. That organization pays for every call.
  • A token carries abilities. There are five: search, read, graph, datapackage and citation-check. All five are ticked by default.
  • A token expires after 30, 90, 180 or 365 days. The default is 365.

Pass the token to the client, or set KLARACASE_API_TOKEN and pass nothing. A missing token raises ConfigurationError when you build the client, not a 401 later. Never commit a token.

Quick start

import { Klaracase } from "@klaracase/sdk";

const client = new Klaracase({ token: "klc_your_token_here" });

const found = await client.decisions.search("Vermögensbetreuungspflicht", {
  court: "BGH",
  date_from: "2015-01-01",
  limit: 5,
});

for (const hit of found.results) {
  console.log(hit.aktenzeichen, hit.court, hit.date, hit.title);
}
console.log(found.credits.cost, "credit used,", found.credits.credits_remaining, "left");

Request parameters use the names of the API, such as date_from and cited_statutes. They are typed strictly. Client options and method names use camel case.

Check the citations in a text

citationChecks.create checks every court-decision and statute citation in a German legal text. It answers when the check is finished.

import { assertComplete, excerpt, groupByVerdict } from "@klaracase/sdk";

const run = await client.citationChecks.create({
  text: draftText, // 50 to 20,000 characters
  reference_date: "2026-10-01", // optional
});

// Check the status first. A run can end as "rejected" or "failed".
assertComplete(run);

const groups = groupByVerdict(run);
console.log(groups.verified_corpus.length, "found,", groups.not_found.length, "to check by hand");

for (const finding of groups.not_found) {
  // Not found in the corpus. This does not mean the citation is invented.
  console.log("check by hand:", excerpt(draftText, finding));
}

Guard on the status

Always check run.status before you read run.findings. The status is complete only when the check finished. A run can also be rejected or failed, or still be working when you read it with citationChecks.get. Then the findings are not a full result, and a citation that is missing from them has not been verified. assertComplete(run) throws CitationCheckIncompleteError for any other status. The error has run_status and the run. Its status is null, because no HTTP request failed. A complete run can still have partial: true. Read partial and findings_unavailable_reason as well.

The helpers are plain functions and make no request:

Helper What it does
groupByVerdict(run) Findings grouped by verdict. Each of the seven verdicts has a list.
assertComplete(run) Throws CitationCheckIncompleteError unless status is complete.
excerpt(text, finding) The cited text. Offsets count code points, so emoji do not shift it.

The offsets of a finding are Unicode code point offsets into the text you sent. text.slice counts UTF-16 units and cuts in the wrong place after an emoji. Use excerpt.

To check a file, upload it. The upload is free. PDF, DOCX, Markdown and plain text work, up to 25 MB. upload takes a File, a Blob, a Uint8Array or an ArrayBuffer. A File brings its own name and media type. For bytes, pass filename.

import { readFile } from "node:fs/promises";

const receipt = await client.citationCheckDocuments.upload(await readFile("schriftsatz.pdf"), {
  filename: "schriftsatz.pdf",
});
const ready = await client.citationCheckDocuments.waitUntilReady(receipt.id, {
  pollInterval: 2, // seconds
  timeout: 120, // seconds
});
console.log("a check will cost", ready.quoted_credits, "credits");

const run = await client.citationChecks.create({ document_id: ready.id });

createFromFile does the three steps in one call:

import { assertComplete } from "@klaracase/sdk";

const run = await client.citationChecks.createFromFile(file, {
  wait: { timeout: 180, interval: 3 }, // seconds
  reference_date: "2026-10-01",
});
assertComplete(run);

waitUntilReady and createFromFile throw DocumentFailedError when the upload fails, for example with encrypted_pdf. They throw DocumentTimeoutError when the time runs out.

All operations

Method Endpoint
client.decisions.search(query, filters) GET /decisions/search
client.search(query, filters) GET /search (laws and decisions)
client.decisions.get(id, options) GET /decisions/{id}
client.decisions.citations(id, options) GET /decisions/{id}/citations
client.norms.get(reference, options) GET /norms/{reference}
client.datapackages.create({ ids, ... }) POST /datapackages
client.citationChecks.create({ text }) POST /citation-checks
client.citationChecks.get(runId) GET /citation-checks/{run}
client.citationCheckDocuments.upload(file, options) POST /citation-check-documents
client.citationCheckDocuments.get(id) GET /citation-check-documents/{id}
client.citationCheckDocuments.waitUntilReady(id) polls get until the upload is ready
client.citationChecks.createFromFile(file) upload, wait and check in one call
client.decisions.searchAll(query, filters, paging) pages of GET /decisions/search
client.decisions.citationsAll(id, options, paging) pages of the citations endpoint
client.corpus.coverage(options) GET /corpus/coverage
client.corpus.facets(field, options) GET /corpus/facets/{field}

An id can be a UUID, an Aktenzeichen or an ECLI. The client encodes every path parameter as one segment, so VIII ZR 271/17 is sent as VIII%20ZR%20271%2F17. Arrays go out as cited_statutes[]=StGB&cited_statutes[]=StPO. Options that you leave out or set to undefined are not sent.

Full parameter lists are in the API reference.

Pagination

decisions.searchAll and decisions.citationsAll are async iterators over pages.

Paging the same query within a credit period costs no extra credits. A changed query, filter or mode is a new charge. Check credits on each response. A search costs 1 credit per distinct query, filter set and mode, and the graph costs 2 credits per decision, direction and depth. Each page is still a call. It counts against the rate limit and the daily volume cap of your plan, and it takes time. So maxPages is required and has no default. It is a guard against a runaway loop.

for await (const page of client.decisions.searchAll("Untreue", { court: "BGH" }, { maxPages: 3 })) {
  for (const hit of page.results) console.log(hit.aktenzeichen, hit.date);
  console.log("this page cost", page.credits.cost, "credits");
}

for await (const page of client.decisions.citationsAll("VIII ZR 271/17", {}, { maxPages: 2 })) {
  console.log(page.references.length, "references,", page.referenced_by.length, "cited by");
}
  • The iterator yields whole pages, so you can read credits and meta of each one.
  • It stops when a page has no results, or when the citations pages have no more, or after maxPages pages. An empty page is not yielded.
  • maxPages is capped at the last page the endpoint serves: 10 for search, 20 for citations. A page in the filters is the first page.
  • Breaking out of the loop stops the calls. Paging of citations works at depth 1 only.
  • The third argument also takes signal, timeout, maxRetries and headers for every call.

Responses

Each method returns the answer body, parsed. Nothing in it is renamed, reordered or cleaned.

The response types list the documented keys and nothing else. They have no index signature, so a misspelled key is a compile error. A key that a later API release adds still exists at run time. Read it with getExtra:

import { getExtra } from "@klaracase/sdk";

const found = await client.decisions.search("Untreue");
const added = getExtra(found, "a_key_from_a_later_release"); // unknown

Enum values in a response are open: verdict is a known name or any other string. Enum values you send are strict unions, such as mode: "keyword" | "semantic" | "hybrid". CITATION_VERDICTS lists the seven verdicts, and isKnownVerdict(value) tells you whether a value is one of them.

Each body also has a meta property with status, api_release (from X-Klaracase-Api-Release), request_duration in seconds and headers (the response Headers). meta is not enumerable. It does not show in JSON.stringify(result), Object.keys(result) or a spread copy. Read result.meta directly. result.credits has metered, cost and credits_remaining, and api_budget while the organization has one (see Credits, API budget and limits). The body itself is a plain mutable object.

All input types accept undefined for an optional key, also under exactOptionalPropertyTypes.

Errors

Every error from the API or the network extends KlaracaseError. The class follows the HTTP status.

import {
  Klaracase,
  KlaracaseError,
  InsufficientCreditsError,
  RateLimitError,
  TransportError,
  ValidationError,
} from "@klaracase/sdk";

try {
  await client.decisions.get("VIII ZR 999/99");
} catch (error) {
  if (error instanceof InsufficientCreditsError) {
    console.log("out of credits:", error.cost, error.balance);
  } else if (error instanceof RateLimitError) {
    console.log("wait", error.retry_after_seconds, "seconds");
  } else if (error instanceof ValidationError) {
    console.log(error.errors); // field name to a list of messages
  } else if (error instanceof TransportError) {
    console.log("no answer:", error.message); // also covers TimeoutError
  } else if (KlaracaseError.is(error)) {
    console.log(error.status, error.code, error.detail, error.retryable);
  } else {
    throw error;
  }
}
Class When
AuthenticationError 401
InsufficientCreditsError 402, with cost, balance, reason and topUpUrl
TokenLimitReachedError 402 token_limit_reached. It extends the class above.
PermissionError 403, with required_ability
NotFoundError 404
ConflictError 409
ValidationError 422, with errors. Also raised before any request.
RateLimitError 429, with retry_after_seconds
ServiceUnavailableError 503
ServerError 5xx except 503. It extends ApiError.
ApiError any other status
TransportError, TimeoutError no answer from the server
DocumentFailedError, DocumentTimeoutError waitUntilReady ended without a ready upload
ConfigurationError bad client setup. Not a KlaracaseError.

An error has type, title, status, detail, code, api_release, headers and extra. extra holds every other member of the problem body. A few members are also typed fields:

Field When
candidates ambiguous_aktenzeichen: the decisions that share the Aktenzeichen
successor statute_superseded: the statute that replaced the cited one
unit, cap, resets_at volume_cap and token_limit_reached: what the cap counts, its size and when it resets
retryable true when the same request may succeed later (429, 5xx, transport)
retryAfterSeconds seconds the server asked you to wait, or null

Use KlaracaseError.is(error) instead of instanceof when your app may load both the ESM and the CommonJS build of the package, or two versions of it. Each copy has its own classes, so instanceof can be false for an error from the other copy. is checks a shared brand. If the server answers with something that is not a problem body, such as an HTML page from a proxy, code is unknown and detail holds the first 1,000 characters of the body.

Passing both text and document_id, or neither, to citationChecks.create raises a ValidationError with the code client_validation. No request is sent.

Retries and timeouts

The client retries at most maxRetries times (default 2).

  • It retries on 429 and 503. Both statuses mean nothing ran and nothing was charged, so a retry is safe on every operation, including POST.
  • It waits for retry_after_seconds from the body, or the Retry-After header. Without one it backs off for up to 0.5 s, then 1 s, then 2 s, with random jitter, never more than 8 s.
  • If the server asks for more than 30 seconds, the client does not sleep. It throws RateLimitError and you decide. The daily volume_cap is handled this way.
  • It never retries 402 or any other 4xx, and it does not retry 500 or 502.
  • It retries a transport error on GET only. A POST whose connection dropped after it was sent is not retried. The same check repeated in the same credit period costs nothing more, so you can repeat it by hand.

timeout is in seconds for each attempt (default 30). You can set timeout, maxRetries and an AbortSignal for a single call:

await client.norms.get("§ 535 BGB", {}, { timeout: 10, maxRetries: 0, signal: controller.signal });

Client options

import { Klaracase } from "@klaracase/sdk";

new Klaracase({
  token: "klc_your_token_here", // or KLARACASE_API_TOKEN
  baseUrl: "https://klaracase.de/api/v1", // default
  timeout: 30, // seconds for each attempt, default 30
  maxRetries: 2, // default 2
  userAgentSuffix: "my-app/1.0", // appended to the User-Agent header
  defaultHeaders: { "X-Trace-Id": "abc" }, // sent with every request
  logLevel: "warn", // off, error, warn, info or debug
  logger: console, // anything with error, warn, info and debug
  fetch: globalThis.fetch, // your own fetch
  onDeprecation: (info) => console.warn(info), // see below
  dangerouslyAllowBrowser: false, // see "Browser and proxy pattern"
});

Timeouts are in seconds, in every Klaracase SDK, so the option means the same in each language. The option for the API address is baseUrl.

The User-Agent is klaracase-node/0.1.0 node/<version>.

When the API announces a deprecation with Deprecation or Sunset headers, the client calls onDeprecation once per process with the raw header values. The default writes one line at the warn level. In tests, call resetDeprecationWarnings() to reset the guard.

Logging

The client logs through the logger option. The default is console. Set the level with logLevel, or with the KLARACASE_LOG environment variable. The option wins over the variable. The default level is warn.

Level What the client logs
warn a deprecation notice from the API
info one line per answer: method, path, status
debug every retry: method, path, status, attempt number and the wait in seconds
off nothing

The client never logs the token, a header, a request body or a query string. The query string is left out because it holds your search text. console.log(client) does not show the token either.

import { Klaracase } from "@klaracase/sdk";

const client = new Klaracase({ logLevel: "debug", logger: console });

Advanced

Per-request headers, and a client with changed options:

const found = await client.decisions.search("Untreue", {}, { headers: { "X-Trace-Id": "abc" } });

// A new client. The original is not changed, and the two share no state.
const patient = client.withOptions({ timeout: 120, maxRetries: 4 });

A header you pass to one call wins over defaultHeaders. Authorization and Content-Type cannot be replaced. withOptions merges defaultHeaders: the new headers win, the rest stay.

A custom fetch lets you add a proxy, a dispatcher or a test double. It receives the URL and the RequestInit that the client built.

import { Klaracase } from "@klaracase/sdk";

const proxied = new Klaracase({
  token: "klc_your_token_here",
  fetch: (input, init) => globalThis.fetch(input, { ...init, keepalive: true }),
});

Credits

Most calls cost credits. The organization of the token pays.

  • decisions.search and search cost 1 credit per distinct query and filter set per credit period; a repeat costs 0.
  • decisions.get and norms.get cost 1 credit per decision or norm per credit period; a repeat costs 0.
  • decisions.citations costs 2 credits. datapackages.create costs 5.
  • citationChecks.create costs max(2, ceil(characters / 5000)) credits.
  • corpus.coverage, corpus.facets, uploads and reading a run or an upload are free.

One charge applies per operation, organization, subject and credit period. The same text checked again in the same credit period costs 0. The response credits field shows the cost of each call. Credits renew per credit period: the UTC calendar month for free organizations, the paid billing period for subscribers. Unused credits do not roll over.

Credits, API budget and limits

Credits pay for calls. Three things decide whether a call can run.

  • Plan credits. Each plan comes with plan credits for one credit period. They are spent first. They do not roll over. The response field credits.credits_remaining is the plan credits that are left. It never counts the API budget.
  • API budget. Organizations on the Professional or Enterprise plan can load a prepaid API budget. It pays for calls on the API and on MCP once the plan credits are used up, and only for those. The web app never draws on it. A call is paid from the budget in whole, never in part. credits.cost counts plan credits and budget credits together.
  • Credit limit per API key. An organization admin can set a credit limit for a single API key and a single period. The key stops at its limit, even when the organization still has credits.

Plans and prices are on klaracase.de/preise. This README states no prices.

Read the budget

credits.api_budget is there only while the budget is available and the organization holds one or may load one. Otherwise the key is absent. It is never null. Unknown keys are kept.

const found = await client.decisions.search("Vermögensbetreuungspflicht");
const budget = found.credits.api_budget;
if (budget !== undefined) {
  // All three are integers.
  console.log(budget.cents, "euro cents left");
  console.log(budget.rate_cents_per_credit, "euro cents per credit");
  console.log(budget.credits_at_rate, "more credits at that rate");
}

credits_at_rate is 0 when the budget is empty or on hold. cents can be negative after a lost payment.

What a refusal means

Both refusals are HTTP 402. The call did not run and nothing was charged. The SDK never retries a 402, and a repeat does not help until something changes.

Error code Meaning What a program does
InsufficientCreditsError insufficient_credits The plan credits are used up and the budget cannot pay for the call. Stop. Report to the user. Tell the user reason.
TokenLimitReachedError token_limit_reached The credit limit set for this API key is used up for the period. Stop. Report to the user. Tell the user when it resets.

InsufficientCreditsError has the optional fields reason and topUpUrl. reason is an open list. Treat a value you do not know like api_budget_not_available.

  • api_budget_empty: the organization may load an API budget, and what it holds does not cover this call.
  • api_budget_not_available: the organization has no API budget, or the budget is on hold, and its plan does not allow a top-up.
  • plan_credits_exhausted: reserved. The API does not send it today.

TokenLimitReachedError extends InsufficientCreditsError, because it is a 402 too, so an existing catch for InsufficientCreditsError still sees it. It has no cost and no balance: both are null. It has unit (always credits), cap (the limit of the key), spent, resets_at (the raw string), resetsAt (the same time as a Date) and the optional settingsUrl.

import { InsufficientCreditsError, TokenLimitReachedError } from "@klaracase/sdk";

try {
  await client.decisions.get("VIII ZR 271/17");
} catch (error) {
  if (error instanceof TokenLimitReachedError) {
    // Check this class first. It is also an InsufficientCreditsError.
    console.log(`Key limit reached: ${error.spent} of ${error.cap} ${error.unit}.`);
    console.log("It resets at", error.resetsAt?.toISOString() ?? error.resets_at);
  } else if (error instanceof InsufficientCreditsError) {
    console.log("Cannot pay for this call. Reason:", error.reason ?? "not stated");
  } else {
    throw error;
  }
}

topUpUrl and settingsUrl are for a human organization admin. A program or an AI agent must never open them or follow them. Show them to your user and stop. The SDK never opens them.

Schemas and validation

@klaracase/sdk/schemas holds a zod schema for every request input and every response body. Use it to check arguments before you send them, or to check a body you got from a cache or a queue.

import * as z from "zod/mini";
import { decisionSearchResponseSchema, searchDecisionsInputSchema } from "@klaracase/sdk/schemas";

const input = z.parse(searchDecisionsInputSchema, { query: "Untreue", limit: 5 });
const found = await client.decisions.search(input.query, { limit: input.limit });

const checked = z.parse(decisionSearchResponseSchema, JSON.parse(JSON.stringify(found)));
  • The schemas use zod/mini. Install zod 4.1 or newer. It is an optional peer dependency: the main entry @klaracase/sdk does not import zod.
  • Every object schema is loose. A key that the schema does not list survives the parse, so a newer API release does not break your validation.
  • Enums in a response are open, like the types. Enums you send are closed lists.
  • The inferred types are the types of the main entry. A test keeps them equal.
  • The folder schemas/ of the package holds the same schemas as JSON Schema files, one per schema, for tools that do not run JavaScript. Import one with @klaracase/sdk/schemas/searchDecisionsInput.json. They are generated by the build and committed. Input schemas refuse keys they do not list.

Tools for agents

@klaracase/sdk/tools has four tool definitions and a dispatcher. It has no runtime dependency, so it works with any agent loop that takes JSON Schema tools: Anthropic, OpenAI, an MCP host, your own.

import { callTool, tools } from "@klaracase/sdk/tools";

// The definitions: name, description and a JSON Schema for the arguments.
const definitions = tools.map((tool) => ({
  name: tool.name,
  description: tool.description,
  input_schema: tool.inputSchema,
}));

// When the model calls a tool, run it. Pass the arguments as the model produced them.
const result = await callTool(client, "getDecision", { id: "VIII ZR 271/17" });
Tool Calls What callTool returns
searchDecisions decisions.search the body
getDecision decisions.get the body, with max_chars 4000 unless you set maxChars
getNorm norms.get the body, with the norm text cut to maxChars
checkCitations citationChecks.create a slim run: id, status, verdict_counts, unique_citations, and per finding id, canonical, verdict, raw_text and offsets

callTool(client, name, args, options) takes maxChars (default 4000), budget and the request options signal, timeout, maxRetries and headers.

  • An error from the API comes back as { error: { code, detail, retry_after_seconds } }, so the model can read it. insufficient_credits and token_limit_reached are never retried. The agent must stop and report them to its user, and must never open a URL named in detail. Bad arguments and an unknown tool name come back the same way. Other errors, such as an abort, are thrown.
  • A budget stops an agent from spending more than you allow. { credits: 10 } is a shared counter: callTool takes the cost of each answer off it, and it does not send a call whose price does not fit. A repeat in the same credit period costs nothing.
import { callTool } from "@klaracase/sdk/tools";

const budget = { credits: 10 };
const answer = await callTool(client, "searchDecisions", { query: "Untreue" }, { budget });
console.log(budget.credits, "credits left");

Read the status of a checkCitations result before you rely on its verdicts.

Vercel AI SDK

@klaracase/ai-sdk is a thin wrapper over these tools. It gives you the four tools in the shape that generateText and streamText take. The AI SDK abort signal cancels the request.

import { generateText } from "ai";
import { Klaracase } from "@klaracase/sdk";
import { klaracaseTools } from "@klaracase/ai-sdk";

const aiClient = new Klaracase();
const { text } = await generateText({
  model: yourModel,
  tools: klaracaseTools(aiClient, { maxChars: 3000, budget: { credits: 20 } }),
  prompt: "Which BGH decisions deal with § 266 StGB and the Vermögensbetreuungspflicht?",
});

Pass { tools: ["searchDecisions", "getNorm"] } to expose only some tools. See packages/ai-sdk.

Other agent frameworks

For an agent with an MCP client, connect the Klaracase MCP server at https://klaracase.de/mcp with the same token. The client itself does not speak MCP.

Semantic versioning

This SDK follows semantic versioning, with the usual 0.x rule.

  • Before 1.0, a minor version (0.1 to 0.2) may change the public surface. A patch version (0.1.0 to 0.1.1) only fixes bugs. Pin the minor version: "@klaracase/sdk": "~0.1.0".
  • After 1.0, a breaking change needs a major version.
  • The public surface is what the package exports from @klaracase/sdk, @klaracase/sdk/schemas and @klaracase/sdk/tools, and what api.md lists. Anything marked @internal is not part of it.
  • An unknown field in a response, or a new value in a response enum, is not a breaking change. The response types are open for this reason. Write code that handles a value it does not know.
  • Every change is listed in CHANGELOG.md. All Klaracase SDKs bump together when the API contract changes.

Stability and privacy

This SDK is 0.x and tracks API release 0.96.0.

The SDK sends no telemetry. The only thing it adds to a request is the User-Agent header klaracase-node/<version> <runtime>/<version>, for example klaracase-node/0.1.0 node/22.12.0.

Klaracase is not affiliated with any court or with the federal government.

Examples

Three programs in packages/sdk/examples show the common paths: quickstart.mjs, citation-check.mjs (upload, poll, read the verdicts) and agent-tools.mjs. They run offline against a small mock of the API, so they cost nothing.

pnpm build
node packages/sdk/examples/quickstart.mjs
node packages/sdk/examples/citation-check.mjs
node packages/sdk/examples/agent-tools.mjs

Add --live to call the real API with KLARACASE_API_TOKEN. That spends credits.

Smoke test

pnpm smoke calls the two free endpoints with a real token. It needs a build first.

pnpm build
KLARACASE_API_TOKEN=<token> pnpm smoke

Development

pnpm install
pnpm typecheck && pnpm lint && pnpm test && pnpm build
pnpm check-package   # publint and Are the Types Wrong? on both packages
pnpm docs:api        # regenerate api.md; fails when an export has no JSDoc
pnpm docs:check      # fail when api.md is out of date
pnpm examples        # run the three examples against the mock

pnpm --filter @klaracase/sdk run test:bun runs the tests on Bun, and test:edge runs them in the Vercel Edge runtime.

More

About

Official Node.js and TypeScript SDK for Klaracase: German case law and statutes API (Rechtsprechung, Gesetze)

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages