# Migrate from Jev

layahost implements `POST /v1/systemone` and `GET /v1/models` with the same requests, responses and errors as TypeSafe AI's Jev API. Existing code keeps working after you change two settings: the base URL and the API key.

## 1. Create a layahost key

Sign up, add credit on the [Billing](https://layahost.com/billing) page, and create a key on the [API keys](https://layahost.com/keys) page. See [Authentication](https://layahost.com/docs/authentication).

## 2. Point your client at layahost

Both official SDKs, `typesafe-sdk` for Python and `@typesafe-ai/sdk` for JavaScript, read their configuration from the same environment variables. Set them and run your code unchanged:

Environment

```bash
export TYPESAFE_BASE_URL=https://layahost.com
export TYPESAFE_API_KEY=lh_...
```

Or pass the same settings to the client constructor: `base_url` in Python, `baseURL` in JavaScript.

Jev SDK on layahost

```python
import os
from typesafe_sdk import Choice, TypeSafeClient

with TypeSafeClient(
    base_url="https://layahost.com",
    api_key=os.environ["LAYAHOST_API_KEY"],
) as client:
    response = client.system_one(
        state={"document": "I was charged twice. Please fix this ASAP."},
        questions={
            "category": Choice(
                instructions="What is this ticket about?",
                criteria={"billing": None, "technical": None, "other": None},
            ),
        },
    )

print(response.choices["category"].choice)
```

Jev SDK on layahost

```javascript
import { choice, TypeSafeClient } from "@typesafe-ai/sdk";

const client = new TypeSafeClient({
  baseURL: "https://layahost.com",
  apiKey: process.env.LAYAHOST_API_KEY,
});

const response = await client.systemOne({
  state: { document: "I was charged twice. Please fix this ASAP." },
  questions: {
    category: choice("What is this ticket about?", {
      billing: null,
      technical: null,
      other: null,
    }),
  },
});

console.log(response.answers.category.choice);
```

If you call the HTTP API directly, replace `https://api.typesafe.ai` with `https://layahost.com`. The paths stay the same.

Tested with

typesafe-sdk

0.7.1 for Python and

@typesafe-ai/sdk

0.6.0 for JavaScript:

system_one

/

systemOne

with all three question types,

models.list()

, request IDs, and the SDKs' authentication and validation errors.

## 3. Check your thresholds

layahost answers with Laya, a different model from Jev. Answers have the same shape, but the probabilities will not match what Jev returned. If your code compares a probability or `noul` value with a threshold, re-check that threshold on a sample of your own data before you switch production traffic.

## What stays the same

- Paths: `POST /v1/systemone` and `GET /v1/models`, with no `/api` prefix.
- The request body: `state`, `model` and a `questions` map with `noul`, `choice` and `score` questions. `instructions` is optional, as in Jev.
- The answer fields: `choice` and `probabilities`; `noul`; `score`, `legend` and `probabilities`; plus `usage`.
- Model names: `jev-latest`, the SDKs' default, and any other `jev-*` name are accepted and answered by `laya-auto`.
- Request IDs: every response has an `x-typesafe-request-id` header, so `response.request_id` in Python and `requestId` in JavaScript work as usual. The same ID is in `x-request-id`, in `meta.request_id` and in [Logs](https://layahost.com/logs).
- Errors: `{"detail": {"error_type": ..., "message": ...}}`, and FastAPI-style lists for `422`. The SDKs raise their usual exception classes, and their automatic retries on `429` and `5xx` honour our `retry-after` header.

## What is different

| Area | On layahost |
| --- | --- |
| Response `model` | Names the Laya checkpoint that answered, `laya-english` or `laya-multilingual`, even when you ask for `jev-latest`. Update any code that checks this field. See [Models](https://layahost.com/docs/models). |
| Extra response fields | A `meta` object (checkpoint, cache hit, latency, request ID, decisions, and the cost as `cost_micros` and `cost_usd`), and `confidence` on yes/no answers. The SDKs ignore fields they don't know. |
| Extra request fields | `lang` (a language hint) and `cache` (default `true`). Both are optional. See [POST /v1/systemone](https://layahost.com/docs/systemone). |
| `usage.output_tokens` | Always `0`: Laya picks between your options and generates no text. |
| Limits | Up to 32 questions per request, 32,000 characters of state and 2 to 10 levels per `score` question. Jev allows up to 255 options per `choice` question; on layahost all option names and descriptions of one question must fit in 192 tokens, roughly 100 to 150 short options, or you get `422`. The rate limit is 600 requests per minute per key. |
| Billing | Prepaid credit at $5 per million decisions, where one decision is one answered question. An empty balance returns `402` with `error_type` `insufficient_credits`. See [Pricing & billing](https://layahost.com/docs/pricing). |

## Read request IDs and meta with the SDKs

The SDKs' own request-ID accessors work, on responses and on errors (`request_id` in Python, `requestId` in JavaScript). Quote the ID when you [contact support](mailto:support@layahost.com). The `meta` object is a layahost extension that the SDKs don't model, but you can read it from the raw response:

Request ID and meta

```python
from typesafe_sdk import Noul, TypeSafeClient

with TypeSafeClient() as client:
    response = client.system_one(
        state="Where is my parcel? It was due on Monday.",
        questions={"shipping": Noul(instructions="Is this about a delivery?")},
    )

print(response.request_id)
print(response.raw_http_response.json()["meta"])  # layahost extension
```

Request ID and meta

```javascript
import { noul, TypeSafeClient } from "@typesafe-ai/sdk";

const client = new TypeSafeClient();

const { data, requestId } = await client
  .systemOne({
    state: "Where is my parcel? It was due on Monday.",
    questions: { shipping: noul("Is this about a delivery?") },
  })
  .withResponse();

console.log(requestId);
console.log(Reflect.get(data, "meta")); // layahost extension, not in the SDK's types
```

## Send layahost extensions

Pass `lang` or `cache` through the SDKs like this. In Python, use `extra_body`. In JavaScript, extra properties on the request object are forwarded as they are; build the request as a variable so TypeScript accepts the extra fields.

lang and cache

```python
from typesafe_sdk import Noul, TypeSafeClient

with TypeSafeClient() as client:
    response = client.system_one(
        model="laya-auto",
        state="Ich möchte mein Abo kündigen.",
        questions={"cancel": Noul(instructions="Does the customer want to cancel?")},
        extra_body={"lang": "de", "cache": False},
    )

print(response.nouls["cancel"].noul)
```

lang and cache

```javascript
import { noul, TypeSafeClient } from "@typesafe-ai/sdk";

const client = new TypeSafeClient();

const request = {
  model: "laya-auto",
  state: "Ich möchte mein Abo kündigen.",
  questions: { cancel: noul("Does the customer want to cancel?") },
  lang: "de",
  cache: false,
};

const response = await client.systemOne(request);
console.log(response.answers.cancel.noul);
```

## Handle a low balance

The Jev SDKs have no special class for `402`. Python raises `TypeSafeAPIError` and JavaScript throws `APIError`, both with `status` set to `402`. Don't retry these: [top up](https://layahost.com/billing) instead. We also email you when your balance runs low; set the threshold in Settings → Data & alerts. See [Errors](https://layahost.com/docs/errors).
