---
title: "List a connected account's tools and their approval behavior"
method: GET
path: "/v1/integrations/accounts/{account_id}/tools"
tags: ["integrations"]
---

# List a connected account's tools and their approval behavior

`GET /v1/integrations/accounts/{account_id}/tools`

## Request

- Base URL: `https://api.patricia.app` (the document declares no server; this is the origin it was published from)
- URL: `https://api.patricia.app/v1/integrations/accounts/{account_id}/tools`
- Auth: HTTP bearer

The read-only list of provider actions Patricia can take through one connected account, each with its risk tier and what a new directed call would do FOR THE CALLING MEMBER (``auto`` runs without asking, ``ask`` asks first). ``gate`` is per-viewer — it reflects that member's own approval policy and per-tool overrides, so two members can see different values on the same row of a shared connection — while ``locked`` stays account-level and marks a mandatory call, or a live tier this build cannot parse, which nothing on this tab can relax. A readable CRITICAL action is NOT locked: it asks by default, and one such action on one connection can carry a member's own always-allow consent. It is the STANDING behavior, before any per-run or per-argument downgrade: a static read has no run, conversation or arguments. Only enabled tools are listed. Tenant- and owner-scoped: a foreign/unknown or not-visible account id 404s, so it is never an existence oracle for another member's private account.

## Path parameters

- `account_id` string, uuid, required

## Headers

- `X-Tenant-Id` string, nullable

## Response `200`

Successful Response

- AccountToolsResponse
  - `items` AccountToolSummary[], required
    - `name` string, required
    - `display_name` string, required
    - `description` string, nullable
    - `risk_tier_code` string, required
    - `gate` 'auto' | 'ask', required
    - `ordinary_write` boolean
    - `locked` boolean, required
    - `enabled` boolean, required
    - `personal_choice` 'ask' | 'block', nullable
    - `tool_ref` string, nullable
    - `override_id` string, uuid, nullable
    - `override_eligible` boolean
    - `override_ineligible_reason` 'critical_tier' | 'unreadable_live_tier' | 'mandatory_call' | 'audience_reaching' | 'no_tool_ref' | 'managed_account' | 'tool_disabled' | 'policy_always' | 'tenant_master_disabled' | 'no_caller_actor': Why a per-tool "Run automatically" override cannot be minted for this tool by this caller. ONE vocabulary for two surfaces that must never disagree: the Tools-tab read renders it as the locked-state tooltip (``AccountToolSummary.override_ineligible_reason``) and the mint route raises it as the error envelope's top-level ``code``. A control offered on the read side and then refused on the write side is the failure this shared enum exists to prevent. The first three are *approval-floor* reasons and come from :func:`~models.approval_policy.evaluate_tool_approval`, so the read side never re-derives a floor. The rest are account-, tool-, or caller-shaped and are decided by their own owners. ``mandatory_call``, never ``mandatory_provider``: ``core.mandatory_providers.is_mandatory_call`` is CALL-scoped, and now exclusively so. It reads a Windsor action-namespace ref or a Composio GitHub merge; its docstring names the refs. The provider-wide arm the name once also covered was deleted with ``MANDATORY_APPROVAL_PROVIDERS``, so the provider-shaped name would now name a rule that does not exist at all rather than merely reading as a bug on one row.
    - `override_needs_reconfirmation` boolean
    - `override_granted_tier` string, nullable
    - `override_stored_id` string, uuid, nullable
    - `overrides` AccountToolConsent[]
      - `id` string, uuid, required
      - `audience_class` 'non_audience' | 'audience' | 'legacy' | 'task_non_audience' | 'task_audience', required: Which calls of one action a standing consent covers, named by the audience each call reaches. ``tools.contracts.call_audience_class`` reads every call as ``non_audience`` or ``audience``, from the call itself and before any consent is consulted. A ``tool_overrides`` row stores the class it covers, and the class is part of its key, so a non-audience consent never covers an audience-reaching call of the same action. The ``ck_tool_overrides_audience_class`` CHECK holds the stored values to this set.
      - `audience_label` string, required
      - `state` 'tool_missing' | 'account_inactive' | 'tool_disabled_on_account' | 'inactive_tenant_master_disabled' | 'inactive_policy_always' | 'floor_blocked' | 'epoch_stale' | 'contract_stale' | 'needs_reconfirmation' | 'active', required: The computed state of a stored override — ONE precedence, FOUR readers. The runtime gate, the Tools-tab pill, the settings inventory and dispatch's own revalidation all resolve this, so an override can never read "active" in the dashboard while the gateway is asking. Not a code table: it is derived per read from live state, never persisted. The fourth is the one to keep in mind when changing the ordering or a field's meaning: it is not a display surface. ``gateway._override_live_state_denial`` asks this question again under row locks, and its answer is what authorizes an unattended provider write with no card and no ``PermissionRequest`` behind it.
    - `model_chosen_route` boolean
    - `consent_token` string, required

## Other responses

- `422`: Validation Error

---

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