Now onboarding law firmsJoin
The corner of a glass and steel office building against clouds

Build on THEMIS.

A scoped API for your practice's own data, a sandbox that never touches real records, and signed webhooks.

Overview

Every key belongs to one practice and can only ever reach that practice's data. Keys carry named permissions, can expire, can be limited to certain addresses, and are rate limited. Every request is logged against the key, and usage is counted from that log. Nothing is estimated.

Create and revoke keys in Settings, Developers. The specification is at /api/v1/openapi.json.

Authentication

Send the key as a bearer token. Only a hash of each key is stored, so it is shown once, when it is created.

curl https://www.themislaw.app/api/v1/me \
  -H "Authorization: Bearer thm_test_..."

Sandbox and production

thm_test_…

Sandbox. Never reads the practice's real records: list endpoints return an empty list marked sandbox. Safe for building and testing.

thm_live_…

Production. Reads the practice's real data, within the permissions you gave the key. Live events are sent only to production webhook endpoints.

Permissions

matters:readList the practice's matters: reference, title, kind of work, status.
usage:readRead this practice's own API request counts and error rates.
workspaces:readList the workspaces the key's creator belongs to, with their open tasks, document requests and approvals. Production keys only.
webhooks:readList the practice's webhook endpoints and their delivery state.
simulations:readList simulations and read their findings, risks, actions and history.
simulations:writeCreate simulations, add facts and documents, change scenarios and start analyses. Analyses run as the key's creator.

A request without the right permission is refused with status 403. A key with no permissions cannot be created.

Endpoints

GET/api/v1/meThe key's identity, environment, permissions and rate limit.any valid key
GET/api/v1/mattersMatters, newest first. Query: limit (1 to 100), before (ISO date).matters:read
GET/api/v1/usageRequests, errors and a daily series for the last 30 days.usage:read
GET/api/v1/webhooksWebhook endpoints. Secrets are never returned.webhooks:read
POST/api/v1/simulationsCreate a legal simulation from a description and queue its analysis. Returns 202 and a job. Send an Idempotency-Key header.simulations:write
GET/api/v1/simulations/{id}/findingsFindings with the wording and library passages relied on. Also /risks, /actions, /dependencies, /history, /compare.simulations:read
PATCH/api/v1/simulations/{id}/variablesChange scenario variables, then POST /recalculate.simulations:write
GET/api/v1/simulations/jobs/{jobId}Status of a queued analysis, stage by stage.simulations:read

Every response is JSON in one shape: { "data": ... } on success and { "error": { "code", "message" } } on failure.

Limits and errors

401missing_key, invalid_key, expired_keyThe key is absent, unknown, revoked or past its expiry.
403missing_scope, ip_not_allowedThe key lacks the permission, or the request came from an address not on its allowlist.
429rate_limitedMore requests in a minute than the key allows. Retry-After is 60.
500server_errorSomething went wrong on our side.

Each response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-Themis-Environment.

Webhooks

Add an https endpoint in Settings, Developers. Each delivery is signed. Failed deliveries are retried after 1, 5, 30 and 120 minutes, then marked dead, and you can resend them from the console.

matter.openedSent when a matter is opened in the practice. Production webhooks only.
simulation.completedSent when an analysis finishes and a new version is saved. Production webhooks only.
workspace.document.addedSent when a document is uploaded or created in a workspace. Production webhooks only.
workspace.document.versionSent when a document gets a new version. Production webhooks only.
workspace.task.completedSent when a task is marked done. Production webhooks only.
workspace.request.fulfilledSent when the person asked supplies the document. Production webhooks only.
workspace.approval.decidedSent when an approval is approved, rejected or withdrawn. Production webhooks only.
webhook.testSent when you press Send test in the console.

Check the signature before trusting a delivery. It is an HMAC-SHA256, in hexadecimal, of the timestamp, a full stop and the raw body, using the endpoint's signing secret.

import { createHmac, timingSafeEqual } from "node:crypto";

function verify(rawBody, headers, secret) {
  const ts = headers["x-themis-timestamp"];
  const want = createHmac("sha256", secret).update(ts + "." + rawBody).digest("hex");
  const got = headers["x-themis-signature"];
  return got.length === want.length && timingSafeEqual(Buffer.from(got), Buffer.from(want))
    && Math.abs(Date.now() / 1000 - Number(ts)) < 300;
}

Explorer

API explorer

Paste a key from Settings, Developers. Use a sandbox key to try it safely.

Ready to build?

Create your practice, then create a sandbox key in Settings.

A matter at a glance
Property verified against registrypass
Identity and authority verifiedpass
Transfer duty reviewedpass
Bond cancellation evidenceblocked
Ready to lodgeblocked