Skip to content

Presets API

Create, list, update, and delete reusable preset configurations.

Presets are reusable configuration bundles that store a default model, system prompt, sampling parameters, and provider routing rules. Reference a preset in any inference request with the @preset/<slug> model syntax.

POST/api/v1/presets

Authenticate with a Management key (ak_…) carrying read:presets (read routes) or write:presets (write routes), or a signed-in dashboard session. An LLM key (sk-ar-…) is not accepted. See Management API Keys.

Request

POST /api/v1/presets creates a preset in the authenticated workspace:

FieldTypeRequiredDescription
slugstringYesUnique identifier. Lowercase alphanumeric and hyphens (/^[a-z0-9][a-z0-9-]*$/). Unique per workspace.
namestringYesDisplay name. Must not be empty.
descriptionstring | nullNoOptional description.
configobjectYesPreset configuration. See PresetConfig fields.

Other routes

Method & pathPurpose
GET /api/v1/presetsList all presets in the workspace, newest first.
GET /api/v1/presets/{slug}Return a single preset by slug (404 if none).
PATCH /api/v1/presets/{slug}Partial update — only included fields change.
DELETE /api/v1/presets/{slug}Delete a preset by slug (404 if none).

PATCH update behavior: name is updated if provided and non-empty; description is tri-state (omit to keep, null to clear, string to set); config is shallow-merged so existing keys not in the patch are preserved.

PresetConfig fields

All fields are optional. When a preset is referenced in a request, these values serve as defaults the request can override.

FieldTypeDescription
modelstringDefault model (e.g. "z-ai/glm-4.7-flash")
modelsstring[]Fallback model list, tried in order if the primary is unavailable.
systemstringSystem prompt, prepended as a {role: "system"} message when the request has none.
temperaturenumberSampling temperature (0–2)
max_tokensnumberMaximum tokens in the response
max_completion_tokensnumberMaximum completion tokens (newer OpenAI parameter)
top_pnumberNucleus sampling threshold
top_knumberTop-K sampling
frequency_penaltynumberFrequency penalty (-2 to 2)
presence_penaltynumberPresence penalty (-2 to 2)
repetition_penaltynumberRepetition penalty
stopstring | string[]Stop sequences
seednumberReproducibility seed
response_formatobjectResponse format (e.g. { "type": "json_object" })
toolsarrayTool definitions
tool_choicestring | objectTool selection strategy
providerobjectProvider routing rules (sort, only, ignore, allow_fallbacks, etc.)
reasoningobjectReasoning config (effort, max_tokens, exclude)
stream_optionsobjectStreaming options (e.g. { "include_usage": true })

