---
title: "Get Governed Hosts"
method: GET
path: "/governed-hosts"
tags: ["Governed Hosts"]
---

# Get Governed Hosts

`GET /governed-hosts`

The caller's governed host set (credential-bound hosts) with an ETag digest.

**Always self-scoped** — derived from the authenticated identity's own
credential bindings; there is no cross-actor or admin variant. Credentials
bind to agents and service accounts, so agent-scoped tokens (the OAuth
agent-consent flow's output) and `sak_` keys are the callers this endpoint
serves — a plain user token yields an empty set. Suspended bindings and
inactive credentials still contribute their hosts: keep diverting that
traffic, so the broker can refuse it — dropping it from the list would
send it direct to the upstream, unbrokered. The ``digest`` covers exactly
the ``data`` list and is also emitted as a strong ``ETag``, so integrators
poll with ``If-None-Match: "<digest>"`` and get an empty ``304`` until
their host set actually changes (the change-poll seam that replaces
``GET /apis`` enumeration for interception scoping). Poll at most once per
minute; on any ``5xx`` retain the last known set — never fall back to an
empty (intercept-nothing) list.

Responses are identity-scoped and marked ``Cache-Control: private,
no-store`` — a shared cache must never serve one actor's host set to
another.

## Headers

- `if-none-match` string, nullable — Change-poll precondition: the `ETag` from a previous response (quoted, `"<digest>"`; the bare digest is accepted as a compatibility form). When it still matches, the response is an empty `304`.

## Response `200`

Successful Response

- GovernedHostsResponse — The caller's governed host set (canonical order) with its change digest. **Hosts only, deliberately**: the set exists for interception scoping (divert lists, host filters), so it carries the minimum knowledge — API detail stays behind the existing authenticated reads (``GET /apis``). Deliberately **unpaginated**: the set is bounded by the caller's own credential bindings (tens of hosts, not thousands) and the digest must cover the whole set atomically — a paginated digest would be meaningless. This is a documented deviation from the list-endpoint pagination convention.
  - `data` string[], required — Governed host entries, normalised (lowercased, FQDN root dot stripped, internationalised names IDNA/punycode-encoded), deduplicated, and sorted — the literal URL-index hosts the broker's discovery matches for the APIs the caller's bound credentials cover. An entry is a hostname, a bare IP literal, or either followed by a non-default port (`host[:port]` — default ports are already stripped). Compare case-insensitively (lowercase the incoming host before matching); a gate keying on hostname or SNI alone must strip any `:port` suffix from the entry first. Variable-bearing hosts (defaultless `{var}` server variables) are excluded — the broker's discovery never matches them. On a `5xx` retain the last known set; never fall back to an empty (intercept-nothing) list.
  - `digest` string, required — SHA-256 hex digest over the newline-joined `data` list; also emitted as the response's strong `ETag`. To change-poll, send it back quoted — `If-None-Match: "<digest>"` (the bare digest is accepted as a compatibility form) — and expect an empty `304` until the host set changes. Poll at most once per minute.

## Other responses

- `304` — The host set still matches the presented `If-None-Match` — empty body, `ETag` echoed.
- `400` — Bad Request
- `401` — Unauthorized
- `403` — Forbidden
- `422` — Unprocessable Entity
- `500` — Internal Server Error
- `503` — Service Unavailable

## Changes

- **2026-09-15** `b34f07a8dacd` — 1 info
  - endpoint added

[Change history](https://skmtc.dev/jentic/apis/jentic-control-plane-api/changes/governed-hosts/get.md)

---

[API](https://skmtc.dev/jentic/apis/jentic-control-plane-api.md) · [All operations](https://skmtc.dev/jentic/apis/jentic-control-plane-api/llms.txt) · [OpenAPI document](https://skmtc.dev/jentic/apis/jentic-control-plane-api/revisions/e4688b93dfc7?raw)
