AgenTrux Topology Request
The Topology Request flow lets an agent declare the Scripts, Topics, and Grants it needs in a single request. A human reviews that declaration once in the Console and approves it; the agent then receives a Bearer access token that is already scoped to the approved Topics — ready to publish and read.
This is the self-service onboarding path. The agent proposes a topology; a person disposes. The agent can ask to create or attach resources, but only a human, by clicking Approve, actually creates them. Nothing is deleted or downgraded by this flow — removing access is always a Console action.
Which flow do I want?
| Flow | The Scripts / Topics / Grants are… | Who approves | Doc |
|---|---|---|---|
| Activation Code | already created by the owner; the agent just redeems a code | (set up beforehand) | API Reference |
| MCP (interactive OAuth) | already exist; the client is granted access to them | human, in the browser | MCP |
| Topology Request | declared by the agent and created on approval | human, in the Console | this page |
Discovery
The endpoint is advertised in the Authorization Server Metadata, so a client can find it without hard-coding:
curl https://api.agentrux.com/.well-known/oauth-authorization-server
{
"token_endpoint": "https://api.agentrux.com/oauth/token",
"topology_request_endpoint": "https://api.agentrux.com/oauth/topology-request",
"authorization_details_types_supported": ["agentrux.topology"],
"...": "..."
}
| Item | Value |
|---|---|
| Topology Request endpoint | https://api.agentrux.com/oauth/topology-request |
| Token endpoint | https://api.agentrux.com/oauth/token (device-code grant) |
| Client | public client registered via Dynamic Client Registration (token_endpoint_auth_method="none") |
| Authorization details type | agentrux.topology (version 1) |
| Approval window | 600 seconds (10 minutes) |
Step 1 — Register a public client
Same Dynamic Client Registration used for MCP. Register once and reuse the
client_id (dcr_…).
curl -X POST https://api.agentrux.com/oauth/register \
-H 'Content-Type: application/json' \
-d '{
"client_name": "weather-bot",
"redirect_uris": ["http://127.0.0.1:8765/callback"],
"token_endpoint_auth_method": "none",
"grant_types": ["urn:ietf:params:oauth:grant-type:device_code", "refresh_token"]
}'
The response contains a client_id (prefixed dcr_); reuse it in the next
steps. See the MCP doc for the full registration details.
Step 2 — Submit the topology request
POST /oauth/topology-request is form-encoded
(application/x-www-form-urlencoded). The authorization_details field
carries the declaration as a URL-encoded JSON array with exactly one
entry.
| Form field | Required | Description |
|---|---|---|
client_id |
yes | the dcr_… from Step 1 |
authorization_details |
yes | URL-encoded JSON array (one agentrux.topology entry) |
client_hint |
no | ≤256 chars, shown to the human (e.g. app name / version) |
The declaration you put in authorization_details:
[
{
"type": "agentrux.topology",
"version": 1,
"script": {
"name": "weather-bot",
"description": "Fetches weather and publishes hourly readings"
},
"topics": [
{ "ref": "weather-data", "name": "weather-data", "retention_s": 86400, "intent": "publish hourly readings" }
],
"grants": [
{ "topic_ref": "weather-data", "scope": "write", "binding_name": "weather-out" }
]
}
]
Field rules (your input contract):
| Field | Rule |
|---|---|
type |
must be "agentrux.topology" |
version |
must be 1 |
script.name |
1–128 chars; lowercase letters, digits, . _ - |
script.description |
1–256 chars |
topics |
1–20 entries |
topics[].ref |
1–128 chars, unique within the request; the connector that ties this topic across request → approval → token |
topics[].name |
1–128 chars; lowercase letters, digits, . _ - |
topics[].retention_s |
3600–2592000 (1 hour – 30 days); the human may shorten it on approval |
topics[].intent |
optional, ≤256 chars; shown to the human |
grants |
1–40 entries |
grants[].topic_ref |
must match one of topics[].ref |
grants[].scope |
"read" or "write" (one per entry — request both as two entries) |
grants[].binding_name |
optional, 1–64 chars, printable ASCII; a stable label you use to find the grant in the token |
The whole declaration must be ≤16 KB. Strings are Unicode-normalized and must not contain control characters.
A complete curl (note --data-urlencode for the JSON field):
curl -X POST https://api.agentrux.com/oauth/topology-request \
--data-urlencode "client_id=dcr_…" \
--data-urlencode "client_hint=weather-bot v1.2" \
--data-urlencode 'authorization_details=[{"type":"agentrux.topology","version":1,"script":{"name":"weather-bot","description":"Fetches weather and publishes hourly readings"},"topics":[{"ref":"weather-data","name":"weather-data","retention_s":86400,"intent":"publish hourly readings"}],"grants":[{"topic_ref":"weather-data","scope":"write","binding_name":"weather-out"}]}]'
Response (200):
{
"device_code": "dc_…",
"user_code": "TVHV-QJZW",
"verification_uri": "https://console.agentrux.com/topology/approve",
"verification_uri_complete": "https://console.agentrux.com/topology/approve?code=TVHV-QJZW",
"expires_in": 600,
"interval": 5
}
Validation errors (400), as OAuth error codes:
error |
Cause |
|---|---|
unsupported_authorization_details_type |
type is not agentrux.topology |
unsupported_authorization_details_version |
version is not 1 |
invalid_scope_in_authorization_details |
a scope is not read / write |
invalid_authorization_details |
schema, naming, size, duplicate, or unknown topic_ref |
Submitting too quickly may return 429 too_many_requests.
Step 3 — Send the user to approve
Show the user the user_code and verification_uri, or open
verification_uri_complete directly (it pre-fills the code). Use the
values the server returned — don't rebuild the URL yourself. In the
Console the person:
- signs in and chooses the workspace (Alias) the resources belong to;
- reviews each requested Script, Topic, and Grant, and per item may create a new one, attach an existing one, or skip it;
- approves or denies each Grant;
- clicks Approve.
That click is the authorization boundary. The agent never establishes a topology unattended, and the human can approve only part of what was requested.
Step 4 — Poll for the token
Exchange the device_code at the token endpoint, polling no faster than
interval (5 seconds).
curl -X POST https://api.agentrux.com/oauth/token \
--data-urlencode "grant_type=urn:ietf:params:oauth:grant-type:device_code" \
--data-urlencode "device_code=dc_…" \
--data-urlencode "client_id=dcr_…"
While the request is still open you get an OAuth error (check error,
not the status code alone):
| HTTP | error |
Meaning |
|---|---|---|
400 |
authorization_pending |
not approved yet — keep polling |
400 |
slow_down |
polling too fast — increase your interval (429 on IP burst) |
400 |
access_denied |
the human denied the request — stop |
400 |
expired_token |
the 10-minute window elapsed — start over |
400 |
invalid_grant |
the code was already used, or the client_id does not match |
On approval (200):
{
"access_token": "aat_…",
"token_type": "Bearer",
"expires_in": 600,
"refresh_token": "art_…",
"scope": "topic.write topic:top_<uuid>:write",
"authorization_details": [
{
"type": "agentrux.topology",
"version": 1,
"granted": {
"script_id": "scr_<uuid>",
"alias_id": "ali_<uuid>",
"topic_id_map": { "weather-data": "top_<uuid>" },
"grant_ids": {
"topic:top_<uuid>:write": { "grant_id": "grt_<uuid>", "binding_name": "weather-out" }
}
}
}
]
}
Read authorization_details.granted rather than assuming your request was
approved verbatim:
topic_id_mapresolves each requestrefto the realtop_<uuid>. The human may have attached an existing Topic, so the id (and name) can differ from what you asked — always publish to thetop_<uuid>you find here.grant_idsmaps each granted scope (topic:top_<uuid>:<read|write>) to itsgrant_idand yourbinding_name.- The granted set can be a subset of the request — Topics or Grants the human skipped or denied simply will not appear.
The device-code grant also returns a refresh_token (art_…). When the
access token expires (~10 minutes), rotate it with the refresh_token
grant (see API Reference). A refresh stays within the
scope that was originally approved; to widen access, submit a new
topology request (a new human approval).
Step 5 — Publish
The access_token is an ordinary script token. Publish to a granted Topic
using the top_<uuid> from topic_id_map:
curl -X POST https://api.agentrux.com/topics/top_<uuid>/events \
-H "Authorization: Bearer aat_…" \
-H 'Content-Type: application/json' \
-d '{ "event_type": "weather.reading", "payload": { "temp_c": 21.4 } }'
The Data Plane (publish / read / stream) is documented in the
API Reference; the same token also works over the
MCP interface (tools/call publish_event).
What this flow guarantees
- Additive only. It can create or attach Scripts, Topics, and Grants; it never deletes them or reduces a scope. Revoking is a Console action.
- Human-gated. A new Script, Topic, or Grant always requires a fresh human approval. An agent cannot self-provision access.
- Approved ⊆ requested. The token reflects what the human actually approved, which may be narrower than the request.
Related links
- Authorization Server Metadata:
https://api.agentrux.com/.well-known/oauth-authorization-server - Client registration & device flow basics: MCP doc
- Token rotation & Data Plane: API Reference
- A2A Agent Card: A2A doc
- Documentation:
https://docs.agentrux.com