{
  "schema_version": 2,
  "id": "develop/ai/context-engine/intent-router/manage-routers",
  "title": "Manage Routers",
  "url": "https://redis.io/docs/latest/develop/ai/context-engine/intent-router/manage-routers/",
  "summary": "Choose an embedding model, create a Router, wait for it to be ready, and update, suspend, or delete it.",
  "tags": [
    "docs",
    "develop",
    "ai"
  ],
  "last_updated": "2026-10-30T10:17:15-04:00",
  "page_type": "content",
  "content_hash": "a7cd3663debcb3a6e9622b071c3f3289c2fdb16e63aa0f61e7bfe252e4d0c39f",
  "sections": [
    {
      "id": "code-examples",
      "title": "Code examples",
      "role": "example",
      "text": "These examples connect to Iris services using SDKs or HTTP requests with curl. Follow one language throughout each walkthrough.\n\n\nUse the control plane to create the Routers that applications author intents in and match against. A Router is the configuration and authorization boundary for a set of intents. See [Redis Intent Router concepts](https://redis.io/docs/latest/develop/ai/context-engine/intent-router/concepts#routers)."
    },
    {
      "id": "before-you-begin",
      "title": "Before you begin",
      "role": "content",
      "text": "These tasks need the control-plane URL and its admin token, which a platform team holds. See [Authentication and authorization](https://redis.io/docs/latest/operate/iris/intent-router/self-managed/authentication). The admin token manages every Router in the deployment, so keep it out of agent and application configuration.\n\nExport these values in the shell where you run the examples:\n\n[code example]\n\nThe examples use Python (`IntentRouter.from_env().service`), TypeScript (`IntentRouterService`), cURL, and the `iris intent-router service` commands, which all read these variables. The Python and TypeScript clients return each response as `result`, next to its `headers`.\n\n`iris intent-router service create` also needs the data-plane URL in `IRIS_INTENT_ROUTER_URL`, because it prints the `IRIS_INTENT_ROUTER_*` values an application needs. With `--write-env PATH` or `--json`, it also mints an agent key through the Identity Service, which needs `IRIS_IDENTITY_URL` and `IRIS_IDENTITY_TOKEN`. The CLI example creates the Router without a key. Follow one tab throughout the page. To choose a client, see the [developer guide](https://redis.io/docs/latest/develop/ai/context-engine/intent-router/developer-guide#choose-a-client)."
    },
    {
      "id": "choose-an-embedding-model",
      "title": "Choose an embedding model",
      "role": "content",
      "text": "A Router's embedding model decides how well matching understands wording, and it can't change after you create the Router. List the models your deployment offers. Embedding models have the `purpose` `EMBEDDING`, and you can filter the list with `purpose=EMBEDDING`.\n\n**Available in:** Python (Iris), TypeScript (Iris), cURL (Iris), CLI (Iris)\n\n##### Python (Iris)\n\n[code example]\n\n##### TypeScript (Iris)\n\n[code example]\n\n##### cURL (Iris)\n\n[code example]\n\n##### CLI (Iris)\n\n[code example]\n\n\n\nEach model reports its `provider`, `model`, and `dimensions`. Pass the same three values as the Router's `vectorSpace`. A self-managed deployment offers one embedding model, configured by your administrator, and holds its provider credentials itself, so `authorization` is `NONE`. Creating a Router with a model that isn't listed fails with `400`.\n\nThe model also sets the Router's memory use. The Router's vector index takes about the number of examples multiplied by `dimensions`, multiplied by 4 bytes, plus the index graph. See [Size memory](https://redis.io/docs/latest/operate/iris/intent-router/self-managed/prerequisites#size-memory)."
    },
    {
      "id": "create-a-router",
      "title": "Create a Router",
      "role": "content",
      "text": "Create a Router with a name and a `vectorSpace`. Optional fields set a description, a `template`, and a default `minScore`.\n\n**Available in:** Python (Iris), TypeScript (Iris), cURL (Iris), CLI (Iris)\n\n##### Python (Iris)\n\n[code example]\n\n##### TypeScript (Iris)\n\n[code example]\n\n##### cURL (Iris)\n\n[code example]\n\n##### CLI (Iris)\n\n[code example]\n\n\n\nSave the `routerId`. The CLI prints it as `IRIS_INTENT_ROUTER_ID`. The new Router starts in `PROVISIONING` while the data plane builds its search index. The response carries a `Retry-After` header that suggests when to check again.\n\nA tenant can hold up to 128 Routers by default. Router names don't have to be unique: the `routerId` identifies a Router.\n\n| Field | Meaning |\n| ----- | ------- |\n| `name` | A display name, 1 to 128 characters. |\n| `vectorSpace` | The embedding model: `provider`, `model`, and `dimensions`. Required and immutable. |\n| `description` | What the Router is for, up to 1,024 characters. |\n| `template` | A label for people and tooling: `CUSTOM`, `MODEL`, `GUARDRAILS`, or `AGENT`. Every template serves the same way. |\n| `minScore` | The Router's minimum score, above 0 and up to 1. Omit it, or send `0`, to use the default, 0.7. |\n| `databaseId` | The customer database that holds the Router's intents, examples, and index. Omit it to use the deployment's default database. Your administrator names the available databases, and a deployment can require one. It can't change after creation. |\n\nTo retry a create safely, send an `Idempotency-Key` header of the form `<client-id>:<hex SHA-256 of the request body>`, with the body's object keys sorted and no extra whitespace. A retry with the same key and body returns the first response with `Idempotent-Replayed: true`, for 24 hours. Reusing the key with a different body fails with `422`. Only a successful response is kept, so a retry after a failure runs the create again. A retry that arrives while the first request is still running gets `409`."
    },
    {
      "id": "wait-for-the-router-to-be-ready",
      "title": "Wait for the Router to be ready",
      "role": "content",
      "text": "Poll the Router until it leaves `PROVISIONING`. Data requests to a Router that is still `PROVISIONING`, or has `FAILED`, return `409`. In the CLI, `service create --wait` already waited for `READY`, so this step reads the Router once with `service show`.\n\n**Available in:** Python (Iris), TypeScript (Iris), cURL (Iris), CLI (Iris)\n\n##### Python (Iris)\n\n[code example]\n\n##### TypeScript (Iris)\n\n[code example]\n\n##### cURL (Iris)\n\n[code example]\n\n##### CLI (Iris)\n\n[code example]\n\n\n\nA `FAILED` Router reports why in `failureReason`, for example a database without vector search. A Router that isn't ready within the deployment's provisioning deadline, 10 minutes by default, also fails. A failed Router can't be resumed: delete it and create it again after you fix the cause."
    },
    {
      "id": "list-and-inspect-routers",
      "title": "List and inspect Routers",
      "role": "content",
      "text": "List the deployment's Routers, or read one by ID. Every read returns the current `revision`, which conditional updates need. Lists return `nextCursor` when more Routers follow.\n\n**Available in:** Python (Iris), TypeScript (Iris), cURL (Iris), CLI (Iris)\n\n##### Python (Iris)\n\n[code example]\n\n##### TypeScript (Iris)\n\n[code example]\n\n##### cURL (Iris)\n\n[code example]\n\n##### CLI (Iris)\n\n[code example]"
    },
    {
      "id": "update-a-router",
      "title": "Update a Router",
      "role": "content",
      "text": "Change a Router's name, description, template, or default `minScore` as a merge patch. An omitted field is unchanged, a `null` description clears it, and a `null` `minScore` resets it to the default. The embedding model and `databaseId` can't change. Changes apply after the data plane has applied the Router's new configuration, usually within seconds.\n\nSend `expectedRevision`, or the revision as a quoted `If-Match` header, to make the update conditional: it fails with `412` if the Router changed since you read it. `iris intent-router service update` reads the current revision and sends it for you unless you pass `--expected-revision`.\n\n**Available in:** Python (Iris), TypeScript (Iris), cURL (Iris), CLI (Iris)\n\n##### Python (Iris)\n\n[code example]\n\n##### TypeScript (Iris)\n\n[code example]\n\n##### cURL (Iris)\n\n[code example]\n\n##### CLI (Iris)\n\n[code example]"
    },
    {
      "id": "suspend-and-resume-a-router",
      "title": "Suspend and resume a Router",
      "role": "content",
      "text": "Suspend a Router to stop it serving without deleting its intents. Data requests return `423` while it is suspended. Setting the state back to `READY` resumes it. In the CLI, run `service update ROUTER_ID --state suspended` or `--state ready`.\n\n**Available in:** Python (Iris), TypeScript (Iris), cURL (Iris), CLI (Iris)\n\n##### Python (Iris)\n\n[code example]\n\n##### TypeScript (Iris)\n\n[code example]\n\n##### cURL (Iris)\n\n[code example]\n\n##### CLI (Iris)\n\n[code example]\n\n\n\n`SUSPENDED` and `READY` are the only states you can request, and only a `READY` or `SUSPENDED` Router can change between these states. Any other change fails with `409`. The control plane sets the other states."
    },
    {
      "id": "delete-a-router",
      "title": "Delete a Router",
      "role": "content",
      "text": "Deleting a Router permanently removes it, its intents, its examples, and its search index. You can delete a Router in any state. Deleting a Router doesn't revoke its agent keys, because the keys live in the Identity Service, not the control plane. A key whose grant names a deleted Router stays in the Identity Service until you revoke it. Revoke the Router's keys before you delete it, as shown in [Rotate or revoke agent keys](https://redis.io/docs/latest/operate/iris/intent-router/self-managed/api-examples#rotate-or-revoke-agent-keys).\n\n**Available in:** Python (Iris), TypeScript (Iris), cURL (Iris), CLI (Iris)\n\n##### Python (Iris)\n\n[code example]\n\n##### TypeScript (Iris)\n\n[code example]\n\n##### cURL (Iris)\n\n[code example]\n\n##### CLI (Iris)\n\n[code example]\n\n\n\nThe delete returns `202` with the Router in `DELETING`, and the data plane finishes the removal. Repeating the delete returns `202` while the removal runs and `204` once the Router is gone, so you can repeat a delete that timed out."
    },
    {
      "id": "issue-agent-keys",
      "title": "Issue agent keys",
      "role": "content",
      "text": "The control plane doesn't issue agent keys. Your platform team issues them with the Redis Iris Identity Service, with a grant on the Router. The CLI mints one with `iris intent-router service key ROUTER_ID --role write --write PATH`, and `iris identity keys` lists, rotates, and revokes them. See [Create a Router and an agent key](https://redis.io/docs/latest/operate/iris/intent-router/self-managed/api-examples).\n\n| Action | What it allows |\n| ------ | -------------- |\n| `read` | Match queries, read intents and examples, and check the Router's health. |\n| `write` | Create, change, and delete intents and examples, and clear the Router. Includes `read`. |\n| `full` | Includes `write`. No operation needs more than `write` today. |\n\nNo agent-key action can create, change, or delete the Router itself. Router creation, change, or deletion requires the admin token. Give an application that only classifies queries a `read` key. Give a curation tool or an authoring pipeline a `write` key."
    },
    {
      "id": "next-steps",
      "title": "Next steps",
      "role": "content",
      "text": "- [Manage intents and examples](https://redis.io/docs/latest/develop/ai/context-engine/intent-router/manage-intents) to author the Router's intents.\n- [Match queries](https://redis.io/docs/latest/develop/ai/context-engine/intent-router/match) to classify text."
    }
  ],
  "examples": [
    {
      "id": "before-you-begin-ex0",
      "language": "bash",
      "code": "export IRIS_INTENT_ROUTER_SERVICE_URL='<control-plane-url>'\nexport IRIS_INTENT_ROUTER_SERVICE_TOKEN='<admin-token>'",
      "section_id": "before-you-begin"
    },
    {
      "id": "choose-an-embedding-model-ex0",
      "language": "python",
      "code": "models = service.list_models(purpose=\"EMBEDDING\").result.items\nfor model in models:\n    print(model.provider, model.model, model.dimensions, model.authorization)",
      "section_id": "choose-an-embedding-model"
    },
    {
      "id": "choose-an-embedding-model-ex1",
      "language": "typescript",
      "code": "const { result: models } = await service.listModels(\"EMBEDDING\");\nfor (const model of models.items) {\n  console.log(model.provider, model.model, model.dimensions, model.authorization);\n}",
      "section_id": "choose-an-embedding-model"
    },
    {
      "id": "choose-an-embedding-model-ex2",
      "language": "bash",
      "code": "SERVICE_API=\"${IRIS_INTENT_ROUTER_SERVICE_URL%/}/iris/v1/intent\"\ncurl --fail-with-body --silent --show-error \\\n  \"$SERVICE_API/models?purpose=EMBEDDING\" \\\n  -H \"Authorization: Bearer $IRIS_INTENT_ROUTER_SERVICE_TOKEN\" > models.json\njq -c '.items[] | {provider, model, dimensions, authorization}' models.json",
      "section_id": "choose-an-embedding-model"
    },
    {
      "id": "choose-an-embedding-model-ex3",
      "language": "bash",
      "code": "iris --json intent-router service models --purpose embedding > models.json\njq -c '.items[] | {provider, model, dimensions, authorization}' models.json",
      "section_id": "choose-an-embedding-model"
    },
    {
      "id": "create-a-router-ex0",
      "language": "python",
      "code": "created = service.create_router(\n    name=\"support-router\",\n    description=\"Routes support requests to tools and agents.\",\n    template=\"AGENT\",\n    min_score=0.7,\n    vector_space={\n        \"provider\": models[0].provider,\n        \"model\": models[0].model,\n        \"dimensions\": models[0].dimensions,\n    },\n).result\nrouter_id = created.router_id\nprint(router_id, created.state, created.revision)",
      "section_id": "create-a-router"
    },
    {
      "id": "create-a-router-ex1",
      "language": "typescript",
      "code": "const [embedding] = models.items;\nif (!embedding?.dimensions) throw new Error(\"No embedding model is offered\");\nconst { result: created } = await service.createRouter({\n  name: \"support-router\",\n  description: \"Routes support requests to tools and agents.\",\n  template: \"AGENT\",\n  minScore: 0.7,\n  vectorSpace: {\n    provider: embedding.provider,\n    model: embedding.model,\n    dimensions: embedding.dimensions,\n  },\n});\nconst routerId = created.routerId;\nconsole.log(routerId, created.state, created.revision);",
      "section_id": "create-a-router"
    },
    {
      "id": "create-a-router-ex2",
      "language": "bash",
      "code": "curl --fail-with-body --silent --show-error \\\n  -X POST \"$SERVICE_API/routers\" \\\n  -H \"Authorization: Bearer $IRIS_INTENT_ROUTER_SERVICE_TOKEN\" \\\n  -H 'Content-Type: application/json' \\\n  --data \"$(jq '{\n    name: \"support-router\",\n    description: \"Routes support requests to tools and agents.\",\n    template: \"AGENT\",\n    minScore: 0.7,\n    vectorSpace: (.items[0] | {provider, model, dimensions})\n  }' models.json)\" > router.json\nROUTER_ID=$(jq -er .routerId router.json)\njq -c '{routerId, state, revision}' router.json",
      "section_id": "create-a-router"
    },
    {
      "id": "create-a-router-ex3",
      "language": "bash",
      "code": "iris intent-router service create --name support-router \\\n  --description \"Routes support requests to tools and agents.\" \\\n  --template agent --min-score 0.7 \\\n  --provider \"$(jq -er '.items[0].provider' models.json)\" \\\n  --model \"$(jq -er '.items[0].model' models.json)\" \\\n  --dimensions \"$(jq -er '.items[0].dimensions' models.json)\" \\\n  --wait | tee router.env\nROUTER_ID=$(sed -n 's/^IRIS_INTENT_ROUTER_ID=//p' router.env)",
      "section_id": "create-a-router"
    },
    {
      "id": "wait-for-the-router-to-be-ready-ex0",
      "language": "python",
      "code": "import time\n\nrouter = service.get_router(router_id=router_id).result\nwhile router.state == \"PROVISIONING\":\n    time.sleep(1)\n    router = service.get_router(router_id=router_id).result\nprint(router.router_id, router.state, router.failure_reason)\nif router.state != \"READY\":\n    raise SystemExit(1)",
      "section_id": "wait-for-the-router-to-be-ready"
    },
    {
      "id": "wait-for-the-router-to-be-ready-ex1",
      "language": "typescript",
      "code": "let router = (await service.getRouter(routerId)).result;\nwhile (router.state === \"PROVISIONING\") {\n  await new Promise((resolve) => setTimeout(resolve, 1000));\n  router = (await service.getRouter(routerId)).result;\n}\nconsole.log(router.routerId, router.state, router.failureReason);\nif (router.state !== \"READY\") process.exit(1);",
      "section_id": "wait-for-the-router-to-be-ready"
    },
    {
      "id": "wait-for-the-router-to-be-ready-ex2",
      "language": "bash",
      "code": "until [ \"$(curl --fail-with-body --silent --show-error \\\n  \"$SERVICE_API/routers/$ROUTER_ID\" \\\n  -H \"Authorization: Bearer $IRIS_INTENT_ROUTER_SERVICE_TOKEN\" | tee router.json | jq -r .state)\" != \"PROVISIONING\" ]; do\n  sleep 1\ndone\njq -c '{routerId, state, failureReason}' router.json\njq -e '.state == \"READY\"' router.json > /dev/null",
      "section_id": "wait-for-the-router-to-be-ready"
    },
    {
      "id": "wait-for-the-router-to-be-ready-ex3",
      "language": "bash",
      "code": "iris --json intent-router service show \"$ROUTER_ID\" > router.json\njq -c '{routerId, state, failureReason}' router.json\njq -e '.state == \"READY\"' router.json > /dev/null",
      "section_id": "wait-for-the-router-to-be-ready"
    },
    {
      "id": "list-and-inspect-routers-ex0",
      "language": "python",
      "code": "for item in service.list_routers().result.items:\n    print(item.router_id, item.name, item.state)",
      "section_id": "list-and-inspect-routers"
    },
    {
      "id": "list-and-inspect-routers-ex1",
      "language": "typescript",
      "code": "const { result: routers } = await service.listRouters();\nfor (const item of routers.items) console.log(item.routerId, item.name, item.state);",
      "section_id": "list-and-inspect-routers"
    },
    {
      "id": "list-and-inspect-routers-ex2",
      "language": "bash",
      "code": "curl --fail-with-body --silent --show-error \\\n  \"$SERVICE_API/routers\" \\\n  -H \"Authorization: Bearer $IRIS_INTENT_ROUTER_SERVICE_TOKEN\" \\\n  | jq -c '.items[] | {routerId, name, state}'",
      "section_id": "list-and-inspect-routers"
    },
    {
      "id": "list-and-inspect-routers-ex3",
      "language": "bash",
      "code": "iris intent-router service list",
      "section_id": "list-and-inspect-routers"
    },
    {
      "id": "update-a-router-ex0",
      "language": "python",
      "code": "updated = service.update_router(\n    router_id=router_id, min_score=0.75, expected_revision=router.revision\n).result\nprint(updated.router_id, updated.min_score, updated.revision)",
      "section_id": "update-a-router"
    },
    {
      "id": "update-a-router-ex1",
      "language": "typescript",
      "code": "const { result: updated } = await service.updateRouter(routerId, undefined, {\n  minScore: 0.75,\n  expectedRevision: router.revision,\n});\nconsole.log(updated.routerId, updated.minScore, updated.revision);",
      "section_id": "update-a-router"
    },
    {
      "id": "update-a-router-ex2",
      "language": "bash",
      "code": "curl --fail-with-body --silent --show-error \\\n  -X PATCH \"$SERVICE_API/routers/$ROUTER_ID\" \\\n  -H \"Authorization: Bearer $IRIS_INTENT_ROUTER_SERVICE_TOKEN\" \\\n  -H 'Content-Type: application/json' \\\n  --data \"$(jq -n --arg rev \"$(jq -er .revision router.json)\" '{minScore: 0.75, expectedRevision: $rev}')\" \\\n  | jq -c '{routerId, minScore, revision}'",
      "section_id": "update-a-router"
    },
    {
      "id": "update-a-router-ex3",
      "language": "bash",
      "code": "iris intent-router service update \"$ROUTER_ID\" --min-score 0.75 \\\n  --expected-revision \"$(jq -er .revision router.json)\"",
      "section_id": "update-a-router"
    },
    {
      "id": "suspend-and-resume-a-router-ex0",
      "language": "python",
      "code": "for state in (\"SUSPENDED\", \"READY\"):\n    changed = service.update_router(router_id=router_id, state=state).result\n    print(changed.router_id, changed.state)",
      "section_id": "suspend-and-resume-a-router"
    },
    {
      "id": "suspend-and-resume-a-router-ex1",
      "language": "typescript",
      "code": "for (const state of [\"SUSPENDED\", \"READY\"] as const) {\n  const { result: changed } = await service.updateRouter(routerId, undefined, { state });\n  console.log(changed.routerId, changed.state);\n}",
      "section_id": "suspend-and-resume-a-router"
    },
    {
      "id": "suspend-and-resume-a-router-ex2",
      "language": "bash",
      "code": "for state in SUSPENDED READY; do\n  curl --fail-with-body --silent --show-error \\\n    -X PATCH \"$SERVICE_API/routers/$ROUTER_ID\" \\\n    -H \"Authorization: Bearer $IRIS_INTENT_ROUTER_SERVICE_TOKEN\" \\\n    -H 'Content-Type: application/json' \\\n    --data \"$(jq -n --arg state \"$state\" '{state: $state}')\" | jq -c '{routerId, state}'\ndone",
      "section_id": "suspend-and-resume-a-router"
    },
    {
      "id": "suspend-and-resume-a-router-ex3",
      "language": "bash",
      "code": "iris intent-router service update \"$ROUTER_ID\" --state suspended\niris intent-router service update \"$ROUTER_ID\" --state ready",
      "section_id": "suspend-and-resume-a-router"
    },
    {
      "id": "delete-a-router-ex0",
      "language": "python",
      "code": "deleted = service.delete_router(router_id=router_id).result\nif deleted:\n    print(deleted.router_id, deleted.state)",
      "section_id": "delete-a-router"
    },
    {
      "id": "delete-a-router-ex1",
      "language": "typescript",
      "code": "const deleted = (await service.deleteRouter(routerId))?.result;\nif (deleted) console.log(deleted.routerId, deleted.state);",
      "section_id": "delete-a-router"
    },
    {
      "id": "delete-a-router-ex2",
      "language": "bash",
      "code": "curl --fail-with-body --silent --show-error \\\n  -X DELETE \"$SERVICE_API/routers/$ROUTER_ID\" \\\n  -H \"Authorization: Bearer $IRIS_INTENT_ROUTER_SERVICE_TOKEN\" | jq -c '{routerId, state}'",
      "section_id": "delete-a-router"
    },
    {
      "id": "delete-a-router-ex3",
      "language": "bash",
      "code": "iris intent-router service delete \"$ROUTER_ID\" --yes",
      "section_id": "delete-a-router"
    }
  ]
}
