Identity

Package: agentrouter.identity.v1 Service: IdentityService

Endpoints

Get user

Changed in v0.9.0 (new features): User directory responses include the team name alongside its ID, without a separate team lookup.

Changed in v0.5.0 (behaviour changes): Reaching a fleet-wide operation now requires an explicit platform binding for a caller that does not hold the admin scope. A management API key that reached one of these operations on a coarse scope alone, such as read or metrics_reader, is refused after this upgrade.

Changed in v0.5.0 (new features): Teams can now be managed end to end through PolicyService instead of the console's own database. AddUserGroupMembers and RemoveUserGroupMembers move users in and out of a team (a user already in another team of the same organization is moved, and the response names the team they left), ListUserGroupMembers pages a team's members with a name or email search and name or recently-added order, and every read of a team carries its member count and the first five members. ListUserGroups can filter by whether a team has members, search descriptions, sort by name, member count, created or updated time, and report how many teams have members. UpdateUserGroup can clear a description with clear_description. A member's email, avatar and ban flag are returned only to credentials that hold users_reader and only for members inside the caller's directory boundary; otherwise callers see ids and names. Email search follows the same scope and per-member boundary rules. Directory reads hide team IDs outside the caller's organization boundary; team filters and counts use the same visible membership.

Changed in v0.2.0 (security updates): Directory lookups through IdentityService.GetUser and ListUsers are now confined to the organizations the calling session belongs to. A signed-in user that held the users_reader scope, including every organization administrator, could previously resolve any user id in any tenant to email, name and role. GetUser now answers "not found" for a user outside the caller's organizations, and ListUsers omits them. A session with no project memberships resolves no users at all, including its own row. This affects users whose only access is an organization-level role, pending invites, and users whose only project is the reserved "default" project. Platform operators keep cross-tenant directory reads only through an explicit platform role binding. In a multi-tenant deployment that binding is a documented provisioning step (docs/runbooks/platform-directory-reach.md).

What it does: Resolves a single user_id to its directory entry (email + name). This is the attribution/display lookup that per-user insights/usage views need: those views key data by the opaque authn.user.id, and this maps that id back to a named person. It is a directory lookup only -- not identity propagation or authorization, and callers must not read the auth DB directly.

Request fields:

FieldRequiredDescription
user_idyesThe user_id to resolve.

Response fields:

FieldRequiredDescription
usernoThe resolved directory entry. Absent only on error (a missing user returns NOT_FOUND, not an empty response).
{"signatures":{"go":"func (x *IdentityClient) GetUser(ctx context.Context, req *identityv1.GetUserRequest) (*identityv1.GetUserResponse, error)","python":"get_user(req: identity_service_pb2.GetUserRequest) -\u003e GetUserResponse","typescript":"getUser(req: MessageInitShape\u003ctypeof tars_identity_v1_identity_service_pb.GetUserRequestSchema\u003e): Promise\u003cGetUserResponse\u003e","curl":"curl \"${AGENTROUTER_BASE_URL}/v1/users/01H...\" \\\n  -H \"Authorization: Bearer ${AGENTROUTER_API_KEY}\""},"examples":{"go":{"files":[{"name":"main.go","content":"// Command example is a runnable example for the AgentRouter Go SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then `go run .`.\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\n\tagentrouter \"github.com/tetrateio/agentrouter-go\"\n\tidentityv1 \"github.com/tetrateio/agentrouter-go/genapi/api/tars/identity/v1\"\n)\n\nfunc main() {\n\tctx := context.Background()\n\n\tclient, err := agentrouter.New(ctx,\n\t\tagentrouter.WithBaseURL(os.Getenv(\"AGENTROUTER_BASE_URL\")),\n\t\tagentrouter.WithAPIKey(os.Getenv(\"AGENTROUTER_API_KEY\")),\n\t)\n\tif err != nil {\n\t\tlog.Fatalf(\"client: %v\", err)\n\t}\n\n\t// Populate the request fields -- see the \"Request fields\" table above for\n\t// the available fields and which are required.\n\treq := \u0026identityv1.GetUserRequest{}\n\n\tresp, err := client.Identity().GetUser(ctx, req)\n\tif err != nil {\n\t\tlog.Fatalf(\"call: %v\", err)\n\t}\n\n\tfmt.Printf(\"%+v\\n\", resp)\n}\n"},{"name":"go.mod","content":"module github.com/tetrateio/agentrouter-go-examples/identity/getuser\n\ngo 1.26\n\nrequire github.com/tetrateio/agentrouter-go v0.1.1\n\n// Point this at the directory you extracted the downloaded Go SDK tarball into.\n// The directory name matches the tarball stem on the Download SDK page.\nreplace github.com/tetrateio/agentrouter-go =\u003e ./third_party/agentrouter-go-0.1.1\n"}]},"python":{"files":[{"name":"main.py","content":"\"\"\"Runnable example for the AgentRouter Python SDK.\n\nSet AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then run `python main.py`.\n\"\"\"\nimport os\n\nfrom tars.identity.v1 import identity_service_pb2\n\nfrom agentrouter_sdk import Client\n\nclient = Client(\n    base_url=os.environ[\"AGENTROUTER_BASE_URL\"],\n    api_key=os.environ[\"AGENTROUTER_API_KEY\"],\n)\n\n# Populate the request fields -- see the \"Request fields\" table above\n# for the available fields and which are required.\nreq = identity_service_pb2.GetUserRequest()\ntry:\n    result = client.identity.get_user(req)\n    print(result)\nexcept Exception as err:\n    print(\"Error:\", err)\n"},{"name":"requirements.txt","content":"# Point this at the directory you extracted the downloaded Python SDK tarball\n# into. The directory name matches the tarball stem on the Download SDK page.\n# To install instead from PyPI once published, replace the line below with:\n#   agentrouter-sdk\u003e=0.1.0\nagentrouter-sdk @ file:./third_party/agentrouter-python-0.1.1\n"}]},"typescript":{"files":[{"name":"index.ts","content":"// Runnable example for the AgentRouter TypeScript SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then run `npm install \u0026\u0026 npx tsx index.ts`.\nimport { Client } from '@tetrate/agentrouter-sdk'\n\nconst client = new Client({\n  baseUrl: process.env.AGENTROUTER_BASE_URL,\n  apiKey: process.env.AGENTROUTER_API_KEY,\n})\n\n// Populate the request fields -- see the \"Request fields\" table above\n// for the available fields and which are required.\nconst req = {}\ntry {\n  const result = await client.identity.getUser(req)\n  console.log(result)\n} catch (err) {\n  console.error('Error:', err)\n}\n"},{"name":"package.json","content":"{\n  \"name\": \"identity\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"dependencies\": {\n    \"@tetrate/agentrouter-sdk\": \"file:./third_party/agentrouter-typescript-0.1.1\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"^20.0.0\",\n    \"typescript\": \"^5.4.0\"\n  }\n}\n"},{"name":"tsconfig.json","content":"{\n  \"compilerOptions\": {\n    \"target\": \"ES2020\",\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"bundler\",\n    \"strict\": true,\n    \"esModuleInterop\": true,\n    \"skipLibCheck\": true\n  }\n}\n"}]},"curl":"curl \"${AGENTROUTER_BASE_URL}/v1/users/01H...\" \\\n  -H \"Authorization: Bearer ${AGENTROUTER_API_KEY}\""},"persona":"Authenticated (API key or session token)","httpVerb":"GET","httpPath":"/v1/users/{user_id}","slug":"get-user"}

List users

Changed in v0.9.0 (new features): User directory responses include the team name alongside its ID, without a separate team lookup.

Changed in v0.5.0 (behaviour changes): Reaching a fleet-wide operation now requires an explicit platform binding for a caller that does not hold the admin scope. A management API key that reached one of these operations on a coarse scope alone, such as read or metrics_reader, is refused after this upgrade.

Changed in v0.5.0 (new features): Teams can now be managed end to end through PolicyService instead of the console's own database. AddUserGroupMembers and RemoveUserGroupMembers move users in and out of a team (a user already in another team of the same organization is moved, and the response names the team they left), ListUserGroupMembers pages a team's members with a name or email search and name or recently-added order, and every read of a team carries its member count and the first five members. ListUserGroups can filter by whether a team has members, search descriptions, sort by name, member count, created or updated time, and report how many teams have members. UpdateUserGroup can clear a description with clear_description. A member's email, avatar and ban flag are returned only to credentials that hold users_reader and only for members inside the caller's directory boundary; otherwise callers see ids and names. Email search follows the same scope and per-member boundary rules. Directory reads hide team IDs outside the caller's organization boundary; team filters and counts use the same visible membership.

Changed in v0.2.0 (security updates): Directory lookups through IdentityService.GetUser and ListUsers are now confined to the organizations the calling session belongs to. A signed-in user that held the users_reader scope, including every organization administrator, could previously resolve any user id in any tenant to email, name and role. GetUser now answers "not found" for a user outside the caller's organizations, and ListUsers omits them. A session with no project memberships resolves no users at all, including its own row. This affects users whose only access is an organization-level role, pending invites, and users whose only project is the reserved "default" project. Platform operators keep cross-tenant directory reads only through an explicit platform role binding. In a multi-tenant deployment that binding is a documented provisioning step (docs/runbooks/platform-directory-reach.md).

What it does: Batch-resolves user_ids to directory entries in one call, so a stats/management caller can resolve a whole page of grouped insights rows without N round-trips. Unknown ids are silently omitted from the response.

Request fields:

FieldRequiredDescription
user_idsnoThe user_ids to resolve. Unknown ids are omitted from the response rather than erroring, so a partially-stale set still resolves. Capped at 1000 -- the insights page-size cap -- so one page of grouped rows resolves in a single call without becoming an unbounded ANY($1) query.

Response fields:

FieldRequiredDescription
usersnoResolved directory entries, in arbitrary order. May be shorter than the request when some ids are unknown.
{"signatures":{"go":"func (x *IdentityClient) ListUsers(ctx context.Context, req *identityv1.ListUsersRequest) (*identityv1.ListUsersResponse, error)","python":"list_users(req: identity_service_pb2.ListUsersRequest) -\u003e ListUsersResponse","typescript":"listUsers(req: MessageInitShape\u003ctypeof tars_identity_v1_identity_service_pb.ListUsersRequestSchema\u003e): Promise\u003cListUsersResponse\u003e","curl":"curl -X POST \"${AGENTROUTER_BASE_URL}/v1/users/lookup\" \\\n  -H \"Authorization: Bearer ${AGENTROUTER_API_KEY}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"user_ids\": []\n  }'"},"examples":{"go":{"files":[{"name":"main.go","content":"// Command example is a runnable example for the AgentRouter Go SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then `go run .`.\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\n\tagentrouter \"github.com/tetrateio/agentrouter-go\"\n\tidentityv1 \"github.com/tetrateio/agentrouter-go/genapi/api/tars/identity/v1\"\n)\n\nfunc main() {\n\tctx := context.Background()\n\n\tclient, err := agentrouter.New(ctx,\n\t\tagentrouter.WithBaseURL(os.Getenv(\"AGENTROUTER_BASE_URL\")),\n\t\tagentrouter.WithAPIKey(os.Getenv(\"AGENTROUTER_API_KEY\")),\n\t)\n\tif err != nil {\n\t\tlog.Fatalf(\"client: %v\", err)\n\t}\n\n\t// Populate the request fields -- see the \"Request fields\" table above for\n\t// the available fields and which are required.\n\treq := \u0026identityv1.ListUsersRequest{}\n\n\tresp, err := client.Identity().ListUsers(ctx, req)\n\tif err != nil {\n\t\tlog.Fatalf(\"call: %v\", err)\n\t}\n\n\tfmt.Printf(\"%+v\\n\", resp)\n}\n"},{"name":"go.mod","content":"module github.com/tetrateio/agentrouter-go-examples/identity/listusers\n\ngo 1.26\n\nrequire github.com/tetrateio/agentrouter-go v0.1.1\n\n// Point this at the directory you extracted the downloaded Go SDK tarball into.\n// The directory name matches the tarball stem on the Download SDK page.\nreplace github.com/tetrateio/agentrouter-go =\u003e ./third_party/agentrouter-go-0.1.1\n"}]},"python":{"files":[{"name":"main.py","content":"\"\"\"Runnable example for the AgentRouter Python SDK.\n\nSet AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then run `python main.py`.\n\"\"\"\nimport os\n\nfrom tars.identity.v1 import identity_service_pb2\n\nfrom agentrouter_sdk import Client\n\nclient = Client(\n    base_url=os.environ[\"AGENTROUTER_BASE_URL\"],\n    api_key=os.environ[\"AGENTROUTER_API_KEY\"],\n)\n\n# Populate the request fields -- see the \"Request fields\" table above\n# for the available fields and which are required.\nreq = identity_service_pb2.ListUsersRequest()\ntry:\n    result = client.identity.list_users(req)\n    print(result)\nexcept Exception as err:\n    print(\"Error:\", err)\n"},{"name":"requirements.txt","content":"# Point this at the directory you extracted the downloaded Python SDK tarball\n# into. The directory name matches the tarball stem on the Download SDK page.\n# To install instead from PyPI once published, replace the line below with:\n#   agentrouter-sdk\u003e=0.1.0\nagentrouter-sdk @ file:./third_party/agentrouter-python-0.1.1\n"}]},"typescript":{"files":[{"name":"index.ts","content":"// Runnable example for the AgentRouter TypeScript SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then run `npm install \u0026\u0026 npx tsx index.ts`.\nimport { Client } from '@tetrate/agentrouter-sdk'\n\nconst client = new Client({\n  baseUrl: process.env.AGENTROUTER_BASE_URL,\n  apiKey: process.env.AGENTROUTER_API_KEY,\n})\n\n// Populate the request fields -- see the \"Request fields\" table above\n// for the available fields and which are required.\nconst req = {}\ntry {\n  const result = await client.identity.listUsers(req)\n  console.log(result)\n} catch (err) {\n  console.error('Error:', err)\n}\n"},{"name":"package.json","content":"{\n  \"name\": \"identity\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"dependencies\": {\n    \"@tetrate/agentrouter-sdk\": \"file:./third_party/agentrouter-typescript-0.1.1\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"^20.0.0\",\n    \"typescript\": \"^5.4.0\"\n  }\n}\n"},{"name":"tsconfig.json","content":"{\n  \"compilerOptions\": {\n    \"target\": \"ES2020\",\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"bundler\",\n    \"strict\": true,\n    \"esModuleInterop\": true,\n    \"skipLibCheck\": true\n  }\n}\n"}]},"curl":"curl -X POST \"${AGENTROUTER_BASE_URL}/v1/users/lookup\" \\\n  -H \"Authorization: Bearer ${AGENTROUTER_API_KEY}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"user_ids\": []\n  }'"},"persona":"Authenticated (API key or session token)","httpVerb":"POST","httpPath":"/v1/users/lookup","slug":"list-users"}

