# Model Pricing API

The Model Pricing API resolves model identifiers to token prices and manages project-specific
overrides.
Provon uses the resolved definition to calculate Gateway and telemetry cost fields.

Base URL:

```text
https://api.provon.dev/v1
```

Public routes use a project API key:

```http
Authorization: Bearer <PROVON_API_KEY>
```

| Capability            | Access                                              |
| --------------------- | --------------------------------------------------- |
| `model-pricing:read`  | List resolved prices and test model matching        |
| `model-pricing:write` | Create, update, reset, and delete project overrides |

The default project key includes `model-pricing:read`.

## Endpoints

| Method   | Path                                   | Purpose                                    |
| -------- | -------------------------------------- | ------------------------------------------ |
| `GET`    | `/model-prices`                        | List defaults plus project-resolved prices |
| `GET`    | `/model-price-matches`                 | Resolve one model/provider pair            |
| `POST`   | `/model-prices`                        | Create or replace a project definition     |
| `PATCH`  | `/model-prices/:modelPriceId`          | Update one project definition              |
| `DELETE` | `/model-prices/:modelPriceId/override` | Reset an overridden default                |
| `DELETE` | `/model-prices/:modelPriceId`          | Delete a project-custom definition         |

Signed-in Workbench calls use `/v1/projects/:projectId/model-prices...`. API integrations should
normally use the routes above, where the key selects the project.

## List Resolved Prices

```http
GET /v1/model-prices
Authorization: Bearer <PROVON_API_KEY>
```

Response:

```json
{
  "models": [
    {
      "id": "openai:gpt-5-mini",
      "modelId": "gpt-5-mini",
      "providerId": null,
      "catalogId": "openai/gpt-5-mini",
      "pricingTiers": [
        {
          "id": "standard",
          "name": "Standard",
          "isDefault": true,
          "priority": 0,
          "conditions": [],
          "prices": {
            "input_tokens": 2.5e-7,
            "output_tokens": 2e-6
          }
        }
      ],
      "source": "default",
      "editable": true,
      "resettable": false
    }
  ]
}
```

The list is already resolved for the project. A project override replaces or extends its base
definition before the response is returned.

Prices are USD per chargeable unit. Token keys are normally per token, not per million tokens.
For example, `$0.25 / 1M` input tokens is represented as `0.00000025`.

## Match A Model

Use the match endpoint before relying on cost projections for a new model:

```http
GET /v1/model-price-matches?providerId=openai&model=gpt-5-mini
Authorization: Bearer <PROVON_API_KEY>
```

Response:

```json
{
  "match": {
    "model": {
      "id": "openai:gpt-5-mini",
      "modelId": "gpt-5-mini",
      "providerId": null,
      "catalogId": "openai/gpt-5-mini"
    },
    "pricingTier": {
      "id": "standard",
      "name": "Standard"
    },
    "source": "default"
  }
}
```

`match` is `null` when no definition matches. `model` is required. A qualified value such as
`custom/gpt-5-mini` supplies both the provider ID and model ID; otherwise the optional `providerId`
query parameter supplies the provider ID.

## Create A Custom Definition

```http
POST /v1/model-prices
Content-Type: application/json
```

```json
{
  "providerId": "acme",
  "modelId": "acme-reasoner-v2",
  "pricingTiers": [
    {
      "id": "standard",
      "name": "Standard",
      "isDefault": true,
      "priority": 0,
      "conditions": [],
      "prices": {
        "input_tokens": 8e-7,
        "output_tokens": 3.2e-6,
        "reasoning_output_tokens": 3.2e-6,
        "cache_read_input_tokens": 8e-8
      }
    }
  ]
}
```

`providerId` scopes a project-custom price to one Gateway provider. Provider IDs are normalized
case-insensitively. `modelId` is an unqualified model identifier and must not contain `/`.

Common price keys are `input_tokens`, `output_tokens`, `reasoning_output_tokens`,
`cache_creation_input_tokens`, and `cache_read_input_tokens`. Additional non-negative numeric keys
are preserved for provider-specific usage detail.

Successful creation returns `201` with `{ "model": ... }`.

## Pricing Tiers

Use tiers when price depends on a usage detail such as context length:

```json
{
  "id": "long-context",
  "name": "Long context",
  "isDefault": false,
  "priority": 100,
  "conditions": [
    {
      "usageDetailPattern": "input_tokens",
      "operator": "gte",
      "value": 200000
    }
  ],
  "prices": {
    "input_tokens": 1.6e-6,
    "output_tokens": 6.4e-6
  }
}
```

Condition operators are `gt`, `gte`, `lt`, `lte`, `eq`, and `neq`. Non-default tiers are evaluated
from the smallest `priority` number to the largest; one tier should be marked as the default
fallback.

## Override And Reset

To override an existing default, create a project definition with `baseModelPriceId` set to the
default definition ID. The returned source becomes `project_override`.

Update the project definition:

```http
PATCH /v1/model-prices/:modelPriceId
```

The update body uses the same complete shape as creation: `modelId` and a non-empty `pricingTiers`
array are required. `providerId` is required for project-custom prices and remains `null` for
overrides of an unscoped default price.

Reset an override to its default:

```http
DELETE /v1/model-prices/:modelPriceId/override
```

Delete a project-custom definition:

```http
DELETE /v1/model-prices/:modelPriceId
```

Both operations return:

```json
{
  "ok": true
}
```

## Cost Resolution

Provon resolves cost in this order:

1. parse the observed value into a provider ID and model ID;
2. choose an exact provider-scoped price when one exists;
3. otherwise choose the unscoped default price for the model ID;
4. choose the highest-priority pricing tier whose conditions match usage details;
5. multiply each normalized usage bucket by its unit price;
6. write the model, tier, source, and cost breakdown into normalized telemetry.

If no model or required price key matches, the corresponding cost remains `null`. Provon does not
invent a zero cost for unknown pricing.

## Errors

| Status | Meaning                                                         |
| ------ | --------------------------------------------------------------- |
| `400`  | Missing model/provider ID, invalid JSON, tier, or condition     |
| `401`  | Missing or invalid project API key                              |
| `403`  | Key lacks `model-pricing:read` or `model-pricing:write`         |
| `501`  | This runtime supports reads but has no pricing store for writes |
