Alternative installation methods: uv, npm, and direct curl to the API
The previous lesson walked through the main route with venv + pip, but TypeSafe SDK installation has more than one path: the official quick start also provides three alternative methods—uv, the JavaScript / TypeScript SDK, and direct Bash access. This lesson walks through all three—after 8 minutes, pick one based on your project.
What you'll be able to do after this lesson
- Complete TypeSafe SDK installation with two uv commands, and lock dependencies into
uv.lock - Install the Jev JavaScript SDK with npm (
@typesafe-ai/sdk) and run the same ticket three-question example as the previous lesson - Run the same code directly with TypeScript (Node.js 22.18+)
- Connect directly to the TypeSafe API with curl 7.76+, and see the raw JSON response in a single line
- State the version requirements of each of the three routes and pick the one that fits your project
Your current dilemma
- The project already uses uv; going back to venv + pip feels like a step backward
- Our tech stack is all Node / TypeScript; setting up an entire Python environment for one API call isn't worth it
- Haven't decided which SDK to use yet, and want to see what the API actually returns with curl before getting started
When to use this approach
When you need to:
- Add Jev to a uv-managed Python project
- Call Jev in a Node.js, frontend, or TypeScript project
- Make direct HTTP calls in any language (to verify results first, or when there's no official SDK for your language)
And you don't want to:
- Manually run
python3 -m venvthenpip install - Install an entire SDK just for one curl request
🎒 Preparation before starting
- Completed Installation and your first request — this lesson reuses the same
route_ticket.pyexample - A Key is in your environment variables:
echo $TYPESAFE_API_KEYcan print it in the terminal (if not, go back to Sign-up and API Key) - Prepare according to the route you'll take: Python 3.10+ (uv route), Node.js 22+ (npm route), curl 7.76+ (curl route)
- Use Bash on the command line (macOS / Linux / Windows WSL — the official quick start commands are written for this environment)
Core idea
The "write code to send your first request" section of the official quick start provides four tabs: Python, JavaScript, TypeScript, and Bash. The previous lesson covered the Python venv + pip route; this lesson completes the rest.
Comparison of the three alternative routes:
| Route | What gets installed | Version requirements | Who it's for |
|---|---|---|---|
| uv | typesafe-sdk (same package as pip) | Python 3.10+, requires uv installed | Python projects already using / wanting to use uv |
| npm | @typesafe-ai/sdk | Node.js 22+ (TS direct run 22.18+) | Node, frontend, and TypeScript projects |
| curl | Nothing to install | curl 7.76+ | Any language; quick verification of API responses |
All three routes call the same endpoint: POST https://api.typesafe.ai/v1/systemone, and authentication is always Authorization: Bearer <Key>. The only difference is "who wraps the HTTP for you".
The SDK does three things for you that you must do yourself with direct access:
| Task | What the SDK does | With direct curl |
|---|---|---|
| Authentication | Reads TYPESAFE_API_KEY automatically | Write -H "Authorization: Bearer $TYPESAFE_API_KEY" by hand |
| Rate-limit retries | 429 / 529 retries with exponential backoff by default | Write it yourself (the official errors page explicitly requires exponential backoff) |
| Request/response format | Status and questions wrapped in classes, answers retrieved by ID | Build and parse the JSON yourself |
Follow along
The three routes are independent of each other; just pick one based on your language (see the comparison table above); the step numbers are continuous for easy cross-referencing.
Step 1: Complete TypeSafe SDK installation with uv
Why: uv is an installation method listed alongside pip in the official quick start—two commands create the project + install the SDK, and it also automatically generates a cross-platform lockfile uv.lock.
First check whether uv is available:
uv --versionIf it isn't installed, use the official install script (macOS / Linux):
curl -LsSf https://astral.sh/uv/install.sh | shInstall the SDK. Run in the project directory:
uv init --bare
uv add typesafe-sdkYou should see: neither command errors; the second outputs something like Installed 14 packages, with a line typesafe-sdk==0.7.5 in the list (the version number is whatever you installed). The directory now contains .venv/ and uv.lock, and the dependencies section of pyproject.toml gains "typesafe-sdk>=0.7.5".
- The project directory now contains
.venv/anduv.lock
Run the example from the previous lesson—no need to activate any environment:
uv run route_ticket.pyYou should see: the exact same three lines as the previous lesson:
technical
0.67
0.99route_ticket.py is the official example from the previous lesson; just copy it over as-is (the code is in Installation and your first request).
Three details about the uv route (based on the official uv documentation):
| Detail | Explanation |
|---|---|
uv init --bare | Only generates pyproject.toml, without creating a src directory, README, or git—enough for a quick start |
.venv | uv manages the virtual environment alongside pyproject.toml; uv run automatically keeps it in sync with the lockfile. Using .venv/bin/python route_ticket.py directly also works |
uv.lock | Locks exact versions; the official recommendation is to put it under version control so the same set of dependencies can be reproduced on another machine |
Step 2: Install the Jev JavaScript SDK with npm and get the example running
Why: The JavaScript / TypeScript SDK is one of the official two client SDKs, uses the same "state + questions" style as the Python SDK, and answer types can even be automatically inferred from the question definitions.
First check the Node version—there's an easy pitfall here:
node --versionThe JavaScript tab of the official quick start says "Node.js 20 or later," but that reflects an older version: the SDK documentation page and changelog both say Node.js 22 or newer (breaking change starting from v0.7.0), and the npm package's engines field is >=22. Go with 22+ (see the pitfall reminder below for details).
Install the SDK:
npm install @typesafe-ai/sdk
npm pkg set type=moduleThe second command marks the project as an ES module—the official example uses import syntax, and without this setting Node will error on loading.
You should see: the first command outputs added 1 package; the directory now contains node_modules/, package.json, package-lock.json. package.json contains "@typesafe-ai/sdk": "^0.7.0", and after the second command runs there's also "type": "module".
Use an editor to create route_ticket.js and paste in the official example:
import { choice, noul, score, TypeSafeClient } from "@typesafe-ai/sdk";
const ticket = "Our API integration started returning 500 errors on every request about 20 minutes ago, and we can't process any customer orders until this is fixed.";
const client = new TypeSafeClient({ defaultModel: "jev-latest" }); // Reads TYPESAFE_API_KEY.
const response = await client.systemOne({
state: ticket,
questions: {
department: choice("Which team should handle this", {
billing: "Payment or subscription issues",
technical: "Bugs or integration problems",
sales: "Pricing or account questions",
}),
frustration: score("How frustrated the customer appears", [
"Calm, just stating facts",
"Frustrated but civil",
"Very angry, strong language",
]),
is_urgent: noul("The message conveys urgency or time-sensitivity"),
},
});
console.log(response.answers.department.choice);
console.log(response.answers.frustration.score);
console.log(response.answers.is_urgent.noul);Run it:
node route_ticket.jsYou should see: three lines of output, consistent with the Python route:
technical
0.67
0.99node route_ticket.jsprintedtechnical/0.67/0.99
Run directly with TypeScript (optional)
Save the same code as-is into route_ticket.ts (identical in content to route_ticket.js), then simply:
node route_ticket.tsYou should see: the same three lines of output. This requires Node.js 22.18 or higher (which supports running TypeScript directly); moreover, the SDK infers answer types from your question definitions—the type of response.answers.department.choice is "billing" | "technical" | "sales", and editor autocompletion works right away.
Step 3: Call the TypeSafe API directly with curl
Why: The Bash example in the official quick start is precisely "a complete HTTP request without the SDK." Any language that can send HTTP (Go, Java, Rust...) can call Jev following this structure; it's also good for verifying the API response before deciding whether to install an SDK.
Use an editor to create route_ticket.sh and paste in the official example:
#!/usr/bin/env bash
set -euo pipefail
: "${TYPESAFE_API_KEY:?Set TYPESAFE_API_KEY first.}"
curl --fail-with-body --silent --show-error \
https://api.typesafe.ai/v1/systemone \
-H "Authorization: Bearer $TYPESAFE_API_KEY" \
-H "Content-Type: application/json" \
--data-binary @- <<'JSON'
{
"state": "Our API integration started returning 500 errors on every request about 20 minutes ago, and we can't process any customer orders until this is fixed.",
"model": "jev-latest",
"questions": {
"department": {
"type": "choice",
"instructions": "Which team should handle this",
"criteria": {
"billing": "Payment or subscription issues",
"technical": "Bugs or integration problems",
"sales": "Pricing or account questions"
}
},
"frustration": {
"type": "score",
"instructions": "How frustrated the customer appears",
"criteria": [
"Calm, just stating facts",
"Frustrated but civil",
"Very angry, strong language"
]
},
"is_urgent": {
"type": "noul",
"instructions": "The message conveys urgency or time-sensitivity"
}
}
}
JSONRun it:
bash route_ticket.shYou should see: one whole line of raw JSON—unlike the three values printed by the SDK route, this is the complete response, containing the confidence and probabilities of each answer, plus usage metering. The formatted full sample is in "Reading the Full Response" in the previous lesson; field meanings are not repeated here: Installation and the First Request.
If no key is set, the script intercepts it locally first: route_ticket.sh: line 3: TYPESAFE_API_KEY: Set TYPESAFE_API_KEY first.—that's the purpose of the guard on line 3 at the top, so an empty key isn't sent to the server for nothing.
bash route_ticket.shprinted one whole line of JSON containing"answers"
Two reminders for the curl route:
| Item | Explanation |
|---|---|
--fail-with-body | Requires curl 7.76+: on HTTP errors the exit code is non-0, but the response body is still printed, making it easy to see what went wrong |
429 / 529 | With the direct-connection approach you have to write your own exponential backoff retries (the SDK includes them by default); if you receive one, take a look at the response body, wait a bit, and send again |
Checkpoint ✅
- uv route: all three of
.venv/,uv.lock,pyproject.tomlare present, anduv run route_ticket.pyoutputs three lines of answers - npm route:
package.jsoncontains both"@typesafe-ai/sdk"and"type": "module" - curl route:
bash route_ticket.shoutputs one whole line of JSON containinganswers - I know all three routes call the same endpoint:
POST /v1/systemone - I know the version requirements: Python 3.10+ / Node 22+ (direct TS run 22.18+) / curl 7.76+
Pitfall Reminders
❌ npm pkg set type=module reports npm error code ENOENT
Symptom: running npm pkg set type=module directly in an empty directory reports Could not read package.json ... no such file or directory.
Cause: npm pkg set is a command that modifies package.json, and it will not create the file for you if it doesn't exist in the directory yet (verified with npm 10.9.7).
Correct approach: follow the official order—first npm install @typesafe-ai/sdk (npm will generate package.json automatically), then npm pkg set type=module.
❌ node reports Warning: Failed to load the ES module
Symptom: run node route_ticket.js, and it reports Failed to load the ES module: route_ticket.js. Make sure to set "type": "module" in the nearest package.json file or use the .mjs extension.
Cause: the project is not marked as an ES module (or was set to commonjs), while the official example uses import syntax.
Correct approach: npm pkg set type=module; or, following the error message's hint, rename the file extension to .mjs.
❌ You set up the environment per "Node 20+", but installed the new SDK version that requires 22+
Cause: the official quickstart page's JavaScript tab says "Node.js 20 or later", but the SDK's current latest version on npm (0.7.0) has an engines of node >=22, and the changelog for v0.7.0 explicitly lists a breaking change: "require Node.js 22 or newer". Historically, only 0.5.7 / 0.6.0 supported 20+—the quickstart sentence refers to the old versions.
Correct approach: check first with node --version; if it's below 22, upgrade Node, or switch to this lesson's uv or curl route.
❌ After uv add, running python route_ticket.py still gives ModuleNotFoundError
Cause: the python in your system is not the environment managed by uv; the SDK was installed in the project's .venv.
Correct approach: follow the official example and consistently use uv run route_ticket.py (it first ensures .venv is in sync with the lockfile); or explicitly specify .venv/bin/python route_ticket.py.
Advanced tips (optional)
The SDK also handles compression for you. The Python SDK uses zstd to compress request bodies by default starting from v0.7.5; the JavaScript SDK automatically compresses request bodies over 1 KiB on Node 22.15+, and you can pass compression: false to disable it. Python 3.13 and earlier require explicitly adding extras: uv add "typesafe-sdk[zstd]" (there are also http2 and pil extras).
Languages without an official SDK. Go, Java, Rust… just send POST /v1/systemone following the structure from step 3; the field definitions for state / questions / answers are in the API quick reference.
Before taking the direct-connection route to production. Wrap exponential backoff retries for 429 / 529 into your HTTP client—this is an explicit requirement from the official errors page for direct-connection users; the SDK route doesn't need to worry about this.
Lesson summary
✅ uv route: uv init --bare + uv add typesafe-sdk, two commands to complete the TypeSafe SDK installation, then run directly with uv run.
✅ npm route: npm install @typesafe-ai/sdk + npm pkg set type=module, getting the same example running in both JavaScript and TypeScript (runs directly on 22.18+).
✅ curl route: --fail-with-body connects directly to the API, showing the full raw JSON with confidence and probabilities.
Key configuration fields recap:
| Field | Recommended value | Description |
|---|---|---|
| Python package name | typesafe-sdk | Same package for pip / uv; uv add typesafe-sdk |
| JS package name | @typesafe-ai/sdk | Install via npm; requires Node 22+ starting from v0.7.0 |
| ES module | type=module | Official JS / TS examples use import, so this must be set |
| Endpoint | POST https://api.typesafe.ai/v1/systemone | Shared by all three routes |
| Authentication | Authorization: Bearer <Key> | Added automatically by the SDK; curl uses $TYPESAFE_API_KEY |
| Environment variable | TYPESAFE_API_KEY | Shared by all three routes; no keys written in code |
Next lesson preview
All three routes are working, and there's one final safety net left: What to do when installation or running fails: initial troubleshooting.
You will learn:
- How to pinpoint whether the environment, key, or network is the problem in 60 seconds using 3 commands
- What the
400/401/413/422/429status codes each mean and how to handle them - Which errors the SDK retries automatically (
429/529), and how to write it yourself when connecting directly - How to include the
request_idand minimal reproduction code when asking about an error