Ban a user

Changed in v0.10.0 (security updates): Banning or deleting a user now revokes their MCP OAuth refresh tokens and unredeemed authorization codes, and /oauth/token refuses every grant for a banned or deleted user with invalid_grant. A token exchange no longer creates an account for a deleted email address, and an authorization code can no longer be redeemed twice. Unbanning a deleted user fails with FAILED_PRECONDITION; restore it instead. An MCP access token already issued stays valid until it expires, 24 hours by default.

Changed in v0.4.0 (new features): Administrators can ban and unban a user through the management API. Banning a user signs them out everywhere and disables their API keys. Unbanning lets them sign in again, and they issue new keys -- keys removed by the ban are not restored. Both calls require an interactive session holding the admin scope or the users.edit permission, which is the same authority the delete call requires. An API key is refused even when it carries that permission.

What it does: Bans a user (authn.user.banned = true) with a reason, and immediately revokes every live session and API key the user holds. It replaced better-auth's admin ban-user endpoint (fraser#9001): the flag lives on the authn.user row, the session revocation matches better-auth's ban-time deletion, and the key soft-delete matches the fraser#8689 hook (keys are marked deleted_at, never restored -- an unbanned user issues fresh keys). Admin-gated and session-only, carrying the same caller contract as DeleteUser (fraser#9151): an interactive session that holds the admin scope, or the users.edit permission on a platform-scope RBAC binding. A ban revokes every session and every API key the target holds, so its blast radius equals a delete's and the two admit the same administrators. users.edit is also the atom the old ban endpoints authorized on (fraser#8689), so a ban still needs the same permission. The request names a bare user_id and no tenant, so users.edit is authorized at PLATFORM reach. The handler re-checks the session half: an API key is refused even when it carries users.edit.

Request fields:

FieldRequiredDescription
user_idyesThe authn.user.id of the user to ban.
reasonnoWhy the user is banned, stored on authn.user.ban_reason and shown to operators. Mirrors the dashboard's cap (fraser#9001).

Response fields:

FieldRequiredDescription
user_idoutput-onlyThe banned user's id (echoes user_id).
bannedoutput-onlyAlways true on success; present so callers can assert the state the way they did against better-auth's user.banned response.
ban_reasonoutput-onlyThe stored ban reason.
{"signatures":{"go":"func (x *IdentityClient) BanUser(ctx context.Context, req *identityv1.BanUserRequest) (*identityv1.BanUserResponse, error)","python":"ban_user(req: identity_service_pb2.BanUserRequest) -\u003e BanUserResponse","typescript":"banUser(req: MessageInitShape\u003ctypeof tars_identity_v1_identity_service_pb.BanUserRequestSchema\u003e): Promise\u003cBanUserResponse\u003e","curl":"curl -X POST \"${AGENTROUTER_BASE_URL}/v1/users/01H.../ban\" \\\n  -H \"Authorization: Bearer $SESSION_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"reason\": \"...\"\n  }'"},"examples":{"go":{"files":[{"name":"main.go","content":"// Command example is a runnable example for the AgentRouter Go SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_SESSION_TOKEN in the environment, then `go run .`.\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\n\tagentrouter \"github.com/tetrateio/agentrouter-go\"\n\tidentityv1 \"github.com/tetrateio/agentrouter-go/genapi/api/tars/identity/v1\"\n)\n\nfunc main() {\n\tctx := context.Background()\n\n\tclient, err := agentrouter.New(ctx,\n\t\tagentrouter.WithBaseURL(os.Getenv(\"AGENTROUTER_BASE_URL\")),\n\t\tagentrouter.WithSessionToken(os.Getenv(\"AGENTROUTER_SESSION_TOKEN\")),\n\t)\n\tif err != nil {\n\t\tlog.Fatalf(\"client: %v\", err)\n\t}\n\n\t// Populate the request fields -- see the \"Request fields\" table above for\n\t// the available fields and which are required.\n\treq := \u0026identityv1.BanUserRequest{}\n\n\tresp, err := client.Identity().BanUser(ctx, req)\n\tif err != nil {\n\t\tlog.Fatalf(\"call: %v\", err)\n\t}\n\n\tfmt.Printf(\"%+v\\n\", resp)\n}\n"},{"name":"go.mod","content":"module github.com/tetrateio/agentrouter-go-examples/identity/banuser\n\ngo 1.26\n\nrequire github.com/tetrateio/agentrouter-go v0.1.1\n\n// Point this at the directory you extracted the downloaded Go SDK tarball into.\n// The directory name matches the tarball stem on the Download SDK page.\nreplace github.com/tetrateio/agentrouter-go =\u003e ./third_party/agentrouter-go-0.1.1\n"}]},"python":{"files":[{"name":"main.py","content":"\"\"\"Runnable example for the AgentRouter Python SDK.\n\nSet AGENTROUTER_BASE_URL and AGENTROUTER_SESSION_TOKEN in the environment, then run `python main.py`.\n\"\"\"\nimport os\n\nfrom tars.identity.v1 import identity_service_pb2\n\nfrom agentrouter_sdk import Client\n\nclient = Client(\n    base_url=os.environ[\"AGENTROUTER_BASE_URL\"],\n    session_token=os.environ[\"AGENTROUTER_SESSION_TOKEN\"],\n)\n\n# Populate the request fields -- see the \"Request fields\" table above\n# for the available fields and which are required.\nreq = identity_service_pb2.BanUserRequest()\ntry:\n    result = client.identity.ban_user(req)\n    print(result)\nexcept Exception as err:\n    print(\"Error:\", err)\n"},{"name":"requirements.txt","content":"# Point this at the directory you extracted the downloaded Python SDK tarball\n# into. The directory name matches the tarball stem on the Download SDK page.\n# To install instead from PyPI once published, replace the line below with:\n#   agentrouter-sdk\u003e=0.1.0\nagentrouter-sdk @ file:./third_party/agentrouter-python-0.1.1\n"}]},"typescript":{"files":[{"name":"index.ts","content":"// Runnable example for the AgentRouter TypeScript SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_SESSION_TOKEN in the environment, then run `npm install \u0026\u0026 npx tsx index.ts`.\nimport { Client } from '@tetrate/agentrouter-sdk'\n\nconst client = new Client({\n  baseUrl: process.env.AGENTROUTER_BASE_URL,\n  sessionToken: process.env.AGENTROUTER_SESSION_TOKEN,\n})\n\n// Populate the request fields -- see the \"Request fields\" table above\n// for the available fields and which are required.\nconst req = {}\ntry {\n  const result = await client.identity.banUser(req)\n  console.log(result)\n} catch (err) {\n  console.error('Error:', err)\n}\n"},{"name":"package.json","content":"{\n  \"name\": \"identity\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"dependencies\": {\n    \"@tetrate/agentrouter-sdk\": \"file:./third_party/agentrouter-typescript-0.1.1\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"^20.0.0\",\n    \"typescript\": \"^5.4.0\"\n  }\n}\n"},{"name":"tsconfig.json","content":"{\n  \"compilerOptions\": {\n    \"target\": \"ES2020\",\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"bundler\",\n    \"strict\": true,\n    \"esModuleInterop\": true,\n    \"skipLibCheck\": true\n  }\n}\n"}]},"curl":"curl -X POST \"${AGENTROUTER_BASE_URL}/v1/users/01H.../ban\" \\\n  -H \"Authorization: Bearer $SESSION_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"reason\": \"...\"\n  }'"},"persona":"Admin","httpVerb":"POST","httpPath":"/v1/users/{user_id}/ban","slug":"ban-a-user"}

Set a user's role

Changed in v0.9.0 (new features): Admins can set a user's role with POST /v1/users/{user_id}/role. The user's access changes at once. Any change that is not a promotion to a higher level signs the user out, also a move between roles at the same level. The change is not saved when its audit record cannot be written.

What it does: Sets a user's legacy role (authn.user.role) and, in the same transaction, the org binding that the role projects, the platform projection, and the user's binding version, so the new access applies at the next request. A change that is not a promotion to a higher level (a demotion, or a move between roles at the same level) also signs the user out. The change and its audit row commit together, or nothing commits. Session only. A platform administrator can change a role for any customer_id. Any other caller needs all three: RBAC_AUTH_DB_MODE is "single", customer_id is the deployment customer, and the caller administers that customer. Anyone else gets PERMISSION_DENIED. FAILED_PRECONDITION: the target is deleted, the target is the caller, the old or the new role is super_admin or admin (use GrantSuperAdmin and RevokeSuperAdmin), or the built-in org role is not seeded yet.

Request fields:

FieldRequiredDescription
user_idyesThe authn.user.id of the user.
roleyesThe new role: user, read_only, model_admin, provider_admin, mcp_admin, user_admin or billing_admin.
customer_idnoThe authorization target: the customer whose administrator makes the change. Empty authorizes a platform administrator only.

Response fields:

FieldRequiredDescription
useroutput-onlyThe user after the change.
sessions_revokedoutput-onlyTrue when the role changed and the change was not a promotion, so the user's sessions were deleted. A request that repeats the current role deletes no session, and this field is false.
{"signatures":{"go":"func (x *IdentityClient) SetUserRole(ctx context.Context, req *identityv1.SetUserRoleRequest) (*identityv1.SetUserRoleResponse, error)","python":"set_user_role(req: identity_service_pb2.SetUserRoleRequest) -\u003e SetUserRoleResponse","typescript":"setUserRole(req: MessageInitShape\u003ctypeof tars_identity_v1_identity_service_pb.SetUserRoleRequestSchema\u003e): Promise\u003cSetUserRoleResponse\u003e","curl":"curl -X POST \"${AGENTROUTER_BASE_URL}/v1/users/01H.../role\" \\\n  -H \"Authorization: Bearer $SESSION_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"role\": \"...\",\n    \"customer_id\": \"...\"\n  }'"},"examples":{"go":{"files":[{"name":"main.go","content":"// Command example is a runnable example for the AgentRouter Go SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_SESSION_TOKEN in the environment, then `go run .`.\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\n\tagentrouter \"github.com/tetrateio/agentrouter-go\"\n\tidentityv1 \"github.com/tetrateio/agentrouter-go/genapi/api/tars/identity/v1\"\n)\n\nfunc main() {\n\tctx := context.Background()\n\n\tclient, err := agentrouter.New(ctx,\n\t\tagentrouter.WithBaseURL(os.Getenv(\"AGENTROUTER_BASE_URL\")),\n\t\tagentrouter.WithSessionToken(os.Getenv(\"AGENTROUTER_SESSION_TOKEN\")),\n\t)\n\tif err != nil {\n\t\tlog.Fatalf(\"client: %v\", err)\n\t}\n\n\t// Populate the request fields -- see the \"Request fields\" table above for\n\t// the available fields and which are required.\n\treq := \u0026identityv1.SetUserRoleRequest{}\n\n\tresp, err := client.Identity().SetUserRole(ctx, req)\n\tif err != nil {\n\t\tlog.Fatalf(\"call: %v\", err)\n\t}\n\n\tfmt.Printf(\"%+v\\n\", resp)\n}\n"},{"name":"go.mod","content":"module github.com/tetrateio/agentrouter-go-examples/identity/setuserrole\n\ngo 1.26\n\nrequire github.com/tetrateio/agentrouter-go v0.1.1\n\n// Point this at the directory you extracted the downloaded Go SDK tarball into.\n// The directory name matches the tarball stem on the Download SDK page.\nreplace github.com/tetrateio/agentrouter-go =\u003e ./third_party/agentrouter-go-0.1.1\n"}]},"python":{"files":[{"name":"main.py","content":"\"\"\"Runnable example for the AgentRouter Python SDK.\n\nSet AGENTROUTER_BASE_URL and AGENTROUTER_SESSION_TOKEN in the environment, then run `python main.py`.\n\"\"\"\nimport os\n\nfrom tars.identity.v1 import identity_service_pb2\n\nfrom agentrouter_sdk import Client\n\nclient = Client(\n    base_url=os.environ[\"AGENTROUTER_BASE_URL\"],\n    session_token=os.environ[\"AGENTROUTER_SESSION_TOKEN\"],\n)\n\n# Populate the request fields -- see the \"Request fields\" table above\n# for the available fields and which are required.\nreq = identity_service_pb2.SetUserRoleRequest()\ntry:\n    result = client.identity.set_user_role(req)\n    print(result)\nexcept Exception as err:\n    print(\"Error:\", err)\n"},{"name":"requirements.txt","content":"# Point this at the directory you extracted the downloaded Python SDK tarball\n# into. The directory name matches the tarball stem on the Download SDK page.\n# To install instead from PyPI once published, replace the line below with:\n#   agentrouter-sdk\u003e=0.1.0\nagentrouter-sdk @ file:./third_party/agentrouter-python-0.1.1\n"}]},"typescript":{"files":[{"name":"index.ts","content":"// Runnable example for the AgentRouter TypeScript SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_SESSION_TOKEN in the environment, then run `npm install \u0026\u0026 npx tsx index.ts`.\nimport { Client } from '@tetrate/agentrouter-sdk'\n\nconst client = new Client({\n  baseUrl: process.env.AGENTROUTER_BASE_URL,\n  sessionToken: process.env.AGENTROUTER_SESSION_TOKEN,\n})\n\n// Populate the request fields -- see the \"Request fields\" table above\n// for the available fields and which are required.\nconst req = {}\ntry {\n  const result = await client.identity.setUserRole(req)\n  console.log(result)\n} catch (err) {\n  console.error('Error:', err)\n}\n"},{"name":"package.json","content":"{\n  \"name\": \"identity\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"dependencies\": {\n    \"@tetrate/agentrouter-sdk\": \"file:./third_party/agentrouter-typescript-0.1.1\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"^20.0.0\",\n    \"typescript\": \"^5.4.0\"\n  }\n}\n"},{"name":"tsconfig.json","content":"{\n  \"compilerOptions\": {\n    \"target\": \"ES2020\",\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"bundler\",\n    \"strict\": true,\n    \"esModuleInterop\": true,\n    \"skipLibCheck\": true\n  }\n}\n"}]},"curl":"curl -X POST \"${AGENTROUTER_BASE_URL}/v1/users/01H.../role\" \\\n  -H \"Authorization: Bearer $SESSION_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"role\": \"...\",\n    \"customer_id\": \"...\"\n  }'"},"persona":"Admin","httpVerb":"POST","httpPath":"/v1/users/{user_id}/role","slug":"set-a-users-role"}

Unban a user

Changed in v0.10.0 (security updates): Banning or deleting a user now revokes their MCP OAuth refresh tokens and unredeemed authorization codes, and /oauth/token refuses every grant for a banned or deleted user with invalid_grant. A token exchange no longer creates an account for a deleted email address, and an authorization code can no longer be redeemed twice. Unbanning a deleted user fails with FAILED_PRECONDITION; restore it instead. An MCP access token already issued stays valid until it expires, 24 hours by default.

