Build with Lexem.
Lexem is version control + evals + safe deploys for the prompts that drive your AI. This page covers everything from signup to shipping a prompt change in production.
Quick start
From zero to a prompt fetched in your code — about five minutes.
- 1
Sign up and create a project
Hit signup with email or Google. A “Personal” team is created automatically; rename it from
Settingsif you'll be inviting teammates. Then create a project from the dashboard. - 2
Add a prompt and commit a version
Open the project, click
New prompt, name it, and write the prompt body in the editor. Add typed variables with{{name: string}}syntax. When you're happy, hitCommit— that snapshot is now an immutable version with an author, timestamp, and commit message. - 3
(Optional) Promote to an environment
Every project ships with
dev,staging, andproductionenvironments. ClickEnvironmentson the project page, thenPromoteto deploy a specific version to dev. Promotion followsdev → staging → production; you can toggle approval-required on any environment column. - 4
Generate an API key
From the project page, click
API keys→New API key. Save thelxm_...token shown — this is the only time you'll see it. Admins and Owners only. - 5
Fetch it from your code
TypeScriptimport { createClient } from "lexem-sdk"; const lexem = createClient({ apiKey: process.env.LEXEM_API_KEY! }); const p = await lexem.get("your-prompt-slug", { env: "production" }); console.log(p.content); // the prompt text console.log(p.versionId); // log this with your LLM callPro tipLogversionIdalongside every LLM call. When a regression appears in production, you'll know exactly which version of which prompt produced the output.
Concepts
A handful of nouns power the entire product. Once these click, the UI navigates itself.
Project
A collection of prompts owned by a team. Permissions, environments, and API keys are scoped to a project.
Prompt & Version
A prompt is a named slot (e.g. summarizer). A version is an immutable snapshot of that prompt's content at a point in time, with a commit message and an author. You never overwrite a version — you commit a new one.
Branches & tags
Branches let you commit experimental variants without touching main. Merge a branch back to main when its eval score beats the incumbent. Tags (v1.0, stable,pre-launch) attach human-readable labels to specific versions.
Evals
An eval is a suite of test cases — each with input variables and an expected output — run against a prompt version. Three scorers are built in: exact match, regex, and LLM-as-judge. Lexem auto-flags regressions when a new version's score drops more than 5 points from the previous one.
Environments
Every project has three environments — dev, staging, production — seeded automatically. Each environment holds an active version per prompt. Promotion is strictly chained: a version must be live in dev before it can be promoted to staging, and live in staging before it can reach production. The order is enforced server-side.
Approvals
Toggle approval required on any environment column. With approval on, a promote click creates a pending Deployment that doesn't flip the active version until a teammate approves it from the Pending approvals panel at the top of the environments page.
API keys
Project-scoped bearer tokens (lxm_...) used by the SDK and REST API to fetch prompts. Generated by Admins, hashed at rest, shown once on creation, and revocable any time. lastUsedAt ticks with every successful auth so you can audit usage.
Team roles
Four tiers. Permissions stack — every tier inherits everything below it:
| Capability | Viewer | Editor | Admin | Owner |
|---|---|---|---|---|
| View prompts, versions, evals, environments | ✓ | ✓ | ✓ | ✓ |
| Commit, branch, merge, tag, rollback | · | ✓ | ✓ | ✓ |
| Create / edit / run evals | · | ✓ | ✓ | ✓ |
| Promote to envs · approve / reject | · | · | ✓ | ✓ |
| Manage provider keys + API keys | · | · | ✓ | ✓ |
| Manage members & invites | · | · | ✓ | ✓ |
| Promote Admins / rename team | · | · | · | ✓ |
TypeScript SDK
The official JavaScript / TypeScript SDK wraps the REST API with a small, typed surface, an in-memory cache, and a render() helper for variable interpolation. Requires Node 18+ (uses globalfetch).
lexem-sdk is published on npm at 0.1.x — beta while the wire format stabilizes. Pin a specific version in production.Install
npm install lexem-sdk
# or
pnpm add lexem-sdk
# or
yarn add lexem-sdkCreate a client
import { createClient } from "lexem-sdk";
const lexem = createClient({
apiKey: process.env.LEXEM_API_KEY!,
baseUrl: "https://api.lexem.site", // optional; default shown
cacheTtlMs: 30_000, // optional; default 30s, 0 to disable
});Get a prompt
const p = await lexem.get("summarizer", { env: "production" });
// {
// content: "You are a helpful summarizer...",
// versionId: "clxa...",
// variables: [{ name: "article", type: "string", default: null }],
// env: "production",
// fetchedAt: "2026-05-17T08:00:00Z"
// }Omit env to fetch the prompt's current main-branch head — useful in early development before you start promoting.
Render variables
const filled = await lexem.render(
"summarizer",
{ article: "Q4 results showed..." },
{ env: "production" },
);
// returns the prompt string with {{article}} substitutedMissing variables throw by default. Pass { missing: "leave" } to keep placeholders, or { missing: "empty" } to substitute an empty string.
Caching
Results are cached in memory for 30 seconds per (slug, env) pair by default. Disable globally with cacheTtlMs: 0 in createClient, or bypass for a single call with fresh: true:
await lexem.get("summarizer", { env: "production", fresh: true });
// Or drop all entries (useful in tests):
lexem.clearCache();Errors
import { LexemError } from "lexem-sdk";
try {
await lexem.get("does-not-exist");
} catch (e) {
if (e instanceof LexemError) {
console.error(e.status, e.message);
// 404, 'Prompt "does-not-exist" not found in this project.'
}
}REST API
If you're not on Node/TypeScript, hit the REST API directly. One public endpoint today — the same one the SDK calls.
Base URL
https://api.lexem.siteAuthentication
Bearer token in the Authorization header. The key is the full lxm_... string from API keys.
curl https://api.lexem.site/v1/prompts/summarizer?env=production \
-H "Authorization: Bearer lxm_your_key_here"For quick debugging only, ?api_key= is also accepted as a query parameter. Don't use this in production — it ends up in access logs and browser history.
GET /v1/prompts/:slug
Fetches the active version of a prompt.
Query params:
env (optional) Environment name: "dev", "staging", "production",
or any custom env. Omit to fetch the current
main-branch head.
Returns 200:
{
"content": "You are a helpful summarizer...",
"versionId": "clxa...",
"variables": [{ "name": "article", "type": "string", "default": null }] | null,
"env": "production" | "current",
"fetchedAt": "2026-05-17T08:00:00Z"
}
Errors:
401 Missing or invalid API key (or key revoked).
404 Prompt not found in this project, or no active version
in the requested environment.Self-hosting
Lexem is open-source under MIT. Full setup instructions live in the repository README. The short version:
git clone https://github.com/rudra016/lexem
cd lexem
pnpm install
pnpm --filter @lexem/db migrate
pnpm devYou'll need:
- Node 20.6+ and pnpm 9+
- A PostgreSQL database
- At least one provider API key (OpenAI / Anthropic / Google) to run evals or generate AI change summaries
When self-hosting, point the SDK at your deployment with baseUrl: "https://lexem.your-domain.com".
FAQ
How is this different from LangSmith or PromptLayer?
Both observe LLM calls; neither treats prompts as versioned source code. Lexem focuses on the artifact: real diff/branch/merge/rollback, eval-gated promotions, and an SDK that returns the active version per environment.
Are my prompts and provider keys safe?
Provider API keys are encrypted at rest with AES-256-GCM, scoped per team, and never returned to the client in plaintext. Lexem API keys are stored as SHA-256 hashes, prefixed in the UI for identification, and revocable.
Can I run evals against my own model deployment?
Today the runner supports OpenAI, Anthropic, and Google. A custom base-URL / endpoint shim is on the roadmap.
Does the SDK work in the browser / Edge runtime?
Anywhere global fetch is available — Node 18+, modern browsers, Cloudflare Workers, Vercel Edge. That said, putting an API key in browser JS exposes it; prefer fetching prompts server-side and shipping the rendered text to the client.
Where do I report bugs or request features?
GitHub Issues. Security disclosures: email rudra619kumar@gmail.com directly.
