Skip to main content
Delegation-governance endpoints are mounted under /orchestrate/*. The base URL is https://api.visiqlabs.com. These endpoints govern agent-to-agent handoffs: a parent agent creates a scoped grant, the named child accepts it and receives a signed token, and the child evaluates each delegated action against that grant. The decision vocabulary here is permit and deny — a delegated action is either within the grant’s intersected authority or it is not.

Authentication

All endpoints require a Bearer credential: Authorization: Bearer <key>. Requests without a valid credential receive 401 Unauthorized. The whole /orchestrate/* surface is management-audience. A harness key (vq_prod_... / vq_test_... minted under Settings → Harness Keys, or an allow_... agent key) is confined to the SDK operational routes and receives 403 {"error": "harness_key_not_permitted"} here — delegation is driven by a management key or a dashboard session. Management keys are launching soon: they are visible in the dashboard under Settings → API Keys, but creating one is not yet enabled. Until then, drive these endpoints from the dashboard, which authenticates with your session. Scoped keys are additionally checked against their granted scopes. Every endpoint on this page requires allow:write (the read-only GET /orchestrate/grants/:id also accepts allow:read); full_access satisfies everything. A key without the required scope receives 403 {"error": "insufficient_scope"} listing the required and granted scopes.

Rate limiting

Every API-key request passes a per-key sliding-window rate limit (default 600 requests per 60 seconds). Responses carry X-RateLimit-Limit and X-RateLimit-Remaining headers; exceeding the window returns 429 with a Retry-After header and body {"error": "rate_limited", "detail": "API key rate limit exceeded.", "retryAfter": <seconds>}.

Grant lifecycle


POST /orchestrate/grants

Create a delegation from a parent agent to a child agent. Both agents must be registered for your organization. The platform runs a policy check on the delegation itself, intersects the requested scopes against the parent’s authority, writes a synchronous record proof, and returns a pending grant awaiting acceptance. Scope: allow:write Request body:
When parent_grant_token is supplied, the delegating parent_agent_id must equal the child_agent_id of that token’s grant — you can only sub-delegate authority you were actually granted. The new grant’s depth is the parent grant’s depth plus one, and its max_depth is inherited from the parent grant.
Response (200):
Decision & failure semantics:
  • If no rule permits orchestrate.delegate for the parent and the parent is in enforce mode, the delegation is denied fail-closed: 403 {"error": "Delegation denied by policy", "reason": "..."}.
  • If the synchronous record proof cannot be written, the just-created grant is revoked and the call returns 500 {"error": "Failed to write ORCHESTRATE proof record"}.
Status codes: 200 OK, 400 Bad Request (invalid body), 401 Unauthorized (missing credential, or invalid parent_grant_token), 403 Forbidden (insufficient scope, delegation denied by policy, or the parent grant does not belong to the delegating agent), 404 Not Found (parent or child agent not registered), 422 Unprocessable Entity (delegation depth ceiling reached), 500 Internal Server Error Example:

GET /orchestrate/grants/:id

Fetch a grant’s current status — the parent polls this to learn whether the child has accepted, and to read the effective scopes and lifecycle timestamps. Scope: allow:write or allow:read Path parameter: :id — the grant UUID (from POST /orchestrate/grants) Response (200):
status is one of pending, active, revoked, or expired. The grant token itself is never returned here — it is issued once, at acceptance. Status codes: 200 OK, 401 Unauthorized, 403 Forbidden (insufficient scope), 404 Not Found, 500 Internal Server Error

POST /orchestrate/grants/:id/accept

The child agent accepts a pending grant and receives its signed grant token. Bilateral identity: the child declares itself in the X-Agent-ID header, which must match the grant’s child_agent_id. Scope: allow:write Request headers: Path parameter: :id — the grant UUID This endpoint takes no request body. Acceptance is guarded against a concurrent double-accept: only the caller that flips the grant from pending to active wins; a lost race returns 409. Response (200):
The grant_token is present in this response only. Subsequent reads never return it. If a record proof write fails after the token is issued, the acceptance is rolled back to pending and the call returns 500 — the token you received in that case is not valid.
Status codes: 200 OK, 400 Bad Request (missing X-Agent-ID), 401 Unauthorized, 403 Forbidden (insufficient scope, X-Agent-ID does not match the grant’s child, or the claimed agent is not registered), 404 Not Found (unknown grant), 409 Conflict (grant not pending, accept window expired, or already accepted), 500 Internal Server Error

POST /orchestrate/grants/:id/revoke

Revoke a grant. Revocation cascades eagerly to every descendant grant in one atomic step, and is checked at enforcement time, so there is no revocation lag. Scope: allow:write Path parameter: :id — the grant UUID Request body:
Response (200):
revoked_count is the number of grants invalidated — the target plus every descendant in its subtree. Status codes: 200 OK, 400 Bad Request, 401 Unauthorized, 403 Forbidden (insufficient scope), 404 Not Found, 422 Unprocessable Entity (grant already revoked), 500 Internal Server Error

Per-action enforcement


POST /orchestrate/evaluate

The child agent’s per-action check. For each action it takes under a grant, the child presents its grant token; the platform verifies the token, walks the ancestor chain, checks the action against the grant’s intersected tool and context scopes, records a handoff event, and returns the decision. Scope: allow:write Request body:
Response (200):
Decision & failure semantics:
  • A token whose signature is invalid, whose vendor does not match the caller, that references an unknown grant, or that no longer matches the grant’s stored token returns 401 {"error": "Invalid grant token"}.
  • An expired token or a grant that is not active returns 403 (Grant expired / Grant is <status>).
  • A revoked or invalid ancestor grant yields decision: "deny" with the reason naming the broken link — a 200 response with a deny, not an error.
  • An action outside the grant’s tool scope, or a resource type outside its context scope, yields decision: "deny".
  • If the handoff event cannot be recorded, the call returns 500 {"error": "Failed to record handoff event"} — never a silent permit.
Status codes: 200 OK, 400 Bad Request, 401 Unauthorized (invalid or mismatched grant token), 403 Forbidden (insufficient scope, expired token, or inactive grant), 500 Internal Server Error

Errors & conventions

Every endpoint on this page follows the platform-wide REST conventions — the validation-error body shape, the /v1/ vs unversioned split, and the API stability policy. See REST API conventions.