Changed in v0.4.0 (new features): Administrators can ban and unban a user through the management API. Banning a user signs them out everywhere and disables their API keys. Unbanning lets them sign in again, and they issue new keys -- keys removed by the ban are not restored. Both calls require an interactive session holding the admin scope or the users.edit permission, which is the same authority the delete call requires. An API key is refused even when it carries that permission.

What it does: Clears a user's ban (authn.user.banned, ban_reason, ban_expires). It deliberately restores nothing: sessions deleted by the ban stay logged out and API keys soft-deleted by the ban stay dead, so a re-enabled user must sign in again and issue fresh keys (fraser#8689). Same caller gate as BanUser, declared the same way: lifting a ban is as consequential as writing one.

Request fields:

FieldRequiredDescription
user_idyesThe authn.user.id of the user to unban.

Response fields:

FieldRequiredDescription
user_idoutput-onlyThe unbanned user's id (echoes user_id).
bannedoutput-onlyAlways false on success; present so callers can assert the state the way they did against better-auth's user.banned response.
{"signatures":{"go":"func (x *IdentityClient) UnbanUser(ctx context.Context, req *identityv1.UnbanUserRequest) (*identityv1.UnbanUserResponse, error)","python":"unban_user(req: identity_service_pb2.UnbanUserRequest) -\u003e UnbanUserResponse","typescript":"unbanUser(req: MessageInitShape\u003ctypeof tars_identity_v1_identity_service_pb.UnbanUserRequestSchema\u003e): Promise\u003cUnbanUserResponse\u003e","curl":"curl -X POST \"${AGENTROUTER_BASE_URL}/v1/users/01H.../unban\" \\\n  -H \"Authorization: Bearer $SESSION_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{}'"},"examples":{"go":{"files":[{"name":"main.go","content":"// Command example is a runnable example for the AgentRouter Go SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_SESSION_TOKEN in the environment, then `go run .`.\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\n\tagentrouter \"github.com/tetrateio/agentrouter-go\"\n\tidentityv1 \"github.com/tetrateio/agentrouter-go/genapi/api/tars/identity/v1\"\n)\n\nfunc main() {\n\tctx := context.Background()\n\n\tclient, err := agentrouter.New(ctx,\n\t\tagentrouter.WithBaseURL(os.Getenv(\"AGENTROUTER_BASE_URL\")),\n\t\tagentrouter.WithSessionToken(os.Getenv(\"AGENTROUTER_SESSION_TOKEN\")),\n\t)\n\tif err != nil {\n\t\tlog.Fatalf(\"client: %v\", err)\n\t}\n\n\t// Populate the request fields -- see the \"Request fields\" table above for\n\t// the available fields and which are required.\n\treq := \u0026identityv1.UnbanUserRequest{}\n\n\tresp, err := client.Identity().UnbanUser(ctx, req)\n\tif err != nil {\n\t\tlog.Fatalf(\"call: %v\", err)\n\t}\n\n\tfmt.Printf(\"%+v\\n\", resp)\n}\n"},{"name":"go.mod","content":"module github.com/tetrateio/agentrouter-go-examples/identity/unbanuser\n\ngo 1.26\n\nrequire github.com/tetrateio/agentrouter-go v0.1.1\n\n// Point this at the directory you extracted the downloaded Go SDK tarball into.\n// The directory name matches the tarball stem on the Download SDK page.\nreplace github.com/tetrateio/agentrouter-go =\u003e ./third_party/agentrouter-go-0.1.1\n"}]},"python":{"files":[{"name":"main.py","content":"\"\"\"Runnable example for the AgentRouter Python SDK.\n\nSet AGENTROUTER_BASE_URL and AGENTROUTER_SESSION_TOKEN in the environment, then run `python main.py`.\n\"\"\"\nimport os\n\nfrom tars.identity.v1 import identity_service_pb2\n\nfrom agentrouter_sdk import Client\n\nclient = Client(\n    base_url=os.environ[\"AGENTROUTER_BASE_URL\"],\n    session_token=os.environ[\"AGENTROUTER_SESSION_TOKEN\"],\n)\n\n# Populate the request fields -- see the \"Request fields\" table above\n# for the available fields and which are required.\nreq = identity_service_pb2.UnbanUserRequest()\ntry:\n    result = client.identity.unban_user(req)\n    print(result)\nexcept Exception as err:\n    print(\"Error:\", err)\n"},{"name":"requirements.txt","content":"# Point this at the directory you extracted the downloaded Python SDK tarball\n# into. The directory name matches the tarball stem on the Download SDK page.\n# To install instead from PyPI once published, replace the line below with:\n#   agentrouter-sdk\u003e=0.1.0\nagentrouter-sdk @ file:./third_party/agentrouter-python-0.1.1\n"}]},"typescript":{"files":[{"name":"index.ts","content":"// Runnable example for the AgentRouter TypeScript SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_SESSION_TOKEN in the environment, then run `npm install \u0026\u0026 npx tsx index.ts`.\nimport { Client } from '@tetrate/agentrouter-sdk'\n\nconst client = new Client({\n  baseUrl: process.env.AGENTROUTER_BASE_URL,\n  sessionToken: process.env.AGENTROUTER_SESSION_TOKEN,\n})\n\n// Populate the request fields -- see the \"Request fields\" table above\n// for the available fields and which are required.\nconst req = {}\ntry {\n  const result = await client.identity.unbanUser(req)\n  console.log(result)\n} catch (err) {\n  console.error('Error:', err)\n}\n"},{"name":"package.json","content":"{\n  \"name\": \"identity\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"dependencies\": {\n    \"@tetrate/agentrouter-sdk\": \"file:./third_party/agentrouter-typescript-0.1.1\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"^20.0.0\",\n    \"typescript\": \"^5.4.0\"\n  }\n}\n"},{"name":"tsconfig.json","content":"{\n  \"compilerOptions\": {\n    \"target\": \"ES2020\",\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"bundler\",\n    \"strict\": true,\n    \"esModuleInterop\": true,\n    \"skipLibCheck\": true\n  }\n}\n"}]},"curl":"curl -X POST \"${AGENTROUTER_BASE_URL}/v1/users/01H.../unban\" \\\n  -H \"Authorization: Bearer $SESSION_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{}'"},"persona":"Admin","httpVerb":"POST","httpPath":"/v1/users/{user_id}/unban","slug":"unban-a-user"}

Revoke a user's sessions

Changed in v0.5.0 (new features): Administrators can sign a user out of every session through the management plane API, for example after lowering their role. The user's API keys keep working and the account is not banned. It needs the same users.edit permission as banning a user, and only a signed-in administrator can call it, not an API key.

What it does: Signs a user out everywhere by deleting every authn.session row they hold. It touches nothing else: API keys stay live and the account is not banned. It replaced better-auth's admin revoke-user-sessions endpoint, and the admin console calls it when it lowers a user's role so the old role's sessions do not outlive the change. Revoking your own sessions is allowed. Same caller contract as BanUser: an interactive session that holds the admin scope, or the users.edit permission on a platform-scope RBAC binding. users.edit is the atom the old revoke-user-sessions endpoint authorized on (the same gate as the ban endpoints), so the same administrators are admitted. The request names a bare user_id and no tenant, so users.edit is authorized at PLATFORM reach, and the handler refuses any credential that is not a session, even an API key carrying users.edit. NOT_FOUND for an unknown user_id. A known user with no sessions succeeds with revoked_count 0.

Request fields:

FieldRequiredDescription
user_idyesThe authn.user.id of the user to sign out.

Response fields:

FieldRequiredDescription
user_idoutput-onlyThe user's id (echoes user_id).
revoked_countoutput-onlyHow many sessions were deleted. Zero when the user held none.
{"signatures":{"go":"func (x *IdentityClient) RevokeUserSessions(ctx context.Context, req *identityv1.RevokeUserSessionsRequest) (*identityv1.RevokeUserSessionsResponse, error)","python":"revoke_user_sessions(req: identity_service_pb2.RevokeUserSessionsRequest) -\u003e RevokeUserSessionsResponse","typescript":"revokeUserSessions(req: MessageInitShape\u003ctypeof tars_identity_v1_identity_service_pb.RevokeUserSessionsRequestSchema\u003e): Promise\u003cRevokeUserSessionsResponse\u003e","curl":"curl -X POST \"${AGENTROUTER_BASE_URL}/v1/users/01H.../revoke-sessions\" \\\n  -H \"Authorization: Bearer $SESSION_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{}'"},"examples":{"go":{"files":[{"name":"main.go","content":"// Command example is a runnable example for the AgentRouter Go SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_SESSION_TOKEN in the environment, then `go run .`.\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\n\tagentrouter \"github.com/tetrateio/agentrouter-go\"\n\tidentityv1 \"github.com/tetrateio/agentrouter-go/genapi/api/tars/identity/v1\"\n)\n\nfunc main() {\n\tctx := context.Background()\n\n\tclient, err := agentrouter.New(ctx,\n\t\tagentrouter.WithBaseURL(os.Getenv(\"AGENTROUTER_BASE_URL\")),\n\t\tagentrouter.WithSessionToken(os.Getenv(\"AGENTROUTER_SESSION_TOKEN\")),\n\t)\n\tif err != nil {\n\t\tlog.Fatalf(\"client: %v\", err)\n\t}\n\n\t// Populate the request fields -- see the \"Request fields\" table above for\n\t// the available fields and which are required.\n\treq := \u0026identityv1.RevokeUserSessionsRequest{}\n\n\tresp, err := client.Identity().RevokeUserSessions(ctx, req)\n\tif err != nil {\n\t\tlog.Fatalf(\"call: %v\", err)\n\t}\n\n\tfmt.Printf(\"%+v\\n\", resp)\n}\n"},{"name":"go.mod","content":"module github.com/tetrateio/agentrouter-go-examples/identity/revokeusersessions\n\ngo 1.26\n\nrequire github.com/tetrateio/agentrouter-go v0.1.1\n\n// Point this at the directory you extracted the downloaded Go SDK tarball into.\n// The directory name matches the tarball stem on the Download SDK page.\nreplace github.com/tetrateio/agentrouter-go =\u003e ./third_party/agentrouter-go-0.1.1\n"}]},"python":{"files":[{"name":"main.py","content":"\"\"\"Runnable example for the AgentRouter Python SDK.\n\nSet AGENTROUTER_BASE_URL and AGENTROUTER_SESSION_TOKEN in the environment, then run `python main.py`.\n\"\"\"\nimport os\n\nfrom tars.identity.v1 import identity_service_pb2\n\nfrom agentrouter_sdk import Client\n\nclient = Client(\n    base_url=os.environ[\"AGENTROUTER_BASE_URL\"],\n    session_token=os.environ[\"AGENTROUTER_SESSION_TOKEN\"],\n)\n\n# Populate the request fields -- see the \"Request fields\" table above\n# for the available fields and which are required.\nreq = identity_service_pb2.RevokeUserSessionsRequest()\ntry:\n    result = client.identity.revoke_user_sessions(req)\n    print(result)\nexcept Exception as err:\n    print(\"Error:\", err)\n"},{"name":"requirements.txt","content":"# Point this at the directory you extracted the downloaded Python SDK tarball\n# into. The directory name matches the tarball stem on the Download SDK page.\n# To install instead from PyPI once published, replace the line below with:\n#   agentrouter-sdk\u003e=0.1.0\nagentrouter-sdk @ file:./third_party/agentrouter-python-0.1.1\n"}]},"typescript":{"files":[{"name":"index.ts","content":"// Runnable example for the AgentRouter TypeScript SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_SESSION_TOKEN in the environment, then run `npm install \u0026\u0026 npx tsx index.ts`.\nimport { Client } from '@tetrate/agentrouter-sdk'\n\nconst client = new Client({\n  baseUrl: process.env.AGENTROUTER_BASE_URL,\n  sessionToken: process.env.AGENTROUTER_SESSION_TOKEN,\n})\n\n// Populate the request fields -- see the \"Request fields\" table above\n// for the available fields and which are required.\nconst req = {}\ntry {\n  const result = await client.identity.revokeUserSessions(req)\n  console.log(result)\n} catch (err) {\n  console.error('Error:', err)\n}\n"},{"name":"package.json","content":"{\n  \"name\": \"identity\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"dependencies\": {\n    \"@tetrate/agentrouter-sdk\": \"file:./third_party/agentrouter-typescript-0.1.1\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"^20.0.0\",\n    \"typescript\": \"^5.4.0\"\n  }\n}\n"},{"name":"tsconfig.json","content":"{\n  \"compilerOptions\": {\n    \"target\": \"ES2020\",\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"bundler\",\n    \"strict\": true,\n    \"esModuleInterop\": true,\n    \"skipLibCheck\": true\n  }\n}\n"}]},"curl":"curl -X POST \"${AGENTROUTER_BASE_URL}/v1/users/01H.../revoke-sessions\" \\\n  -H \"Authorization: Bearer $SESSION_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{}'"},"persona":"Admin","httpVerb":"POST","httpPath":"/v1/users/{user_id}/revoke-sessions","slug":"revoke-a-users-sessions"}

Set a user's password

Changed in v0.11.0 (new features): SetUserPassword (POST /v1/users/{user_id}/password) lets a Super Admin set another user's password. It signs that user out everywhere and is recorded in the audit log as SET_USER_PASSWORD.

