---
title: "Create a model route"
method: POST
path: "/v1/projects/{project_id}/model-routes"
tags: ["Model Routes"]
---

# Create a model route

`POST /v1/projects/{project_id}/model-routes`

## Request

- Base URL: `https://api.naturali.ai`
- URL: `https://api.naturali.ai/v1/projects/{project_id}/model-routes`
- Auth: one of:
  - HTTP bearer
  - OAuth 2 (authorization code, authorization URL `https://api.naturali.ai/authorize`, token URL `https://api.naturali.ai/token`, scopes: `mcp:access`)

Creates a project-scoped model route: a named, ordered list of provider+model targets tried in array order. Every target must reference an AI provider in the same project (400 otherwise), and the total attempt budget — the sum of `1 + max_retries` over all targets — may not exceed 10 (400 naming the computed total). A duplicate `name` in the project is rejected with 409.

## Request body

- object
  - `name` string, required: Human-readable name, unique per project
  - `targets` ModelRouteTarget[], required: Ordered failover targets. Position is priority: target 0 is tried first, and a retryable failure falls through to the next target.
    - `ai_provider_id` string, required: AI provider in the route's project
    - `model` string, required: Model name to call on that provider
    - `timeout_seconds` integer: Per-attempt deadline, enforced with an AbortSignal composed with the caller's signal. A timeout classifies as `timeout`; the caller's own signal firing aborts the run without failover. Omitted means no per-target deadline.
    - `max_retries` integer: Retries on this target before falling through to the next one. The route is the only retry authority — routed calls pass `maxRetries: 0` to the AI SDK so its own retry loop cannot multiply these.
  - `retry_on` string[]: Which failure classes are failover-eligible. A failure whose class is not listed (and every deterministic failure — 400-class, auth, content policy) fails the generation immediately instead of spending another target's budget.
  - `failure_threshold` integer: Consecutive retryable failures after which a target is skipped for `cooldown_seconds`. Breaker state is in-process per node and keyed by (provider, model), so it is shared by every route pointing at the same backend.
  - `cooldown_seconds` integer: How long a tripped target is skipped before being probed again

## Response `201`

Model route created successfully

- ModelRoute
  - `id` string
  - `project_id` string
  - `name` string
  - `targets` ModelRouteTarget[]
    - `ai_provider_id` string, required: AI provider in the route's project
    - `model` string, required: Model name to call on that provider
    - `timeout_seconds` integer: Per-attempt deadline, enforced with an AbortSignal composed with the caller's signal. A timeout classifies as `timeout`; the caller's own signal firing aborts the run without failover. Omitted means no per-target deadline.
    - `max_retries` integer: Retries on this target before falling through to the next one. The route is the only retry authority — routed calls pass `maxRetries: 0` to the AI SDK so its own retry loop cannot multiply these.
  - `retry_on` string[]
  - `failure_threshold` integer
  - `cooldown_seconds` integer
  - `created_at` string, date-time
  - `updated_at` string, date-time

## Other responses

- `400`: Bad request (invalid targets, attempt cap exceeded, unknown provider)
- `401`: Unauthorized
- `403`: Forbidden
- `409`: A model route with this name already exists in the project
- `500`: Internal server error

---

[API](https://skmtc.dev/naturali/apis/naturali-ai-api.md) · [All operations](https://skmtc.dev/naturali/apis/naturali-ai-api/llms.txt) · [OpenAPI document](https://skmtc.dev/naturali/apis/naturali-ai-api/revisions/e4905b78f422?raw)
