Skip to main content

Projects

Projects are isolated workspaces within your organisation, available on every plan. They exist to give different teams or products their own:

Provider connections
Which AI providers are available.
Routing configuration
Strategy, speed and model aliases specific to this project.
Policies
Allowed and blocked models, providers and regions, and max tokens per request, enforced on every request.
Budgets
Monthly spend caps with an optional hard limit. Alert thresholds are saved, but alerts are not sent yet.
Environments
Dev, staging, and production within one project.
API keys
Keys scoped to this project only.

When to use projects​

ScenarioRecommendation
Multiple products sharing one NuRoute accountOne project per product
Different teams with different provider accessOne project per team
Dev / staging / production separationEnvironments within one project
Cost isolation between workloadsOne project per workload with its own budget

Project-scoped API keys​

A key created for a project can only access that project's resources, so you can give a team an API key without granting access to other projects.

Chat requests made with the key belong to its project: the project's routing and provider settings apply, with no extra header. A request that names a different project with the X-NuRoute-Project-ID header is refused with 403, as is a header naming a project outside your organization. Project IDs are matched without regard to letter case.

What a project-scoped key can use:

EndpointWith a project-scoped key
POST /v1/chat/completionsRequests run under the key's project
GET /v1/models, GET /v1/models/catalog, GET /v1/providers/catalog, GET /v1/routing/speedAs for any key
GET /projectsLists only the key's project
GET /projects/{id} and /v1/projects/{id}/...The key's project only. Its settings, usage, budget and environments can be read; changing them is refused with 403, as it needs an owner's or admin's session
GET /v1/usage/...The key's project's usage. Leave out project_id or pass the key's project
GET /v1/requests, GET /v1/routing/historyOnly the key's project's requests
GET /v1/requests/{id}, its replay, GET /v1/routing/history/{id}Only a request of the key's project

Everything else is refused with 403 forbidden: your organization's routing configuration, model aliases and policy preview, billing, members, API keys, provider connections, the audit log and organization settings. An organization-wide key can read the routing configuration, aliases, policy preview, usage and requests. Changing any setting, and managing API keys and members, needs an owner's or admin's session (the dashboard, or in the SDKs a token from auth.login()): no API key has a role, so even an organization-wide key gets 403 there.