Reasoning on anyrouter/* routes

The built-in anyrouter/auto, anyrouter/free, anyrouter/cowork, anyrouter/hermes, and anyrouter/latest ids are routing presets, not fixed models. They do not add a synthetic reasoning level list. The rules below describe explicit request controls on these built-in routes; a custom preset's stored reasoning object is not a promise that every endpoint will merge it:

  • Chat-style requests use flat reasoning_effort. Responses uses the nested reasoning.effort form, with reasoning.enabled: false as the endpoint-specific disable form. For flat Chat and converted Messages, none is a disable sentinel, not an effort tier.
  • Messages requests use thinking.type (enabled / disabled); native Anthropic routes receive the body unchanged. When Messages is converted to a non-native upstream, enabled maps to flat high and disabled maps to none; thinking.budget_tokens is not carried into that flat field.
  • For flat reasoning_effort paths (including the converted Messages form), before each upstream attempt AnyRouter clamps the requested effort against the selected concrete model's declared levels, using a route-specific level override when one exists. It prefers the nearest lower supported level and falls back to the model default for an unknown tier. An omitted effort stays omitted; the declared default is a clamp fallback, not automatic injection. A branch's levels never leak into the next fallback branch. none remains a disable sentinel and is never clamped on these flat paths.
  • If a resolved branch has no declared effort enum, AnyRouter does not invent one and forwards the caller's flat value unchanged. For a selected route, its per-upstream supported_parameters is the authority; the top-level model list is a model-level/legacy projection, not a promise shared by every fallback.
  • Preset-level require_params is forwarded as a hard candidate constraint on paths that support preset candidate selection. Preset, key, and request provider lists are unioned at the preference layer, but provider/key requirements are not a universal hard filter on every concrete dispatch. Built-in virtual resolution uses tool-related entries in the list (and request tools) to require tool-capable members; it does not turn every arbitrary parameter into a capability guarantee.
  • provider.require_parameters: true is a separate strict model-level check over the request body. It also has no effect when the model has no declared supported_parameters list. Ordinary body-inferred parameters such as tools, tool_choice, and response_format use the softer candidate-reorder/strip path. If a route has no declared support list, the current router treats support as unknown/permissive rather than claiming universal support.
  • Routing floors merge deliberately: min_context takes the stricter (higher) value, key ignore is unioned, and key/request only lists intersect. The top-level preset require_params is the legacy alias for provider.require_params; min_context is likewise accepted at the top level.
  • A normal fallback chain may still try a different resolved model after an upstream failure. Reasoning clamping is recalculated for that attempt; it is not a promise that every model in the chain has identical effort levels.

These rules describe the current router. They do not claim that every provider supports a toggle, a reasoning token budget, or interleaved thinking. Interleaved thinking is a provider beta request (anthropic-beta: interleaved-thinking-2025-05-14), not a preset/model guarantee; use the model response's declared fields and the endpoint-specific parameters instead.

Response

GET /api/v1/presets returns a list envelope; a single preset record looks like:

{
  "id": "preset_abc123",
  "object": "preset",
  "slug": "code-reviewer",
  "name": "Code Reviewer",
  "description": "Strict senior-engineer review focused on bugs, security, and clarity.",
  "config": {
    "model": "anthropic/claude-sonnet-4",
    "system": "You are a senior code reviewer...",
    "temperature": 0.2,
    "max_tokens": 4096
  },
  "created_at": "2026-03-01T12:00:00.000Z",
  "updated_at": "2026-03-01T12:00:00.000Z"
}

DELETE /api/v1/presets/{slug} returns:

{
  "deleted": true,
  "slug": "code-reviewer"
}

Examples

curl -X POST https://anyrouter.dev/api/v1/presets \
  -H "Authorization: Bearer ak_your-management-key" \
  -H "Content-Type: application/json" \
  -d '{
    "slug": "code-reviewer",
    "name": "Code Reviewer",
    "description": "Strict senior-engineer review focused on bugs, security, and clarity.",
    "config": {
      "model": "anthropic/claude-sonnet-4",
      "system": "You are a senior code reviewer. Focus on bugs, security issues, and clarity.",
      "temperature": 0.2,
      "max_tokens": 4096
    }
  }'
import httpx

httpx.post(
    "https://anyrouter.dev/api/v1/presets",
    headers={"Authorization": "Bearer ak_your-management-key"},
    json={
        "slug": "code-reviewer",
        "name": "Code Reviewer",
        "config": {
            "model": "anthropic/claude-sonnet-4",
            "system": "You are a senior code reviewer.",
            "temperature": 0.2,
            "max_tokens": 4096,
        },
    },
)
await fetch("https://anyrouter.dev/api/v1/presets", {
  method: "POST",
  headers: {
    Authorization: "Bearer ak_your-management-key",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    slug: "code-reviewer",
    name: "Code Reviewer",
    config: {
      model: "anthropic/claude-sonnet-4",
      system: "You are a senior code reviewer.",
      temperature: 0.2,
      max_tokens: 4096,
    },
  }),
})

Once created, use it in any inference request by setting model to @preset/code-reviewer.

Errors

StatusCodeMeaning
400invalid_slugSlug doesn't match the required pattern.
400name_requiredName is empty or missing.
400config_requiredConfig is missing or not an object.
401—Missing or invalid credential.
403—Key lacks the required read:presets / write:presets scope.
409duplicate_slugA preset with this slug already exists in the workspace.