Self-managed Intent Router API examples
Create a Router, mint an agent key, verify agent access, hand off to a developer, and rotate or revoke keys.
After you deploy Intent Router, developers still can't use it until a Router and an agent key exist. This page walks through that setup: you create a Router, issue an agent key for it, check that the key works, and give the connection values to a developer. The developer then continues with the developer quickstart. The page also covers rotating and revoking agent keys after handoff.
Before you begin, deploy the chart and load the administration tokens.
Set connection values
Forward the control plane, the data plane, and the Identity Service in separate terminals, as in Deploy, then export the connection values:
export IRIS_INTENT_ROUTER_SERVICE_URL='http://localhost:9400'
export IRIS_INTENT_ROUTER_URL='http://localhost:9401'
export IRIS_IDENTITY_URL='http://localhost:9200'
export IRIS_IDENTITY_TENANT='<tenant-id>'
umask 077
IRIS_INTENT_ROUTER_SERVICE_TOKEN holds the admin token and IRIS_IDENTITY_TOKEN holds the Identity Service control token. IRIS_IDENTITY_TENANT is the deployment's tenantId. For application deployment, use the endpoints your platform exposes.
Create a Router
List the offered models, then create a Router with one of them and wait for it to be ready. See Manage Routers for each step with Python, TypeScript, and the command-line interface (CLI).
SERVICE_API="${IRIS_INTENT_ROUTER_SERVICE_URL%/}/iris/v1/intent"
curl --fail-with-body --silent --show-error \
"$SERVICE_API/models?purpose=EMBEDDING" \
-H "Authorization: Bearer $IRIS_INTENT_ROUTER_SERVICE_TOKEN" > models.json
jq -c '.items[] | {provider, model, dimensions, authorization}' models.json
set -euo pipefail
SERVICE_API="${IRIS_INTENT_ROUTER_SERVICE_URL%/}/iris/v1/intent"
curl --fail-with-body --silent --show-error \
"$SERVICE_API/models?purpose=EMBEDDING" \
-H "Authorization: Bearer $IRIS_INTENT_ROUTER_SERVICE_TOKEN" > models.json
jq -c '.items[] | {provider, model, dimensions, authorization}' models.json
curl --fail-with-body --silent --show-error \
-X POST "$SERVICE_API/routers" \
-H "Authorization: Bearer $IRIS_INTENT_ROUTER_SERVICE_TOKEN" \
-H 'Content-Type: application/json' \
--data "$(jq '{
name: "support-router",
description: "Routes support requests to tools and agents.",
template: "AGENT",
minScore: 0.7,
vectorSpace: (.items[0] | {provider, model, dimensions})
}' models.json)" > router.json
ROUTER_ID=$(jq -er .routerId router.json)
jq -c '{routerId, state, revision}' router.json
until [ "$(curl --fail-with-body --silent --show-error \
"$SERVICE_API/routers/$ROUTER_ID" \
-H "Authorization: Bearer $IRIS_INTENT_ROUTER_SERVICE_TOKEN" | tee router.json | jq -r .state)" != "PROVISIONING" ]; do
sleep 1
done
jq -c '{routerId, state, failureReason}' router.json
jq -e '.state == "READY"' router.json > /dev/null
curl --fail-with-body --silent --show-error \
"$SERVICE_API/routers" \
-H "Authorization: Bearer $IRIS_INTENT_ROUTER_SERVICE_TOKEN" \
| jq -c '.items[] | {routerId, name, state}'
curl --fail-with-body --silent --show-error \
-X PATCH "$SERVICE_API/routers/$ROUTER_ID" \
-H "Authorization: Bearer $IRIS_INTENT_ROUTER_SERVICE_TOKEN" \
-H 'Content-Type: application/json' \
--data "$(jq -n --arg rev "$(jq -er .revision router.json)" '{minScore: 0.75, expectedRevision: $rev}')" \
| jq -c '{routerId, minScore, revision}'
for state in SUSPENDED READY; do
curl --fail-with-body --silent --show-error \
-X PATCH "$SERVICE_API/routers/$ROUTER_ID" \
-H "Authorization: Bearer $IRIS_INTENT_ROUTER_SERVICE_TOKEN" \
-H 'Content-Type: application/json' \
--data "$(jq -n --arg state "$state" '{state: $state}')" | jq -c '{routerId, state}'
done
curl --fail-with-body --silent --show-error \
-X DELETE "$SERVICE_API/routers/$ROUTER_ID" \
-H "Authorization: Bearer $IRIS_INTENT_ROUTER_SERVICE_TOKEN" | jq -c '{routerId, state}'
iris --json intent-router service models --purpose embedding > models.json
jq -c '.items[] | {provider, model, dimensions, authorization}' models.json
set -euo pipefail
iris --json intent-router service models --purpose embedding > models.json
jq -c '.items[] | {provider, model, dimensions, authorization}' models.json
iris intent-router service create --name support-router \
--description "Routes support requests to tools and agents." \
--template agent --min-score 0.7 \
--provider "$(jq -er '.items[0].provider' models.json)" \
--model "$(jq -er '.items[0].model' models.json)" \
--dimensions "$(jq -er '.items[0].dimensions' models.json)" \
--wait | tee router.env
ROUTER_ID=$(sed -n 's/^IRIS_INTENT_ROUTER_ID=//p' router.env)
iris --json intent-router service show "$ROUTER_ID" > router.json
jq -c '{routerId, state, failureReason}' router.json
jq -e '.state == "READY"' router.json > /dev/null
iris intent-router service list
iris intent-router service update "$ROUTER_ID" --min-score 0.75 \
--expected-revision "$(jq -er .revision router.json)"
iris intent-router service update "$ROUTER_ID" --state suspended
iris intent-router service update "$ROUTER_ID" --state ready
iris intent-router service delete "$ROUTER_ID" --yes
curl --fail-with-body --silent --show-error \
-X POST "$SERVICE_API/routers" \
-H "Authorization: Bearer $IRIS_INTENT_ROUTER_SERVICE_TOKEN" \
-H 'Content-Type: application/json' \
--data "$(jq '{
name: "support-router",
description: "Routes support requests to tools and agents.",
template: "AGENT",
minScore: 0.7,
vectorSpace: (.items[0] | {provider, model, dimensions})
}' models.json)" > router.json
ROUTER_ID=$(jq -er .routerId router.json)
jq -c '{routerId, state, revision}' router.json
iris intent-router service create --name support-router \
--description "Routes support requests to tools and agents." \
--template agent --min-score 0.7 \
--provider "$(jq -er '.items[0].provider' models.json)" \
--model "$(jq -er '.items[0].model' models.json)" \
--dimensions "$(jq -er '.items[0].dimensions' models.json)" \
--wait | tee router.env
ROUTER_ID=$(sed -n 's/^IRIS_INTENT_ROUTER_ID=//p' router.env)
until [ "$(curl --fail-with-body --silent --show-error \
"$SERVICE_API/routers/$ROUTER_ID" \
-H "Authorization: Bearer $IRIS_INTENT_ROUTER_SERVICE_TOKEN" | tee router.json | jq -r .state)" != "PROVISIONING" ]; do
sleep 1
done
jq -c '{routerId, state, failureReason}' router.json
jq -e '.state == "READY"' router.json > /dev/null
iris --json intent-router service show "$ROUTER_ID" > router.json
jq -c '{routerId, state, failureReason}' router.json
jq -e '.state == "READY"' router.json > /dev/null
The create step saves the new Router's ID in ROUTER_ID. With the CLI, iris intent-router service create --wait --write-env .env creates the Router, waits until it is READY, and also mints its agent key into .env, so you can skip the next section.
Mint an agent key
Issue one key with a write grant on the Router. In a new shell, first set ROUTER_ID to the saved routerId.
: "${ROUTER_ID:?Set ROUTER_ID to the saved routerId}"
jq -n --arg tenant "$IRIS_IDENTITY_TENANT" --arg router "$ROUTER_ID" '{
name: "support-app",
grants: [
{tenant: $tenant, product: "intent", resourceType: "intent-router", resourceId: $router, actions: ["write"]}
]
}' > agent-grant.json
curl --fail-with-body --silent --show-error \
-X POST "$IRIS_IDENTITY_URL/v1/api-keys" \
-H "Authorization: Bearer $IRIS_IDENTITY_TOKEN" \
-H 'Content-Type: application/json' \
--data-binary @agent-grant.json > agent-key.json
export IRIS_INTENT_ROUTER_ID="$ROUTER_ID"
export IRIS_INTENT_ROUTER_API_KEY="$(jq -er '.token' agent-key.json)"
KEY_ID="$(jq -er '.keyId' agent-key.json)"
jq '{keyId, createdAt}' agent-key.json
The response holds the key's token only once, when you mint or rotate the key. Store it in a secret manager and keep it out of source control.
Use read instead of write for an application that only classifies queries.
With the CLI, iris intent-router service key "$ROUTER_ID" --role write --name support-app --write .env mints the same key and writes the IRIS_INTENT_ROUTER_* values to .env. It reads IRIS_IDENTITY_URL, IRIS_IDENTITY_TOKEN, and IRIS_IDENTITY_TENANT.
Verify agent access
Check that the key reaches the Router:
curl --fail-with-body --silent --show-error \
"$IRIS_INTENT_ROUTER_URL/iris/data/v1/intent/routers/$IRIS_INTENT_ROUTER_ID/health" \
-H "Authorization: Bearer $IRIS_INTENT_ROUTER_API_KEY" | jq .
Hand off to a developer
Give the developer the data-plane endpoint as IRIS_INTENT_ROUTER_URL, the Router ID as IRIS_INTENT_ROUTER_ID, and the agent key as IRIS_INTENT_ROUTER_API_KEY. They continue with the quickstart. Give platform teams who manage Routers the control-plane URL and admin token for Manage Routers. Remove agent-key.json and agent-grant.json after you save the token.
Rotate or revoke agent keys
Rotating a key mints a replacement with a new keyId and token. The old key keeps working for a grace period, 3,600 seconds by default, so applications can switch over. Set graceSeconds to change it. Rotation returns the new token once:
curl --fail-with-body --silent --show-error \
-X POST "$IRIS_IDENTITY_URL/v1/api-keys/$KEY_ID/rotate?graceSeconds=600" \
-H "Authorization: Bearer $IRIS_IDENTITY_TOKEN" > agent-key.json
export IRIS_INTENT_ROUTER_API_KEY="$(jq -er '.token' agent-key.json)"
KEY_ID="$(jq -er '.keyId' agent-key.json)"
jq '{keyId, createdAt, oldExpiresAt}' agent-key.json
With the CLI, run iris identity keys rotate "$KEY_ID" --grace 10m --write .env --env IRIS_INTENT_ROUTER_API_KEY.
Revoke a key to stop it working:
curl --fail-with-body --silent --show-error \
-X DELETE "$IRIS_IDENTITY_URL/v1/api-keys/$KEY_ID" \
-H "Authorization: Bearer $IRIS_IDENTITY_TOKEN"
With the CLI, run iris identity keys revoke "$KEY_ID". iris identity keys list --resource-id "$ROUTER_ID" lists a Router's keys.
A revoked key stops working within 300 seconds, the Identity Service's default cache lifetime. Revoke the keys for a Router before you delete it.