Another project's data, even one in your organization, answers 404, exactly as if it did not exist: another project's settings, a project_id of another project (or none) on the usage endpoints, and a request made under another project or under no project. A 403 means the endpoint (or, for a project's settings, the change) is closed to project-scoped keys; a 404 means an endpoint the key may use was asked about something outside its project.

A project-scoped key can be created only for an active project of your organization; any other project_id is refused with 400. Once its project is deleted, the key stops working: requests made with it are refused with 401, as for a revoked key.

Environments​

Environments let you run dev and production workloads in the same project without spinning up separate projects. Each environment gets its own slug (e.g. dev, staging, prod) and can be marked as production.

Policy enforcement​

A project's policy (GET / PUT /v1/projects/{id}/policy) applies to every request of the project: those made with its project-scoped API key, and those that name it with the X-NuRoute-Project-ID header. It narrows your organization's policy and never loosens it. Your organization's fallbackBehavior cannot relax it either: when the organization's rules rule out every provider, a relaxing fallback still picks only among what the project allows.

FieldEffect when set
allowed_modelsOnly these models. A request that names another model is refused with 403 policy_violation
blocked_modelsNever these models, even if allowed_models lists them. Naming one is a 403
allowed_providersOnly these providers. A request that pins another with provider is a 403
blocked_providersNever these providers. Pinning one is a 403
allowed_regionsOnly providers that serve one of these regions, written like the organization's allowed regions (EU, US, APAC, Global, or an exact region such as eu-west-1; not case-sensitive; how regions match). A provider that declares no region is left out unless you allow Global. It applies on top of the organization's regions
max_tokensThe most output tokens a request may ask for. A larger max_tokens is refused with 400 invalid_request_error. A request that sets no max_tokens is sent to the provider with this limit, or with the model's own output budget if that is smaller. 0 sets no limit

Empty lists, and max_tokens: 0, set no rule. Model names are matched exactly against catalogue ids; model aliases are not resolved first. A PUT replaces the whole policy: a field you leave out is cleared, so read the policy first and send it back with your change.

With model: "auto", only models, providers and regions the policy allows are chosen: Artemis Task weighs only the allowed models, and so does fallback-router:apollo when it decides a request Artemis Task cannot. When nothing the policy allows can serve the request, the gateway answers 422 no_available_model, for example with the message this project's policy leaves no provider that can serve this request.

HTTP/1.1 403 Forbidden
Content-Type: application/json

{ "error": { "type": "policy_violation", "message": "model \"gpt-4o\" is not in this project's allowed models" } }
HTTP/1.1 400 Bad Request
Content-Type: application/json

{ "error": { "type": "invalid_request_error", "message": "max_tokens 8000 exceeds this project's limit of 4096" } }

Budget enforcement​

A project's budget (GET / PUT /v1/projects/{id}/budget: monthly_usd, alert_pct, hard_limit) covers the calendar month in UTC. alert_pct defaults to 80 when a PUT leaves it out. current_spend is the project's spend since the first of the month (UTC), the same figure the hard limit reads. Spend is the provider cost of the project's requests at NuRoute's catalogue prices, before any NuRoute fee. A running request counts at its estimate, and at its real token count once it completes. A failed request counts nothing.

With hard_limit: true and a monthly_usd above 0, once the month's spend reaches monthly_usd, new requests of the project are refused with 402 Payment Required, type budget_exceeded. Requests already running finish. Requests are accepted again once you raise the budget, or on the first of the next month (UTC). Without hard_limit, nothing is refused. A PUT replaces the whole budget: a field you leave out is reset (a missing hard_limit turns the hard limit off), so send all three.

HTTP/1.1 402 Payment Required
Content-Type: application/json

{ "error": { "type": "budget_exceeded", "message": "this project has spent its monthly budget of $500.00 ($500.12 this month, UTC); requests are refused until the budget is raised or the month ends" } }
  • The check can lag by 30 seconds. NuRoute re-reads a project's spend at most every 30 seconds, and a request's cost is known only once it completes. A busy project can pass its budget by that much traffic. The budget itself is read on every request, so a raised budget applies from the next request.
  • It fails closed. If the spend cannot be read, requests of a project with a hard limit are refused with 503 (routing_error) until it can be read again. Projects without a hard limit are not affected.
  • It is not your credit balance. 402 insufficient_credits means your organization's prepaid credits are used up; 402 budget_exceeded means this project's own budget is spent.
  • Alerts are not sent yet. alert_pct is saved and returned, but nothing reads it: no email, webhook or event fires when spend crosses it or reaches the budget.

Relationship to org-level config​

Project settings override org-level settings for the project's requests: those made with a project-scoped API key, or that name the project with the X-NuRoute-Project-ID header:

Org routing strategy: fastest
Project routing strategy: cheapest ← this wins for the project's requests

Requests authenticated with an org-level key use org-level settings unless explicitly routed to a project.

Once a project has a routing configuration of its own, its mode and strategy replace the organization's for the project's requests. The Speed setting, the provider order, the provider affinity and the balanced weights follow the organization while the project leaves them unset ("", an empty list or object, or never sent); the project's dashboard page sets only its mode (with the pinned provider and model), strategy, Speed and model aliases. Governance stays the organization's: blocked providers, preferred providers (which come ahead of the project's provider order), the cost and latency caps, provider types and fallbackBehavior apply to every project, and a project's allowed regions can only narrow the organization's. The project's policy narrows them further, and so do its provider connections: a project that has connected providers is served only by them, and a request none of them can serve is refused with 422 no_available_model (none of this project's connected providers (…) can serve this request). Only enabled connections count, and a project whose connections are all disabled is refused the same way. A connection's allowed_models is saved but not enforced yet; to limit a project's models, use its policy's allowed_models. Cost, latency and quality controls lists where each setting lives.

Strategy setting​

Artemis Task decides every model: "auto" request of a project, as it does for the organization; no router can be chosen (Routing). The project's strategy selects its preset: balanced → Balanced, highest_quality → Quality-first, cheapest → Cost-first, fastest → Balanced at speed Fastest on a capability version with a speed table (else Fastest). manual has no preset: the pinned provider serves the request, recorded with the reason manual_mode. See Strategies and presets.

Set it in the dashboard under Projects → your project → Routing, or with strategy on PUT /v1/projects/{id}/routing/config. That call keeps every field you leave out, so you can send only your change.

Speed setting​

A project can also set its own Speed: how much a quicker answer is worth, at the quality target its strategy's preset sets (see Artemis Task: Speed). The fallback router ignores it.

ValueDashboard labelAPI router_speedEffect
Follow the organizationFollow the organization""The organization's Speed applies; if it has none, Off
OffOff"off"The preset alone decides
FasterFaster"faster"A quicker answer counts a little; the preset keeps its quality target
FastestFastest"fastest"A quicker answer counts strongly; the preset keeps its quality target

Set it in the dashboard with Speed (Artemis Task) under Projects → your project → Routing (on Cloud; the self-hosted dashboard has no Speed setting), or with router_speed on PUT /v1/projects/{id}/routing/config. A PUT that leaves router_speed out keeps it (or, if the stored configuration cannot be read at that moment, is refused with 503 and saves nothing), and any other value is a 400. The JavaScript SDK calls the field speed and uses null to follow the organization. No request header or model name sets the speed.