AgentNotify
Model router · Opt in

Change the model path, not the agent.

Codex, Claude Code, and any OpenAI-compatible client keep speaking their own wire format. Point them at the local AgentNotify router and it resolves a provider and model, translates when the wires differ, retries a cooling target, and records what happened — off until you switch it on.

Your agent

Codex

POST /router/v1/responses
model: "fast"

AgentNotify router

combo/fast

Resolves the selector, translates the wire only when the upstream speaks a different one.

responseschatmessages

Targets, in order

opencode-go/kimi-k3waiting
deepseek/deepseek-chatwaiting
codex-plan/gpt-5.1-codex-miniwaiting

Ledger row

combo/fast → deepseek/deepseek-chat · 2 attempts · 200 · in 4,180 · out 612 · no prompt stored

The agent asks for “fast” on the Responses wire. The router checks its own key.

One request through the router: admit, resolve, fail over, record. Provider names and numbers are illustrative.

3

wire formats accepted

1

loopback entry point

0

prompts stored in the ledger

What it does

A router, not a proxy you forget about.

Every behaviour is explicit, reversible, and visible in the web interface.

Loopback and off by default
The router listens on 127.0.0.1 under its own key, separate from the notification API token. Nothing routes until you enable it.
Three wires, one decision
OpenAI Responses, OpenAI Chat Completions, and Anthropic Messages are accepted, translated only when needed, and passed through unchanged when the upstream already speaks the same format.
Aliases, combos, failover
Address an upstream directly, use a nickname, or define an ordered combo. A target that fails, is cooling, or rejects the request hands it to the next eligible one — each gets its own rendering, so one provider's refusal does not end the chain. Requests that already streamed are never replayed blindly.
Agent connectors
Generate a Codex model catalogue and provider block, or Claude Code model-picker rows with matching behaviour classes. AgentNotify copies the file before every write, and restore or disconnect puts your values back.
A ledger you can trust
One row per logical request with provider, model, attempts, status, and token counts when the upstream reports them. Prompts, responses, headers, keys, and provider error bodies are never stored.
A separate spending boundary
Upstream keys are sealed with the same current-user encryption as delivery channels and are write-only in the interface. The router key cannot read notifications, and the notification token cannot route traffic.
How a request travels

Resolve, translate, send, record.

  1. 1AdmitThe router authenticates the request with its own key and applies the same bounded body limits as the notification API.
  2. 2ResolveA model selector becomes a concrete provider and model: an exact upstream, a unique alias, a combo, or a smart-switching group across providers that expose the same model.
  3. 3TranslateIf the client wire and the upstream wire differ, the request is normalised and the response is reframed for the client. Same-wire traffic is passed through untouched.
  4. 4RecordEach physical attempt is appended to the local ledger, and the visible row keeps the redacted route trace so a surprising answer can be explained.

what the router accepts

OpenAI Responses
OpenAI Chat Completions
Anthropic Messages
model: "fast"

  resolve  combo/fast
    ├─ opencode-go/kimi-k3       cooling
    ├─ deepseek/deepseek-chat    served
    └─ codex-plan/gpt-5.1-mini   standby

  wire     responses → chat
  usage    reported · 4,792 tokens
  ledger   1 row · no prompt stored
Boundaries

Your traffic goes where you point it.

Routing sends request content to the upstream provider you selected — that is the point of a router, and it is stated plainly rather than buried. Notification history, credentials, and non-router traffic never take part. Subscription-based integrations are labelled unofficial and stay opt-in.

  • Local routing decisionsModel resolution and failover happen on your machine; the decision trace is stored with the ledger row.
  • Bounded retriesAttempts are capped, timeouts are enforced, and a partially delivered stream is never retried as if nothing was sent.
  • No hidden fallbackA request that cannot be routed fails with a clear error instead of quietly choosing a different provider.