Skip to Content
API referenceTRQP authorization & recognition

TRQP: POST /v1/trqp/authorization and POST /v1/trqp/recognition

AgentValet answers Trust Registry Query Protocol v2.0  queries over its own grants. A partner, an auditor, or another organisation’s gateway can ask “is this agent authorised by this authority for this action on this resource” without an AgentValet SDK, and get the effective answer: the one the governed call would get, computed at query time.

Both endpoints implement the v2.0 HTTPS binding as we read it. ToIP publishes no conformance suite, so “TRQP-conformant” is a self-assessed claim by every implementer, including us. What you can rely on is the shape below.

Identifiers

TRQP fieldWhat AgentValet acceptsMeaning
entity_iddid:web:api.agentvalet.ai:agents:<agent_id>, a bare agt_… id, or a spiffe://… idThe agent
authority_iddid:web:api.agentvalet.ai (this registry) or did:web:api.agentvalet.ai:orgs:<organisation uuid> (one governing organisation)Who is being asked
actionA scope id, verbatim as granted: chat:write, Mail.Read, github:contents.readWhat
resourceA platform id: slack, microsoft-outlook, mcp-…On what

The agent’s own DID document (see did:web identity documents) carries #trqp and #trqp-recognition service entries pointing here, which is TRQP’s only discovery mechanism. Because a document never names its organisation, a partner holding only an agent DID uses the root DID as authority_id.

Authorization

POST /v1/trqp/authorization Content-Type: application/json { "entity_id": "did:web:api.agentvalet.ai:agents:agt_7o00saqajyahinuuc6g83", "authority_id": "did:web:api.agentvalet.ai", "action": "chat:write", "resource": "slack", "context": { "time": "2026-09-16T08:00:00Z" } }
{ "entity_id": "did:web:api.agentvalet.ai:agents:agt_7o00saqajyahinuuc6g83", "authority_id": "did:web:api.agentvalet.ai", "action": "chat:write", "resource": "slack", "authorized": true, "time_requested": "2026-09-16T08:00:00Z", "time_evaluated": "2026-09-16T08:00:01.204Z", "message": "authorized; endpoint, recipient and external-reach controls apply at call time", "context": { "decision": "allow", "policy_id": "…", "policy_version": 7, "evaluated": ["status", "grant", "ttl", "breaker", "child_liveness", "guardrails", "policy", "scope"], "not_evaluated": ["endpoint", "recipient_constraints", "reach"] } }

authorized is exactly the answer the broker’s permission check gives for that agent, platform and scope: agent status is active, a grant row exists and is not suspended or expired, the circuit breaker is closed, a child agent’s parent is still live and still covers the scope, no organisation guardrail or policy denies, and the scope is in the grant verbatim.

An approval requirement is authorized: false. TRQP has no third state, and a party asking “may this agent act unilaterally” must hear no. The reason is in message and context.decision is require_approval. Every false carries the broker’s reason verbatim: scope_not_granted, denied_by_policy, denied_by_guardrail, grant_expired, circuit_breaker_open, agent_suspended, agent_revoked, approval_required_by_policy, and so on: the same vocabulary a 403 from POST /v1/actions uses.

What is deliberately not evaluated

resource is a platform, not an endpoint, so the oracle never sees a method, a path or a request body. Three controls that need those are therefore skipped by construction and named in context.not_evaluated on every answer:

  • Endpoint patterns: whether chat:write covers POST /api/chat.postMessage specifically.
  • Recipient constraints: the grant’s channel, address or domain allow-list.
  • External reach: the organisation’s Ask-me-first or Block control on messages leaving the organisation.

They all still apply when the real call arrives. If you need the call-time answer, make the call through POST /v1/actions. There is no context.endpoint or context.body extension to upgrade this endpoint; that would make a coarse answer look precise.

Optional context

  • context.time: echoed as time_requested. The registry only evaluates now; if the value is more than 60 seconds from the evaluation time the response adds context.time_requested_ignored: true.
  • context.connection_id: selects a per-connection grant when the agent has more than one account on the platform.

Recognition

Same request shape. recognized is true when entity_id resolves to an agent governed by authority_id, its status is active, and, for a child agent, its parent is still active and its own expiry has not passed. action and resource are required by the protocol, echoed, and not evaluated; * is the conventional value.

{ "recognized": false, "message": "agent_suspended", "…": "…" }

Recognition is the cheap “does this agent exist and is it alive” probe; recognized: true with authorized: false is a normal pair.

Who may ask

CallerAnswered forNotes
No bearer (public)Agents whose organisation has turned on Agent identity documents in Settings, the same toggle that publishes their did.jsonRate-limited to 60 requests per minute per IP. Everything else is a uniform authorized: false / recognized: false with message: "not_public", never a 404, so the public surface reveals nothing about an id or its organisation
Agent bearerItself onlySame JWT as /v1/actions
Owner bearerAny agent in its organisationThe dashboard’s session token

An unknown or org-mismatched authority answers not_public on the public path and entity_not_governed_by_authority on a bearer path. A bearer that does not verify is 401.

Every answered query writes an audit row with action: "trqp_eval", the four fields, the answer, the reason and the caller kind. Refusals (not_public), 400, 401 and 429 write nothing.

Errors

RFC 7807 Problem Details, Content-Type: application/problem+json:

StatusWhen
400A required field is missing or empty, authority_id is not a did:web on this host, entity_id is not a recognised form, or context.time is not RFC 3339
401A bearer was presented and it may not query this entity
429Public path rate limit; Retry-After is set

Freshness

There is no cache. A revocation, grant edit, policy edit, expiry or breaker trip is reflected on the next query, because none of those paths are memoised anywhere in the broker. That is a stronger statement than a stored-tuple registry can make, and it is the reason to point a gateway here rather than at a copy.

  • did:web identity documents carry the service entries that point here
  • Scopes and permissions for what action and resource mean
  • The same check answers AuthZEN 1.0 at POST /v1/authzen/access (agent or owner bearer required) with decision, reason and require_approval
  • The full OpenAPI document  is held to the live route table by a contract test on every push
Last updated on