What it does: Sets another user's password and signs them out everywhere. It replaced better-auth's admin set-user-password endpoint (fraser#11247). The new hash, the session revocation and the audit row commit together, or nothing commits. API keys are left alone. Only a signed-in super_admin may call it, and the handler decides, as on GrantSuperAdmin: the session must hold the admin scope (which needs the platform_admin binding as well as the role), and authn.user.role must be super_admin, read in the same transaction as the write. There is no users.edit leg, so a user_admin or an org admin is refused, and an API key is refused even when its owner is a super_admin. INVALID_ARGUMENT: user_id is empty, or the password is shorter than 8 or longer than 128 characters. FAILED_PRECONDITION: the target is the caller (use ChangePassword), is deleted, or has no password account (an SSO-only user). NOT_FOUND for an unknown user_id.

Request fields:

FieldRequiredDescription
user_idyesThe authn.user.id of the user whose password is set.
new_passwordyesThe new password, 8 to 128 characters, as on sign-up and reset. Never stored or logged as given; only its hash is written.

Response fields:

FieldRequiredDescription
user_idoutput-onlyThe user's id (echoes user_id).
revoked_countoutput-onlyHow many of the user's sessions were deleted. Zero when they held none.
{"signatures":{"go":"func (x *IdentityClient) SetUserPassword(ctx context.Context, req *identityv1.SetUserPasswordRequest) (*identityv1.SetUserPasswordResponse, error)","python":"set_user_password(req: identity_service_pb2.SetUserPasswordRequest) -\u003e SetUserPasswordResponse","typescript":"setUserPassword(req: MessageInitShape\u003ctypeof tars_identity_v1_identity_service_pb.SetUserPasswordRequestSchema\u003e): Promise\u003cSetUserPasswordResponse\u003e","curl":"curl -X POST \"${AGENTROUTER_BASE_URL}/v1/users/01H.../password\" \\\n  -H \"Authorization: Bearer $SESSION_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"new_password\": \"...\"\n  }'"},"examples":{"go":{"files":[{"name":"main.go","content":"// Command example is a runnable example for the AgentRouter Go SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_SESSION_TOKEN in the environment, then `go run .`.\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\n\tagentrouter \"github.com/tetrateio/agentrouter-go\"\n\tidentityv1 \"github.com/tetrateio/agentrouter-go/genapi/api/tars/identity/v1\"\n)\n\nfunc main() {\n\tctx := context.Background()\n\n\tclient, err := agentrouter.New(ctx,\n\t\tagentrouter.WithBaseURL(os.Getenv(\"AGENTROUTER_BASE_URL\")),\n\t\tagentrouter.WithSessionToken(os.Getenv(\"AGENTROUTER_SESSION_TOKEN\")),\n\t)\n\tif err != nil {\n\t\tlog.Fatalf(\"client: %v\", err)\n\t}\n\n\t// Populate the request fields -- see the \"Request fields\" table above for\n\t// the available fields and which are required.\n\treq := \u0026identityv1.SetUserPasswordRequest{}\n\n\tresp, err := client.Identity().SetUserPassword(ctx, req)\n\tif err != nil {\n\t\tlog.Fatalf(\"call: %v\", err)\n\t}\n\n\tfmt.Printf(\"%+v\\n\", resp)\n}\n"},{"name":"go.mod","content":"module github.com/tetrateio/agentrouter-go-examples/identity/setuserpassword\n\ngo 1.26\n\nrequire github.com/tetrateio/agentrouter-go v0.1.1\n\n// Point this at the directory you extracted the downloaded Go SDK tarball into.\n// The directory name matches the tarball stem on the Download SDK page.\nreplace github.com/tetrateio/agentrouter-go =\u003e ./third_party/agentrouter-go-0.1.1\n"}]},"python":{"files":[{"name":"main.py","content":"\"\"\"Runnable example for the AgentRouter Python SDK.\n\nSet AGENTROUTER_BASE_URL and AGENTROUTER_SESSION_TOKEN in the environment, then run `python main.py`.\n\"\"\"\nimport os\n\nfrom tars.identity.v1 import identity_service_pb2\n\nfrom agentrouter_sdk import Client\n\nclient = Client(\n    base_url=os.environ[\"AGENTROUTER_BASE_URL\"],\n    session_token=os.environ[\"AGENTROUTER_SESSION_TOKEN\"],\n)\n\n# Populate the request fields -- see the \"Request fields\" table above\n# for the available fields and which are required.\nreq = identity_service_pb2.SetUserPasswordRequest()\ntry:\n    result = client.identity.set_user_password(req)\n    print(result)\nexcept Exception as err:\n    print(\"Error:\", err)\n"},{"name":"requirements.txt","content":"# Point this at the directory you extracted the downloaded Python SDK tarball\n# into. The directory name matches the tarball stem on the Download SDK page.\n# To install instead from PyPI once published, replace the line below with:\n#   agentrouter-sdk\u003e=0.1.0\nagentrouter-sdk @ file:./third_party/agentrouter-python-0.1.1\n"}]},"typescript":{"files":[{"name":"index.ts","content":"// Runnable example for the AgentRouter TypeScript SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_SESSION_TOKEN in the environment, then run `npm install \u0026\u0026 npx tsx index.ts`.\nimport { Client } from '@tetrate/agentrouter-sdk'\n\nconst client = new Client({\n  baseUrl: process.env.AGENTROUTER_BASE_URL,\n  sessionToken: process.env.AGENTROUTER_SESSION_TOKEN,\n})\n\n// Populate the request fields -- see the \"Request fields\" table above\n// for the available fields and which are required.\nconst req = {}\ntry {\n  const result = await client.identity.setUserPassword(req)\n  console.log(result)\n} catch (err) {\n  console.error('Error:', err)\n}\n"},{"name":"package.json","content":"{\n  \"name\": \"identity\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"dependencies\": {\n    \"@tetrate/agentrouter-sdk\": \"file:./third_party/agentrouter-typescript-0.1.1\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"^20.0.0\",\n    \"typescript\": \"^5.4.0\"\n  }\n}\n"},{"name":"tsconfig.json","content":"{\n  \"compilerOptions\": {\n    \"target\": \"ES2020\",\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"bundler\",\n    \"strict\": true,\n    \"esModuleInterop\": true,\n    \"skipLibCheck\": true\n  }\n}\n"}]},"curl":"curl -X POST \"${AGENTROUTER_BASE_URL}/v1/users/01H.../password\" \\\n  -H \"Authorization: Bearer $SESSION_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"new_password\": \"...\"\n  }'"},"persona":"Dashboard user (session token)","httpVerb":"POST","httpPath":"/v1/users/{user_id}/password","slug":"set-a-users-password"}

Delete a user

Changed in v0.10.0 (security updates): Banning or deleting a user now revokes their MCP OAuth refresh tokens and unredeemed authorization codes, and /oauth/token refuses every grant for a banned or deleted user with invalid_grant. A token exchange no longer creates an account for a deleted email address, and an authorization code can no longer be redeemed twice. Unbanning a deleted user fails with FAILED_PRECONDITION; restore it instead. An MCP access token already issued stays valid until it expires, 24 hours by default.

Changed in v0.4.0 (new features): Platform operators can soft-delete and restore user accounts over the management API. DELETE /v1/users/{user_id} marks the account deleted, revokes its API keys, and ends its sessions in one atomic operation; POST /v1/users/{user_id}/restore reverses it. The delete refuses the last active administrator of the platform or of any organization the user administers, so a deployment cannot be left unadministrable.

What it does: Soft-deletes a user (fraser#8692). The authn.user row is never removed: delete sets banned = true with the USER_DELETED ban reason, stamps deleted_at/deleted_by, revokes every live API key the user owns (writing deleted_at, revoked_at and is_active), deletes the RBAC bindings those keys carried, and removes their authn.session rows -- all in one transaction, so a key-revocation failure leaves the user unchanged. RestoreUser reverses it. Restorable by design, and visibly distinct from an operator ban: a deleted account carries the USER_DELETED sentinel as its ban reason. Refuses with FAILED_PRECONDITION when the target is the last active administrator -- of the platform (legacy admin role or a platform-scope admin binding) or of any customer they hold an org-scope admin binding for -- so a deployment cannot delete its way out of being administrable. Refuses with FAILED_PRECONDITION when the user is already deleted; NOT_FOUND for an unknown user_id.

Request fields:

FieldRequiredDescription
user_idyesThe authn.user.id of the account to soft-delete.

Response fields:

FieldRequiredDescription
user_idnoThe deleted user's id (echoes the request).
deleted_atoutput-onlyWhen the delete landed (the authn.user.deleted_at stamp).
revoked_api_keysoutput-onlyLive API keys the transaction revoked (deleted_at + revoked_at + is_active).
deleted_sessionsoutput-onlyauthn.session rows the transaction deleted.
{"signatures":{"go":"func (x *IdentityClient) DeleteUser(ctx context.Context, req *identityv1.DeleteUserRequest) (*identityv1.DeleteUserResponse, error)","python":"delete_user(req: identity_service_pb2.DeleteUserRequest) -\u003e DeleteUserResponse","typescript":"deleteUser(req: MessageInitShape\u003ctypeof tars_identity_v1_identity_service_pb.DeleteUserRequestSchema\u003e): Promise\u003cDeleteUserResponse\u003e","curl":"curl -X DELETE \"${AGENTROUTER_BASE_URL}/v1/users/01H...\" \\\n  -H \"Authorization: Bearer $SESSION_TOKEN\""},"examples":{"go":{"files":[{"name":"main.go","content":"// Command example is a runnable example for the AgentRouter Go SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_SESSION_TOKEN in the environment, then `go run .`.\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\n\tagentrouter \"github.com/tetrateio/agentrouter-go\"\n\tidentityv1 \"github.com/tetrateio/agentrouter-go/genapi/api/tars/identity/v1\"\n)\n\nfunc main() {\n\tctx := context.Background()\n\n\tclient, err := agentrouter.New(ctx,\n\t\tagentrouter.WithBaseURL(os.Getenv(\"AGENTROUTER_BASE_URL\")),\n\t\tagentrouter.WithSessionToken(os.Getenv(\"AGENTROUTER_SESSION_TOKEN\")),\n\t)\n\tif err != nil {\n\t\tlog.Fatalf(\"client: %v\", err)\n\t}\n\n\t// Populate the request fields -- see the \"Request fields\" table above for\n\t// the available fields and which are required.\n\treq := \u0026identityv1.DeleteUserRequest{}\n\n\tresp, err := client.Identity().DeleteUser(ctx, req)\n\tif err != nil {\n\t\tlog.Fatalf(\"call: %v\", err)\n\t}\n\n\tfmt.Printf(\"%+v\\n\", resp)\n}\n"},{"name":"go.mod","content":"module github.com/tetrateio/agentrouter-go-examples/identity/deleteuser\n\ngo 1.26\n\nrequire github.com/tetrateio/agentrouter-go v0.1.1\n\n// Point this at the directory you extracted the downloaded Go SDK tarball into.\n// The directory name matches the tarball stem on the Download SDK page.\nreplace github.com/tetrateio/agentrouter-go =\u003e ./third_party/agentrouter-go-0.1.1\n"}]},"python":{"files":[{"name":"main.py","content":"\"\"\"Runnable example for the AgentRouter Python SDK.\n\nSet AGENTROUTER_BASE_URL and AGENTROUTER_SESSION_TOKEN in the environment, then run `python main.py`.\n\"\"\"\nimport os\n\nfrom tars.identity.v1 import identity_service_pb2\n\nfrom agentrouter_sdk import Client\n\nclient = Client(\n    base_url=os.environ[\"AGENTROUTER_BASE_URL\"],\n    session_token=os.environ[\"AGENTROUTER_SESSION_TOKEN\"],\n)\n\n# Populate the request fields -- see the \"Request fields\" table above\n# for the available fields and which are required.\nreq = identity_service_pb2.DeleteUserRequest()\ntry:\n    result = client.identity.delete_user(req)\n    print(result)\nexcept Exception as err:\n    print(\"Error:\", err)\n"},{"name":"requirements.txt","content":"# Point this at the directory you extracted the downloaded Python SDK tarball\n# into. The directory name matches the tarball stem on the Download SDK page.\n# To install instead from PyPI once published, replace the line below with:\n#   agentrouter-sdk\u003e=0.1.0\nagentrouter-sdk @ file:./third_party/agentrouter-python-0.1.1\n"}]},"typescript":{"files":[{"name":"index.ts","content":"// Runnable example for the AgentRouter TypeScript SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_SESSION_TOKEN in the environment, then run `npm install \u0026\u0026 npx tsx index.ts`.\nimport { Client } from '@tetrate/agentrouter-sdk'\n\nconst client = new Client({\n  baseUrl: process.env.AGENTROUTER_BASE_URL,\n  sessionToken: process.env.AGENTROUTER_SESSION_TOKEN,\n})\n\n// Populate the request fields -- see the \"Request fields\" table above\n// for the available fields and which are required.\nconst req = {}\ntry {\n  const result = await client.identity.deleteUser(req)\n  console.log(result)\n} catch (err) {\n  console.error('Error:', err)\n}\n"},{"name":"package.json","content":"{\n  \"name\": \"identity\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"dependencies\": {\n    \"@tetrate/agentrouter-sdk\": \"file:./third_party/agentrouter-typescript-0.1.1\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"^20.0.0\",\n    \"typescript\": \"^5.4.0\"\n  }\n}\n"},{"name":"tsconfig.json","content":"{\n  \"compilerOptions\": {\n    \"target\": \"ES2020\",\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"bundler\",\n    \"strict\": true,\n    \"esModuleInterop\": true,\n    \"skipLibCheck\": true\n  }\n}\n"}]},"curl":"curl -X DELETE \"${AGENTROUTER_BASE_URL}/v1/users/01H...\" \\\n  -H \"Authorization: Bearer $SESSION_TOKEN\""},"persona":"Admin","httpVerb":"DELETE","httpPath":"/v1/users/{user_id}","slug":"delete-a-user"}

Restore a deleted user

Changed in v0.4.0 (new features): Platform operators can soft-delete and restore user accounts over the management API. DELETE /v1/users/{user_id} marks the account deleted, revokes its API keys, and ends its sessions in one atomic operation; POST /v1/users/{user_id}/restore reverses it. The delete refuses the last active administrator of the platform or of any organization the user administers, so a deployment cannot be left unadministrable.

What it does: Reverses DeleteUser: it clears deleted_at/deleted_by, and lifts the USER_DELETED ban the delete wrote. It does not touch an operator's separate suspension (a ban whose reason is not the USER_DELETED sentinel survives a restore), and it does not resurrect the revoked API keys or deleted sessions -- those stay revoked, and the user mints new credentials on next login. NOT_FOUND when the user_id has no row or carries no deleted_at (shaped as "not deleted").

Request fields:

FieldRequiredDescription
user_idyesThe authn.user.id of the account to restore.

Response fields:

FieldRequiredDescription
user_idnoThe restored user's id (echoes the request).
bannedoutput-onlyThe account's ban state AFTER the restore. False when restore lifted the USER_DELETED ban; true when an operator suspension with a different ban reason was left in place (see RestoreUser's docs).
ban_reasonoutput-onlyThe surviving ban reason, when banned is true.
{"signatures":{"go":"func (x *IdentityClient) RestoreUser(ctx context.Context, req *identityv1.RestoreUserRequest) (*identityv1.RestoreUserResponse, error)","python":"restore_user(req: identity_service_pb2.RestoreUserRequest) -\u003e RestoreUserResponse","typescript":"restoreUser(req: MessageInitShape\u003ctypeof tars_identity_v1_identity_service_pb.RestoreUserRequestSchema\u003e): Promise\u003cRestoreUserResponse\u003e","curl":"curl -X POST \"${AGENTROUTER_BASE_URL}/v1/users/01H.../restore\" \\\n  -H \"Authorization: Bearer $SESSION_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{}'"},"examples":{"go":{"files":[{"name":"main.go","content":"// Command example is a runnable example for the AgentRouter Go SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_SESSION_TOKEN in the environment, then `go run .`.\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\n\tagentrouter \"github.com/tetrateio/agentrouter-go\"\n\tidentityv1 \"github.com/tetrateio/agentrouter-go/genapi/api/tars/identity/v1\"\n)\n\nfunc main() {\n\tctx := context.Background()\n\n\tclient, err := agentrouter.New(ctx,\n\t\tagentrouter.WithBaseURL(os.Getenv(\"AGENTROUTER_BASE_URL\")),\n\t\tagentrouter.WithSessionToken(os.Getenv(\"AGENTROUTER_SESSION_TOKEN\")),\n\t)\n\tif err != nil {\n\t\tlog.Fatalf(\"client: %v\", err)\n\t}\n\n\t// Populate the request fields -- see the \"Request fields\" table above for\n\t// the available fields and which are required.\n\treq := \u0026identityv1.RestoreUserRequest{}\n\n\tresp, err := client.Identity().RestoreUser(ctx, req)\n\tif err != nil {\n\t\tlog.Fatalf(\"call: %v\", err)\n\t}\n\n\tfmt.Printf(\"%+v\\n\", resp)\n}\n"},{"name":"go.mod","content":"module github.com/tetrateio/agentrouter-go-examples/identity/restoreuser\n\ngo 1.26\n\nrequire github.com/tetrateio/agentrouter-go v0.1.1\n\n// Point this at the directory you extracted the downloaded Go SDK tarball into.\n// The directory name matches the tarball stem on the Download SDK page.\nreplace github.com/tetrateio/agentrouter-go =\u003e ./third_party/agentrouter-go-0.1.1\n"}]},"python":{"files":[{"name":"main.py","content":"\"\"\"Runnable example for the AgentRouter Python SDK.\n\nSet AGENTROUTER_BASE_URL and AGENTROUTER_SESSION_TOKEN in the environment, then run `python main.py`.\n\"\"\"\nimport os\n\nfrom tars.identity.v1 import identity_service_pb2\n\nfrom agentrouter_sdk import Client\n\nclient = Client(\n    base_url=os.environ[\"AGENTROUTER_BASE_URL\"],\n    session_token=os.environ[\"AGENTROUTER_SESSION_TOKEN\"],\n)\n\n# Populate the request fields -- see the \"Request fields\" table above\n# for the available fields and which are required.\nreq = identity_service_pb2.RestoreUserRequest()\ntry:\n    result = client.identity.restore_user(req)\n    print(result)\nexcept Exception as err:\n    print(\"Error:\", err)\n"},{"name":"requirements.txt","content":"# Point this at the directory you extracted the downloaded Python SDK tarball\n# into. The directory name matches the tarball stem on the Download SDK page.\n# To install instead from PyPI once published, replace the line below with:\n#   agentrouter-sdk\u003e=0.1.0\nagentrouter-sdk @ file:./third_party/agentrouter-python-0.1.1\n"}]},"typescript":{"files":[{"name":"index.ts","content":"// Runnable example for the AgentRouter TypeScript SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_SESSION_TOKEN in the environment, then run `npm install \u0026\u0026 npx tsx index.ts`.\nimport { Client } from '@tetrate/agentrouter-sdk'\n\nconst client = new Client({\n  baseUrl: process.env.AGENTROUTER_BASE_URL,\n  sessionToken: process.env.AGENTROUTER_SESSION_TOKEN,\n})\n\n// Populate the request fields -- see the \"Request fields\" table above\n// for the available fields and which are required.\nconst req = {}\ntry {\n  const result = await client.identity.restoreUser(req)\n  console.log(result)\n} catch (err) {\n  console.error('Error:', err)\n}\n"},{"name":"package.json","content":"{\n  \"name\": \"identity\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"dependencies\": {\n    \"@tetrate/agentrouter-sdk\": \"file:./third_party/agentrouter-typescript-0.1.1\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"^20.0.0\",\n    \"typescript\": \"^5.4.0\"\n  }\n}\n"},{"name":"tsconfig.json","content":"{\n  \"compilerOptions\": {\n    \"target\": \"ES2020\",\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"bundler\",\n    \"strict\": true,\n    \"esModuleInterop\": true,\n    \"skipLibCheck\": true\n  }\n}\n"}]},"curl":"curl -X POST \"${AGENTROUTER_BASE_URL}/v1/users/01H.../restore\" \\\n  -H \"Authorization: Bearer $SESSION_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{}'"},"persona":"Admin","httpVerb":"POST","httpPath":"/v1/users/{user_id}/restore","slug":"restore-a-deleted-user"}

Search users

Changed in v0.9.0 (new features): User directory responses include the team name alongside its ID, without a separate team lookup.

Changed in v0.5.0 (behaviour changes): Reaching a fleet-wide operation now requires an explicit platform binding for a caller that does not hold the admin scope. A management API key that reached one of these operations on a coarse scope alone, such as read or metrics_reader, is refused after this upgrade.

Changed in v0.5.0 (new features): Teams can now be managed end to end through PolicyService instead of the console's own database. AddUserGroupMembers and RemoveUserGroupMembers move users in and out of a team (a user already in another team of the same organization is moved, and the response names the team they left), ListUserGroupMembers pages a team's members with a name or email search and name or recently-added order, and every read of a team carries its member count and the first five members. ListUserGroups can filter by whether a team has members, search descriptions, sort by name, member count, created or updated time, and report how many teams have members. UpdateUserGroup can clear a description with clear_description. A member's email, avatar and ban flag are returned only to credentials that hold users_reader and only for members inside the caller's directory boundary; otherwise callers see ids and names. Email search follows the same scope and per-member boundary rules. Directory reads hide team IDs outside the caller's organization boundary; team filters and counts use the same visible membership.

Changed in v0.4.0 (new features): The management API now serves the user directory the Admin Console reads. POST /v1/users/search returns a page of users matched on name or email substring, filtered by account status, role, team or id set, sorted on a chosen field, and POST /v1/users/stats returns the per-status counts over those same filters. A request that names no status leaves deleted users out, so the active population is what a caller sees by default. A directory user also carries its account status, ban reason and deletion time, so a console can render a deleted user in history instead of resolving nothing.

What it does: Browses the directory the way the admin console's Users page needs (fraser#8695): a case-insensitive substring match on name or email, a status filter, and limit/offset paging with a total, so the console renders its numbered pages and stat cards without reading the auth DB directly (fraser#5521). The same users_reader gate and in-handler tenant boundary as GetUser/ListUsers apply. An UNSPECIFIED status hides deleted users -- deleted_at IS NULL is the default everywhere active lists are rendered -- so a caller sees the active population unless it asks for USER_STATUS_DELETED explicitly. ACTIVE and BANNED also exclude deleted users: a delete is a separate state from an operator ban, not a flavor of one.

Request fields:

FieldRequiredDescription
searchnoCase-insensitive substring match on name OR email. Empty matches everyone (under the filters below).
statusnoStatus filter. UNSPECIFIED hides deleted users (the default active population); USER_STATUS_DELETED returns only deleted users. The value never selects the wire status USER_STATUS_UNSPECIFIED.
rolenoRestrict to this built-in role (authn.user.role). Empty matches any role. An explicit "user" also matches rows whose role is NULL -- the same normalization the console's Role filter applies.
user_group_idnoRestrict to this team (authn.user.tag group id). Empty matches every team; use unassigned_user_group to select users in no team.
user_idsnoRestrict the search to these users (a roster view sends the roster's id set). Empty means no restriction -- a caller holding an empty roster must short-circuit rather than send an empty set.
exclude_user_idsnoExclude these users (the add-member picker sends the current roster so it only offers candidates). Empty excludes none.
limitnoPage size. 0 selects the default of 50; values above 100 are rejected.
offsetnoPage offset; 0 is the first page.
unassigned_user_groupnoRestrict to users in NO team (authn.user.tag is null) -- the console's Team filter's "Unassigned" option. When true, user_group_id is ignored.
sort_bynoPage ordering. The default is created_at descending, then updated_at -- the console's "Recent" default.
sort_dirnoSort direction; UNSPECIFIED follows the console's per-field default (descending for the timestamps, ascending otherwise).
exclude_user_group_idnoExclude members of this team (users whose tag is this id). Users in no team are NOT excluded -- they are not in the team. The team-add picker's candidate list (fraser#8695).
has_user_groupnoRestrict to users in SOME team (authn.user.tag is not null) -- the team-management screens enumerate their org's teamed users in one paged pass and bucket them per team, instead of one call per team.

Response fields:

FieldRequiredDescription
usersnoThe matched page, ordered per the request's sort_by/sort_dir with the id as the tiebreaker.
totalnoUsers matching the filters across all pages -- the count numbered pagination and stat cards render.
{"signatures":{"go":"func (x *IdentityClient) SearchUsers(ctx context.Context, req *identityv1.SearchUsersRequest) (*identityv1.SearchUsersResponse, error)","python":"search_users(req: identity_service_pb2.SearchUsersRequest) -\u003e SearchUsersResponse","typescript":"searchUsers(req: MessageInitShape\u003ctypeof tars_identity_v1_identity_service_pb.SearchUsersRequestSchema\u003e): Promise\u003cSearchUsersResponse\u003e","curl":"curl -X POST \"${AGENTROUTER_BASE_URL}/v1/users/search\" \\\n  -H \"Authorization: Bearer ${AGENTROUTER_API_KEY}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"search\": \"...\",\n    \"status\": \"...\",\n    \"role\": \"...\",\n    \"user_group_id\": \"...\",\n    \"user_ids\": [],\n    \"exclude_user_ids\": [],\n    \"limit\": 0,\n    \"offset\": 0,\n    \"unassigned_user_group\": false,\n    \"sort_by\": \"...\",\n    \"sort_dir\": \"...\",\n    \"exclude_user_group_id\": \"...\",\n    \"has_user_group\": false\n  }'"},"examples":{"go":{"files":[{"name":"main.go","content":"// Command example is a runnable example for the AgentRouter Go SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then `go run .`.\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\n\tagentrouter \"github.com/tetrateio/agentrouter-go\"\n\tidentityv1 \"github.com/tetrateio/agentrouter-go/genapi/api/tars/identity/v1\"\n)\n\nfunc main() {\n\tctx := context.Background()\n\n\tclient, err := agentrouter.New(ctx,\n\t\tagentrouter.WithBaseURL(os.Getenv(\"AGENTROUTER_BASE_URL\")),\n\t\tagentrouter.WithAPIKey(os.Getenv(\"AGENTROUTER_API_KEY\")),\n\t)\n\tif err != nil {\n\t\tlog.Fatalf(\"client: %v\", err)\n\t}\n\n\t// Populate the request fields -- see the \"Request fields\" table above for\n\t// the available fields and which are required.\n\treq := \u0026identityv1.SearchUsersRequest{}\n\n\tresp, err := client.Identity().SearchUsers(ctx, req)\n\tif err != nil {\n\t\tlog.Fatalf(\"call: %v\", err)\n\t}\n\n\tfmt.Printf(\"%+v\\n\", resp)\n}\n"},{"name":"go.mod","content":"module github.com/tetrateio/agentrouter-go-examples/identity/searchusers\n\ngo 1.26\n\nrequire github.com/tetrateio/agentrouter-go v0.1.1\n\n// Point this at the directory you extracted the downloaded Go SDK tarball into.\n// The directory name matches the tarball stem on the Download SDK page.\nreplace github.com/tetrateio/agentrouter-go =\u003e ./third_party/agentrouter-go-0.1.1\n"}]},"python":{"files":[{"name":"main.py","content":"\"\"\"Runnable example for the AgentRouter Python SDK.\n\nSet AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then run `python main.py`.\n\"\"\"\nimport os\n\nfrom tars.identity.v1 import identity_service_pb2\n\nfrom agentrouter_sdk import Client\n\nclient = Client(\n    base_url=os.environ[\"AGENTROUTER_BASE_URL\"],\n    api_key=os.environ[\"AGENTROUTER_API_KEY\"],\n)\n\n# Populate the request fields -- see the \"Request fields\" table above\n# for the available fields and which are required.\nreq = identity_service_pb2.SearchUsersRequest()\ntry:\n    result = client.identity.search_users(req)\n    print(result)\nexcept Exception as err:\n    print(\"Error:\", err)\n"},{"name":"requirements.txt","content":"# Point this at the directory you extracted the downloaded Python SDK tarball\n# into. The directory name matches the tarball stem on the Download SDK page.\n# To install instead from PyPI once published, replace the line below with:\n#   agentrouter-sdk\u003e=0.1.0\nagentrouter-sdk @ file:./third_party/agentrouter-python-0.1.1\n"}]},"typescript":{"files":[{"name":"index.ts","content":"// Runnable example for the AgentRouter TypeScript SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then run `npm install \u0026\u0026 npx tsx index.ts`.\nimport { Client } from '@tetrate/agentrouter-sdk'\n\nconst client = new Client({\n  baseUrl: process.env.AGENTROUTER_BASE_URL,\n  apiKey: process.env.AGENTROUTER_API_KEY,\n})\n\n// Populate the request fields -- see the \"Request fields\" table above\n// for the available fields and which are required.\nconst req = {}\ntry {\n  const result = await client.identity.searchUsers(req)\n  console.log(result)\n} catch (err) {\n  console.error('Error:', err)\n}\n"},{"name":"package.json","content":"{\n  \"name\": \"identity\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"dependencies\": {\n    \"@tetrate/agentrouter-sdk\": \"file:./third_party/agentrouter-typescript-0.1.1\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"^20.0.0\",\n    \"typescript\": \"^5.4.0\"\n  }\n}\n"},{"name":"tsconfig.json","content":"{\n  \"compilerOptions\": {\n    \"target\": \"ES2020\",\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"bundler\",\n    \"strict\": true,\n    \"esModuleInterop\": true,\n    \"skipLibCheck\": true\n  }\n}\n"}]},"curl":"curl -X POST \"${AGENTROUTER_BASE_URL}/v1/users/search\" \\\n  -H \"Authorization: Bearer ${AGENTROUTER_API_KEY}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"search\": \"...\",\n    \"status\": \"...\",\n    \"role\": \"...\",\n    \"user_group_id\": \"...\",\n    \"user_ids\": [],\n    \"exclude_user_ids\": [],\n    \"limit\": 0,\n    \"offset\": 0,\n    \"unassigned_user_group\": false,\n    \"sort_by\": \"...\",\n    \"sort_dir\": \"...\",\n    \"exclude_user_group_id\": \"...\",\n    \"has_user_group\": false\n  }'"},"persona":"Authenticated (API key or session token)","httpVerb":"POST","httpPath":"/v1/users/search","slug":"search-users"}

Get user stats

Changed in v0.5.0 (behaviour changes): Reaching a fleet-wide operation now requires an explicit platform binding for a caller that does not hold the admin scope. A management API key that reached one of these operations on a coarse scope alone, such as read or metrics_reader, is refused after this upgrade.

Changed in v0.4.0 (new features): The management API now serves the user directory the Admin Console reads. POST /v1/users/search returns a page of users matched on name or email substring, filtered by account status, role, team or id set, sorted on a chosen field, and POST /v1/users/stats returns the per-status counts over those same filters. A request that names no status leaves deleted users out, so the active population is what a caller sees by default. A directory user also carries its account status, ban reason and deletion time, so a console can render a deleted user in history instead of resolving nothing.

What it does: Returns the per-status counts the Users page's stat cards render, over the same boundary and the same non-status filters as SearchUsers -- one call instead of four filtered searches. The status filter is the one SearchUsers predicate this request omits: the counts ARE the status breakdown. total counts non-deleted users (active + banned), so deleted users never inflate a "total users" card.

Request fields:

FieldRequiredDescription
searchnoOptional case-insensitive substring match on name OR email, the same match SearchUsers applies. Empty counts everyone.
user_idsnoRestrict the counts to these users (a roster view scopes its stat cards to the roster's id set). Empty means no restriction.
exclude_user_idsnoExclude these users from the counts, the same exclusion semantics as SearchUsers. Empty excludes none.
rolenoRestrict the counts to this built-in role, the same normalization SearchUsers applies (an explicit "user" also counts rows whose role is NULL). Empty counts any role.
user_group_idnoRestrict the counts to this team (authn.user.tag group id). Empty counts every team; use unassigned_user_group to count users in no team.
unassigned_user_groupnoCount only users in NO team (authn.user.tag is null). When true, user_group_id is ignored -- the same precedence SearchUsers applies.
exclude_user_group_idnoExclude members of this team from the counts. Users in no team are NOT excluded: they are not in the team.
has_user_groupnoCount only users in SOME team (authn.user.tag is not null).

Response fields:

FieldRequiredDescription
totalnoNon-deleted users (active + banned): a "total users" card never counts deleted accounts.
activenoUsers with no ban in effect and not deleted.
bannednoOperator suspensions only -- a ban in effect on a row that is not deleted.
deletednoDeleted users (deleted_at set).
{"signatures":{"go":"func (x *IdentityClient) GetUserStats(ctx context.Context, req *identityv1.GetUserStatsRequest) (*identityv1.GetUserStatsResponse, error)","python":"get_user_stats(req: identity_service_pb2.GetUserStatsRequest) -\u003e GetUserStatsResponse","typescript":"getUserStats(req: MessageInitShape\u003ctypeof tars_identity_v1_identity_service_pb.GetUserStatsRequestSchema\u003e): Promise\u003cGetUserStatsResponse\u003e","curl":"curl -X POST \"${AGENTROUTER_BASE_URL}/v1/users/stats\" \\\n  -H \"Authorization: Bearer ${AGENTROUTER_API_KEY}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"search\": \"...\",\n    \"user_ids\": [],\n    \"exclude_user_ids\": [],\n    \"role\": \"...\",\n    \"user_group_id\": \"...\",\n    \"unassigned_user_group\": false,\n    \"exclude_user_group_id\": \"...\",\n    \"has_user_group\": false\n  }'"},"examples":{"go":{"files":[{"name":"main.go","content":"// Command example is a runnable example for the AgentRouter Go SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then `go run .`.\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\n\tagentrouter \"github.com/tetrateio/agentrouter-go\"\n\tidentityv1 \"github.com/tetrateio/agentrouter-go/genapi/api/tars/identity/v1\"\n)\n\nfunc main() {\n\tctx := context.Background()\n\n\tclient, err := agentrouter.New(ctx,\n\t\tagentrouter.WithBaseURL(os.Getenv(\"AGENTROUTER_BASE_URL\")),\n\t\tagentrouter.WithAPIKey(os.Getenv(\"AGENTROUTER_API_KEY\")),\n\t)\n\tif err != nil {\n\t\tlog.Fatalf(\"client: %v\", err)\n\t}\n\n\t// Populate the request fields -- see the \"Request fields\" table above for\n\t// the available fields and which are required.\n\treq := \u0026identityv1.GetUserStatsRequest{}\n\n\tresp, err := client.Identity().GetUserStats(ctx, req)\n\tif err != nil {\n\t\tlog.Fatalf(\"call: %v\", err)\n\t}\n\n\tfmt.Printf(\"%+v\\n\", resp)\n}\n"},{"name":"go.mod","content":"module github.com/tetrateio/agentrouter-go-examples/identity/getuserstats\n\ngo 1.26\n\nrequire github.com/tetrateio/agentrouter-go v0.1.1\n\n// Point this at the directory you extracted the downloaded Go SDK tarball into.\n// The directory name matches the tarball stem on the Download SDK page.\nreplace github.com/tetrateio/agentrouter-go =\u003e ./third_party/agentrouter-go-0.1.1\n"}]},"python":{"files":[{"name":"main.py","content":"\"\"\"Runnable example for the AgentRouter Python SDK.\n\nSet AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then run `python main.py`.\n\"\"\"\nimport os\n\nfrom tars.identity.v1 import identity_service_pb2\n\nfrom agentrouter_sdk import Client\n\nclient = Client(\n    base_url=os.environ[\"AGENTROUTER_BASE_URL\"],\n    api_key=os.environ[\"AGENTROUTER_API_KEY\"],\n)\n\n# Populate the request fields -- see the \"Request fields\" table above\n# for the available fields and which are required.\nreq = identity_service_pb2.GetUserStatsRequest()\ntry:\n    result = client.identity.get_user_stats(req)\n    print(result)\nexcept Exception as err:\n    print(\"Error:\", err)\n"},{"name":"requirements.txt","content":"# Point this at the directory you extracted the downloaded Python SDK tarball\n# into. The directory name matches the tarball stem on the Download SDK page.\n# To install instead from PyPI once published, replace the line below with:\n#   agentrouter-sdk\u003e=0.1.0\nagentrouter-sdk @ file:./third_party/agentrouter-python-0.1.1\n"}]},"typescript":{"files":[{"name":"index.ts","content":"// Runnable example for the AgentRouter TypeScript SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then run `npm install \u0026\u0026 npx tsx index.ts`.\nimport { Client } from '@tetrate/agentrouter-sdk'\n\nconst client = new Client({\n  baseUrl: process.env.AGENTROUTER_BASE_URL,\n  apiKey: process.env.AGENTROUTER_API_KEY,\n})\n\n// Populate the request fields -- see the \"Request fields\" table above\n// for the available fields and which are required.\nconst req = {}\ntry {\n  const result = await client.identity.getUserStats(req)\n  console.log(result)\n} catch (err) {\n  console.error('Error:', err)\n}\n"},{"name":"package.json","content":"{\n  \"name\": \"identity\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"dependencies\": {\n    \"@tetrate/agentrouter-sdk\": \"file:./third_party/agentrouter-typescript-0.1.1\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"^20.0.0\",\n    \"typescript\": \"^5.4.0\"\n  }\n}\n"},{"name":"tsconfig.json","content":"{\n  \"compilerOptions\": {\n    \"target\": \"ES2020\",\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"bundler\",\n    \"strict\": true,\n    \"esModuleInterop\": true,\n    \"skipLibCheck\": true\n  }\n}\n"}]},"curl":"curl -X POST \"${AGENTROUTER_BASE_URL}/v1/users/stats\" \\\n  -H \"Authorization: Bearer ${AGENTROUTER_API_KEY}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"search\": \"...\",\n    \"user_ids\": [],\n    \"exclude_user_ids\": [],\n    \"role\": \"...\",\n    \"user_group_id\": \"...\",\n    \"unassigned_user_group\": false,\n    \"exclude_user_group_id\": \"...\",\n    \"has_user_group\": false\n  }'"},"persona":"Authenticated (API key or session token)","httpVerb":"POST","httpPath":"/v1/users/stats","slug":"get-user-stats"}

Create or update the SSO provider

Changed in v0.9.0 (new features): SSO providers now carry scopes, attribute mapping, the discovery URL and the organization name and slug. Writes can rename the organization, reset the OIDC endpoints, clear the admin emails and lift role-sync bans, and RegisterSSOProvider creates a provider without overwriting one. With customer_id set, an organization administrator of a single-customer deployment can manage SSO. CreateOrUpdateSSOProvider with customer_id only updates a provider; create one with RegisterSSOProvider.

Changed in v0.9.0 (new features): CreateOrUpdateSSOProvider takes validate_config. It refuses, and does not save, a role mapping with an unknown role, a SAML role sync that cannot work, or IdP client credentials that the IdP refuses. Only a save that creates the provider or changes the IdP connection contacts the IdP, over https. A token endpoint that the API cannot reach, or that answers with a server error, a rate limit or no OAuth error, refuses the save.

Changed in v0.8.0 (bug fixes): Moving the corporate OIDC SSO provider to a different identity provider no longer keeps the previous provider's endpoints. Before, sign-in could keep going to the old identity provider with the new client ID, which locked out everyone not already signed in. Same-provider edits keep any explicitly configured endpoints.

Changed in v0.7.0 (behaviour changes): Saving an SSO provider with role sync set to strict is refused when the provider is SAML, has no usable role mapping (a role claim path and at least one entry), or names no recovery administrator. In shared auth database mode a mapping whose only entries target super_admin does not count, nor do the provider's own admin emails. Stored providers keep working; the check runs on their next save.

Changed in v0.5.0 (new features): Corporate SSO can now be registered and used through the management API. Administrators holding sso_providers permissions can create, read and delete the OIDC provider, a sign-in page can ask whether corporate login is available before anyone signs in, and a browser can complete a corporate login against the management API itself. Set SSO_CALLBACK_BASE_URL to the management API's public origin to enable the browser flow.

Changed in v0.5.0 (new features): Corporate SSO now supports SAML 2.0 as well as OIDC. Register the IdP entity id, sign-on URL and signing certificate, then import the service provider metadata the management API publishes for that provider. Encrypted assertions and IdP-initiated sign-in are not supported.

What it does: Upserts the deployment's SSO provider row. The oidc_config jsonb it writes keeps better-auth's camelCase keys, because valet's own login parses those exact keys back out (identitysvc.SSOProviderOIDC): a write that renamed them would break the next sign-in rather than fail here. One gate, both verbs. The old auth service reached this row two ways and answered differently on each -- /api/sso-providers required a grant while /api/sso-init required nothing at all -- and that split was deliberately not ported: create and edit are separate verbs on one resource behind one annotation, so there is no second way in.

Request fields:

FieldRequiredDescription
provider_idnoEmpty selects the deployment default ("corporate").
domainnoDefaults to "*" on create; left alone on an update that omits it.
provider_typeno"oidc" or "saml". The matching config field is required on a create and refused when it names the other protocol, so a row can never describe a provider valet cannot serve.
oidcnoundocumented
samlnoundocumented
organization_idnoundocumented
admin_emailsnoundocumented
enablednoDefaults to true on create; left alone on an update that omits it.
role_claim_pathnoUnset leaves the stored path; "" clears it.
role_mappingnoUnset leaves the stored mapping; a message with no entries clears it.
role_sync_modenoUnset leaves the stored mode; "" resets it to "permissive". Any other value must be one of "off", "on_create", "permissive", "strict" -- an unknown mode is refused rather than stored, because the SSO login would read it back and fall through to a default the operator never chose. "strict" is refused, judged on the provider as it stands after the write, for a SAML provider, without both role_claim_path and a non-empty role_mapping, and without a recovery administrator in admin_emails or CORPORATE_ADMIN_EMAIL.
organization_namenoRenames the linked organization. A provider that links no organization returns NOT_FOUND. When a create sets this field and no organization_id, the provider first links the organization whose slug is provider_id, and creates it when it does not exist. When a create also sets organization_id, the provider links that organization, and this field renames it. A create without this field links no organization, as before. RegisterSSOProvider always links the organization whose slug is provider_id, and refuses organization_id.
lift_role_sync_bansnoClears the bans that strict role sync placed, when this write changes role_sync_mode from "strict" to a different mode. Off by default.
reset_oidc_endpointsnoDeletes the stored endpoints and discovery URLs before this write applies its own, so that the login discovers them again from the issuer.
clear_admin_emailsnoClears admin_emails. Refused together with a non-empty admin_emails.
customer_idnoThe authorization target (ADR 093), not the rows: the provider row is deployment-wide. Empty keeps the platform-only authorization. When set, a platform administrator passes for any customer. An organization administrator, or a holder of an organization sso_providers.edit grant, passes only when RBAC_AUTH_DB_MODE is "single" and customer_id is the customer of the deployment. A call with an API key is refused. With customer_id set the call only updates: a provider_id with no row is refused with FAILED_PRECONDITION. Create it with RegisterSSOProvider.
validate_confignoRuns the checks the Admin Console needs before a save, and refuses the write with INVALID_ARGUMENT when one fails. Nothing is written then. The checks: every role_mapping target is a TARS role; a SAML provider is not on_create or strict; a new OIDC provider has a client_secret. When the write creates the provider or changes what the login connects to, valet also fetches the discovery document and checks client_id and client_secret at the token endpoint. A write that moves the issuer, discovery URL or token endpoint to a new host needs a client_secret for the credential check: without one, the check is skipped so that the stored secret is not sent to the new host. The probes use https only, stop after 5 seconds, do not follow redirects and refuse a private, loopback or link-local address. ALLOW_PRIVATE_OIDC_DISCOVERY "true" admits a private address (RFC 1918 or IPv6 ULA) only. Off by default.

Response fields:

FieldRequiredDescription
providernoundocumented
createdoutput-onlyTrue when this call inserted the row, false when it updated one.
{"signatures":{"go":"func (x *IdentityClient) CreateOrUpdateSSOProvider(ctx context.Context, req *identityv1.CreateOrUpdateSSOProviderRequest) (*identityv1.CreateOrUpdateSSOProviderResponse, error)","python":"create_or_update_sso_provider(req: identity_service_pb2.CreateOrUpdateSSOProviderRequest) -\u003e CreateOrUpdateSSOProviderResponse","typescript":"createOrUpdateSSOProvider(req: MessageInitShape\u003ctypeof tars_identity_v1_identity_service_pb.CreateOrUpdateSSOProviderRequestSchema\u003e): Promise\u003cCreateOrUpdateSSOProviderResponse\u003e","curl":"curl -X POST \"${AGENTROUTER_BASE_URL}/v1/sso-providers\" \\\n  -H \"Authorization: Bearer ${AGENTROUTER_API_KEY}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"provider_id\": \"...\",\n    \"domain\": \"...\",\n    \"provider_type\": \"...\",\n    \"oidc\": {},\n    \"saml\": {},\n    \"organization_id\": \"...\",\n    \"admin_emails\": \"...\",\n    \"enabled\": false,\n    \"role_claim_path\": \"...\",\n    \"role_mapping\": {},\n    \"role_sync_mode\": \"...\",\n    \"organization_name\": \"...\",\n    \"lift_role_sync_bans\": false,\n    \"reset_oidc_endpoints\": false,\n    \"clear_admin_emails\": false,\n    \"customer_id\": \"...\",\n    \"validate_config\": false\n  }'"},"examples":{"go":{"files":[{"name":"main.go","content":"// Command example is a runnable example for the AgentRouter Go SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then `go run .`.\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\n\tagentrouter \"github.com/tetrateio/agentrouter-go\"\n\tidentityv1 \"github.com/tetrateio/agentrouter-go/genapi/api/tars/identity/v1\"\n)\n\nfunc main() {\n\tctx := context.Background()\n\n\tclient, err := agentrouter.New(ctx,\n\t\tagentrouter.WithBaseURL(os.Getenv(\"AGENTROUTER_BASE_URL\")),\n\t\tagentrouter.WithAPIKey(os.Getenv(\"AGENTROUTER_API_KEY\")),\n\t)\n\tif err != nil {\n\t\tlog.Fatalf(\"client: %v\", err)\n\t}\n\n\t// Populate the request fields -- see the \"Request fields\" table above for\n\t// the available fields and which are required.\n\treq := \u0026identityv1.CreateOrUpdateSSOProviderRequest{}\n\n\tresp, err := client.Identity().CreateOrUpdateSSOProvider(ctx, req)\n\tif err != nil {\n\t\tlog.Fatalf(\"call: %v\", err)\n\t}\n\n\tfmt.Printf(\"%+v\\n\", resp)\n}\n"},{"name":"go.mod","content":"module github.com/tetrateio/agentrouter-go-examples/identity/createorupdatessoprovider\n\ngo 1.26\n\nrequire github.com/tetrateio/agentrouter-go v0.1.1\n\n// Point this at the directory you extracted the downloaded Go SDK tarball into.\n// The directory name matches the tarball stem on the Download SDK page.\nreplace github.com/tetrateio/agentrouter-go =\u003e ./third_party/agentrouter-go-0.1.1\n"}]},"python":{"files":[{"name":"main.py","content":"\"\"\"Runnable example for the AgentRouter Python SDK.\n\nSet AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then run `python main.py`.\n\"\"\"\nimport os\n\nfrom tars.identity.v1 import identity_service_pb2\n\nfrom agentrouter_sdk import Client\n\nclient = Client(\n    base_url=os.environ[\"AGENTROUTER_BASE_URL\"],\n    api_key=os.environ[\"AGENTROUTER_API_KEY\"],\n)\n\n# Populate the request fields -- see the \"Request fields\" table above\n# for the available fields and which are required.\nreq = identity_service_pb2.CreateOrUpdateSSOProviderRequest()\ntry:\n    result = client.identity.create_or_update_sso_provider(req)\n    print(result)\nexcept Exception as err:\n    print(\"Error:\", err)\n"},{"name":"requirements.txt","content":"# Point this at the directory you extracted the downloaded Python SDK tarball\n# into. The directory name matches the tarball stem on the Download SDK page.\n# To install instead from PyPI once published, replace the line below with:\n#   agentrouter-sdk\u003e=0.1.0\nagentrouter-sdk @ file:./third_party/agentrouter-python-0.1.1\n"}]},"typescript":{"files":[{"name":"index.ts","content":"// Runnable example for the AgentRouter TypeScript SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then run `npm install \u0026\u0026 npx tsx index.ts`.\nimport { Client } from '@tetrate/agentrouter-sdk'\n\nconst client = new Client({\n  baseUrl: process.env.AGENTROUTER_BASE_URL,\n  apiKey: process.env.AGENTROUTER_API_KEY,\n})\n\n// Populate the request fields -- see the \"Request fields\" table above\n// for the available fields and which are required.\nconst req = {}\ntry {\n  const result = await client.identity.createOrUpdateSSOProvider(req)\n  console.log(result)\n} catch (err) {\n  console.error('Error:', err)\n}\n"},{"name":"package.json","content":"{\n  \"name\": \"identity\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"dependencies\": {\n    \"@tetrate/agentrouter-sdk\": \"file:./third_party/agentrouter-typescript-0.1.1\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"^20.0.0\",\n    \"typescript\": \"^5.4.0\"\n  }\n}\n"},{"name":"tsconfig.json","content":"{\n  \"compilerOptions\": {\n    \"target\": \"ES2020\",\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"bundler\",\n    \"strict\": true,\n    \"esModuleInterop\": true,\n    \"skipLibCheck\": true\n  }\n}\n"}]},"curl":"curl -X POST \"${AGENTROUTER_BASE_URL}/v1/sso-providers\" \\\n  -H \"Authorization: Bearer ${AGENTROUTER_API_KEY}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"provider_id\": \"...\",\n    \"domain\": \"...\",\n    \"provider_type\": \"...\",\n    \"oidc\": {},\n    \"saml\": {},\n    \"organization_id\": \"...\",\n    \"admin_emails\": \"...\",\n    \"enabled\": false,\n    \"role_claim_path\": \"...\",\n    \"role_mapping\": {},\n    \"role_sync_mode\": \"...\",\n    \"organization_name\": \"...\",\n    \"lift_role_sync_bans\": false,\n    \"reset_oidc_endpoints\": false,\n    \"clear_admin_emails\": false,\n    \"customer_id\": \"...\",\n    \"validate_config\": false\n  }'"},"persona":"Admin","httpVerb":"POST","httpPath":"/v1/sso-providers","slug":"create-or-update-the-sso-provider"}

List SSO providers

Changed in v0.9.0 (new features): SSO providers now carry scopes, attribute mapping, the discovery URL and the organization name and slug. Writes can rename the organization, reset the OIDC endpoints, clear the admin emails and lift role-sync bans, and RegisterSSOProvider creates a provider without overwriting one. With customer_id set, an organization administrator of a single-customer deployment can manage SSO. CreateOrUpdateSSOProvider with customer_id only updates a provider; create one with RegisterSSOProvider.

Changed in v0.5.0 (new features): Corporate SSO can now be registered and used through the management API. Administrators holding sso_providers permissions can create, read and delete the OIDC provider, a sign-in page can ask whether corporate login is available before anyone signs in, and a browser can complete a corporate login against the management API itself. Set SSO_CALLBACK_BASE_URL to the management API's public origin to enable the browser flow.

What it does: Returns every registered provider. Secrets are never included: client_secret is write-only on this surface.

Request body: None.

Response fields:

FieldRequiredDescription
providersnoundocumented
{"signatures":{"go":"func (x *IdentityClient) ListSSOProviders(ctx context.Context, req *identityv1.ListSSOProvidersRequest) (*identityv1.ListSSOProvidersResponse, error)","python":"list_sso_providers(req: identity_service_pb2.ListSSOProvidersRequest) -\u003e ListSSOProvidersResponse","typescript":"listSSOProviders(req: MessageInitShape\u003ctypeof tars_identity_v1_identity_service_pb.ListSSOProvidersRequestSchema\u003e): Promise\u003cListSSOProvidersResponse\u003e","curl":"curl \"${AGENTROUTER_BASE_URL}/v1/sso-providers\" \\\n  -H \"Authorization: Bearer ${AGENTROUTER_API_KEY}\""},"examples":{"go":{"files":[{"name":"main.go","content":"// Command example is a runnable example for the AgentRouter Go SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then `go run .`.\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\n\tagentrouter \"github.com/tetrateio/agentrouter-go\"\n\tidentityv1 \"github.com/tetrateio/agentrouter-go/genapi/api/tars/identity/v1\"\n)\n\nfunc main() {\n\tctx := context.Background()\n\n\tclient, err := agentrouter.New(ctx,\n\t\tagentrouter.WithBaseURL(os.Getenv(\"AGENTROUTER_BASE_URL\")),\n\t\tagentrouter.WithAPIKey(os.Getenv(\"AGENTROUTER_API_KEY\")),\n\t)\n\tif err != nil {\n\t\tlog.Fatalf(\"client: %v\", err)\n\t}\n\n\t// Populate the request fields -- see the \"Request fields\" table above for\n\t// the available fields and which are required.\n\treq := \u0026identityv1.ListSSOProvidersRequest{}\n\n\tresp, err := client.Identity().ListSSOProviders(ctx, req)\n\tif err != nil {\n\t\tlog.Fatalf(\"call: %v\", err)\n\t}\n\n\tfmt.Printf(\"%+v\\n\", resp)\n}\n"},{"name":"go.mod","content":"module github.com/tetrateio/agentrouter-go-examples/identity/listssoproviders\n\ngo 1.26\n\nrequire github.com/tetrateio/agentrouter-go v0.1.1\n\n// Point this at the directory you extracted the downloaded Go SDK tarball into.\n// The directory name matches the tarball stem on the Download SDK page.\nreplace github.com/tetrateio/agentrouter-go =\u003e ./third_party/agentrouter-go-0.1.1\n"}]},"python":{"files":[{"name":"main.py","content":"\"\"\"Runnable example for the AgentRouter Python SDK.\n\nSet AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then run `python main.py`.\n\"\"\"\nimport os\n\nfrom tars.identity.v1 import identity_service_pb2\n\nfrom agentrouter_sdk import Client\n\nclient = Client(\n    base_url=os.environ[\"AGENTROUTER_BASE_URL\"],\n    api_key=os.environ[\"AGENTROUTER_API_KEY\"],\n)\n\n# Populate the request fields -- see the \"Request fields\" table above\n# for the available fields and which are required.\nreq = identity_service_pb2.ListSSOProvidersRequest()\ntry:\n    result = client.identity.list_sso_providers(req)\n    print(result)\nexcept Exception as err:\n    print(\"Error:\", err)\n"},{"name":"requirements.txt","content":"# Point this at the directory you extracted the downloaded Python SDK tarball\n# into. The directory name matches the tarball stem on the Download SDK page.\n# To install instead from PyPI once published, replace the line below with:\n#   agentrouter-sdk\u003e=0.1.0\nagentrouter-sdk @ file:./third_party/agentrouter-python-0.1.1\n"}]},"typescript":{"files":[{"name":"index.ts","content":"// Runnable example for the AgentRouter TypeScript SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then run `npm install \u0026\u0026 npx tsx index.ts`.\nimport { Client } from '@tetrate/agentrouter-sdk'\n\nconst client = new Client({\n  baseUrl: process.env.AGENTROUTER_BASE_URL,\n  apiKey: process.env.AGENTROUTER_API_KEY,\n})\n\n// Populate the request fields -- see the \"Request fields\" table above\n// for the available fields and which are required.\nconst req = {}\ntry {\n  const result = await client.identity.listSSOProviders(req)\n  console.log(result)\n} catch (err) {\n  console.error('Error:', err)\n}\n"},{"name":"package.json","content":"{\n  \"name\": \"identity\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"dependencies\": {\n    \"@tetrate/agentrouter-sdk\": \"file:./third_party/agentrouter-typescript-0.1.1\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"^20.0.0\",\n    \"typescript\": \"^5.4.0\"\n  }\n}\n"},{"name":"tsconfig.json","content":"{\n  \"compilerOptions\": {\n    \"target\": \"ES2020\",\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"bundler\",\n    \"strict\": true,\n    \"esModuleInterop\": true,\n    \"skipLibCheck\": true\n  }\n}\n"}]},"curl":"curl \"${AGENTROUTER_BASE_URL}/v1/sso-providers\" \\\n  -H \"Authorization: Bearer ${AGENTROUTER_API_KEY}\""},"persona":"Admin","httpVerb":"GET","httpPath":"/v1/sso-providers","slug":"list-sso-providers"}

Get an SSO provider

Changed in v0.9.0 (new features): SSO providers now carry scopes, attribute mapping, the discovery URL and the organization name and slug. Writes can rename the organization, reset the OIDC endpoints, clear the admin emails and lift role-sync bans, and RegisterSSOProvider creates a provider without overwriting one. With customer_id set, an organization administrator of a single-customer deployment can manage SSO. CreateOrUpdateSSOProvider with customer_id only updates a provider; create one with RegisterSSOProvider.

Changed in v0.5.0 (new features): Corporate SSO can now be registered and used through the management API. Administrators holding sso_providers permissions can create, read and delete the OIDC provider, a sign-in page can ask whether corporate login is available before anyone signs in, and a browser can complete a corporate login against the management API itself. Set SSO_CALLBACK_BASE_URL to the management API's public origin to enable the browser flow.

What it does: Returns one provider by id. Same secret rule as the list.

Request fields:

FieldRequiredDescription
provider_idyesundocumented
customer_idnoThe authorization target, with the same rules as CreateOrUpdateSSOProviderRequest.customer_id.

Response fields:

FieldRequiredDescription
providernoundocumented
{"signatures":{"go":"func (x *IdentityClient) GetSSOProvider(ctx context.Context, req *identityv1.GetSSOProviderRequest) (*identityv1.GetSSOProviderResponse, error)","python":"get_sso_provider(req: identity_service_pb2.GetSSOProviderRequest) -\u003e GetSSOProviderResponse","typescript":"getSSOProvider(req: MessageInitShape\u003ctypeof tars_identity_v1_identity_service_pb.GetSSOProviderRequestSchema\u003e): Promise\u003cGetSSOProviderResponse\u003e","curl":"curl \"${AGENTROUTER_BASE_URL}/v1/sso-providers/01H...\" \\\n  -H \"Authorization: Bearer ${AGENTROUTER_API_KEY}\""},"examples":{"go":{"files":[{"name":"main.go","content":"// Command example is a runnable example for the AgentRouter Go SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then `go run .`.\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\n\tagentrouter \"github.com/tetrateio/agentrouter-go\"\n\tidentityv1 \"github.com/tetrateio/agentrouter-go/genapi/api/tars/identity/v1\"\n)\n\nfunc main() {\n\tctx := context.Background()\n\n\tclient, err := agentrouter.New(ctx,\n\t\tagentrouter.WithBaseURL(os.Getenv(\"AGENTROUTER_BASE_URL\")),\n\t\tagentrouter.WithAPIKey(os.Getenv(\"AGENTROUTER_API_KEY\")),\n\t)\n\tif err != nil {\n\t\tlog.Fatalf(\"client: %v\", err)\n\t}\n\n\t// Populate the request fields -- see the \"Request fields\" table above for\n\t// the available fields and which are required.\n\treq := \u0026identityv1.GetSSOProviderRequest{}\n\n\tresp, err := client.Identity().GetSSOProvider(ctx, req)\n\tif err != nil {\n\t\tlog.Fatalf(\"call: %v\", err)\n\t}\n\n\tfmt.Printf(\"%+v\\n\", resp)\n}\n"},{"name":"go.mod","content":"module github.com/tetrateio/agentrouter-go-examples/identity/getssoprovider\n\ngo 1.26\n\nrequire github.com/tetrateio/agentrouter-go v0.1.1\n\n// Point this at the directory you extracted the downloaded Go SDK tarball into.\n// The directory name matches the tarball stem on the Download SDK page.\nreplace github.com/tetrateio/agentrouter-go =\u003e ./third_party/agentrouter-go-0.1.1\n"}]},"python":{"files":[{"name":"main.py","content":"\"\"\"Runnable example for the AgentRouter Python SDK.\n\nSet AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then run `python main.py`.\n\"\"\"\nimport os\n\nfrom tars.identity.v1 import identity_service_pb2\n\nfrom agentrouter_sdk import Client\n\nclient = Client(\n    base_url=os.environ[\"AGENTROUTER_BASE_URL\"],\n    api_key=os.environ[\"AGENTROUTER_API_KEY\"],\n)\n\n# Populate the request fields -- see the \"Request fields\" table above\n# for the available fields and which are required.\nreq = identity_service_pb2.GetSSOProviderRequest()\ntry:\n    result = client.identity.get_sso_provider(req)\n    print(result)\nexcept Exception as err:\n    print(\"Error:\", err)\n"},{"name":"requirements.txt","content":"# Point this at the directory you extracted the downloaded Python SDK tarball\n# into. The directory name matches the tarball stem on the Download SDK page.\n# To install instead from PyPI once published, replace the line below with:\n#   agentrouter-sdk\u003e=0.1.0\nagentrouter-sdk @ file:./third_party/agentrouter-python-0.1.1\n"}]},"typescript":{"files":[{"name":"index.ts","content":"// Runnable example for the AgentRouter TypeScript SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then run `npm install \u0026\u0026 npx tsx index.ts`.\nimport { Client } from '@tetrate/agentrouter-sdk'\n\nconst client = new Client({\n  baseUrl: process.env.AGENTROUTER_BASE_URL,\n  apiKey: process.env.AGENTROUTER_API_KEY,\n})\n\n// Populate the request fields -- see the \"Request fields\" table above\n// for the available fields and which are required.\nconst req = {}\ntry {\n  const result = await client.identity.getSSOProvider(req)\n  console.log(result)\n} catch (err) {\n  console.error('Error:', err)\n}\n"},{"name":"package.json","content":"{\n  \"name\": \"identity\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"dependencies\": {\n    \"@tetrate/agentrouter-sdk\": \"file:./third_party/agentrouter-typescript-0.1.1\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"^20.0.0\",\n    \"typescript\": \"^5.4.0\"\n  }\n}\n"},{"name":"tsconfig.json","content":"{\n  \"compilerOptions\": {\n    \"target\": \"ES2020\",\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"bundler\",\n    \"strict\": true,\n    \"esModuleInterop\": true,\n    \"skipLibCheck\": true\n  }\n}\n"}]},"curl":"curl \"${AGENTROUTER_BASE_URL}/v1/sso-providers/01H...\" \\\n  -H \"Authorization: Bearer ${AGENTROUTER_API_KEY}\""},"persona":"Admin","httpVerb":"GET","httpPath":"/v1/sso-providers/{provider_id}","slug":"get-an-sso-provider"}

Register the SSO provider

Changed in v0.9.0 (new features): SSO providers now carry scopes, attribute mapping, the discovery URL and the organization name and slug. Writes can rename the organization, reset the OIDC endpoints, clear the admin emails and lift role-sync bans, and RegisterSSOProvider creates a provider without overwriting one. With customer_id set, an organization administrator of a single-customer deployment can manage SSO. CreateOrUpdateSSOProvider with customer_id only updates a provider; create one with RegisterSSOProvider.

What it does: Creates the deployment's SSO provider row. It refuses with ALREADY_EXISTS when the row exists, so a console that only wants to create cannot overwrite a live provider. It also links the organization whose slug is the provider id. customer_id names the authorization target (ADR 093), not the rows: the provider row is deployment-wide. A platform administrator passes for any customer. An organization administrator, or a holder of an organization sso_providers.create grant, passes only when RBAC_AUTH_DB_MODE is "single" and customer_id is the customer of the deployment. A call with an API key and a customer_id is refused.

Request fields:

FieldRequiredDescription
provideryesThe provider to create. Its customer_id must be empty: name the target in the customer_id of this message.
customer_idnoThe authorization target, with the same rules as CreateOrUpdateSSOProviderRequest.customer_id.

Response fields:

FieldRequiredDescription
providernoundocumented
{"signatures":{"go":"func (x *IdentityClient) RegisterSSOProvider(ctx context.Context, req *identityv1.RegisterSSOProviderRequest) (*identityv1.RegisterSSOProviderResponse, error)","python":"register_sso_provider(req: identity_service_pb2.RegisterSSOProviderRequest) -\u003e RegisterSSOProviderResponse","typescript":"registerSSOProvider(req: MessageInitShape\u003ctypeof tars_identity_v1_identity_service_pb.RegisterSSOProviderRequestSchema\u003e): Promise\u003cRegisterSSOProviderResponse\u003e","curl":"curl -X POST \"${AGENTROUTER_BASE_URL}/v1/sso-providers:register\" \\\n  -H \"Authorization: Bearer ${AGENTROUTER_API_KEY}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"provider\": {},\n    \"customer_id\": \"...\"\n  }'"},"examples":{"go":{"files":[{"name":"main.go","content":"// Command example is a runnable example for the AgentRouter Go SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then `go run .`.\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\n\tagentrouter \"github.com/tetrateio/agentrouter-go\"\n\tidentityv1 \"github.com/tetrateio/agentrouter-go/genapi/api/tars/identity/v1\"\n)\n\nfunc main() {\n\tctx := context.Background()\n\n\tclient, err := agentrouter.New(ctx,\n\t\tagentrouter.WithBaseURL(os.Getenv(\"AGENTROUTER_BASE_URL\")),\n\t\tagentrouter.WithAPIKey(os.Getenv(\"AGENTROUTER_API_KEY\")),\n\t)\n\tif err != nil {\n\t\tlog.Fatalf(\"client: %v\", err)\n\t}\n\n\t// Populate the request fields -- see the \"Request fields\" table above for\n\t// the available fields and which are required.\n\treq := \u0026identityv1.RegisterSSOProviderRequest{}\n\n\tresp, err := client.Identity().RegisterSSOProvider(ctx, req)\n\tif err != nil {\n\t\tlog.Fatalf(\"call: %v\", err)\n\t}\n\n\tfmt.Printf(\"%+v\\n\", resp)\n}\n"},{"name":"go.mod","content":"module github.com/tetrateio/agentrouter-go-examples/identity/registerssoprovider\n\ngo 1.26\n\nrequire github.com/tetrateio/agentrouter-go v0.1.1\n\n// Point this at the directory you extracted the downloaded Go SDK tarball into.\n// The directory name matches the tarball stem on the Download SDK page.\nreplace github.com/tetrateio/agentrouter-go =\u003e ./third_party/agentrouter-go-0.1.1\n"}]},"python":{"files":[{"name":"main.py","content":"\"\"\"Runnable example for the AgentRouter Python SDK.\n\nSet AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then run `python main.py`.\n\"\"\"\nimport os\n\nfrom tars.identity.v1 import identity_service_pb2\n\nfrom agentrouter_sdk import Client\n\nclient = Client(\n    base_url=os.environ[\"AGENTROUTER_BASE_URL\"],\n    api_key=os.environ[\"AGENTROUTER_API_KEY\"],\n)\n\n# Populate the request fields -- see the \"Request fields\" table above\n# for the available fields and which are required.\nreq = identity_service_pb2.RegisterSSOProviderRequest()\ntry:\n    result = client.identity.register_sso_provider(req)\n    print(result)\nexcept Exception as err:\n    print(\"Error:\", err)\n"},{"name":"requirements.txt","content":"# Point this at the directory you extracted the downloaded Python SDK tarball\n# into. The directory name matches the tarball stem on the Download SDK page.\n# To install instead from PyPI once published, replace the line below with:\n#   agentrouter-sdk\u003e=0.1.0\nagentrouter-sdk @ file:./third_party/agentrouter-python-0.1.1\n"}]},"typescript":{"files":[{"name":"index.ts","content":"// Runnable example for the AgentRouter TypeScript SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then run `npm install \u0026\u0026 npx tsx index.ts`.\nimport { Client } from '@tetrate/agentrouter-sdk'\n\nconst client = new Client({\n  baseUrl: process.env.AGENTROUTER_BASE_URL,\n  apiKey: process.env.AGENTROUTER_API_KEY,\n})\n\n// Populate the request fields -- see the \"Request fields\" table above\n// for the available fields and which are required.\nconst req = {}\ntry {\n  const result = await client.identity.registerSSOProvider(req)\n  console.log(result)\n} catch (err) {\n  console.error('Error:', err)\n}\n"},{"name":"package.json","content":"{\n  \"name\": \"identity\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"dependencies\": {\n    \"@tetrate/agentrouter-sdk\": \"file:./third_party/agentrouter-typescript-0.1.1\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"^20.0.0\",\n    \"typescript\": \"^5.4.0\"\n  }\n}\n"},{"name":"tsconfig.json","content":"{\n  \"compilerOptions\": {\n    \"target\": \"ES2020\",\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"bundler\",\n    \"strict\": true,\n    \"esModuleInterop\": true,\n    \"skipLibCheck\": true\n  }\n}\n"}]},"curl":"curl -X POST \"${AGENTROUTER_BASE_URL}/v1/sso-providers:register\" \\\n  -H \"Authorization: Bearer ${AGENTROUTER_API_KEY}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"provider\": {},\n    \"customer_id\": \"...\"\n  }'"},"persona":"Admin","httpVerb":"POST","httpPath":"/v1/sso-providers:register","slug":"register-the-sso-provider"}

Delete an SSO provider

Changed in v0.5.0 (new features): Corporate SSO can now be registered and used through the management API. Administrators holding sso_providers permissions can create, read and delete the OIDC provider, a sign-in page can ask whether corporate login is available before anyone signs in, and a browser can complete a corporate login against the management API itself. Set SSO_CALLBACK_BASE_URL to the management API's public origin to enable the browser flow.

What it does: Removes a provider row. Corporate login stops working the moment it lands, so it admits the same callers a write does.

Request fields:

FieldRequiredDescription
provider_idyesundocumented

Response fields:

FieldRequiredDescription
provider_idnoThe deleted provider id (echoes the request).
{"signatures":{"go":"func (x *IdentityClient) DeleteSSOProvider(ctx context.Context, req *identityv1.DeleteSSOProviderRequest) (*identityv1.DeleteSSOProviderResponse, error)","python":"delete_sso_provider(req: identity_service_pb2.DeleteSSOProviderRequest) -\u003e DeleteSSOProviderResponse","typescript":"deleteSSOProvider(req: MessageInitShape\u003ctypeof tars_identity_v1_identity_service_pb.DeleteSSOProviderRequestSchema\u003e): Promise\u003cDeleteSSOProviderResponse\u003e","curl":"curl -X DELETE \"${AGENTROUTER_BASE_URL}/v1/sso-providers/01H...\" \\\n  -H \"Authorization: Bearer ${AGENTROUTER_API_KEY}\""},"examples":{"go":{"files":[{"name":"main.go","content":"// Command example is a runnable example for the AgentRouter Go SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then `go run .`.\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\n\tagentrouter \"github.com/tetrateio/agentrouter-go\"\n\tidentityv1 \"github.com/tetrateio/agentrouter-go/genapi/api/tars/identity/v1\"\n)\n\nfunc main() {\n\tctx := context.Background()\n\n\tclient, err := agentrouter.New(ctx,\n\t\tagentrouter.WithBaseURL(os.Getenv(\"AGENTROUTER_BASE_URL\")),\n\t\tagentrouter.WithAPIKey(os.Getenv(\"AGENTROUTER_API_KEY\")),\n\t)\n\tif err != nil {\n\t\tlog.Fatalf(\"client: %v\", err)\n\t}\n\n\t// Populate the request fields -- see the \"Request fields\" table above for\n\t// the available fields and which are required.\n\treq := \u0026identityv1.DeleteSSOProviderRequest{}\n\n\tresp, err := client.Identity().DeleteSSOProvider(ctx, req)\n\tif err != nil {\n\t\tlog.Fatalf(\"call: %v\", err)\n\t}\n\n\tfmt.Printf(\"%+v\\n\", resp)\n}\n"},{"name":"go.mod","content":"module github.com/tetrateio/agentrouter-go-examples/identity/deletessoprovider\n\ngo 1.26\n\nrequire github.com/tetrateio/agentrouter-go v0.1.1\n\n// Point this at the directory you extracted the downloaded Go SDK tarball into.\n// The directory name matches the tarball stem on the Download SDK page.\nreplace github.com/tetrateio/agentrouter-go =\u003e ./third_party/agentrouter-go-0.1.1\n"}]},"python":{"files":[{"name":"main.py","content":"\"\"\"Runnable example for the AgentRouter Python SDK.\n\nSet AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then run `python main.py`.\n\"\"\"\nimport os\n\nfrom tars.identity.v1 import identity_service_pb2\n\nfrom agentrouter_sdk import Client\n\nclient = Client(\n    base_url=os.environ[\"AGENTROUTER_BASE_URL\"],\n    api_key=os.environ[\"AGENTROUTER_API_KEY\"],\n)\n\n# Populate the request fields -- see the \"Request fields\" table above\n# for the available fields and which are required.\nreq = identity_service_pb2.DeleteSSOProviderRequest()\ntry:\n    result = client.identity.delete_sso_provider(req)\n    print(result)\nexcept Exception as err:\n    print(\"Error:\", err)\n"},{"name":"requirements.txt","content":"# Point this at the directory you extracted the downloaded Python SDK tarball\n# into. The directory name matches the tarball stem on the Download SDK page.\n# To install instead from PyPI once published, replace the line below with:\n#   agentrouter-sdk\u003e=0.1.0\nagentrouter-sdk @ file:./third_party/agentrouter-python-0.1.1\n"}]},"typescript":{"files":[{"name":"index.ts","content":"// Runnable example for the AgentRouter TypeScript SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then run `npm install \u0026\u0026 npx tsx index.ts`.\nimport { Client } from '@tetrate/agentrouter-sdk'\n\nconst client = new Client({\n  baseUrl: process.env.AGENTROUTER_BASE_URL,\n  apiKey: process.env.AGENTROUTER_API_KEY,\n})\n\n// Populate the request fields -- see the \"Request fields\" table above\n// for the available fields and which are required.\nconst req = {}\ntry {\n  const result = await client.identity.deleteSSOProvider(req)\n  console.log(result)\n} catch (err) {\n  console.error('Error:', err)\n}\n"},{"name":"package.json","content":"{\n  \"name\": \"identity\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"dependencies\": {\n    \"@tetrate/agentrouter-sdk\": \"file:./third_party/agentrouter-typescript-0.1.1\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"^20.0.0\",\n    \"typescript\": \"^5.4.0\"\n  }\n}\n"},{"name":"tsconfig.json","content":"{\n  \"compilerOptions\": {\n    \"target\": \"ES2020\",\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"bundler\",\n    \"strict\": true,\n    \"esModuleInterop\": true,\n    \"skipLibCheck\": true\n  }\n}\n"}]},"curl":"curl -X DELETE \"${AGENTROUTER_BASE_URL}/v1/sso-providers/01H...\" \\\n  -H \"Authorization: Bearer ${AGENTROUTER_API_KEY}\""},"persona":"Admin","httpVerb":"DELETE","httpPath":"/v1/sso-providers/{provider_id}","slug":"delete-an-sso-provider"}