---
title: "List Installed On"
method: GET
path: "/api/integrations/instances/installed-on/{identity_id}"
tags: ["integrations"]
---

# List Installed On

`GET /api/integrations/instances/installed-on/{identity_id}`

## Request

- Base URL: `https://app.thinqos.com` (the document declares no server; this is the origin it was published from)
- URL: `https://app.thinqos.com/api/integrations/instances/installed-on/{identity_id}`
- Auth: none declared

The connections THIS identity holds (TOS-6647).

Historically this endpoint returned the full live set, whose first leg is
every account in any folder the subject REACHES - so a person who never
connected anything saw the whole folder catalog rendered as "Attached" on
their profile. That conflated two questions: what is reachable (the
Marketplace/Discover read, served by ``/instances`` with its reach
parameters) and what the subject HOLDS (this read).

Held means one of exactly three things, resolved as a case() over the
instance rows in the SAME statement that fetches them (zero extra round
trips past the route's sql-trace baseline):

- the subject's own credential row exists on the account (per_caller
  connected by them, or the shared account's connection is theirs) ->
  "connected",
- an active, unexpired ConnectionAccessGrant names them (TOS-3500) ->
  "granted", or
- the account was explicitly attached for them -> "approved". The
  attaching decision may be the subject's own (``decided_by`` == subject,
  what ``POST /instances/{id}/install`` records in a normal folder) or
  their controller-owner's (TOS-3444: the owner attaching their own
  home folder integration to their agent is the ONLY attach path a
  home folder allows, and the route deliberately records decided_by =
  the owner, "the owner authorized it, not the agent itself").

Plain folder reach, wrapper projection and approvals decided by someone
who is neither the subject nor their controller-owner are NOT holds and
are dropped from the response; the raw live set remains available to
callers that genuinely answer the reach question (turn-pipeline
liveness). ``hold_state`` tells the three cases apart so the shelf can
render "Connected" (your credential) vs "Granted" (shared to you) vs
"Approved" (attached by or for you).

## Path parameters

- `identity_id` string, uuid, required

## Headers

- `x-impersonate-identity-id` string, nullable

## Response `200`

Successful Response

- IntegrationInstallOut[]
  - `registration_id` string, uuid, required
  - `identity_id` string, uuid, required
  - `installed_by` string, uuid, nullable, required
  - `enabled` boolean, required
  - `hold_state` 'connected' | 'granted' | 'approved', nullable
  - `credential_connected` boolean, nullable
  - `credential_status` string, nullable
  - `test_result` ConnectionTestResult: Returned by RegistrationService.test_connection.
    - `success` boolean, required
    - `tool_count` integer
    - `error` string, nullable
    - `duration_ms` integer, required
    - `failure_kind` string, nullable
    - `evidence` 'credential' | 'transport' | 'none'
    - `http_status` integer, nullable
    - `provider_reason` string, nullable
    - `missing_scopes` string[], nullable
    - `probe_is_error` boolean
    - `account_mismatch` boolean
    - `failure_code` string, nullable
    - `diagnostic_reference` string, nullable
    - `tool_names` string[], nullable
    - `health` ConnectionHealthOut: Health of one connection, rendered without the client inventing anything. ``status`` carries the Connected-filter buckets directly (``ready`` / ``degraded`` / ``needs_attention`` / ``unknown``), so the list filters and the degraded/failure screens all read one field. ``credential_status`` keeps the underlying evidence-ladder value for callers that need it. ``unknown`` means CONNECTED BUT NOT YET MEASURED — a credential-free integration that owns no connection row, or a connection probed before the rollup existed. There is no evidence of a problem, so it belongs in the **Ready** filter bucket; render the detail as "Not checked yet" rather than showing ``summary`` (which would read "0 of 0 tools available"). It is a distinct value rather than being folded into ``ready`` because claiming "no capability is failing" when nothing was measured is a fabrication.
      - `status` 'ready' | 'degraded' | 'needs_attention' | 'unknown', required
      - `credential_status` 'connected' | 'needs_reconnect' | 'admin_repair' | 'unreachable' | 'missing', required
      - `healthy_tools` integer, required
      - `total_tools` integer, required
      - `affected_tools` AffectedToolOut[]
        - `name` string, required
        - `reason` string, required
        - `failure_code` string, nullable
        - `evidence` 'probe' | 'telemetry', required
        - `observed_at` string, date-time, nullable
      - `healthy_summary` string, required
      - `summary` string, required
      - `checked_at` string, date-time, nullable
      - `failure` ConnectionFailureDetail: The four named fields the connection-failure screen renders, plus the action. Populated from the closed ``IntegrationFailureCode`` taxonomy (TOS-2708), so every failure category — OAuth denial, wrong account, invalid/expired key, missing scopes, failed safe probe, rate limit, unreachable endpoint, partially unavailable tools — resolves to named copy rather than an error string the user has to interpret.
        - `code` string, required
        - `failed_check` string, required
        - `cause` string, required
        - `preserved` string, required
        - `diagnostic_reference` string, required
        - `next_action` string, required
  - `auto_granted` boolean
  - `grant_attention` string, nullable
  - `connection_id` string, uuid, nullable

## Other responses

- `422`: Validation Error

---

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