{"openapi":"3.1.0","info":{"title":"AgentValet API","version":"0.79.1","description":"AgentValet is a hosted governance layer for AI agents: cryptographic identity, scoped permissions, an encrypted credential proxy, human-in-the-loop approval and audit logging. Agents authenticate with short-lived RS256 JWTs. Every /v1/* endpoint requires a Bearer JWT except the registration and bootstrap routes marked security: [] below."},"servers":[{"url":"https://api.agentvalet.ai"}],"components":{"securitySchemes":{"agentJWT":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"RS256 JWT signed with the agent's private key. Claims: { agent_id, owner_id, session_id?, iat, exp }. TTL 60s: mint a fresh token per request. A child identity issued by POST /v1/agents/children presents its bearer_token instead (no key; it may add an X-AV-Session header)."},"ownerJWT":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Session JWT issued by the dashboard's identity provider, for owner-level operations."}},"schemas":{"Error":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]},"EnvelopeMeta":{"type":"object","required":["capability","timestamp","next_actions","docs"],"properties":{"capability":{"type":"string","example":"agent.permissions"},"timestamp":{"type":"string","format":"date-time"},"next_actions":{"type":"array","items":{"type":"object","properties":{"capability":{"type":"string"},"endpoint":{"type":"string"}}}},"docs":{"type":"string","format":"uri"}},"additionalProperties":true},"AgentHalt":{"type":"object","description":"Why the agent cannot act right now and what would restore it. Present on the 403 agent_halted envelope and on GET /v1/agents/me while halted.","required":["state","since","cause","detail","recoverable_by","retry_after_seconds","instructions"],"properties":{"state":{"type":"string","enum":["suspended","revoked","breaker_open"]},"since":{"type":["string","null"],"format":"date-time"},"cause":{"type":"string","enum":["suspended_by_policy","owner_manual","anomaly_auto_suspend","breaker_auth_failure","breaker_api_error","breaker_upstream_failure","unknown"]},"detail":{"type":["string","null"]},"recoverable_by":{"type":"string","enum":["owner","time"]},"retry_after_seconds":{"type":["integer","null"]},"instructions":{"type":"string"}}},"PendingApproval":{"type":"object","description":"The action was queued for a human decision. Poll GET /v1/approvals/{approval_id}; never re-submit the action (a retry queues a duplicate).","required":["status","approval_id","message"],"properties":{"status":{"type":"string","const":"pending_approval"},"approval_id":{"type":["string","null"],"format":"uuid"},"message":{"type":"string"}}},"TrqpRequest":{"type":"object","required":["entity_id","authority_id","action","resource"],"properties":{"entity_id":{"type":"string","description":"did:web:<host>:agents:<agt_...>, a bare agt_ id, or a spiffe:// id","example":"did:web:api.agentvalet.ai:agents:agt_7o00saqajyahinuuc6g83"},"authority_id":{"type":"string","description":"did:web:<host> (this registry) or did:web:<host>:orgs:<organisation uuid>","example":"did:web:api.agentvalet.ai"},"action":{"type":"string","description":"A scope id, verbatim as granted (e.g. chat:write, Mail.Read). Recognition: any non-empty string, conventionally *","example":"chat:write"},"resource":{"type":"string","description":"A platform id (e.g. slack). Recognition: any non-empty string, conventionally *","example":"slack"},"context":{"type":"object","properties":{"time":{"type":"string","format":"date-time","description":"Echoed as time_requested. The registry only evaluates now; a skew over 60 s sets context.time_requested_ignored"},"connection_id":{"type":"string","description":"AgentValet extension: select a per-connection grant (Model B). Authorization only"}},"additionalProperties":true}}},"TrqpResponseBase":{"type":"object","required":["entity_id","authority_id","action","resource","time_requested","time_evaluated","message"],"properties":{"entity_id":{"type":"string"},"authority_id":{"type":"string"},"action":{"type":"string"},"resource":{"type":"string"},"time_requested":{"type":"string","format":"date-time"},"time_evaluated":{"type":"string","format":"date-time"},"message":{"type":"string","description":"On true: a fixed sentence naming the controls that apply at call time. On false: the permission check reason verbatim (scope_not_granted, denied_by_policy, denied_by_guardrail, grant_expired, circuit_breaker_open, agent_suspended, agent_revoked, approval_required_by_*, ...), not_public (public path, any unanswerable query), entity_not_governed_by_authority (bearer path, org mismatch), or a recognition status reason"}}},"Problem":{"type":"object","description":"RFC 7807 Problem Details","required":["title","status"],"properties":{"type":{"type":"string"},"title":{"type":"string"},"status":{"type":"integer"},"detail":{"type":"string"}}},"DidDocument":{"type":"object","description":"W3C DID Core document. verificationMethod is absent for identities the broker authenticates itself; authentication/assertionMethod are empty arrays while the agent is suspended.","required":["@context","id","controller"],"properties":{"@context":{"type":"array","items":{"type":"string"}},"id":{"type":"string","example":"did:web:api.agentvalet.ai:agents:agt_7o00saqajyahinuuc6g83"},"controller":{"oneOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}]},"verificationMethod":{"type":"array","items":{"type":"object","required":["id","type","controller","publicKeyJwk"],"properties":{"id":{"type":"string"},"type":{"type":"string","const":"JsonWebKey2020"},"controller":{"type":"string"},"publicKeyJwk":{"type":"object","additionalProperties":true}}}},"authentication":{"type":"array","items":{"type":"string"}},"assertionMethod":{"type":"array","items":{"type":"string"}},"service":{"type":"array","items":{"type":"object","required":["id","type","serviceEndpoint"],"properties":{"id":{"type":"string"},"type":{"type":"string"},"serviceEndpoint":{"type":"string","format":"uri"}}}}}},"ActionDenied":{"type":"object","description":"The governance gate refused the call. `reason` is the stable code to branch on (docs/contract/public-governance-gate.md); `halt` is present only when the agent itself is halted.","required":["error","reason"],"properties":{"error":{"type":"string","enum":["Permission denied","agent_halted"]},"reason":{"type":"string","enum":["scope_not_granted","agent_suspended","agent_revoked","platform_suspended","no_permission_record","circuit_breaker_open","no_policy_assigned","denied_by_guardrail","denied_by_policy","endpoint_scope_mismatch","grant_expired","recipient_not_allowed","repo_not_allowed"]},"halt":{"$ref":"#/components/schemas/AgentHalt"},"violating":{"type":"array","items":{"type":"string"},"description":"recipient_not_allowed / repo_not_allowed only: the addresses/domains (or owner/repo names) the grant does not allow"}}}}},"tags":[{"name":"agents","description":"Agent registration and identity"},{"name":"permissions","description":"Agent permission queries"},{"name":"actions","description":"Platform action proxy"},{"name":"git","description":"Git smart-HTTP passthrough (governed clone/fetch/push)"},{"name":"audit","description":"Audit log and monitoring"},{"name":"discovery","description":"Discovery and metadata endpoints"},{"name":"hitl","description":"Human-in-the-loop approval flows"},{"name":"health","description":"Service health"}],"paths":{"/health":{"get":{"summary":"Service health check","tags":["health"],"security":[],"responses":{"200":{"description":"Service is healthy","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","example":"ok"}}}}}}}}},"/v1/actions":{"post":{"summary":"Proxy an API call to a connected SaaS platform","description":"Decrypts credentials server-side and proxies the request to the target platform. Returns the upstream response, or 202 if the action is queued for human-in-the-loop approval.","tags":["actions"],"security":[{"agentJWT":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["platform","endpoint","method","scope"],"properties":{"platform":{"type":"string","description":"Platform identifier (e.g. slack, gmail, stripe)","example":"slack"},"endpoint":{"type":"string","description":"Platform-relative API path","example":"/chat.postMessage"},"method":{"type":"string","enum":["GET","POST","PUT","PATCH","DELETE"],"example":"POST"},"scope":{"type":"string","description":"Required permission scope","example":"slack:chat:write"},"data":{"type":"object","description":"Request body forwarded to the platform"},"headers":{"type":"object","additionalProperties":{"type":"string"},"description":"Additional headers forwarded to the platform"},"connection_id":{"type":"string","description":"Target a specific connection when the platform has several. Omit for the default."},"reason":{"type":"string","description":"Optional justification shown to the human approver and recorded in the audit trail. Free text, untrusted."},"caller":{"description":"Who the human is that this call is for, when one agent serves many people. { assertion } is a JWT from an issuer the organisation trusts (verified); { source, id } is an opaque id (asserted; honoured only where the platform allows it). Also accepted as the X-AV-Caller header ('jwt <token>' or 'id <source>:<id>'). Selects the person's own upstream token on platforms with per-person tokens enabled.","oneOf":[{"type":"object","required":["assertion"],"properties":{"assertion":{"type":"string"}}},{"type":"object","required":["source","id"],"properties":{"source":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{0,31}$"},"id":{"type":"string","maxLength":256}}}]}}}}}},"parameters":[{"name":"X-AV-Session","in":"header","required":false,"schema":{"type":"string"},"description":"Bearer-mode child identities only: a session marker recorded on the audit row (agents signing their own JWT put session_id in the claims instead)."}],"responses":{"200":{"description":"Upstream platform response, wrapped: data is the upstream body, _meta describes the call","content":{"application/json":{"schema":{"type":"object","required":["data","_meta"],"properties":{"data":{},"_meta":{"$ref":"#/components/schemas/EnvelopeMeta"}}}}}},"202":{"description":"Action queued for human-in-the-loop approval","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Invalid request body"},"401":{"description":"Missing or invalid Bearer JWT"},"403":{"description":"Refused by the governance gate: scope, policy, guardrail, recipient constraint, or a halted agent","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActionDenied"}}}},"409":{"description":"needs_consent: the agent is granted on a remote MCP OAuth server nobody has authorised yet. The proxy raises (or dedupes) an access request and returns request_token, connect_url (the dashboard page an admin finishes the consent on) and poll_url; poll until approved, then retry. Also platform_reauth_needed or an unusable connection.","content":{"application/json":{"schema":{"type":"object","required":["error","platform","consent_requested","message","correlation_id"],"properties":{"error":{"type":"string","enum":["needs_consent","platform_reauth_needed","agent_inactive"]},"reason_code":{"type":"string","const":"needs_consent"},"platform":{"type":"string"},"scope":{"type":"string"},"consent_requested":{"type":"boolean","description":"true when a pending access request exists for an admin to act on"},"request_token":{"type":"string"},"connect_url":{"type":"string","format":"uri"},"consent_url":{"type":"string","format":"uri","description":"Alias of connect_url kept for pre-01 clients"},"poll_url":{"type":"string","example":"/v1/access-request/status/{token}"},"message":{"type":"string"},"correlation_id":{"type":"string"},"audit_id":{"type":"string","nullable":true}}}}}},"412":{"description":"platform_not_connected (no usable connection for this platform), or consent_required: the platform holds one token per person and this person has none (or no honoured caller identity was sent). For consent_required with a person, the body carries consent_url (the page the person finishes their own authorization on), consent_token and poll_url.","content":{"application/json":{"schema":{"type":"object","required":["error","platform","correlation_id"],"properties":{"error":{"type":"string","enum":["platform_not_connected","consent_required"]},"code":{"type":"string"},"reason":{"type":"string","enum":["no_caller","asserted_not_allowed","not_consented"]},"platform":{"type":"string"},"detail":{"type":"string"},"consent_url":{"type":"string","format":"uri"},"consent_token":{"type":"string"},"poll_url":{"type":"string","example":"/v1/consent/caller/status/{token}"},"expires_at":{"type":"string","format":"date-time"},"message":{"type":"string"},"correlation_id":{"type":"string"},"audit_id":{"type":"string","nullable":true}}}}}},"502":{"description":"Upstream platform unreachable"}}}},"/v1/consent/caller/start":{"post":{"summary":"Ask a person to authorize the platform for this agent (per-person tokens)","description":"Pre-authorize mode of per-person token isolation: raise the consent before the first call instead of meeting it as a 412. The caller is hashed server-side and never stored; the response carries the page the person finishes on. Requires the platform's per-person setting to be on.","tags":["hitl"],"security":[{"agentJWT":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["platform","caller"],"properties":{"platform":{"type":"string"},"caller":{"oneOf":[{"type":"object","required":["assertion"],"properties":{"assertion":{"type":"string"}}},{"type":"object","required":["source","id"],"properties":{"source":{"type":"string"},"id":{"type":"string"}}}]}}}}}},"responses":{"202":{"description":"Consent raised","content":{"application/json":{"schema":{"type":"object","required":["status","consent_url","consent_token","expires_at","poll_url"],"properties":{"status":{"type":"string","const":"consent_pending"},"consent_url":{"type":"string","format":"uri"},"consent_token":{"type":"string"},"expires_at":{"type":"string","format":"date-time"},"poll_url":{"type":"string"},"message":{"type":"string"}}}}}},"400":{"description":"caller missing, malformed, from an unregistered issuer, or asserted where only verified is accepted"},"401":{"description":"Missing or invalid Bearer JWT"},"402":{"description":"Per-person tokens are not on this organisation's plan"},"409":{"description":"per_caller is off for this platform, or the platform is not an OAuth platform brokered through the connect flow"},"429":{"description":"Too many consents started"}}}},"/v1/consent/caller/status/{token}":{"get":{"summary":"Poll a person's consent","description":"pending until the person finishes their authorization, then completed; expired after 30 minutes. The consent token is the capability; no other auth.","tags":["hitl"],"parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Current state","content":{"application/json":{"schema":{"type":"object","required":["status","platform_id","expires_at"],"properties":{"status":{"type":"string","enum":["pending","completed","expired"]},"platform_id":{"type":"string"},"expires_at":{"type":"string","format":"date-time"}}}}}},"404":{"description":"Unknown token"}}}},"/v1/agent/permissions":{"get":{"summary":"Discover the platforms, scopes and connections the authenticated agent may use","description":"The effective grant (grants intersected with policy) for this agent. Cache the body and revalidate with If-None-Match: the ETag is a hash of everything an agent can act on, so a 304 means nothing changed. A halted agent gets 403 with the agent_halted envelope.","tags":["permissions"],"security":[{"agentJWT":[]}],"parameters":[{"name":"If-None-Match","in":"header","required":false,"schema":{"type":"string"},"description":"The ETag from the last 200"}],"responses":{"200":{"description":"Granted permissions, wrapped","headers":{"ETag":{"schema":{"type":"string"},"description":"Version hash; send back as If-None-Match"}},"content":{"application/json":{"schema":{"type":"object","required":["data","_meta"],"properties":{"data":{"type":"object","required":["agent","platforms","version"],"properties":{"agent":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"additionalProperties":true},"version":{"type":"string","description":"Same value as the ETag"},"platforms":{"type":"array","items":{"type":"object","required":["platformId","platformName","scopes","requireApproval"],"properties":{"platformId":{"type":"string"},"platformName":{"type":"string"},"scopes":{"type":"array","items":{"type":"string"}},"requireApproval":{"type":"boolean","description":"Grant-level flag: every call prompts"},"approvalScopes":{"type":"array","items":{"type":"string"},"description":"Subset of scopes a policy or guardrail routes to human approval"},"conditionalScopes":{"type":"array","items":{"type":"string"},"description":"Subset of scopes whose policy verdict depends on a CEL condition on a rule (Team+). Advertised as usable; the condition is evaluated on the real call and may deny or hold it. Absent when none apply. Folded into `version`."},"unenforcedScopes":{"type":"array","items":{"type":"string"}},"constraints":{"type":"object","description":"Recipient allow-list on the grant, verbatim","additionalProperties":true},"connections":{"type":"array","description":"Per-connection grants (Model B). Pass connection_id on POST /v1/actions to pick one.","items":{"type":"object","additionalProperties":true}},"expiresInSeconds":{"type":"integer"},"callStyle":{"type":"string","enum":["graphql"]},"serverHost":{"type":"string"},"serverName":{"type":"string"}}}},"egress":{"type":"object","description":"The org's external-reach control, present only for entitled orgs","additionalProperties":true}},"additionalProperties":true},"_meta":{"$ref":"#/components/schemas/EnvelopeMeta"}}}}}},"304":{"description":"Unchanged since the ETag in If-None-Match; no body"},"401":{"description":"Missing or invalid Bearer JWT"},"403":{"description":"Agent halted (suspended, revoked, breaker open)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActionDenied"}}}}}}},"/v1/register":{"post":{"summary":"Self-register a new agent (no auth required)","description":"Agent submits identity and requested scopes. Returns a registration token to poll status. Owner receives approval request.","tags":["agents"],"security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["owner_id","agent_name","requested_scopes"],"properties":{"owner_id":{"type":"string","format":"uuid","description":"UUID of the owner who will approve this agent"},"agent_name":{"type":"string","description":"Human-readable agent name"},"agent_description":{"type":"string","description":"Brief description of what the agent does"},"requested_scopes":{"type":"array","items":{"type":"object","required":["platformId","scopes"],"properties":{"platformId":{"type":"string"},"scopes":{"type":"array","items":{"type":"string"}}}}}}}}}},"responses":{"202":{"description":"Registration pending owner approval. The server generates the agent's keypair; once approved, the private key is collected exactly once from GET /v1/register/claim-key/{token}.","content":{"application/json":{"schema":{"type":"object","properties":{"registration_token":{"type":"string"},"poll_url":{"type":"string"},"status":{"type":"string","example":"pending"},"client_id":{"type":"string"},"scope":{"type":"string"},"expires_in":{"type":"integer"}}}}}},"400":{"description":"Invalid registration payload"}}}},"/v1/register/status/{token}":{"get":{"summary":"Poll registration status by token","tags":["agents"],"security":[],"parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string"},"description":"Registration token returned from POST /v1/register"}],"responses":{"200":{"description":"Current registration status","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["pending","approved","rejected","expired"]},"agent_id":{"type":"string"},"mcp_config":{"type":"object"}}}}}},"404":{"description":"Token not found or expired"}}}},"/v1/register/approve/{token}":{"post":{"summary":"Approve a pending agent registration (owner only)","tags":["agents","hitl"],"security":[{"ownerJWT":[]}],"parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Agent approved successfully"},"401":{"description":"Missing or invalid owner JWT"},"403":{"description":"Not authorized to approve this registration"},"404":{"description":"Token not found"}}}},"/v1/register/reject/{token}":{"post":{"summary":"Reject a pending agent registration (owner only)","tags":["agents","hitl"],"security":[{"ownerJWT":[]}],"parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Agent rejected successfully"},"401":{"description":"Missing or invalid owner JWT"},"403":{"description":"Not authorized to reject this registration"},"404":{"description":"Token not found"}}}},"/v1/register/stream/{token}":{"get":{"summary":"SSE stream for real-time registration status updates (CIBA-style)","tags":["agents"],"security":[],"parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Server-Sent Events stream emitting pending/approved/rejected/expired events","content":{"text/event-stream":{"schema":{"type":"string"}}}},"404":{"description":"Token not found"}}}},"/v1/authzen/access":{"post":{"summary":"Evaluate an AuthZEN access decision for an agent+platform+scope triple","description":"Returns the effective answer the governed call would get (grant, policy, guardrails, grant TTL, circuit breaker, child liveness), not merely whether a grant row exists. Requires a bearer: an agent JWT may query only itself; a Clerk owner JWT may query any agent in its org. An approval-required scope is decision=false with require_approval=true.","tags":["permissions"],"security":[{"agentJWT":[]},{"ownerJWT":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["subject","action","resource"],"properties":{"subject":{"type":"object","properties":{"type":{"type":"string","example":"agent"},"id":{"type":"string","format":"uuid"}}},"action":{"type":"object","properties":{"name":{"type":"string","example":"slack:chat:write"}}},"resource":{"type":"object","properties":{"type":{"type":"string","example":"platform"},"id":{"type":"string","example":"slack"}}}}}}}},"responses":{"200":{"description":"AuthZEN access decision","content":{"application/json":{"schema":{"type":"object","properties":{"decision":{"type":"boolean"},"reason":{"type":"string","description":"approved | denied | revoked | scope_not_granted | denied_by_policy | denied_by_guardrail | grant_expired | circuit_breaker_open | agent_suspended | approval_required_by_policy | approval_required_by_guardrail | approval_required_by_grant | ..."},"require_approval":{"type":"boolean","description":"Present and true when the call would be held for owner approval."}}}}}},"400":{"description":"Invalid request body"},"401":{"description":"Missing or invalid bearer, or the bearer may not query this subject"}}}},"/v1/trqp/authorization":{"post":{"summary":"ToIP TRQP v2.0 authorization query over the same grant ∩ policy ∩ liveness check that gates every governed call","description":"Trust Registry Query Protocol v2.0 HTTPS binding (self-assessed: ToIP publishes no conformance suite). entity_id is an agent (did:web:<host>:agents:<agt_...>, a bare agt_ id, or a spiffe:// id); authority_id is this registry (did:web:<host>) or one of its organisations (did:web:<host>:orgs:<uuid>); action is a scope, verbatim; resource is a platform id. authorized is exactly checkPermission().allowed (status allowlist, grant, TTL, breaker, child liveness, guardrails, policy, scope), computed live at query time, so revocation, grant and policy edits are reflected on the next query. Endpoint patterns, recipient constraints and external-reach classification need the real request and are NOT evaluated here (stated in context.not_evaluated); an approval requirement is authorized:false with the reason in message and context.decision require_approval. Public callers are rate-limited per IP and answered only for agents whose organisation publishes identity documents; every other public query is a uniform authorized:false / not_public. A bearer (agent JWT for itself, owner JWT for its org) always answers. Errors are RFC 7807 application/problem+json.","tags":["permissions"],"security":[{},{"agentJWT":[]},{"ownerJWT":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrqpRequest"}}}},"responses":{"200":{"description":"TRQP authorization response","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/TrqpResponseBase"},{"type":"object","required":["authorized"],"properties":{"authorized":{"type":"boolean"},"context":{"type":"object","properties":{"decision":{"type":"string","enum":["allow","require_approval","deny"]},"policy_id":{"type":"string"},"policy_version":{"type":"integer"},"evaluated":{"type":"array","items":{"type":"string"},"example":["status","grant","ttl","breaker","child_liveness","guardrails","policy","scope"]},"not_evaluated":{"type":"array","items":{"type":"string"},"example":["endpoint","recipient_constraints","reach"]}}}}}]}}}},"400":{"description":"Missing or malformed field (RFC 7807)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"A bearer was presented and it may not query this entity (RFC 7807)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Public path rate limited (60/min per IP); Retry-After set","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/trqp/recognition":{"post":{"summary":"ToIP TRQP v2.0 recognition query: is the entity a live principal of the authority?","description":"recognized is true when entity_id resolves to an agent governed by authority_id whose status is active and, for a child identity, whose parent is still active and whose own expiry has not passed. action and resource are required by the spec, echoed, and not evaluated (conventionally *). Same caller classes, publish gate, rate limit and error format as /v1/trqp/authorization.","tags":["permissions"],"security":[{},{"agentJWT":[]},{"ownerJWT":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrqpRequest"}}}},"responses":{"200":{"description":"TRQP recognition response","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/TrqpResponseBase"},{"type":"object","required":["recognized"],"properties":{"recognized":{"type":"boolean"},"context":{"type":"object","additionalProperties":true}}}]}}}},"400":{"description":"Missing or malformed field (RFC 7807)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"A bearer was presented and it may not query this entity (RFC 7807)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Public path rate limited; Retry-After set","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/audit/{id}/receipt":{"get":{"summary":"Signed governance receipt for one audit row","description":"Every decision the proxy makes on POST /v1/actions (allowed, held for approval, denied, blocked, errored) and every approval replay is written with a compact JWS receipt signed at decision time by the AgentValet evidence key (ES256, typ agentvalet-receipt+jwt, kid in the header). jti is the audit row id; the payload carries the agent, organisation, platform, scope, method, path (never the query string), result, the policy/guardrail decision with the matched rule events, approval provenance for replays, PII flags, upstream status and a hash of the stored row. It never carries request or response bodies, IP/user-agent/geo, the free-text agent reason (hash only) or recipient addresses. The audit_id arrives on the call itself: _meta.audit_id on a 2xx, audit_id on a refusal body. An agent bearer may fetch its own rows; an owner bearer any row in its organisation. Verify offline against the JWKS below or the #receipt-<kid> verificationMethod in /.well-known/did.json. Receipts are retained and purged with the audit row.","tags":["audit"],"security":[{"agentJWT":[]},{"ownerJWT":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"audit_log.id (= receipt jti)"},{"name":"Accept","in":"header","required":false,"schema":{"type":"string","enum":["application/json","application/jose"]},"description":"application/jose returns the bare compact JWS"}],"responses":{"200":{"description":"The receipt and where to verify it","content":{"application/json":{"schema":{"type":"object","required":["audit_id","kid","receipt","jwks_url","did"],"properties":{"audit_id":{"type":"string","format":"uuid"},"kid":{"type":"string"},"receipt":{"type":"string","description":"Compact JWS (ES256, typ agentvalet-receipt+jwt)"},"jwks_url":{"type":"string","format":"uri"},"did":{"type":"string","example":"did:web:api.agentvalet.ai"},"verify_hint":{"type":"string"}}}},"application/jose":{"schema":{"type":"string"}}}},"401":{"description":"Missing or invalid bearer, or the bearer may not read this row"},"404":{"description":"Unknown row, or a row without a receipt (pre-feature, broker-ingested, observe relay, connect event, breaker transition)"}}}},"/.well-known/agentvalet-receipt-keys.json":{"get":{"summary":"Evidence key set: the ES256 public keys that sign governance receipts and child attestations","description":"RFC 7517 JWK Set, active and retired keys, each with a kid. Deliberately separate from /v1/oauth/jwks.json (the bearer-token key) so a receipt verifier can never be pointed at the wrong key set. The same keys appear as #receipt-<kid> in /.well-known/did.json. Cache-Control public, max-age=3600.","tags":["discovery"],"security":[],"responses":{"200":{"description":"JWK Set","content":{"application/json":{"schema":{"type":"object","required":["keys"],"properties":{"keys":{"type":"array","items":{"type":"object","additionalProperties":true}}}}}}},"404":{"description":"This deployment has no evidence key configured"}}}},"/.well-known/agentvalet-attestation":{"get":{"summary":"How to verify the attestation AgentValet attaches when it calls an MCP server you connected","description":"When an MCP-as-platform's attestation mode is on, every governed call to it carries a compact JWS (ES256, typ av-attest+jwt, 120 s lifetime, audience = the server's own URL) stating which agent called (and its parent, for a child), which scopes it holds, which one it used, the session and trace id, and the human approval if the call was held. Sent as the X-AgentValet-Attestation header, as _meta[\"ai.agentvalet/attestation\"] on the tool call, or both. This document names the key set and the fields so a verifier needs nothing else.","tags":["discovery"],"security":[],"responses":{"200":{"description":"Verification hints","content":{"application/json":{"schema":{"type":"object","required":["v","issuer","jwks_uri","header","meta_key","typ","alg","ttl_seconds"],"properties":{"v":{"type":"integer","const":1},"issuer":{"type":"string"},"jwks_uri":{"type":"string","format":"uri"},"header":{"type":"string","const":"X-AgentValet-Attestation"},"meta_key":{"type":"string","const":"ai.agentvalet/attestation"},"typ":{"type":"string","const":"av-attest+jwt"},"alg":{"type":"string","const":"ES256"},"ttl_seconds":{"type":"integer"},"audience":{"type":"string"},"docs":{"type":"string","format":"uri"}}}}}}}}},"/git/{owner}/{repo}/info/refs":{"get":{"summary":"git smart-HTTP: advertise refs (clone/fetch/push discovery)","description":"Git smart-HTTP passthrough. `git clone https://api.agentvalet.ai/git/<owner>/<repo>.git` with the agent JWT as the HTTP Basic password (the `agentvalet git-credential` helper supplies it). Governed exactly like POST /v1/actions on platform `github` with the real `/git/...` path: scope `github:contents.read`, the grant's repository allow-list (`repo_not_allowed`), policies and guardrails. The brokered GitHub credential never reaches the client. Errors are one-line text/plain so git can print them. An approval requirement cannot hold a git operation and is answered 403 `approval_required_unsupported_for_git`.","tags":["git"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"},"description":"Repository name, optionally with a .git suffix"},{"name":"service","in":"query","required":true,"schema":{"type":"string","enum":["git-upload-pack","git-receive-pack"]}}],"responses":{"200":{"description":"Ref advertisement (Content-Type application/x-git-<service>-advertisement), streamed from GitHub"},"401":{"description":"No or invalid agent credential; WWW-Authenticate: Basic is set when the header is absent"},"403":{"description":"Denied: repo_not_allowed, denied_by_policy, approval_required_unsupported_for_git, ... (text/plain, one line)"},"412":{"description":"GitHub is not connected for this agent's organisation"},"502":{"description":"upstream_auth_failed (stored credential refused by GitHub) or upstream_unreachable"}}}},"/git/{owner}/{repo}/git-upload-pack":{"post":{"summary":"git smart-HTTP: fetch / clone / pull (upload-pack)","description":"Scope github:contents.read. Request body application/x-git-upload-pack-request (may be gzip), buffered up to GIT_MAX_REQUEST_BYTES (default 64 MiB → 413 git_request_too_large). Response streamed.","tags":["git"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/x-git-upload-pack-request":{"schema":{"type":"string","format":"binary"}}}},"responses":{"200":{"description":"application/x-git-upload-pack-result, streamed"},"401":{"description":"No or invalid agent credential"},"403":{"description":"Denied (text/plain, one line)"},"412":{"description":"GitHub not connected"},"413":{"description":"git_request_too_large"},"502":{"description":"upstream_auth_failed or upstream_unreachable"}}}},"/git/{owner}/{repo}/git-receive-pack":{"post":{"summary":"git smart-HTTP: push (receive-pack)","description":"Scope github:contents.write. The audit row records the refs pushed (metadata.git.refs: ref + create/update/delete) parsed from the command header; pack data is never inspected. Same limits and errors as upload-pack.","tags":["git"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/x-git-receive-pack-request":{"schema":{"type":"string","format":"binary"}}}},"responses":{"200":{"description":"application/x-git-receive-pack-result, streamed"},"401":{"description":"No or invalid agent credential"},"403":{"description":"Denied (text/plain, one line)"},"412":{"description":"GitHub not connected"},"413":{"description":"git_request_too_large"},"502":{"description":"upstream_auth_failed or upstream_unreachable"}}}},"/lab/mcp":{"post":{"summary":"The public lab's mock MCP upstream (no auth, no state worth attacking)","description":"A stateless Streamable HTTP MCP server with three tools (echo, whoami, write_note), served by the broker so the Codespaces lab can connect an MCP-as-platform upstream with zero secrets (authMode mcp_none). whoami reports verified:false and the request header names it saw: nothing today identifies the caller to an upstream. write_note keeps a bounded in-memory list. Rate-limited per IP; 64 KB body cap. Connect it in the dashboard as https://api.agentvalet.ai/lab/mcp. GET and DELETE are 405 (no server-push channel).","tags":["discovery"],"security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"JSON-RPC 2.0 request (MCP)","additionalProperties":true}}}},"responses":{"200":{"description":"JSON-RPC 2.0 response (application/json or text/event-stream)"},"405":{"description":"GET/DELETE: stateless server, POST only"},"429":{"description":"Rate limited (60/min per IP); Retry-After set"}}}},"/agents/{agentId}/did.json":{"get":{"summary":"W3C did:web document for an agent (did:web:<host>:agents:<agentId>)","description":"Publishes the same RSA public key the proxy verifies this agent's bearers against, as a DID Core document (JsonWebKey2020). Public and unauthenticated, but opt-in per organisation: until the owning org enables DID publishing this is 404, indistinguishable from an unknown id. Agents authenticated by the broker itself (MCP OAuth clients, child identities) resolve as keyless documents controlled by the root DID. Suspended agents keep their key but have empty authentication/assertionMethod; revoked or deleted agents are 410.","tags":["discovery"],"security":[],"parameters":[{"name":"agentId","in":"path","required":true,"schema":{"type":"string","pattern":"^agt_[a-z0-9]{20,21}$"}}],"responses":{"200":{"description":"DID document (Content-Type application/did+json, Cache-Control public max-age=60, CORS *)","content":{"application/did+json":{"schema":{"$ref":"#/components/schemas/DidDocument"}}}},"404":{"description":"Unknown, unbound, org-less, or not published by its organisation"},"410":{"description":"Deactivated (revoked or deleted)","content":{"application/json":{"schema":{"type":"object","required":["deactivated","id"],"properties":{"deactivated":{"type":"boolean","const":true},"id":{"type":"string"}}}}}},"429":{"description":"Rate limited (30/min per IP); Retry-After set"}}}},"/.well-known/did.json":{"get":{"summary":"W3C did:web document for the broker itself (did:web:<host>)","description":"The authorization server's signing key as a DID document: the controller of keyless agent DIDs and the signer a verifier resolves for broker-issued tokens and receipts.","tags":["discovery"],"security":[],"responses":{"200":{"description":"DID document (application/did+json)","content":{"application/did+json":{"schema":{"$ref":"#/components/schemas/DidDocument"}}}},"404":{"description":"Signing key not configured on this deployment"},"429":{"description":"Rate limited"}}}},"/.well-known/oauth-authorization-server":{"get":{"summary":"RFC 8414 OAuth authorization server metadata","tags":["discovery"],"security":[],"responses":{"200":{"description":"OAuth server metadata","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/.well-known/aa-agent-schema":{"get":{"summary":"Agent registration JSON Schema (RFC 7591 / DCR compatible)","tags":["discovery"],"security":[],"responses":{"200":{"description":"JSON Schema for agent registration payload","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/.well-known/authzen-configuration":{"get":{"summary":"AuthZEN evaluation endpoint configuration metadata","tags":["discovery"],"security":[],"responses":{"200":{"description":"AuthZEN configuration","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/llms.txt":{"get":{"summary":"LLM-readable plain-text description of the AgentValet API","tags":["discovery"],"security":[],"responses":{"200":{"description":"Plain text API overview","content":{"text/plain":{"schema":{"type":"string"}}}}}}},"/agent-manifest.json":{"get":{"summary":"Machine-readable agent capability manifest","tags":["discovery"],"security":[],"responses":{"200":{"description":"Agent manifest JSON","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/version":{"get":{"summary":"Build identity of the running proxy","tags":["health"],"security":[],"responses":{"200":{"description":"Version, git SHA and build time (Cache-Control: no-store)","content":{"application/json":{"schema":{"type":"object","required":["service","version","sha","builtAt"],"properties":{"service":{"type":"string","const":"proxy"},"version":{"type":"string"},"sha":{"type":"string","description":"Git SHA baked in at deploy, or \"unknown\""},"builtAt":{"type":["string","null"],"format":"date-time"}}}}}}}}},"/v1/agents/me":{"get":{"summary":"Who am I: the authenticated agent's identity and standing","description":"Answers even while the agent is halted: a suspended or breaker-open agent gets its row plus a `halt` envelope explaining why and what restores it, so it can report something useful instead of probing tools.","tags":["agents"],"security":[{"agentJWT":[]}],"responses":{"200":{"description":"Agent identity","content":{"application/json":{"schema":{"type":"object","required":["status","agent_id","did","name","owner_id","org_id","agent_type","agent_status","parent_agent_id","created_at","expires_at"],"properties":{"status":{"type":"string","const":"approved"},"agent_id":{"type":"string"},"did":{"type":"string","description":"W3C did:web identifier; resolves at /agents/{agentId}/did.json once the org publishes","example":"did:web:api.agentvalet.ai:agents:agt_7o00saqajyahinuuc6g83"},"name":{"type":"string"},"owner_id":{"type":"string","format":"uuid"},"org_id":{"type":["string","null"],"format":"uuid"},"agent_type":{"type":["string","null"]},"agent_status":{"type":"string","enum":["active","suspended","revoked","pending","pending_bind","deleted"]},"parent_agent_id":{"type":["string","null"],"description":"Set for a child identity issued by POST /v1/agents/children"},"created_at":{"type":["string","null"],"format":"date-time"},"expires_at":{"type":["string","null"],"format":"date-time"},"halt":{"$ref":"#/components/schemas/AgentHalt"}}}}}},"401":{"description":"Missing or invalid Bearer JWT"},"404":{"description":"agent_not_found"},"429":{"description":"Rate limited (30/min); Retry-After set"}}}},"/v1/agents/me/pending-actions":{"get":{"summary":"Actions of this agent awaiting, or recently decided by, a human","tags":["hitl"],"security":[{"agentJWT":[]}],"responses":{"200":{"description":"Pending and recently completed approvals","content":{"application/json":{"schema":{"type":"object","required":["pending","recently_completed"],"properties":{"pending":{"type":"array","items":{"type":"object","required":["approval_id","platform_id","scope","created_at","expires_at"],"properties":{"approval_id":{"type":"string","format":"uuid"},"platform_id":{"type":"string"},"scope":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"expires_at":{"type":["string","null"],"format":"date-time"}}}},"recently_completed":{"type":"array","items":{"type":"object","required":["approval_id","platform_id","scope","status","created_at"],"properties":{"approval_id":{"type":"string","format":"uuid"},"platform_id":{"type":"string"},"scope":{"type":"string"},"status":{"type":"string","enum":["approved","denied","expired"]},"created_at":{"type":"string","format":"date-time"},"executed_at":{"type":["string","null"],"format":"date-time"},"result_summary":{"type":["string","null"]},"execution_error":{"type":["string","null"]}}}}}}}}},"401":{"description":"Missing or invalid Bearer JWT"},"429":{"description":"Rate limited"}}}},"/v1/agents/children":{"post":{"summary":"Issue a child identity with attenuated grants (child <= parent)","description":"The parent mints a short-lived child agent whose scopes are the intersection of what it asks for and what the parent currently holds. Revoking or narrowing the parent contains every child on its next call. Depth is capped at 1: a child cannot issue children. Ungranted scopes are dropped, not refused; a platform left with no scopes is a 400.","tags":["agents"],"security":[{"agentJWT":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["grants"],"properties":{"name":{"type":"string"},"ttl_seconds":{"type":"integer","description":"Bounded by the server; the child expires lazily on its next call after this"},"grants":{"type":"array","maxItems":25,"items":{"type":"object","required":["platform","scopes"],"properties":{"platform":{"type":"string"},"scopes":{"type":"array","items":{"type":"string"}},"connection_id":{"type":["string","null"]},"constraints":{"type":"object","description":"Recipient constraint; must be at least as tight as the parent's","additionalProperties":true}}}}}}}}},"responses":{"201":{"description":"Child issued. bearer_token is presented as the child's Bearer (no key); it is not retrievable again.","content":{"application/json":{"schema":{"type":"object","required":["child_agent_id","bearer_token","expires_at","granted"],"properties":{"child_agent_id":{"type":"string"},"bearer_token":{"type":"string"},"expires_at":{"type":"string","format":"date-time"},"granted":{"type":"array","items":{"type":"object","properties":{"platform":{"type":"string"},"scopes":{"type":"array","items":{"type":"string"}}},"additionalProperties":true}}}}}}},"400":{"description":"invalid_grants, no_scopes_after_attenuation, invalid_constraints, child_constraints_looser_than_parent, bad ttl_seconds"},"401":{"description":"Missing or invalid Bearer JWT"},"403":{"description":"agent_not_active, or the caller is itself a child (depth cap)"},"409":{"description":"Live-child cap reached for this parent"},"503":{"description":"as_signing_unavailable"}}}},"/v1/agents/self/diagnostics":{"post":{"summary":"Report a self-diagnostic (an error the agent hit) to its owner's audit trail","tags":["audit"],"security":[{"agentJWT":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["message"],"properties":{"severity":{"type":"string","enum":["info","warning","error"]},"code":{"type":"string"},"message":{"type":"string"},"platform":{"type":"string"},"endpoint":{"type":"string"},"correlation_id":{"type":"string"},"context":{"type":"object","additionalProperties":true}}}}}},"responses":{"201":{"description":"Recorded","content":{"application/json":{"schema":{"type":"object","required":["id","received_at"],"properties":{"id":{"type":"string","format":"uuid"},"received_at":{"type":"string","format":"date-time"}}}}}},"400":{"description":"message required, invalid severity/code/endpoint/platform/correlation_id, context must be an object"},"401":{"description":"Missing or invalid Bearer JWT"},"413":{"description":"message or context too large"},"429":{"description":"Rate limited"}}}},"/v1/agents/bind":{"post":{"summary":"Bind an agent-generated public key to an agent using a one-time bootstrap token","description":"Bootstrap flow for agents that generate their own keypair (CLI/IDE installs): the owner's install token is exchanged, exactly once, for the agent row. The private key never leaves the agent.","tags":["agents"],"security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["bootstrap_token","public_key_pem"],"properties":{"bootstrap_token":{"type":"string"},"public_key_pem":{"type":"string","description":"SPKI PEM (BEGIN PUBLIC KEY)"}}}}}},"responses":{"200":{"description":"Bound","content":{"application/json":{"schema":{"type":"object","required":["agent_id","owner_id","status","proxy_url"],"properties":{"agent_id":{"type":"string"},"owner_id":{"type":"string","format":"uuid"},"status":{"type":"string"},"proxy_url":{"type":"string","format":"uri"}}}}}},"400":{"description":"bootstrap_token and public_key_pem are required, or the PEM is not SPKI"},"410":{"description":"Token invalid, expired, already used, or agent already bound"},"429":{"description":"Too many bind attempts"}}}},"/v1/approvals/{id}":{"get":{"summary":"Poll the state of a queued approval","description":"Long-poll target after a 202 from POST /v1/actions. When approved and executed, `result` carries the upstream status and body; `execution_error` explains a failed or blocked replay.","tags":["hitl"],"security":[{"agentJWT":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Approval state","content":{"application/json":{"schema":{"type":"object","required":["approval_id","status","expires_at","executed_at","created_at"],"properties":{"approval_id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["pending","approved","denied","expired"]},"expires_at":{"type":["string","null"],"format":"date-time"},"executed_at":{"type":["string","null"],"format":"date-time"},"created_at":{"type":"string","format":"date-time"},"result":{"type":"object","description":"Present once an approved action has executed","properties":{"status":{"type":"integer"},"data":{}}},"execution_error":{"type":"string"}}}}}},"400":{"description":"invalid id"},"401":{"description":"Missing or invalid Bearer JWT"},"404":{"description":"not_found (or not this agent's approval)"},"429":{"description":"Rate limited"}}}},"/v1/access-request":{"post":{"summary":"Ask the org's admins for a scope the agent does not hold","description":"The governed way to react to a 403 scope_not_granted: the request is queued for an org admin to approve in the dashboard. Idempotent per (agent, platform, scope) while pending: a duplicate returns 200 with the existing token.","tags":["hitl"],"security":[{"agentJWT":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["platform"],"properties":{"platform":{"type":"string"},"scope":{"type":"string"},"reason":{"type":"string"}}}}}},"responses":{"200":{"description":"A matching request is already pending; same body as 202"},"202":{"description":"Queued for an admin","content":{"application/json":{"schema":{"type":"object","required":["status","request_token","poll_url","reason_code","message"],"properties":{"status":{"type":"string","const":"pending_approval"},"request_token":{"type":"string"},"poll_url":{"type":"string","example":"/v1/access-request/status/{token}"},"reason_code":{"type":"string","enum":["not_connected","not_granted","grant_expired","needs_consent","scope_denied"]},"message":{"type":"string"}}}}}},"400":{"description":"platform is required"},"401":{"description":"Missing or invalid Bearer JWT"},"403":{"description":"The requested scope cannot be granted to this agent (e.g. platform not connected, policy denies)"},"404":{"description":"Agent or platform not found"},"409":{"description":"Agent not active, or owner has no org context"},"429":{"description":"Too many open requests"}}}},"/v1/access-request/status/{token}":{"get":{"summary":"Poll an access request","tags":["hitl"],"security":[{"agentJWT":[]}],"parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Current state","content":{"application/json":{"schema":{"type":"object","required":["status"],"properties":{"status":{"type":"string","enum":["pending","approved","denied","expired"]}}}}}},"401":{"description":"Missing or invalid Bearer JWT"},"403":{"description":"Not this agent's request"},"404":{"description":"Not found"}}}},"/v1/register/claim-key/{token}":{"get":{"summary":"Collect the private key for an approved registration, exactly once","description":"The server generated the keypair at POST /v1/register. After the owner approves, the agent fetches its private key here once; the copy is destroyed on delivery. A second call is 410.","tags":["agents"],"security":[],"parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The PKCS#8 private key PEM. Store it; it is not retrievable again.","content":{"application/json":{"schema":{"type":"object","required":["private_key"],"properties":{"private_key":{"type":"string"}}}}}},"404":{"description":"Registration request not found"},"409":{"description":"Registration not yet approved"},"410":{"description":"Already claimed, or the claim window expired"}}}},"/v1/invites/bind":{"post":{"summary":"Claim an owner invite: bind an agent-generated public key with the invite's bind secret","tags":["agents"],"security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["bind_secret","public_key_pem"],"properties":{"bind_secret":{"type":"string"},"public_key_pem":{"type":"string","description":"SPKI PEM (BEGIN PUBLIC KEY)"}}}}}},"responses":{"200":{"description":"Bound","content":{"application/json":{"schema":{"type":"object","required":["agent_id","owner_id","status","proxy_url"],"properties":{"agent_id":{"type":"string"},"owner_id":{"type":"string","format":"uuid"},"status":{"type":"string"},"proxy_url":{"type":"string","format":"uri"}}}}}},"400":{"description":"Invalid body or PEM"},"410":{"description":"Invite invalid, expired, or already claimed"},"429":{"description":"Too many attempts"}}}},"/v1/oauth/jwks.json":{"get":{"summary":"JWKS for tokens this proxy issues (child bearers, MCP OAuth)","tags":["discovery"],"security":[],"responses":{"200":{"description":"RFC 7517 key set (Cache-Control: public, max-age=300)","content":{"application/json":{"schema":{"type":"object","required":["keys"],"properties":{"keys":{"type":"array","items":{"type":"object","additionalProperties":true}}}}}}}}}},"/.well-known/oauth-protected-resource":{"get":{"summary":"RFC 9728 protected-resource metadata for the API","tags":["discovery"],"security":[],"responses":{"200":{"description":"Protected resource metadata (resource, authorization_servers, bearer_methods_supported)","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/v1/bind-secret":{"post":{"summary":"Bind an encrypted secret to an agent/platform credential record","tags":["agents"],"security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"agent_id":{"type":"string","format":"uuid"},"platform_id":{"type":"string"},"encrypted_secret":{"type":"string"}}}}}},"responses":{"200":{"description":"Secret bound successfully"},"400":{"description":"Invalid request body"}}}},"/v1/openapi.json":{"get":{"summary":"This OpenAPI 3.1 specification","tags":["discovery"],"security":[],"responses":{"200":{"description":"OpenAPI 3.1 spec","content":{"application/json":{"schema":{"type":"object"}}}}}}}}}