List agents connected to the authenticated organization
GET/api/public/v1/agents
Returns one row per agent (one platform used on one host by one OS user in one profile) within the caller's organization, ordered by most-recently-seen first. Includes a 24h verdict breakdown joined from event_stats_daily. Paginated with page/page_size. The five ownership filters (owner / function / org_unit / unattributed / owner_status) narrow the list, and every row carries the six ownership fields (owner_kind / owner_person_id / owner_display_name / owner_status / function / org_unit_path). zone_id, assignment_source, environment, category, control_level and q narrow it further, and every row names the zone that governs the agent (assigned_zone_id, assigned_zone_unit_id, assigned_zone_label, assignment_source).
curl --fail-with-body -X GET 'https://app.openlatch.ai/api/public/v1/agents?page=1&page_size=50' \ -H "Authorization: Bearer $OPENLATCH_API_KEY"import jsonimport osimport urllib.request
request = urllib.request.Request( 'https://app.openlatch.ai/api/public/v1/agents?page=1&page_size=50', method='GET', headers={ 'Authorization': 'Bearer ' + os.environ['OPENLATCH_API_KEY'], },)with urllib.request.urlopen(request) as response: text = response.read().decode() print(json.dumps(json.loads(text), indent=2) if text else response.status)#!/usr/bin/env bashset -euo pipefail
curl -sS --fail-with-body -X GET 'https://app.openlatch.ai/api/public/v1/agents?page=1&page_size=50' \ -H "Authorization: Bearer $OPENLATCH_API_KEY" | jq .$headers = @{ Authorization = "Bearer $env:OPENLATCH_API_KEY"}Invoke-RestMethod -Method GET -Uri 'https://app.openlatch.ai/api/public/v1/agents?page=1&page_size=50' -Headers $headers | ConvertTo-Json -Depth 20Authorization
Section titled “Authorization”| Role in the organization | This endpoint |
|---|---|
owner | Allowed |
admin | Allowed |
editor | Allowed |
viewer | Allowed |
A key carries the role its owner holds in the organization. See Authentication.
API key scope: agents:read
Query parameters
Section titled “Query parameters”| Parameter | Type | Description |
|---|---|---|
page | integer | 1-indexed page number |
page_size | integer | Page size (max 200) |
platform | array of string or null | Filter to one or more agent platforms (repeat the param) |
window | string — one of 24h, 7d, 30d, 90d | Rolling window for the per-agent action_mix rollup. One of 24h, 7d, 30d, 90d. Does not affect verdicts_24h, which always reflects the last 24h. |
compatibility | string or null | unavailable narrows to installs whose latest compatibility evidence does not intersect what the platform currently retains. |
capability_limit | string — one of cannot_prompt, shell_only or null | Narrow to the agents whose binding limits what an ASK can do: cannot_prompt (its ASKs take the fallback verdict) or shell_only (its refusals reach shell tools only). An install that has not reported is never in either set. |
host_id | string or null | Narrow to the agents reporting from one licensed host. |
zone_id | string (uuid) or null | Narrow to the agents whose assigned zone (manual pin, else the resolver's) is this zone. |
assignment_source | string — one of auto, manual or null | manual — pinned to a zone by hand; auto — the resolver decides. |
environment | array of string or null | Narrow to these environments (repeat the param). |
category | array of string or null | Narrow to these categories (repeat the param). |
control_level | array of string — one of enforced, monitored, known or null | Narrow to these derived control levels (repeat the param). |
q | string or null | Free text over display name, agent id, platform and the owner's display name. |
owner | array of string | Agents whose resolved human owner is any of these directory_people ids (repeat the param). |
function | array of string | Agents whose business function is any of these frozen-enum values (repeat the param). An unknown value is a 400, never an empty list. |
org_unit | array of string | Agents whose placement unit (the human owner's unit, else the service-accountable person's unit, else the service-accountable unit) is any of these org_units ids OR any descendant (repeat the param). Descendants are resolved server-side. |
unattributed | boolean | Only agents with owner_kind = 'unattributed' (the PRD literal). Deliberately EXCLUDES owner_kind IS NULL (still resolving) and a human owner whose person row is gone. |
owner_status | string or null | Only 'inactive' is accepted: agents whose owner exists AND is marked inactive in the directory. |
Response
Section titled “Response”- 200 OK — the call succeeded.
- 400 Bad Request —
BAD_REQUEST - 401 Unauthorized —
UNAUTHORIZED - 402 Payment Required —
license_expired - 403 Forbidden —
FEATURE_DISABLED,FORBIDDEN,INSUFFICIENT_SCOPE - 422 Unprocessable Content —
INVALID_ID,VALIDATION_ERROR - 429 Too Many Requests —
TOO_MANY_REQUESTS - 500 Internal Server Error —
INTERNAL_SERVER_ERROR,tenant_isolation_violation - 503 Service Unavailable —
FEATURE_STATE_UNAVAILABLE,SERVICE_UNAVAILABLE
Response fields
Section titled “Response fields”| Field | Type | Description |
|---|---|---|
data | array of object | Required. |
data[].action_mix | object | Domain / file / command action counts rolled up from the agent_action_* tables, scoped to the window supplied by the caller (defaults to 24h). Zero across the board means no actionable events were extracted in the window. |
data[].action_mix.commands | integer | |
data[].action_mix.domains | integer | |
data[].action_mix.files | integer | |
data[].agent_id | string | Required. |
data[].assigned_zone_id | string or null | The zone governing this agent: its manual pin, else the one the resolver gives it. |
data[].assigned_zone_label | string or null | The assigned zone's org-unit name — the label every zone surface shows. |
data[].assigned_zone_unit_id | string or null | The org unit the assigned zone hangs off. |
data[].assignment_source | string — one of auto, manual | manual — pinned to a zone by hand; auto — the resolver decides. |
data[].budget_source | string | Which scope produced budget_usd: agent | subscription_default | org_default | none. |
data[].budget_usd | string or null | The resolved monthly budget - this agent's own row, else the default of the declared subscription that makes it Sanctioned, else the org default. Null when none is set. |
data[].category | string or null | Open category string; known values coding | analysis | ops | content | service. |
data[].control_level | string | enforced (fetched a bundle in the last 24h — evaluating policy locally) | monitored (sent events, no bundle — we see it and cannot stop it) | known (neither). Derived per request, never stored. |
data[].display_name | string or null | |
data[].environment | string | Frozen Environment — production | staging | development | unknown. |
data[].first_seen_at | string (date-time) | Required. |
data[].function | string or null | Business function (frozen enum; wire name for the ORM's business_function). 'unknown' = not yet classified; null while the resolver has not yet run. |
data[].host_id | string or null | |
data[].instance_qualifier | object or null | What tells this agent apart from the other agents of its platform on its host, shown beside the name and never inside it; null for a host's first OS user in their default profile. |
data[].last_seen_at | string (date-time) | Required. When the agent last sent activity. For an agent that has never been active (found by discovery, never run since), this is first_seen_at and never_active is true. |
data[].latest_agent_version | string or null | |
data[].latest_arch | string or null | |
data[].latest_client_version | string or null | |
data[].latest_hook_version | string or null | |
data[].latest_local_ipv4 | string or null | |
data[].latest_local_ipv6 | string or null | |
data[].latest_os | string or null | |
data[].latest_public_ipv4 | string or null | |
data[].latest_public_ipv6 | string or null | |
data[].mtd_consumption_usd | string or null | Month-to-date consumption for this agent (pricebook-weighted cost_total, every billing mode). What the budget is measured against. Null when the surface did not compute it; 0 means the agent really has not spent. |
data[].never_active | boolean | True when the agent has never sent any activity (found by discovery and not run since): last_seen_at then repeats first_seen_at and is not a sighting of activity. |
data[].org_unit_path | string or null | Slash-joined path of the agent's placement unit: the human owner's unit, else the service-accountable person's unit, else the service-accountable unit. Null when none applies. |
data[].owner_display_name | string or null | The owner person's display name; null when there is no (or an orphaned) owner person. |
data[].owner_kind | string or null | Ownership verdict: human | service | unattributed; null while the resolver has not yet run. |
data[].owner_person_id | string or null | The resolved human owner's directory_people id — the value the owner filter of GET /agents takes (it cannot filter by display name). Null for service / unattributed / resolving. |
data[].owner_status | string or null | The owner person's directory status (active | inactive); null when there is no owner person. |
data[].platform | string | Required. |
data[].proxy_as_of | string (date-time) or null | Timestamp of the latest event that actually CARRIED the proxy attributes — not of the latest event overall, so a stale shape is never reported as freshly confirmed. Null until the first carrying event. |
data[].proxy_source | string or null | How the client found that route: manual | env | windows | macos | gnome | pac | wpad. Null under the same conditions as proxy_type. Open string, same reason. |
data[].proxy_tls_intercepted | boolean or null | Whether the client observed the proxy terminating and re-issuing TLS. Null is NOT false: null = never observed (the client omits the attribute until a TLS handshake has actually completed), false = observed and clean. |
data[].proxy_type | string or null | Egress proxy type last reported by this agent's client: http | https | socks5 | pac. Null = the agent egresses direct, or never reported one. Open string: the client owns the vocabulary, so an unknown value is stored and returned verbatim rather than rejected. |
data[].sessions_count | integer | Distinct session count for this agent over the same rolling window as action_mix (defaults to 24h): how many sessions blend into one agent identity, without a per-agent sessions call. Zero means no sessions were active in the window. |
data[].stale | boolean | This install's latest compatibility evidence (I-15 plan 02) does not intersect what the platform currently retains. NOT a fourth control level: a stale agent keeps whatever level its last bundle earned it, because 'we stopped serving it' and 'it is not under control' are different facts. |
data[].total_events_lifetime | integer | Lifetime event count for this agent — sum of event_stats_daily.event_count across every day, never zero-floored to a 24h window. Gives an accurate count for agents idle longer than 24h, for which verdicts_24h shows 0 even when the agent has many events in older sessions. |
data[].used_pct | number or null | mtd_consumption_usd as a percent of budget_usd. Null when there is no budget. A zero budget reports 100 when nothing was spent and 999 otherwise. |
data[].verdicts_24h | object | Verdict counts over the last 24h. Zero if no activity. |
data[].verdicts_24h.allow | integer | Actions the policy allowed |
data[].verdicts_24h.ask | integer | Actions the policy sent to Ask |
data[].verdicts_24h.block | integer | Actions the policy blocked |
data[].verdicts_24h.total | integer | Total events in the 24h window |
data[].window_consumption_usd | string or null | Consumption for this agent over the request's window (pricebook-weighted cost_total, every billing mode) — the figure the fleet tile prints. Never compare it to budget_usd: a budget is a calendar month. Null when the surface did not compute it; 0 means the agent really has not spent in the window. |
page | integer | Required. |
page_size | integer | Required. |
total | integer | Required. |
Example response
Section titled “Example response”{ "data": [ { "action_mix": { "commands": 0, "domains": 0, "files": 0 }, "agent_id": "agt_01jbwa2k1vfrn9y42xhgxhmzj2", "assigned_zone_id": "3f6c1a2e-8b4d-4c1f-9a7e-0d5b2c8e4f10", "assigned_zone_label": "string", "assigned_zone_unit_id": "3f6c1a2e-8b4d-4c1f-9a7e-0d5b2c8e4f10", "assignment_source": "auto", "budget_source": "none", "budget_usd": "string", "category": "string", "control_level": "known", "display_name": "release-bot", "environment": "production", "first_seen_at": "2026-01-15T14:32:05Z", "function": "string", "host_id": "hst_01jbwa2k1vfrn9y42xhgxhmzj2", "instance_qualifier": { "os_user": "string", "profile_suffix": "string", "user_suffix": "string" }, "last_seen_at": "2026-01-15T14:32:05Z", "latest_agent_version": "string", "latest_arch": "string", "latest_client_version": "string", "latest_hook_version": "string", "latest_local_ipv4": "string", "latest_local_ipv6": "string", "latest_os": "string", "latest_public_ipv4": "string", "latest_public_ipv6": "string", "mtd_consumption_usd": "string", "never_active": false, "org_unit_path": "Engineering / Platform", "owner_display_name": "release-bot", "owner_kind": "string", "owner_person_id": "3f6c1a2e-8b4d-4c1f-9a7e-0d5b2c8e4f10", "owner_status": "string", "platform": "string", "proxy_as_of": "2026-01-15T14:32:05Z", "proxy_source": "string", "proxy_tls_intercepted": false, "proxy_type": "string", "sessions_count": 0, "stale": false, "total_events_lifetime": 0, "used_pct": 0.25, "verdicts_24h": { "allow": 0, "ask": 0, "block": 0, "total": 0 }, "window_consumption_usd": "string" } ], "page": 1, "page_size": 3, "total": 3}