Skip to main content

Projects

Projects are isolated workspaces. Each project has its own provider allow-list, routing rules, budgets, environments, and API keys. Use them to separate dev/staging/production or different products.

Managing projects​

// List
const { projects } = await client.projects.list();

// Create
const project = await client.projects.create({
name: 'My App',
slug: 'my-app', // optional URL-safe identifier
description: 'Production AI', // optional
});

// Update
await client.projects.update(project.id, { name: 'My App v2' });

// Delete
await client.projects.delete(project.id);

With a project-scoped API key​

A client created with a project-scoped API key works inside that project only: client.projects.list() returns just the key's project, and client.project(id) works for that project alone (any other project is not found, 404). The key can read the project's settings, but creating, updating or deleting projects, and changing a project's settings, needs an owner's or admin's session (403). Organization-level calls such as client.providers, client.routing.getConfig() and client.keys are refused with 403. client.requests.list(), client.routing.listHistory() and client.usage cover the key's project only, and client.requests.get() or client.routing.getDecision() on a request of another project (or of no project) is not found, 404. See Project-scoped API keys.

Project-scoped sub-client​

Once you have a project ID, use client.project(id) to get a sub-client for all project-level operations:

const proj = client.project('proj_abc123');

Connections (project provider allow-list)​

Project connections restrict which providers this project's requests may use. There's no API key here, just a providerId. This is a different concept from the org-level client.providers namespace (see Providers), which is where you actually connect your own key (BYOK); a project's allow-list applies on top of whatever providers your org already has access to, whether that's a platform-connected provider or one you've connected yourself with your own key. If a project sets an explicit allow-list, any provider not on it, including a BYOK provider, is excluded from that project's requests. Only enabled connections count. A request none of them can serve, or any request of a project whose connections are all disabled, is refused with 422 no_available_model.

allowedModels is saved with the connection but not enforced yet. To limit the project's models, use its policy's allowedModels.

// Restrict this project to a platform-connected provider
await proj.connections.add({
providerId: 'openai',
enabled: true,
});

// List this project's allow-list
const { providers } = await proj.connections.list();

// Toggle
await proj.connections.disable('openai');
await proj.connections.enable('openai');

// Remove it from the list; with no connections left, the project may use every provider again
await proj.connections.remove('openai');

Project routing​

Each project can override the org-level routing strategy. Project-level config only covers mode, strategy, weights, provider order/affinity, aliases, regions and the Speed setting. The governance policy block (blocked providers, cost/latency ceilings, fallbackBehavior, etc.) is org-level only and has no project override today: the project's allowedRegions and its policy can only narrow it. What each setting does: Cost, latency and quality controls.

Once a project has a routing config, its mode and strategy replace the organization's for that project's requests: those made with a project-scoped API key, or that name the project with the X-NuRoute-Project-ID header. Unset fields fall through to the organization: speed: null, an empty providerOrder or providerAffinity, and weightsJson: ''. speed is Artemis Task's Speed setting. weightsJson holds the balanced weights, which rank the providers of a model you name, and the choice fallback-router:apollo makes inside its tier when Artemis Task cannot decide; Artemis Task does not read them.

setConfig() keeps every field you leave out, so you can send only your change. If the stored config cannot be read at that moment, a call that leaves a field out fails with 503 and saves nothing. A project without a config of its own reads back the defaults, not the organization's settings.

const current = await proj.routing.getConfig();
await proj.routing.setConfig({
...current,
strategy: 'cheapest',
speed: 'faster', // 'off' | 'faster' | 'fastest', or null to follow the organization
});

// Per-project model aliases
await proj.routing.setAliases({ smart: 'gpt-4o-mini' });
await proj.routing.deleteAlias('smart');

Policy​

The policy applies to every request of the project: see Policy enforcement. set() replaces the whole policy, so a field you leave out is cleared; start from get().

const policy = await proj.policy.get();

await proj.policy.set({
...policy,
maxTokens: 4096,
allowedModels: ['gpt-4o', 'gpt-4o-mini'],
});

Budget​

set() replaces the whole budget: send all three fields, since a missing hardLimit turns the hard limit off. See Budget enforcement.

await proj.budget.set({
monthlyUsd: 500, // monthly cap, per calendar month (UTC)
alertPct: 80, // stored; budget alerts are not sent yet
hardLimit: true, // refuse the project's requests (402 budget_exceeded) once the cap is spent
});

const budget = await proj.budget.get();
console.log(budget.currentSpend, budget.monthlyUsd);

Environments​

const { environments } = await proj.environments.list();

const { id } = await proj.environments.create({
name: 'Staging',
slug: 'staging',
isProduction: false,
});

await proj.environments.delete(id);

Project usage​

const overview = await proj.usage.overview(24); // last 24 h
const series = await proj.usage.series(168); // last 7 days
const breakdown = await proj.usage.breakdown(24);