Skip to main content
A preset is a team-scoped, versioned bundle of a canonical model, sampling parameters, an optional system prompt, and provider preferences — addressed by a short slug like support-bot. Clients reference the slug at request time; the proxy resolves it to the current preset body before it dispatches upstream. Every save creates an immutable snapshot in preset_versions, so you can diff, audit, or roll back a bad change without stitching state back together from request logs.

When to use presets

Reach for a preset instead of hardcoding request fields when you want to:
  • Pin one production configuration — model, temperature, system prompt, provider preferences — behind a stable name that clients don’t need to redeploy to change.
  • Ship a config change (a smarter model, a lower temperature, a rewritten system prompt) and be able to instantly point back at the previous snapshot if quality regresses.
  • Keep an auditable history of who changed which field, and when, without carrying that history in your own database.
Aliases (Routing & fallbacks) rewrite the model name. Presets rewrite the whole request body. Use aliases for a single-field indirection; use presets when the temperature, system prompt, or provider preferences should travel with the config.

Anatomy

Every preset row is scoped by team_id; a slug in team A and the same slug in team B are two independent presets. Cross-team reads return preset_not_found — never a distinguishable “wrong team” error.

Reference a preset from the SDK

@routeshift/sdk accepts a preset slug on any chat or chatStream call. Because the preset carries its own canonical model, you can call the proxy without configuring a defaultModel on the client and without passing model on the request — the proxy resolves both from the preset.
The SDK will not merge its defaultModel into a request that carries a preset, so a client-side default won’t accidentally override the preset’s canonical model. Passing model, models, or a :online suffix alongside preset still works — those inputs win, exactly as they do on the raw HTTP endpoint. If you call chat with neither a preset, a model, a models array, nor a configured defaultModel, the SDK throws before any network call:
Streaming works the same way:

Manage presets

Open Routing → Presets in the dashboard, or drive the API directly. Only workspace admins can create, update, or delete presets. Any team member can read them.

Create

Returns { "id": "preset_…", "slug": "support-bot", "version": 1, "proxy_cache_invalidated": true } with 201. On slug collision the API returns 409 preset_slug_taken. Invalid slugs return 400 invalid_preset_slug; unknown models return 400 invalid_preset_model. Every accepted write (POST, PUT, disable, delete) also invalidates the proxy’s preset cache so the change takes effect on the next request. The response reports the outcome of that step explicitly:
  • On success: "proxy_cache_invalidated": true.
  • On failure: "proxy_cache_invalidated": false, "proxy_cache_error": "proxy_cache_invalidation_failed", and "cache_ttl_seconds" — the database write still committed, but the proxy may keep serving the previous preset until the TTL elapses.

Update

PUT is a full-body replace. model, params, system_prompt, and provider_prefs are all required — omit any and the request fails with 400 full_preset_body_required. enabled is optional and preserves its current value when omitted. Every accepted PUT bumps version by one and appends a preset_versions row. The response is { "slug": "support-bot", "version": 2, "proxy_cache_invalidated": true } — see Proxy cache invalidation for the failure shape.

Delete or disable

Use ?disable=true when you want the slug to fail closed but keep the version history discoverable. A hard DELETE removes the presets row (preset_versions rows persist for audit). Both variants return { "ok": true, "disabled": <bool>, "proxy_cache_invalidated": true } on success. If the proxy invalidation call fails, the database write still commits and the response reports proxy cache invalidation explicitly.

Proxy cache invalidation

After a successful create, publish, delete, or disable, the dashboard tells the proxy to drop its cached copy of the preset so the next request resolves the new body immediately. Every successful mutation response includes an explicit status for that side effect: The database write is committed before the invalidation call runs, so a proxy_cache_invalidated: false response still means the preset was saved. The proxy may serve the previous version for up to cache_ttl_seconds before it re-reads from the database.

How to handle it

  • Non-blocking clients (dashboards, background jobs): treat the response as a success — the preset is saved, and the proxy will pick it up within cache_ttl_seconds.
  • Change-control flows where new traffic must land on the new version immediately: surface proxy_cache_error to the operator and retry the same request (idempotent for PUT, DELETE, and DELETE?disable=true) until you see proxy_cache_invalidated: true, or wait out cache_ttl_seconds before declaring the rollout done.
  • POST retries: the create already committed, so retrying with the same slug returns 409 preset_slug_taken. To force invalidation after a failed POST, issue a PUT with the same body — it bumps the version and re-runs invalidation.
The proxy admin endpoint the dashboard calls is documented in Admin API; a proxy_admin_secret_not_configured response (500) means the dashboard is misconfigured and no mutations will invalidate the cache until the secret is set.

Version history

Every accepted create and update writes an immutable snapshot to preset_versions. Snapshots are keyed by (team_id, preset_id, version) and carry the full body (model, params, system_prompt, provider_prefs) plus created_by and created_at.
Preset version history is scoped strictly to the caller’s team. A request for GET /api/presets/{slug}/versions from a team that doesn’t own {slug} returns the same 404 preset_not_found response as a truly missing preset — teams can’t probe the existence of another team’s presets.

List snapshots

Returns every snapshot for the slug, newest first:

Read one snapshot

{version} must be a positive base-10 integer within the PostgreSQL 32-bit integer range (max 2147483647). Leading zeros, negative values, decimals, and values outside the range return 400 invalid_preset_version before any database lookup.

Roll back to a previous version

There’s no POST /rollback shortcut — read the snapshot you want and replay it as a PUT:
The result is a new snapshot (say version = 4) whose body is identical to version 2. History stays linear and append-only, which keeps the audit story simple — no version is ever mutated or removed.

Errors

Demo mode

Preset reads (list, detail, version history) transparently substitute the demo team for the caller’s team when demo mode is active — you see the demo workspace’s presets and version history, exactly as they’d render in production. Preset writes (POST, PUT, DELETE) are blocked with 403 so a demo session can’t mutate the real workspace.