# Models
URL: /docs/reference/models

Per-model wire contracts — the shapes the NT inference API enforces for each base model.



The NT inference API supports multiple base models. Each base has a different input shape — different state dimensions, different camera names, different image resolution. These per-model shapes are the contract.

When you call `Robot(model="so101").run(...)`, the API resolves the model, reads its contract, and validates your first observation frame against it. If the shapes don't match, the connection closes with code `4422` before any inference runs.

## What a contract is [#what-a-contract-is]

A contract declares five things about a model's wire interface:

| Field          | Type        | What it means                                                                                                                                                                |
| -------------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `state_shape`  | list of int | Expected dimensions of the joint-state vector your `read_state()` callback returns                                                                                           |
| `state_dtype`  | str         | Expected dtype (`"float32"`)                                                                                                                                                 |
| `cameras`      | object      | `{required: [...], expected: [...]}` — `required` camera keys must be present in your `images` dict (missing one closes `4422`); `expected` camera keys zero-fill if missing |
| `image_shape`  | list of int | Expected shape of each camera array, CHW (`[C, H, W]`)                                                                                                                       |
| `action_shape` | list of int | Shape of each action chunk the server returns (`[horizon, action_dim]`)                                                                                                      |

The first four fields are checked on your first observation frame. `action_shape` is informational — the server outputs it, not you.

## How to read the contract for your model [#how-to-read-the-contract-for-your-model]

```bash
curl -H "Authorization: Bearer $NT_API_KEY" \
  https://nt-registry-production.up.railway.app/v1/models/ft_6341c5_d13da9
```

The response includes the full contract block. Fine-tune entries return the resolved contract inherited from their base. The registry is an always-on service — the curl returns in about a second without waking the GPU container.

## Coercion within a contract [#coercion-within-a-contract]

The API coerces missing data within a contract, but not for every camera. If your `images` dict is missing a camera listed in `cameras.expected` (and not in `cameras.required`), the server fills that slot with zeros and the connection stays alive. A missing camera listed in `cameras.required` closes `4422` — `contract_mismatch.camera_missing` — instead.

Coercion does not apply across contracts. An `[8]`-dimensional state vector sent to a model expecting `[14]` closes `4422`, not coerced.

## Choosing a model [#choosing-a-model]

Pick the model whose embodiment matches your hardware.

* [MA2-SO-101](/docs/models/molmoact2/so101) — SO-101 single-arm, 6D state, 2 cameras
