---
title: "Edit author-editable metadata on a single tool (response_field_map, is_enabled, input_defaults — defaults + pinned)"
method: PATCH
path: "/v1/integrations/connections/{connection_id}/tools/{tool_id}"
tags: ["integrations"]
---

# Edit author-editable metadata on a single tool (response_field_map, is_enabled, input_defaults — defaults + pinned)

`PATCH /v1/integrations/connections/{connection_id}/tools/{tool_id}`

Update author-editable tool metadata.

Three mutable fields:

* ``response_field_map`` — the customer-owned field map
  (``{"field_mappings": [...]}``) selecting which response fields the agent
  receives. Narrows the agent-facing payload and drives the AOP compiler's
  ``kind=llm`` → ``kind=action`` promotion (ENG-631). Omit the field to
  no-op; pass an explicit ``null`` to clear. The response *shape*
  (``response_schema``) is sync-owned and read-only over this API (D2), so
  it is rejected by the request schema's ``extra="forbid"``.
* ``is_enabled`` — turn the individual tool on/off (the PRD's per-tool
  toggle). A disabled tool is excluded from resolution, so the agent can no
  longer call it. Omit to no-op.
* ``input_defaults`` — operator input-arg config for input fields
  (``{field_name: {"value": ..., "type": "default"|"pinned"}}``, ENG-590). A
  ``default`` is a suggestion the agent may overwrite; a ``pinned`` value the
  caller cannot overwrite. Validated against the tool's current
  ``input_schema``: an unknown field or wrong-type value is rejected with a
  422 listing each offending field. Omit to no-op; pass an explicit ``null``
  to clear. Surfaced on every tool read (``default_count`` / ``pinned_count``
  / ``stale_input_fields`` / ``merged_input_preview``); neither kind is
  injected into a tool call in this scope (runtime enforcement is a follow-on).

Only the fields the caller actually set are written. Verifies
parent-connection org ownership before the tool update so a correctly-org'd
tool id under a foreign-org connection still 404s.

## Path parameters

- `connection_id` string, uuid, required
- `tool_id` string, uuid, required

## Request body

- ConnectionToolPatchRequest — Author-editable fields on a single ``IntegrationConnectionTool``. Three mutable fields: ``response_field_map`` (the customer-owned field map ``{"field_mappings": [...]}`` that narrows the agent-facing response and drives the AOP compiler's ``kind=llm`` → ``kind=action`` promotion), ``is_enabled`` (turn the individual tool on/off for the agent), and ``input_defaults`` (operator input-arg config, ``{field_name: {"value": ..., "type": "default"|"pinned"}}`` — ENG-590; a ``default`` may be overwritten by the agent, a ``pinned`` value cannot). ``input_defaults`` is validated at the service layer against the row's current ``input_schema`` (it needs the row, which a Pydantic validator can't see), so a wrong-type value or unknown field is rejected there with a 422. The response *shape* (``response_schema``) is sync-owned and read-only over the API (ENG-631 D2), so it is deliberately NOT accepted here — ``extra="forbid"`` rejects it with a 422. Pydantic's ``model_fields_set`` is used at the service layer to distinguish "omit this field" (no-op) from "explicit ``None``" (clear the field).
  - `response_field_map` object, nullable
  - `is_enabled` boolean, nullable
  - `input_defaults` object, nullable

## Response `200`

Successful Response

- ConnectionToolResponse
  - `id` string, uuid, required
  - `connection_id` string, uuid, required
  - `organization_id` string, uuid, required
  - `integration_key` 'slack' | 'custom_mcp' | 'notion' | 'google_drive' | 'calcom' | 'twilio' | 'email', required — Stable identifier for each supported integration. The string value must match the vendor's per-provider identifier (for Nango: ``provider_config_key``). Persisted on ``integration_connections.integration_key`` and ``integration_connection_tools.integration_key``. Adding a new integration: append a member here, add an ``IntegrationSpec`` to ``INTEGRATIONS``, register the provider in the vendor cloud via ``src/integrations/bootstrap.py`` (Step 3).
  - `nickname_slug` string, required
  - `tool_path` string, required
  - `slug` string, required
  - `display_name` string, required
  - `description` string, nullable, required
  - `input_schema` object, required
  - `response_schema` object, nullable
  - `response_field_map` object, nullable
  - `input_defaults` object, nullable
  - `is_enabled` boolean, required
  - `last_seen_at` string, date-time, required
  - `created_at` string, date-time, required
  - `updated_at` string, date-time, required
  - `stale_field_paths` string[], required — Saved field-map paths no longer present in the shape (ENG-631 D4).
  - `default_count` integer, required — How many input fields carry an operator *default* (ENG-590 / PRD 7.7).
  - `pinned_count` integer, required — How many input fields are operator-*pinned* (ENG-590).
  - `stale_input_fields` string[], required — Configured input fields removed/renamed/retyped by a re-sync (PRD 7.6).
  - `merged_input_preview` object, required — ``input_schema`` with each operator value injected (PRD 7.8).

## Other responses

- `400` — Bad request
- `401` — Authentication required
- `404` — Resource not found
- `422` — Validation Error

## Changes

- **2026-08-12** `5a5597ebb2d6` — 6 info
  - added the optional property `retry_after` to the response with the `400` status
  - added the optional property `retry_after` to the response with the `401` status
  - added the optional property `retry_after` to the response with the `404` status
  - added the optional property `retryable` to the response with the `400` status
  - …2 more

[Change history](https://skmtc.dev/oneloop-hq/apis/feather-api/changes/v1/integrations/connections/:connection_id/tools/:tool_id/patch.md)

---

[API](https://skmtc.dev/oneloop-hq/apis/feather-api.md) · [All operations](https://skmtc.dev/oneloop-hq/apis/feather-api/llms.txt) · [OpenAPI document](https://skmtc-service-production.skmtc.workers.dev/v1/apis/oneloop-hq/feather-api/revisions/9cd7291d4228/schema)
