# TypeFuse guides

How to build, test, version, deploy and call Decisions with TypeFuse. Each guide is also on its own:

- [Decisions and Definitions](https://typefuse.dev/guides/decisions.md): What a Decision is, and how a Definition describes its input, its context and the questions it answers.
- [Per-item questions](https://typefuse.dev/guides/per-item-questions.md): Ask a question once for each item in a list, in the same call.
- [The Definition assistant](https://typefuse.dev/guides/definition-assistant.md): Review a Definition for problems, accept suggested fixes, and describe a change in your own words.
- [Examples, Test sets and the Sandbox](https://typefuse.dev/guides/examples-and-test-sets.md): Worked Examples explain intent, Test sets are the evidence, and the Sandbox tries one input at a time.
- [Test runs and Evaluations](https://typefuse.dev/guides/evaluations.md): Run your Test sets against a draft or a version, read the evidence and compare two runs.
- [Datasets and Comparisons](https://typefuse.dev/guides/comparisons.md): Run two releases over the same real inputs and see where their answers differ before you ship.
- [Versions and releases](https://typefuse.dev/guides/versions.md): Publishing freezes a version with a semantic release, and callers choose the releases they accept.
- [Deploying, environments and approvals](https://typefuse.dev/guides/deployments.md): Deploy a release to an environment, ask for approval where it is required, and roll back.
- [Calling a Decision](https://typefuse.dev/guides/calling.md): Call a deployed Decision over HTTP, from the SDK or from the CLI, with your own TypeSafe key or TypeFuse's.
- [The Decision log and audit](https://typefuse.dev/guides/decision-log.md): Every answer a deployed Decision gives is recorded before it is returned; the records can be verified, exported and gathered into an evidence pack.
- [Notifications](https://typefuse.dev/guides/notifications.md): Deployment requests waiting for you, and notices as your plan's allowances run low.
- [Organisations and moving Decisions](https://typefuse.dev/guides/organisations.md): Organisations own Decisions, members and credentials, and a Decision can move between them.
- [Account security and two-factor](https://typefuse.dev/guides/account-security.md): Passkeys, an authenticator app and recovery codes, confirming it's you, and requiring two-factor.
- [Plans and usage](https://typefuse.dev/guides/plans.md): What each plan includes, where to see your usage and what happens at a limit.
- [Connecting an agent](https://typefuse.dev/guides/agents.md): Let Claude Code, Codex or claude.ai build, test and publish Decisions for you.
- [API reference](https://api.typefuse.dev/docs)

## Decisions and Definitions

What a Decision is, and how a Definition describes its input, its context and the questions it answers.

### What a Decision is

A Decision is one judgment your software asks for, such as "is this refund eligible?" or "which
team should handle this ticket?". It has a defined input, a set of questions and typed answers, and
it is versioned like code. Every Decision belongs to an organisation, and its identifier is
`organisation/decision`, for example `acme/refunds`.

Decisions are answered by TypeSafe, the AI model provider TypeFuse runs on. Its model, Jev, reads the
input and returns typed answers with probabilities rather than text. Each call to a Decision is a
single call to one pinned version of that model. A Decision is not a workflow or a chain of prompts:
it asks all of its questions together and returns their answers together.

### The Definition

A Decision is described by its Definition. You write it in YAML, or in JSON if you prefer; the two
are interchangeable, and TypeFuse keeps the source exactly as you wrote it. Open a Decision and
choose **Definition** to edit it.

```yaml
format: 3
model: jev-1.13.0
state_schema:
  type: object
  properties:
    request: { type: string, description: What the customer wrote }
    days_since_delivery: { type: number }
  required: [request, days_since_delivery]
  additionalProperties: false
context:
  policy: Refund within 30 days for unused items or anything defective.
questions:
  eligible:
    type: noul
    instructions: Is this request eligible for a refund under the policy?
    criteria:
      "true": Within 30 days and unused, or defective.
      "false": Outside 30 days with no defect, or used.
  reason:
    type: choice
    instructions: Which policy rule decides this request?
    criteria:
      within_window: Inside 30 days and unused.
      defective: Faulty or damaged in transit.
      unclear: The supplied facts do not settle it.
```

A Definition has five parts:

- `format` is always `3`.
- `model` names an exact TypeSafe model version, such as `jev-1.13.0`. A range or a bare model name
  is refused, so a Decision never changes model without a new version.
- `state_schema` is the JSON Schema for the input your software sends with each call. Every call is
  checked against it before anything is asked.
- `context` is optional. It holds the static rules and reference material that go with every call,
  such as a policy or a price list. It is versioned with the Decision, so changing it is a release.
- `questions` holds one or more questions, keyed by the answer's name. Names start with a lower-case
  letter and use lower-case letters, digits and underscores.

The model sees `{ context, input }`: your static context beside the caller's input.

### Questions and answers

There are three kinds of question, using TypeSafe's own names. TypeSafe calls a yes-or-no question a
`noul`, and its answer is the probability of yes.

| Type | Asks | `criteria` | Answer |
| --- | --- | --- | --- |
| `noul` | A yes-or-no judgment | Optional `"true"` and `"false"` descriptions | `noul`, the likelihood of yes, from 0 to 1 |
| `choice` | One of a set of options | A map of at least two option keys to their meaning | `choice`, with `probabilities` for every option and `confidence` |
| `score` | A position on an ordered scale | A list of at least two levels, lowest first | `score`, with the `legend` of levels, `probabilities` and `confidence` |

`instructions` say what to judge. A `noul` question needs instructions, criteria or both; a
`choice` or `score` question needs its criteria. A score's levels are written like this:

```yaml
urgency:
  type: score
  instructions: How urgently does a person need to inspect this request?
  criteria:
    - level: routine
      description: The facts and policy give a clear answer.
    - level: immediate
      description: There is active harm or fraud.
```

Answers come back exactly as TypeSafe returns them:

```json
{
  "eligible": { "type": "noul", "noul": 0.94 },
  "reason": {
    "type": "choice",
    "choice": "within_window",
    "probabilities": { "within_window": 0.9, "defective": 0.07, "unclear": 0.03 },
    "confidence": 0.9
  }
}
```

To ask the same question once for every item in a list, see
[Per-item questions](https://typefuse.dev/guides/per-item-questions).

### Drafts and publishing

Every Decision has one draft. You can save a draft whenever its source parses, even while it has
problems, so you can work on it in steps. Problems show in the editor with the exact path that needs
attention, and the [Definition assistant](https://typefuse.dev/guides/definition-assistant) suggests fixes.

Publishing is stricter. A draft publishes only when it compiles, and publishing refuses open-ended
schemas: every object needs its `properties` or a typed `additionalProperties` map (use
`additionalProperties: false` for an object with no fields), and every array needs the shape of its
`items`. Context and questions together must also fit within the model's input size.

You publish from **Versions**; see [Versions and releases](https://typefuse.dev/guides/versions). Callers keep getting the
deployed version until you publish and deploy the draft.

### Settings, moving and archiving

A Decision's **Settings** hold its name, description and category. The category is chosen by
TypeFuse from the Definition whenever it changes; **Recategorise** asks again. From Settings an owner
or admin can also move it to another organisation (see
[Moving a Decision](https://typefuse.dev/guides/organisations#moving-a-decision)) or archive it.

A Decision can't be deleted: its calls are sealed into the audit trail, and its versions, approvals
and evidence packs are the record an auditor asks for. Archiving takes it out of every environment
it serves (take it out of production first), off the Decisions list and off your plan's count, and
makes it read-only. Its history stays, and you can restore it from the Decisions list.

### Where to go next

- Try the draft on real input in the [Sandbox](https://typefuse.dev/guides/examples-and-test-sets#the-sandbox).
- Write [Examples and Test sets](https://typefuse.dev/guides/examples-and-test-sets) so the Decision can be checked.
- Let an agent write the Definition with you: [Connecting an agent](https://typefuse.dev/guides/agents).

## Per-item questions

Ask a question once for each item in a list, in the same call.

### When to use one

Some judgments apply to every item in a list: does each tender match the search, is each line of an
order allowed, which category does each message belong to. Instead of numbered copies of a question
(`match_1`, `match_2`…), add `each` to one question. It is asked once for every element of the list,
in the same call as the rest of the Decision, and answers with one native answer per element.

### Writing one

```yaml
format: 3
model: jev-1.13.0
state_schema:
  type: object
  required: [search, tenders]
  additionalProperties: false
  properties:
    search: { type: string }
    tenders:
      type: array
      maxItems: 25
      items:
        type: object
        required: [ref, summary]
        additionalProperties: false
        properties:
          ref: { type: string }
          summary: { type: string }
questions:
  match:
    type: noul
    each: input.tenders
    instructions: Would a supplier searching with `input.search` want to bid for `item`?
```

- `each` names a list in the input (`input.tenders`) or in the context (`context.categories`), as a
  plain dotted path.
- An input list must declare the shape of its `items` and a `maxItems` between 1 and 100. Between 10
  and 25 suits most lists. A list in the context is bounded by its own length, up to 100.
- In the instructions, `` `item` `` stands for the element being judged, and `` `item.summary` `` for
  one of its fields. Each copy of the question is told exactly which element it is about.
  `` `item` `` means nothing outside an `each` question, so it is refused there.

Any kind of question can be asked per item: `noul`, `choice` or `score`.

### The answer

The answer is a list of native answers in the same order as the elements:

```json
"match": [
  { "type": "noul", "noul": 0.93 },
  { "type": "noul", "noul": 0.03 }
]
```

An empty list answers `[]`. When every question is per item and every list is empty, the Decision
answers straight away and the run records no tokens.

In the SDK, `perItem(items, answers)` pairs each answer with the item it belongs to.

### Examples and Test cases

An expected answer for a per-item question is a list too, one entry per element. Use `null` for an
element you have no expectation about. Evaluations compare per-item answers item by item.

### Limits

The whole Decision can expand to at most 500 questions, and it must still fit the model's input size
when every list is at its `maxItems`. If it doesn't, the error names a `maxItems` that would fit.

Publishing always asks each copy with its own element. Test runs of a draft can also try the
`shared` expansion, which sends the list once and asks every copy against it; that option is in the
CLI (`typefuse evaluate --expansion shared`) and MCP, not in the web app yet.

The [Definition assistant](https://typefuse.dev/guides/definition-assistant) spots numbered copies of a question and
offers to turn them into one `each` question.

## The Definition assistant

Review a Definition for problems, accept suggested fixes, and describe a change in your own words.

### Opening it

The assistant sits beside the Definition editor. Choose **Assistant** in the editor's toolbar, or
switch the side rail from **Contract** to **Assistant**. It has three parts: Review, Assistant review
and Ask the assistant. **Esc** or the close button above it takes you back to the contract. It never publishes, deploys or approves anything; every change it proposes
goes into the draft only when you accept it.

### Review

Review looks for structural problems as you type, such as:

- numbered copies of a question that should be one [per-item question](https://typefuse.dev/guides/per-item-questions)
- fixed policy text sitting in the input schema that belongs in `context`
- a list with no `maxItems`
- a reference to an input field that doesn't exist, or an input field no question uses
- two questions that ask exactly the same thing

Where there is a clear fix, the finding shows the proposed change as a diff, whether it compiles and
what kind of release it would make. **Accept into draft** saves the change to the draft, and Undo
takes it back. If accepting would lose comments or formatting in your YAML, the finding says so first.

**Dismiss** offers **Not now**, which hides a finding for this visit, and **Not a problem**, which
hides it until that question changes.

### Assistant review

Some problems need judgment rather than a rule: vague criteria, options that overlap, a missing
option, a question that asks two things, instructions that contradict the context, or a question
that needs facts the input doesn't carry. **Review my Definition** has the assistant read each
question and look for these. Findings are marked **Likely** or **Possible**, and weaker ones
are grouped under **Worth a look**. After you edit the draft, the results are marked as out of date
until you choose **Review again**.

**Suggest a fix** writes a rewrite of just the part in question, then checks it again and tells you
whether it fixes the problem without introducing new ones. If it can't find a safe rewrite, it says
so rather than guessing. When you accept a fix to a deployed Decision, you can go straight to
comparing it with the live version.

If the assistant review isn't available on your deployment, the panel says so, and Review keeps working.

### Ask the assistant

Describe what you want in your own words, such as "Also flag messages that mention a chargeback",
and choose **Ask the assistant**. It proposes a change to your draft that you can accept or leave. The assistant works out what kind of change you mean (such as adding a judgment,
changing behaviour, explaining behaviour or restructuring) and proposes one next step. That
might be a change to the draft, a Test case or Example to add, or a run, Test run or Comparison to
start. **Try another** asks for a different proposal.

Save the draft before you ask, so the assistant works from what you see.

### What it reads

The assistant reads your Definition, your Examples and Test cases, and totals from your recent
runs. It does not read the inputs of individual recorded calls.

## Examples, Test sets and the Sandbox

Worked Examples explain intent, Test sets are the evidence, and the Sandbox tries one input at a time.

### Three ways to try a Decision

- **Examples** explain the Decision to people: worked inputs with the answers you intend.
- **Test sets** hold the cases a version is checked against: the evidence.
- **The Sandbox** runs one input you choose, right now, against the draft or a version.

Examples are never sent to the model and never change its answers. Only cases in a Test set count
as evidence in an [Evaluation](https://typefuse.dev/guides/evaluations).

### Examples

Open **Examples** in a Decision to add them. Each Example is an input that fits the state schema
plus the answer you expect to each question: `true` or `false` for a `noul` question, the option key
for a `choice`, and the level's position (counting from 0) for a `score`.

Three to five Examples that cover the ordinary case, the edge cases and the cases people disagree
about make a Decision easy to review. **Generate examples** suggests more from the Definition;
suggestions are marked **Suggested** until you approve them.

When an Example should count as evidence, choose **Add to Test sets**. It becomes a confirmed case in
each set you pick. If a case with the same input already exists, it is reused rather than copied.

### Test sets

Open **Test sets** to manage them. A Test set is a named group of cases with a purpose, such as
"Core" or "Edge cases", and a case can belong to several sets without being copied. Every Decision
has a **Core** set, the baseline used when no other set is chosen; it can't be deleted.

A case is either **Confirmed** or a proposal. Cases you write or promote from an Example are
confirmed. Generated cases start as proposals, so a person decides what the right answer is before
they count. When you run tests you choose whether to include proposals.

Good Test sets:

- cover each answer every question can give, not only the common ones
- include the inputs that caused trouble, so a fixed problem stays fixed
- keep the expected answer to what the policy says, not what the model said last time

A case records only the questions it has an expectation for; the others are not checked.

### The Sandbox

Open **Sandbox** to run the draft, or any published version, with the exact JSON your software will
send. Start from an input built from the state schema, or from one of your Examples, then edit it and
choose **Run**. The input is checked against the state schema first.

The Sandbox shows each answer with its probabilities and confidence. **Open the run record** shows
everything that was sent and returned. When a run is a good test of the Decision, **Save as a test
case** keeps it: its answers become the expected answers, and you can correct them.

Sandbox runs are kept under **Recent Sandbox runs**, apart from production calls.

## Test runs and Evaluations

Run your Test sets against a draft or a version, read the evidence and compare two runs.

### Test runs

A Test run asks a draft or a published version every case in the Test sets you choose, and keeps
every answer it gave. Open **Evaluations** in a Decision and choose **Run tests**. You pick:

- **Run**: the draft or any published version
- **Cases**: confirmed cases only, or proposals too
- **Test sets**: the sets to include (Core is ticked for you)
- **Cases per request**: 1, as in production, or several cases asked together to finish sooner

A Test run takes up to 200 cases. **Run tests** is available once a Test set has cases; see
[Examples, Test sets and the Sandbox](https://typefuse.dev/guides/examples-and-test-sets).

From the CLI, `typefuse evaluate <org/decision>` runs the tests and prints the Evaluation. Through
MCP, an agent uses `run_tests`.

### Test run evidence

A Test run's page lists every answer beside what each case expected, with the result, the returned
answer and its confidence (the probabilities show on hover). Its summary shows how many answers
matched, differed or failed, how long it took, and the model and plan that answered.

**Run again** repeats it, or runs only the failed cases, or the current draft, depending on what the
run was. **Export JSON** downloads the whole record. A Test run is never changed after it finishes.

### Reading an Evaluation

Every Test run gets an Evaluation as soon as it finishes: an analysis of the recorded answers
against the expected ones. It reads the Test run and asks the model nothing, so evaluating the same
run always gives the same result.

An Evaluation shows:

- how many cases agree, and the percentage
- agreement for each question
- exact matches, differences and errors
- which questions no case checks, so you can see gaps in your evidence
- the trend in agreement over recent runs

Answers match only when they are exactly equal: the same `noul` side, the same `choice` or the same
`score` level. Per-item answers are compared item by item.

A difference is a prompt to look again at the expected answer and the Definition, not an automatic
failure. An Evaluation says where the Decision and your expectations disagree; it doesn't decide
which one is right. There is no pass mark or threshold.

### Comparing two Evaluations

To see what changed between two runs, tick two in the list and choose **Compare**. The earlier run
is always "Before". The comparison shows the change in agreement and why it changed (a different
version, cases added or removed, or the same cases answered differently), then agreement per question,
the cases whose result changed and the cases that were only in one run.

Compare runs over the same Test sets when you want to judge a change to the Definition; otherwise the
difference in evidence explains part of the result.

To compare two versions on real production inputs rather than Test cases, use a
[Comparison](https://typefuse.dev/guides/comparisons).

## Datasets and Comparisons

Run two releases over the same real inputs and see where their answers differ before you ship.

### What a Comparison is for

A Comparison answers "what would change?" before you ship. It runs two targets over the same inputs
and shows, question by question, how their answers differ. Unlike a Test run, it needs no expected
answers, so it works on real traffic.

Each side, A and B, can be:

- **Recorded production answers**: what production actually said (side A, for replayed calls)
- **Live in production**: the release deployed there now
- **Draft**: the current draft
- any published version

### Datasets

A Dataset is a fixed set of inputs with no expected answers. Open **Evaluations**, then
**Comparisons**, then **Datasets**, and make one of two kinds:

- **Upload JSONL**: a file or pasted rows, one JSON object per line. Rows that aren't JSON objects
  are refused and listed by line number.
- **Snapshot production**: the inputs of recent production calls, over the days you choose, up to a
  number of calls. Owners and admins can make one, and the snapshot is recorded in the audit trail.
  Only calls whose Decision kept their inputs can be replayed. Relayed calls are left out, and
  calls whose input wasn't kept or is no longer stored (see
  [How long records are kept](https://typefuse.dev/guides/decision-log#how-long-records-are-kept)) are skipped and
  counted.

A Dataset never changes once made. Deleting it keeps the Comparisons made from it.

### Comparisons

Choose **Start a Comparison**. The same dialog opens from **What would change?** in the Definition
editor and **Compare with production** on Versions. Pick a Dataset (by default, **Replay recent
production calls**), how many rows to compare, and sides A and B.

Comparisons run in the background, and the page updates as they progress; you can cancel one.

The result shows:

- how many rows answer differently, and how many were compared, not accepted or failed
- for each question, the spread of A's and B's answers, and what changed from A to B
- the rows themselves, filtered to those that differ, failed or weren't accepted
- how each side performed: speed, tokens and spend, over the rows both answered

When A and B disagree on a row, you can decide which answer is right and add the row to Core as a Test
case, so the difference becomes evidence.

### Replaying production on your plan

Your plan sets how many rows a replay of production compares; past that, it compares a sample. With
your own TypeSafe key saved for the session (on your Account page), a replay compares every row. See
[Plans and usage](https://typefuse.dev/guides/plans).

From the CLI, `typefuse compare` starts a Comparison and `typefuse comparison <id>` shows one; through
MCP an agent uses `compare` and `get_comparison`.

## Versions and releases

Publishing freezes a version with a semantic release, and callers choose the releases they accept.

### Publishing

Publishing turns the draft into a version that never changes. Open **Versions** in a Decision. The
**Next release** panel shows the version number it will get, what kind of release it is and the list
of changes. Add a label if you like, then choose **Publish**.

A draft publishes only when it compiles (see [Decisions and Definitions](https://typefuse.dev/guides/decisions#drafts-and-publishing)),
and only when something has changed since the last version. Publishing is done by people, in the app,
the CLI (`typefuse publish`) or through an agent; deploy tokens can't publish.

A published version isn't live until you deploy it. See
[Deploying, environments and approvals](https://typefuse.dev/guides/deployments).

### Two numbers for every version

Every version has:

- a **revision**, a plain number counting up from 1 (the page `/:org/:decision/v/3` is revision 3)
- a **semantic release**, such as `1.4.0`, worked out from what changed

The revision identifies one exact version, for replaying or debugging a call. The semantic release
tells callers whether a new version is safe for them.

### How releases are classified

TypeFuse compares the new Definition with the last one and chooses the release type for you. The
first release is always `1.0.0`.

| Release | Called | When |
| --- | --- | --- |
| Major (`2.0.0`) | New contract | A caller could break: an input field removed or newly required, a type narrowed or changed, allowed values removed, `additionalProperties` closed, a question removed, a question's type changed, per-item `each` added, removed or changed, or the set of choice options or score levels changed |
| Minor (`1.5.0`) | Behaviour | Callers keep working but answers may differ: an optional input field added, a requirement relaxed, a new question, changed instructions or criteria wording, a different model, or changed context |
| Patch (`1.4.1`) | Patch | Only descriptions and other annotations, the README, or the source's formatting and comments changed |

Each version's page shows what changed, the Definition exactly as published, the execution plan the
runtime uses, and the evidence behind it.

### Accepting a range of releases

Callers say which releases they accept, the way package managers do:

| `accepts` | Means |
| --- | --- |
| `^1.2.0` | 1.2.0 or any later 1.x |
| `~1.2.0` | 1.2.0 or any later 1.2.x |
| `1` or `1.x` | any 1.x |
| `1.2` or `1.2.x` | any 1.2.x |
| `1.2.3` | exactly 1.2.3 |

Pre-release and build tags such as `1.2.0-beta` are not supported.

With `accepts`, the release the environment serves answers when it is in the range; otherwise the
newest deployed release in the range does. If nothing deployed matches, the call is refused with
`409 INCOMPATIBLE_VERSION` rather than answered by a release the caller didn't accept.

TypeFuse never falls back from one major line to another on its own: a caller that accepts `^1`
gets 1.x or an error, never 2.x. The SDK also takes a list, such as `["^2.0.0", "^1.4.0"]`, and
tries each in order; that is the caller accepting both contracts. Only use a list like that if your
code handles both answer shapes. The answer's `version` and `semanticVersion` say which release
answered.

A call can name an exact revision with `version` instead, for replaying or debugging; that revision
must be deployed in the environment. Use `accepts` in application code.

### A new major keeps the old line

When you deploy a new major release, the newest release of each earlier major line stays live beside
it. Callers that accept `^1` keep getting 1.x until they change to `^2`, while callers without a
range get the new release. So after deploying 2.0.0 over 1.4.2, an environment has two live
releases:

| Release | Serves |
| --- | --- |
| 2.0.0 | every caller by default, and callers accepting `^2` |
| 1.4.2 | only callers accepting `^1` |

Each environment card on the Deployments page lists every live release with its calls in the last
24 hours, so you can see whether anyone still uses an older line. Call it, the Versions page and
the Environments page show every live release too.

#### Retiring a line

When nobody needs an older line any more, choose **Retire this line** beside it on the environment's
card. The dialog says how many calls it answered in the last 7 days. Once it's retired, callers that
accept only that line get `409 INCOMPATIBLE_VERSION` from that environment; nothing else changes.
A retired line doesn't come back with later deployments.

Retiring follows the same rules as removing a Decision: where the environment requires approval, it
waits for an approver (a second person or the named approvers, where the environment asks for
them) and the line keeps serving until then. It is recorded in the history and the audit trail.

You can also retire the older lines as part of a deploy (the deploy dialog names them), or when you
remove the Decision from an environment.

## Deploying, environments and approvals

Deploy a release to an environment, ask for approval where it is required, and roll back.

### Environments

An environment is a place a Decision is served from, with its own runtime keys. Every organisation
starts with two:

- **Test**, for trying a release with your own software
- **Production**, which is marked as production; a deploy an agent asks for waits there for a
  person to approve it

Open **Environments** in the sidebar to see what each one serves and to change them. They are listed
in the order a release is promoted through them; you can add more (such as a staging environment),
reorder them, and choose for each:

- **How callers reach TypeSafe**: BYOK, where each call brings the caller's own TypeSafe key;
  Relay, where the caller's app sends the request to TypeSafe itself so its key never reaches
  TypeFuse; or Managed, where TypeFuse's key answers (see
  [Calling a Decision](https://typefuse.dev/guides/calling#byok-managed-and-relay) for the trade-offs)
- **Production**: whether it serves your live traffic
- **Approval**: which deploys wait for a person to approve them (see [Approvals](#approvals))
- **Who approves**: with approval on, named approvers, and whether a second person must always
  approve (see [Who approves](#who-approves))

An environment can be deleted only if it has never been used. On the Free plan you can deploy to
Test; production needs Founder, Scale or Enterprise. See [Plans and usage](https://typefuse.dev/guides/plans).

### Deploying a release

Open **Deployments** in a Decision. Each environment has a card showing the release it serves.
Choose **Deploy** and pick:

- **Release**: the version to serve
- **Canary** (optional): a second release that answers a share of calls, from 1% to 99%
- **Shadow** (optional): a release that answers alongside the live one and is recorded, but whose
  answers are never returned to callers (shadows don't run for relayed calls)
- **Goes live**: now, or at a set time
- **Note** (optional)

When a release has been live in an earlier environment, the card offers to **Promote** it to the next
one. A scheduled deployment can be cancelled before it goes live.

A deployment to a production environment warns you when the release has no Evaluation, or hasn't been
live in the environment before it in the promotion path. The warnings never block you: confirm to go
ahead, or cancel. `typefuse deploy --check` shows them without deploying.

### Approvals

Each environment has one approval setting:

- **Agent deploys only** (where a new Production starts): a deploy you click in the app goes live at
  once, and so does one from a CI deploy token. A deploy an agent asks for (through MCP, an app you
  connected, a personal access token, or the CLI signed in with one) waits for one click from a
  person, and the person it acts for can give it.
- **Every deploy**: every deploy waits for a person to approve it, your own and CI's included.
- **Off**: every deploy goes live at once.

Where a deploy waits, it becomes a request, and the current release keeps serving until an
approver puts the new one live. Removing a release works the same way.

Owners and admins approve. Under **Every deploy**, the person who made the request can't approve
it, unless they are the organisation's only owner or admin. Under **Agent deploys only**, the
person an agent acted for approves its request themselves. Either way the record notes that it
was self-approved. Approvers see the request in
their [notifications](https://typefuse.dev/guides/notifications) and choose **Approve** or **Reject** on the Decision's
Deployments page.

Approving is always done by a person in the app. Deploy tokens, personal access tokens and agents can
request a deployment but can never approve one. A request an agent made says so: "Requested by Claude
Code, acting for Tom". The request also says whether you can approve it, and why.

### Who approves

An owner or admin chooses who approves in the environment's **Edit** dialog (under
**Environments**). Both choices are off by default, so a solo founder is never blocked.

- **Named approvers.** Without names, any owner or admin (or a maintainer of a team that owns the
  Decision) approves. Choose people and only they approve there, whatever their role; viewers can be
  chosen too, for a compliance sign-off. Owners and admins can always change the list.
- **Require a second person to approve.** Nobody approves their own request, not even the
  organisation's only owner or admin. A request made by your agent (a personal access token or an
  app you connected) counts as yours, and so does one made by a deploy token you created. You won't
  be able to approve your own deployments there, including ones your agents request.

Without a second person, the only owner or admin still approves their own request (if they are
among the named approvers, where there are any).

If a choice would leave fewer than two people able to approve, the dialog says so and asks you to
confirm; invite someone or choose a second approver first if you can. A request that nobody can
approve says so on the Deployments page and in notifications. To unblock it, an owner or admin
changes who approves or turns the second person off, which is always allowed; the requester can
also withdraw it.

Turning the second person on or off and changing the approvers is recorded in the audit trail, and every
approved deployment records who approved it and, where an agent asked, which one.

### Live major lines

An environment serves one release by default, but it can have more than one live release. When you
deploy a new major, the release it replaces keeps serving callers whose `accepts` names its major
line. The deploy dialog says so before you confirm: "1.4.2 keeps serving callers who accept ^1 until
you retire it." Tick **Retire … with this deploy** to stop those lines at the same time; the dialog
then names the lines that will stop and how many calls they answered in the last 7 days.

Each environment card lists every live release, one row each, with its calls in the last 24 hours:

- **2.0.0 · default**: what callers get unless they ask for an older line
- **1.4.2 · for callers accepting ^1**: an older major line
- a canary with its share, and a shadow, which never answers

To retire an older line on its own, choose **Retire this line** under it. The dialog warns you if
the line answered calls in the last 7 days. Retiring needs the same approval as removing the
Decision: at once for someone who could approve it, otherwise a request that waits for an approver,
with the line still serving. Callers that accept only a retired line get `409
INCOMPATIBLE_VERSION`. See [Versions and releases](https://typefuse.dev/guides/versions#retiring-a-line).

### Rolling back

Rolling back is deploying: deploy the earlier release you want to serve. If you roll back from 2.x to
1.x, 2.x stays live as a line for callers that accept `^2`. See
[Versions and releases](https://typefuse.dev/guides/versions).

### Deploying from CI

A deploy token (`tf_deploy_…`, under **API keys** → **Deploy tokens**) lets a pipeline deploy, schedule
and cancel. Owners and admins create them; each is shown once. In an environment that requires
approval, a deploy token's deployment waits for an approver like anyone else's.

## Calling a Decision

Call a deployed Decision over HTTP, from the SDK or from the CLI, with your own TypeSafe key or TypeFuse's.

### What you need

- A deployed release in an environment; see [Deploying, environments and approvals](https://typefuse.dev/guides/deployments).
- A runtime key for that environment, from **API keys** → **Runtime keys**. Keys for a production
  environment start `tf_live_`, the others `tf_test_`. The key decides which environment answers.
  Each key belongs to an Application, so every call in the [Decision log](https://typefuse.dev/guides/decision-log) says
  which of your systems made it. A runtime key can only call Decisions; it can't edit, publish or
  deploy. Making runtime keys needs a plan that includes production.
- For a BYOK or Relay environment, your own key for TypeSafe, the AI model provider that answers
  every Decision.

### BYOK, Managed and Relay

Each environment says how callers reach TypeSafe (Environments → **How callers reach TypeSafe**):

- **BYOK** (bring your own key) is the default for new production environments. Each call carries
  your TypeSafe key, which is used for that call only and never stored. A call without one is
  refused with `400 TYPESAFE_KEY_REQUIRED`. A BYOK environment also accepts relayed calls.
- **Relay** is for a TypeSafe key that must never leave your own systems, because a policy, a
  contract or a regulator says so. TypeFuse prepares the exact request, your app sends it to
  TypeSafe with its own key and hands the response back, and TypeFuse checks it, records the run and
  answers as usual. Calls are a little slower, shadow releases don't run, replays of production leave
  these calls out, and the record marks each response as reported by your app. A Relay environment
  accepts only relayed calls; a plain call is refused with `409 RELAY_REQUIRED`.
- **Managed** means TypeFuse's own TypeSafe key answers, and its usage is billed. Environments that
  aren't production, such as Test, start as Managed, and so does production on Enterprise, where
  Managed is part of the contract. Relayed calls are refused with `409 ROUTE_NOT_ENABLED`.

Choose BYOK unless your key must never leave your systems: it is the simpler integration and the
faster call. In every case the request is validated, the release chosen and the answer recorded on
TypeFuse's servers, and the answer has the same shape.

### HTTP

```sh
curl https://run.typefuse.dev/v1/acme/refunds \
  -H "Authorization: Bearer $TYPEFUSE_KEY" \
  -H "X-TypeSafe-Key: $TYPESAFE_KEY" \
  -H "Content-Type: application/json" \
  -d '{"input": {"request": "Arrived damaged", "days_since_delivery": 12}, "accepts": "^1.0.0"}'
```

Leave out `X-TypeSafe-Key` for a Managed environment. The body takes:

- `input`: the state your Definition's `state_schema` describes
- `accepts`: the releases you accept, such as `^1.0.0` (see [Versions and releases](https://typefuse.dev/guides/versions#accepting-a-range-of-releases)), or
  `version`, an exact revision, but not both. TypeFuse never falls back to another major line on its
  own: a caller that accepts `^1` gets a 1.x release or `409 INCOMPATIBLE_VERSION`, never 2.x
- `subject` (optional): a stable id for the thing being decided, so a canary gives it the same
  release every time
- `reference` (optional): `{ "type": "order", "id": "A-1042" }`, your own record this call is about
- `correlationId` (optional): an id that ties the call to the rest of your request; the
  `X-Correlation-Id` header works too

The answer:

```json
{
  "runId": "run_…",
  "decision": "refunds",
  "identifier": "acme/refunds",
  "environment": "production",
  "version": 4,
  "semanticVersion": "1.2.0",
  "role": "primary",
  "answers": { "eligible": { "type": "noul", "noul": 0.94 } },
  "latencyMs": 412
}
```

`GET https://run.typefuse.dev/v1/acme/refunds` with the same key describes what is deployed: the releases, the
state schema and the questions.

Errors come back as `{ "code", "message" }`. The ones to handle:

| Status | Code | Means |
| --- | --- | --- |
| 400 | `INVALID_INPUT` | The input doesn't fit the state schema |
| 400 | `TYPESAFE_KEY_REQUIRED` / `TYPESAFE_KEY_NOT_USED` | A BYOK call without a key, or a Managed call with one |
| 401 | `UNAUTHORIZED` | The runtime key is missing or wrong |
| 402 | `PLAN_LIMIT` | Your plan doesn't serve this call; see [Plans and usage](https://typefuse.dev/guides/plans) |
| 404 | `NOT_DEPLOYED` / `VERSION_NOT_DEPLOYED` | Nothing, or not that revision, is deployed in the key's environment |
| 409 | `INCOMPATIBLE_VERSION` | No deployed release is in the range you accept |
| 429 | `RATE_LIMITED` | Your organisation has made its plan's calls for this minute, across all its keys; wait for `Retry-After` |
| 502 | `PROVIDER_ERROR` | TypeSafe couldn't answer; the call is still recorded |
| 503 | `NOT_RECORDED` | With "Record before answering" on: the call couldn't be recorded, so no answer was sent; try again |

Every call is recorded. Your answer is sent as soon as the model replies, and its record follows
within seconds; if storing it fails, TypeFuse keeps retrying from a second copy. For work that must
never give an answer without its record, owners and admins can turn on **Record before answering**
(Environments → Recording calls): each call then waits for its record, about a tenth of a second
more, and you get `503 NOT_RECORDED` instead of an answer if it can't be kept.

### SDK

`@typefuse/sdk` is a small client for the same HTTP API. It is not on npm yet; it will be published
with the move to the `typefuse.ai` domain.

```ts
import { createRuntime } from "@typefuse/sdk";

const typefuse = createRuntime({
  apiKey: env.TYPEFUSE_KEY,
  typesafeKey: env.TYPESAFE_KEY, // leave out for a Managed environment
});

const { answers } = await typefuse.decide(
  "acme/refunds",
  { request: "Arrived damaged", days_since_delivery: 12 },
  { accepts: "^1.0.0", reference: { type: "order", id: "A-1042" } },
);
```

`accepts` can also be a list, such as `["^2.0.0", "^1.0.0"]`, tried in order. That is your code
saying it accepts both contracts, so use it only when your code handles both answer shapes: a new
major can change the input, the questions or the answers. The result's `version` and
`semanticVersion` say which release answered; check them before reading the answers.

The SDK retries briefly when the runtime is busy or unavailable, and turns error codes into typed
errors such as `InvalidInputError`, `IncompatibleVersionError` and `PlanLimitError`. `noul()`,
`choice()` and `score()` read an answer and check its type, and `perItem()` pairs
[per-item answers](https://typefuse.dev/guides/per-item-questions) with their items.

For a Relay environment, add `route: "relay"`. The SDK then sends your TypeSafe key only to
TypeSafe, never to TypeFuse:

```ts
const typefuse = createRuntime({
  apiKey: env.TYPEFUSE_KEY,
  typesafeKey: env.TYPESAFE_KEY,
  route: "relay",
});

const { answers } = await typefuse.decide("acme/refunds", input, { accepts: "^1.0.0" });
await typefuse.flush(); // before your process exits
```

`flush()` waits for any completion still being retried in the background.

### Relay without the SDK

A relayed call is three requests. Never send `X-TypeSafe-Key` to TypeFuse on Relay.

1. `POST https://run.typefuse.dev/v1/acme/refunds/prepare` with your runtime key and the ordinary call body. It
   answers with a `token`, the release that will answer, and `call`: the `url`, `method`, `headers`
   and `body` to send to TypeSafe. When there is nothing to ask (every question is per item and every
   list is empty), `call` is `null` and the answers are already there.
2. Send `call` to TypeSafe with `Authorization: Bearer <your TypeSafe key>`, passing `call.body`
   exactly as given, without parsing or re-serialising it.
3. `POST https://run.typefuse.dev/v1/acme/refunds/complete` with `{ "token": …, "response": { "status", "body",
   "requestId", "sentAt", "receivedAt" } }`, where `body` is TypeSafe's response text as received
   and `requestId` its `x-typesafe-request-id` header. If TypeSafe couldn't be reached, send
   `{ "token": …, "response": { "error": "network", "message": … } }`. It answers in the ordinary
   shape.

The token can be used once and lasts 10 minutes; repeating the same completion is safe and gets the
same answer. A call counts once, when it is prepared, and a prepared call that is never completed
shows in the Decision log as abandoned. A response that doesn't fit the request is refused with
`502 PROVIDER_ERROR` and recorded as an error.

### CLI

The `typefuse` command calls a Decision the same way. Like the SDK, it isn't on npm yet.

```sh
typefuse call acme/refunds --key $TYPEFUSE_KEY --typesafe-key $TYPESAFE_KEY \
  --input '{"request": "Arrived damaged", "days_since_delivery": 12}' --accepts ^1.0.0
```

`--reference order:A-1042`, `--correlation` and `--subject` match the HTTP fields.

### MCP

Agents connected over MCP build, test, publish and deploy Decisions (see
[Connecting an agent](https://typefuse.dev/guides/agents)). They don't call deployed Decisions through MCP: your software
calls the runtime, as above. The full list of control-plane endpoints is in the
[API reference](https://api.typefuse.dev/docs).

## The Decision log and audit

Every answer a deployed Decision gives is recorded before it is returned; the records can be verified, exported and gathered into an evidence pack.

### Every call is recorded

When a deployed Decision answers, the answer goes back as soon as the model replies, and the record of
the call follows within seconds. If storing it fails, TypeFuse keeps a second copy and keeps
retrying, and our team is alerted. If your organisation must never give an answer without its
record, turn on **Record before answering** (Environments → Recording calls): each call then waits
for its record, and the caller gets `503 NOT_RECORDED` instead of an answer if it can't be kept.

A record keeps:

- which Decision, release, execution plan and environment answered, and whether it was the main
  release, a canary, a compatible line or a shadow
- which Application and runtime key called, and when
- the caller's `reference` and `correlationId`, when given
- the answers returned, and fingerprints of the input, the state sent to the model and the raw
  answers; the input itself only when the Decision keeps its calls' inputs (below)
- the model, timing and token counts, or the error

Sandbox runs, Test runs and Comparisons are recorded the same way, marked with where they came from.
A relayed call (see [Calling a Decision](https://typefuse.dev/guides/calling#byok-managed-and-relay)) is marked as
relayed, with the response reported by the caller's app, and a relayed call that was prepared but
never completed shows as abandoned.

### Finding a call

**Audit** in the sidebar opens the **Decision log**: every call across the organisation, newest first.
Search by run id, reference or correlation id (an exact match), and filter by Application, Decision,
environment, status and period.

Inside a Decision, under Monitor, **Runs** lists that Decision's calls wherever they ran, and **Insights** shows how
its answers are distributed over time.

A run's page shows the input, the state sent to the model, the raw answers and an **Audit** panel with
the Application, reference, correlation id and where the call came from.

To make calls easy to find later, pass a `reference` for the record in your own system that the call
is about, such as `{ "type": "order", "id": "A-1042" }`, and a `correlationId` that ties it to the
rest of the request. See [Calling a Decision](https://typefuse.dev/guides/calling#http).

### Sealed and verifiable

Within about a minute of a call, its record is sealed into the organisation's audit trail. Each
batch of records gets a seal over their hashes, and every seal joins one hash chain with the
organisation's changes, such as publishing, deploying and approving. Changing or removing a record
afterwards breaks the chain. For a relayed call, the seal includes the fact that the response was
reported by the caller: TypeFuse checks that the response fits the request, but can't prove
TypeSafe sent it.

A run's Audit panel shows **Seal verifies**, **Seal broken** or **Not sealed yet**, with the hashes of
its plan, input, state and answers. An owner or admin can check the whole trail:

```sh
typefuse audit verify acme
```

It reports any break, and exits with status 2 when the trail isn't intact. TypeFuse also verifies
every organisation's trail each night.

### Changes

**Change history**, beside the Decision log, lists every change in the organisation, newest first:
edits, publishes, deployments, approvals and access, who made them and how. Each Decision's **Audit**
page, under Monitor, shows the changes to that Decision.

### Exports and evidence packs

Owners, admins and viewers can take the audit trail away as files. **Export** on the Decision log
downloads the calls in a period as CSV (for spreadsheets) or JSON (whole records), starting from the
filters on the page: a Decision, environments and a period. Each row has the call's id and time, the
Decision and release that answered, the environment, the Application and runtime key that called,
the caller's reference and correlation id, the answers and how sure each was, the tokens, the
latency, the seal and the deployment that served it. Tick **Include each call's input** to add the
input and the state sent to the model; a call that didn't keep its input says `not kept` there, and
one past your plan's retention says `no longer stored`; its hashes and seal are still in the row.

Exports and evidence packs are part of the paid plans (a trial included); on Free the Decision log, the
Change history and their verification are all here to read.

**Export** on the Change history downloads the changes in a period: versions, deployments, approvals
with who asked, who approved and whether an agent or token asked on the person's behalf, access and
settings. Narrow it to a Decision or an environment the same way.

An **evidence pack** is one download for a Decision and a period, made from **Audit → Exports** or
from the Decision itself. It is a zip holding:

- `summary.pdf`: what the Decision decides in plain words, which releases were live in each
  environment and when, their Evaluations and calibration, the approvals and who gave them, the
  calls each release answered, and the seals that fix those calls' records, with how to check them
- `calls.csv` and `calls.json`, `changes.csv` and `changes.json`: the records the summary counts
- `evaluations.json` and `seals.json`
- `definitions/`: the Definition of every release involved, exactly as published
- `manifest.json`: the SHA-256 of every file, so a copy can be checked against the original

#### From a Decision

The same exports start from a Decision's own pages, already narrowed to it:

- **Runs**: **Export** downloads the calls it answered, starting from the environment you picked,
  and **Evidence pack** makes its pack for a period.
- **Audit**: **Export** downloads its history (versions published, deployments, approvals and who
  gave them), and **Evidence pack** as on Runs.
- **Versions**: **Export history**, the same as on its Audit page.

Choose the first and last day, and for calls and history an environment and CSV or JSON. What you
ask for opens under **Audit → Exports**, listed with that Decision's other exports; **Every export**
shows the whole organisation's again.

A small export downloads straight away. A larger one is prepared in the background: you get a
notification saying **Your export is ready**, and it waits under **Audit → Exports**. Files are kept
for seven days and then deleted; ask again for a fresh copy. Every export is recorded in the Change
history, with who asked and which filters. One export holds up to 50,000 calls or changes (5,000
with inputs); choose a shorter period for more.

From the command line, an agent or a script (a read-only token is enough):

```sh
typefuse export acme log --from 2026-09-01 --to 2026-09-30 --env production
typefuse export acme changes --format json --decision refunds
typefuse export acme/refunds evidence --from 2026-09-01 --to 2026-09-30
```

`--to` is the last day included. The command waits for a large export and saves the file under its
own name, or `--out`. The SDK has the same as `tf.exports` (`tf.exports.list(org, { decision })`
lists one Decision's), and agents connected over MCP have the `export_audit` and `get_export` tools
(`get_export` with `decision` lists that Decision's exports).

### How long records are kept

Every run stays in the log for the life of the organisation, with its answers, hashes and seal.

**A deployed Decision's calls don't keep their inputs** unless you ask: on Scale and Enterprise, an
owner or admin can keep inputs for every Decision (Settings → Security) or for one
(its Deployments page, "Calls' inputs"). A kept input is kept for the plan's retention: 90 days on
Scale, 365 days (or as agreed) on Enterprise. A call that didn't keep its input says so on its page;
the state it sent is rebuilt from the release and the input when the input is there.

| Where the call came from | Detail kept for |
| --- | --- |
| A deployed Decision, inputs not kept (the default) | Fingerprints only |
| A deployed Decision keeping inputs, on Scale | 90 days |
| A deployed Decision keeping inputs, on Enterprise | 365 days, or as agreed |
| The Sandbox, on every plan | 90 days |

Calls recorded before inputs became a setting keep their detail for the retention that applied
then (7 days on Free, 30 on Founder). Test runs, Evaluations, Test cases, Examples, Datasets and
Comparisons keep what they hold for as long as they exist, so a call's input that one of them still
uses is kept too.

Without its input, a run's page says the input wasn't kept (or, once its detail expires, that it is
no longer stored), and the answers and their proof stay with the run. Its seal still verifies,
because the hashes are kept, and `typefuse audit verify` still passes. A run without its input
can't be saved as a Test case or replayed in a Comparison.

TypeFuse backs up its data every day and keeps each backup for 30 days, after which it is deleted
automatically. Deleted data is removed from backups within 30 days: a call's expired detail, and
everything an organisation held once the organisation is deleted. The sealed copy of each call is
kept in locked storage for the plan's retention period from when it was recorded, then deleted
automatically; it can't be removed sooner, even when the organisation is deleted.

Expiry isn't switched on in production yet, so for now the detail of every call is still kept.

## Notifications

Deployment requests waiting for you, and notices as your plan's allowances run low.

### What you are told about

TypeFuse notifies you about three things:

- **Approval requests.** When someone asks to deploy or remove a release in an environment that
  requires approval, everyone who could approve it is notified. The notification goes away once the
  request is approved, rejected or cancelled, and it can't be dismissed while it waits. Where an
  environment's approval rules leave nobody able to approve a request, its owners and admins and
  the requester are told so instead, with how to resolve it. See
  [Deploying, environments and approvals](https://typefuse.dev/guides/deployments#approvals).
- **Plan usage.** Owners and admins are told when the organisation passes 85% and then 95% of an
  allowance that resets, such as the month's production calls. Each notice comes once a period and
  can be dismissed. See [Plans and usage](https://typefuse.dev/guides/plans).
- **Exports.** When a large export you asked for is ready (or couldn't be made), you are told, for
  as long as its file is kept. It can be dismissed. See
  [The Decision log and audit](https://typefuse.dev/guides/decision-log#exports-and-evidence-packs).
- **New members.** When someone accepts an invite you sent, you're told who joined and with which
  role. The notice stays for 30 days and can be dismissed. See
  [Inviting people](https://typefuse.dev/guides/organisations#inviting-people).

### The bell

The bell in the top bar shows notifications from every organisation you belong to, whichever one you
are in, and each names its organisation. Its count is approvals waiting for you plus notices you
haven't read. It opens the latest eight:

- **Review** opens the Decision's Deployments page, where you approve or reject. You never approve
  from the list itself.
- **Billing** opens Settings → Plan.
- **Members** opens the organisation's Members page.
- **Exports** opens Audit → Exports, where the file waits to be downloaded.
- **See all notifications** opens the full list, where **Mark all as read** clears the count.

In the organisation switcher, each organisation shows its own count, so you can see where something
is waiting.

Agents can read your notifications too, through MCP or `typefuse notifications`.

### Email

Approvers are emailed about requests in production environments, and owners about the 85% and 95%
notices. TypeFuse's email isn't switched on yet, so for now these notifications appear only in the
app, and nothing sent while email was off is sent later.

## Organisations and moving Decisions

Organisations own Decisions, members and credentials, and a Decision can move between them.

### Your personal space and organisations

Everyone has a **personal space**, named by their username: `@tom`, with Decisions such as
`@tom/refunds`. You choose the username the first time you sign in, and can change it on the
**Account** page from your menu. Your personal space is on the Free plan for as long as you like:
build, test and deploy to Test there, on your own.

An **organisation** is where a team publishes and runs Decisions in production. It has its own name
and identifier (the `acme` in `acme/refunds`), its own members and its own plan.

To make one, open the organisation switcher at the top of the sidebar and choose **New
organisation** (or **Create an organisation** on your personal space's Billing page). Give it a name,
check its identifier, choose Founder or Scale and continue to payment. Founder starts with a 14-day
trial if you haven't had one. You become the owner, and you can own up to ten.

Once the payment is confirmed, the organisation's **Welcome** page lets you choose Decisions from
your personal space to bring across (see [Moving a Decision](#moving-a-decision)). If you leave the
payment unfinished, the organisation waits on that page, marked **Payment not finished** in the
switcher, where you can pay or discard it. Nothing is charged until you pay, and an unpaid
organisation is removed after a day.

### Names

Settings → **General** holds the organisation's name, which owners and admins can change, and its
identifier, which owners can change. A changed identifier keeps working: links, keys and agents
that use the old one are sent to the new one.

A name has one owner. Your username and the identifiers of organisations you own can share a name
(`@acme` and `acme`), but nobody else can take it or one that reads the same, such as `acme`,
`ac-me` or `acrne`. A name you stop using stays yours rather than passing to someone else. Some
well-known names are held for their owners. If someone holds a name that's yours by right, such as
your company's name or trademark, write to us (see the [Terms](https://typefuse.dev/terms#names)).

### Members and roles

Settings → **Members** lists who belongs to the organisation. There are four roles:

- **Owners** manage everything, including other owners and the plan.
- **Admins** manage members, environments, Applications and deploy tokens, and approve deployments.
- **Members** build, test, run, publish and deploy Decisions.
- **Viewers** read everything: Decisions, drafts, versions, Test sets, Evaluations, Comparisons,
  Insights, the Decision log, the audit trail, deployments, approvals and members. They can't create,
  edit or run anything, publish, deploy, manage keys or tokens, or change settings or billing. Where
  an environment names a viewer as an approver, they approve and reject its deployment requests (see
  [Who approves](https://typefuse.dev/guides/deployments#who-approves)). A viewer's agents and connected apps only
  read in that organisation, whatever access they were given. Viewers count as members for your
  plan.

An organisation always keeps at least one owner. Anyone else can leave from the Members page.

Teams, for grouping members and sharing approvals, are coming later.

### Inviting people

Everyone joins an organisation by invite, whether or not they already use TypeFuse. Owners and
admins invite people from Settings → **Members**: enter their email and role under **Invite
someone**. Only an owner can invite another owner. How many members you can have depends on your
plan and its extra seats (see [Extra seats](https://typefuse.dev/guides/plans#extra-seats)), and waiting invites count
towards it.

Each invite has a link, shown as soon as you invite someone and again from **Copy link** on the
waiting invite. TypeFuse also emails the link once its email is switched on, which it isn't yet, so
for now copy the link and send it yourself.

Opening the link shows which organisation and role the invite is for. The invited person signs up
with the invited email address, or logs in if they already have an account, and joins straight
away. Someone new to TypeFuse also gets a personal space of their own, but they land in the
organisation that invited them. When they join, whoever invited them is notified.

An invite link:

- works only for the email address it was sent to. Someone signed in with a different account is
  told so and isn't joined; they can log out and continue with the invited email.
- works once. After it has been accepted, the link no longer does anything.
- lasts 14 days. An expired invite is marked on the Members page; **Resend** sends it again and
  gives the same link another 14 days.

**Cancel** withdraws a waiting invite, and its link stops working at once. Inviting, resending,
cancelling and accepting are all recorded in the organisation's Audit.

When you open TypeFuse without choosing an organisation, it opens the one you last used, or the one
you just joined.

### Keys and tokens

**API keys** in the sidebar holds the organisation's credentials, a tab for each:

- **Runtime keys** call deployed Decisions in one environment; see [Calling a Decision](https://typefuse.dev/guides/calling).
- **Applications** name the systems that call your Decisions, so the
  [Decision log](https://typefuse.dev/guides/decision-log) shows which one made each call. Every organisation has a
  **Default** Application.
- **Deploy tokens** let CI deploy; see [Deploying from CI](https://typefuse.dev/guides/deployments#deploying-from-ci).

Personal access tokens and connected apps belong to you rather than the organisation. Find them under
**Connect an agent**, then **Manage**. Owners and admins can also see and revoke members' connected
apps on the Members page.

### Moving a Decision

A Decision can move to another organisation, for example from your personal space to your
company's. A new organisation's Welcome page offers this for your personal Decisions; at any other
time, open the Decision's **Settings**, then **Move…** under **Move to another organisation**.
You need to be an owner or admin of both organisations, and the one it moves to needs a paid plan.

**What moves:** the Decision with its source, README, versions, Examples, Test sets, Test runs,
Evaluations, Datasets, Comparisons and deployment history. Environments it needs are created in the
new organisation.

**What stays:** recorded calls and the audit trail, usage and billing history, and the old
organisation's runtime keys, Applications and deploy tokens. The Decision's own pages still show all
its calls.

A move waits while a deployment is waiting for approval or a set time, or while a Comparison is
running. The dialog lists anything that stops it.

**Callers don't need to change anything.** The old identifier keeps working: it forwards
permanently, and its name stays reserved in the old organisation. The old organisation's runtime
keys keep working at the old identifier, and answers carry a `TypeFuse-Moved-To` header so you know
to update. The new organisation's keys work at either identifier, and calls count towards the new
organisation's plan.

Once your callers have moved to new keys, an owner or admin of the new organisation can choose **Stop
accepting keys** for the old identifier in the Decision's **Settings**; after that, old keys are refused with
`403 OLD_KEYS_STOPPED`.

There is no undo, but you can move a Decision back the same way.

### Deleting an organisation

An owner can delete an organisation at the bottom of Settings → **General**, by typing its
identifier. Its runtime keys stop working at once and everyone can only read it. After 7 days it is
deleted for good, with its Decisions, versions, recorded calls, test evidence, keys and audit trail.
Until then an owner can restore it from the same place.

An organisation can't be deleted while it has a paid subscription, while a Decision is live or
waiting to go live in one of its environments, or while another organisation runs one of its
Decisions as a companion. The page says which.

**Decisions that moved out keep working.** If a Decision moved from this organisation to another,
callers of its old identifier are still forwarded to it after the deletion, with the new
organisation's keys. The deleted organisation's own keys stop, like all its keys. Nobody can take
the deleted organisation's identifier while any such forward exists.

**Decisions that moved in are deleted with it.** Their old identifiers in the organisations they
came from stop answering. The delete dialog lists those identifiers and the keys that called them in
the last 30 days, and you confirm that you understand before it goes ahead.

### Deleting your account

Choose **Account** in your menu, then **Delete account** and type `DELETE`. Your personal space,
and every organisation where you are the only member, is deleted with your account: locked at once,
and deleted for good after 7 days. You leave every other organisation. Until then you can sign in
and restore your account from the same page, which restores those organisations too.

If you are the only owner of an organisation that has other members, make one of them an owner on
its Members page, or delete the organisation, before deleting your account.

## Account security and two-factor

Passkeys, an authenticator app and recovery codes, confirming it's you, and requiring two-factor.

### Two-factor

Two-factor means signing in, and making changes that matter, takes something you have as well as
something you know. TypeFuse offers two ways, and you can use both:

- **Passkeys.** Your device confirms it's you with your fingerprint, face or PIN. You can add
  several (a laptop, a phone, a security key), name them, and see when each was last used. A
  passkey also signs you in on its own, with no password.
- **An authenticator app.** An app such as 1Password, Google Authenticator or Authy shows a new
  six-digit code every 30 seconds. You can have one per account.

TypeFuse doesn't send codes by email or text message: both can be intercepted.

### Setting it up

Open **Account** from the menu under your name. Under **Two-factor**, choose **Add a passkey** and
follow your browser, or **Set up an authenticator app**, scan the QR code and type the code it shows.

When you add your first passkey or app, TypeFuse shows ten **recovery codes**. Each signs you in once
if you lose your devices. They're shown only this once: keep them in your password manager or
somewhere safe. You can make a new set at any time, which stops the old ones working.

After you sign up, TypeFuse offers to add a passkey straight away. You can skip it and come back to
Account later.

### Signing in

With two-factor on, signing in with your password or with Google asks for your passkey, a code from
your app or a recovery code next. On the log-in page, **Continue with a passkey** signs you in with the
passkey alone.

Once you have a passkey and your recovery codes, you can remove your password from Account and sign in
with the passkey only. **Forgot your password?** on the log-in page sets one again if you need it.

Repeated wrong codes lock the second step for a minute, then longer each time.

### Confirming it's you

Some changes ask you to confirm it's you again, with your passkey or a code, even though you're
signed in. Once you have, you're not asked again for ten minutes.

Your own account always asks before you delete it, change its two-factor or password, make a personal
access token or connect an app. An organisation's owners choose which of its actions ask, in Settings →
Members → **Security**:

- Billing and plan changes (on unless an owner turns it off).
- Creating API keys, deploy tokens and access tokens (on).
- Changing security settings and member roles, inviting people, and who approves deployments (on).
- Deleting the organisation (on).
- Deploying to Production, approving deployments and taking a Decision out of Production (off).

If you don't have two-factor, these actions aren't blocked; the app suggests adding a passkey
instead. Personal access tokens, connected apps and deploy tokens aren't asked: they were confirmed when
they were made.

### Requiring two-factor in an organisation

An owner can turn on **Require two-factor for members** in Settings → **Security**. It's off
by default, and an owner needs two-factor themselves before turning it on. The Security section shows
how many members have it, and the Members list shows who.

When it's turned on, every member without two-factor gets an email and a notification, and the next
time they open the organisation they're asked to add a passkey or an authenticator app before going
on. Their other organisations aren't affected. New keys, tokens and connected apps for the organisation
need two-factor, and personal access tokens and connected apps made without two-factor stop working
there: before it's turned on, the owner sees which ones and whose they are, and the people who hold
them are told how to replace them (make a new token, or sign in from the app again, once they have
two-factor). API keys → **Personal tokens** marks each one "Made without two-factor". Runtime API keys and
deploy tokens belong to the organisation and keep working. Signing in with Google counts
only once a passkey or an authenticator app is added. Owners can't set up or reset a member's
two-factor for them; they can see who hasn't set it up and remove someone from the organisation.

### If you lose your devices

1. **Use another factor.** Another passkey or your authenticator app still works.
2. **Use a recovery code.** On the second step of signing in, type one of your recovery codes. Then
   remove the lost passkey or app in Account and add a new one.
3. **Recover by email.** If you've lost every passkey, your app and your recovery codes, sign in with
   your password (or Google) and choose **Lost your passkey, app and recovery codes?** We email your
   account straight away, and a reminder a day before the **72-hour** wait is over. Each has a
   **This wasn't me** link that cancels it, and anyone still signed in to your account can cancel it
   from Account too. If an organisation you belong to requires two-factor, its owners are told.
4. **Finish from the email.** When the wait is over we email a **Finish recovery** link. Nothing
   changes until you open it and confirm. Then two-factor is removed, you're signed out everywhere,
   and your personal access tokens and connected apps stop working. Log in and you're asked to add a
   new passkey or app straight away. The link works once, for seven days; after that the recovery
   lapses and you can start again.

If you sign in with a passkey, your app or a recovery code before the recovery is finished, it's
cancelled, and so is its link.

Your passkeys and app belong to you across every organisation you're in, so an organisation's
owners and admins can't reset them. If none of the steps above works, contact TypeFuse support: once
they've checked it's you, they can reset your two-factor. You're signed out everywhere, your personal
access tokens and connected apps stop working, and you add a new passkey or app when you next sign in; the reset is recorded in your account's security activity and
you're emailed.

### Staying informed

Every change to your two-factor (a passkey added or removed, the app set up or removed, new recovery
codes, a recovery code used, a reset or a recovery) is emailed to you and appears in your
notifications, with **Recent security activity** on the Account page.

## Plans and usage

What each plan includes, where to see your usage and what happens at a limit.

### The plans

Your personal space is on the Free plan, and each organisation has its own paid plan. The
[pricing page](https://typefuse.dev/pricing) lists exactly what each includes.

- **Personal** (your personal space, on the Free plan) is for building and testing: write
  Definitions, run the Sandbox, Test runs and Comparisons, and deploy to Test with runtime keys to
  call it. Calls to Test use the same starter test tokens, up to 30 a minute and 1,000 a day,
  however many keys you use. For production, create an organisation (see
  [Organisations](https://typefuse.dev/guides/organisations)).
- **Founder** is for one person or a small team, with production: one member included, and up to
  three people with extra seats. It starts with a 14-day trial, which needs a card. While it
  runs, production is limited to 5,000 calls, and the owners get an email three days before it ends.
  Each person, email address and card has one trial.
- **Scale** includes five members and takes up to 25 with extra seats, with larger allowances. Production calls past the month's
  allowance are still served and billed with the next invoice, rather than refused.
- **Enterprise** is agreed with TypeFuse, including Managed production, larger limits and longer
  record keeping.

Plans differ in how many production calls they serve each month, how much testing runs on
TypeFuse's TypeSafe key, how many Decisions, environments and members an organisation can have, and
how long the detail of each call is kept (see
[How long records are kept](https://typefuse.dev/guides/decision-log#how-long-records-are-kept)).

### Extra seats

On Founder and Scale you can add people beyond the plan's members, at the seat price on the
[pricing page](https://typefuse.dev/pricing): up to 3 people on Founder and 25 on Scale. Owners add them under **Extra seats** in
Settings → **Plan**, and the change is prorated. Seats add people only; the plan's allowances stay
the same. You can't go below the people the organisation has (members and pending invites), and
moving between plans keeps as many places as the new plan allows.

### Your plan, usage and invoices

Open **Settings** at the foot of the sidebar; its Billing pages are **Plan**, **Usage** and
**Payments**. In an organisation, **Plan** shows what it's on (when its trial ends, when it renews
or when it cancels), its extra seats and every plan as a card, the same as the pricing page, with
**Cancel plan** at the foot. **Usage** shows how much of each allowance the organisation has used
this period. **Payments**, for owners and admins, lists its invoices (each opens the invoice, to
download or pay), its billing details and the cards on file; **Edit** and **Add new** change them.

Owners change the plan from the cards: another plan's button moves the organisation to it, and
choosing the other billing period on your own plan's card switches between monthly and annual. A
change applies at once and the difference is added to, or taken off, the next invoice. **Manage
billing** on your own plan opens the billing portal for payment details, invoices and cancelling. In your personal space, Billing shows what you've used and
offers **Create an organisation**. **Contact us** on Enterprise writes
to TypeFuse.

When the organisation passes 85% and then 95% of an allowance, owners and admins get a
[notification](https://typefuse.dev/guides/notifications).

### At a limit

When a limit is reached, the page or command that hit it says which limit it was and how to go on.

- **Production calls.** The runtime refuses a call the plan doesn't cover with `402 PLAN_LIMIT`,
  until the month renews or the plan changes. The SDK raises it as `PlanLimitError`. In a personal space, calls
  to Test stop once the test tokens are used up, and once you've made your 1,000 calls
  for the day, its keys answer `429 DAILY_LIMIT` until midnight UTC. Every refusal carries `billingUrl`, the page
  where the plan changes.
- **Testing.** When the testing allowance runs out, you can keep testing with your own TypeSafe key
  (below) or change plan.
- **Counts** such as Decisions, environments and members are checked when you add one, with a way
  forward in the message.

### Your own TypeSafe key for testing

On any plan, you can test with your own TypeSafe key. Add it on your **Account** page, in **Your own
TypeSafe key for testing**, and choose **Save for this session**. Sandbox runs, Test runs and
Comparisons then use your key and don't count towards the testing allowance, and replays of
production can compare every row. The key is kept only in your browser session, for every
organisation you use, and is forgotten when you sign out. It doesn't change anything else about your
plan.

In the CLI, pass `--typesafe-key` or set `TYPESAFE_KEY`; through MCP, send it in the `X-TypeSafe-Key`
header.

## Connecting an agent

Let Claude Code, Codex or claude.ai build, test and publish Decisions for you.

### What an agent can do

Claude Code, Codex and claude.ai can work on your Decisions for you: write and review a Definition,
add Examples and Test cases, run the Sandbox and tests, start Comparisons, publish versions and
request deployments. They use the TypeFuse skill, which teaches them how Decisions work, and the
TypeFuse tools over MCP.

The [Agents page](https://typefuse.dev/agents) has every step for each agent, with commands you can copy. In the app,
**Connect an agent** at the foot of the sidebar shows the same steps.

### Setting up

For Claude Code and Codex, one command installs the skill and adds the TypeFuse tools to Claude
Code. It writes only under `~/.claude` and `~/.codex`, and you can
[read the script](https://typefuse.dev/install) first:

```sh
curl -fsSL https://typefuse.dev/install | sh
```

Then sign in:

- **Claude Code**: run `/mcp`, choose **typefuse** and **Authenticate**.
- **Codex**: add the tools with `codex mcp add typefuse --url https://api.typefuse.dev/v1/mcp`, then run
  `codex mcp login typefuse`.
- **claude.ai**: in Settings → Connectors, add a custom connector with the URL `https://api.typefuse.dev/v1/mcp`, then
  sign in when asked.

Signing in opens TypeFuse in your browser, where you choose which organisation the agent may use
(or all of yours) and how much it may do.

Or paste this into any agent and let it set itself up:

```text
Install the TypeFuse skill and MCP server from https://typefuse.dev/agents.md, then ask me to sign in.
```

### What you allow

| Access | The agent can |
| --- | --- |
| Read | See Decisions, runs, Test runs, Evaluations, Datasets and your notifications |
| Run | Also run drafts and tests, evaluate and compare |
| Full | Also create and edit Decisions, publish versions and request deployments |

An agent can never approve a deployment, and it can't manage members, keys or billing. A deployment
it requests to Production waits for one click from you (or another approver) in the app; its answer
includes the link to that page.

Your connected apps are listed under **Connect an agent**, then **Manage**, where you can revoke any
of them. A connection that goes unused for 30 days ends on its own.

### CI and scripts

For CI and other automated agents that can't sign in through a browser, create a personal access
token with Read, Run or Full access, and give it to the tool as `TYPEFUSE_TOKEN`. Treat it like a
password, and never paste it into a conversation.

The `typefuse` CLI also signs in through the browser with `typefuse login`, or with a device code.
It isn't on npm yet; it will be published with the move to the `typefuse.ai` domain.

### Where agents read more

- [The TypeFuse skill](https://typefuse.dev/agents/typefuse/SKILL.md)
- [These guides as Markdown](https://typefuse.dev/guides.md), and each guide on its own at `/guides/<guide>.md`
- [llms.txt](https://typefuse.dev/llms.txt)
- [The API reference](https://api.typefuse.dev/docs)
