airbyte.agents

PyAirbyte classes and methods for the Airbyte Agents platform.

⚠️ Experimental Interface

The Airbyte Agents Python interfaces are experimental. Class names, method signatures, and result models may change or be removed without notice between minor versions of PyAirbyte. Pin an exact PyAirbyte version if you depend on them.

Airbyte Agents connectors expose read and write actions on individual entities, executed one action at a time, rather than the batch record replication that airbyte.cloud provides. This module is that interface.

Airbyte Cloud credentials authenticate against the Agents API, so the AIRBYTE_CLOUD_* variables are reused. Set AIRBYTE_AGENTS_API_URL to override the Agents API root when connecting through a proxy or local development endpoint.

Usage Examples

Read entities from a connector, paging automatically as you iterate:

from airbyte import agents

workspace = agents.AgentWorkspace.from_env()
connector = workspace.get_connector("GitHub")  # by ID or name (case insensitive)

for issue in connector.iter_entities(
    "issues",
    api_args={"repository": "airbytehq/PyAirbyte"},  # Passthrough API args
):
    print(issue["title"])

Fetch a single page instead, when the result's status and metadata are needed:

result = connector.list_entities(
    "issues",
    api_args={"repository": "airbytehq/PyAirbyte"},
    page_size=50,
)
print(result.status, result.has_next_page)
for entity in result.entities:
    print(entity["title"])

Pass result.end_cursor back as cursor to page through manually:

cursor = None
while True:
    result = connector.list_entities(
        "issues",
        api_args={"repository": "airbytehq/PyAirbyte"},
        cursor=cursor,
    )
    print(len(result.entities))
    if not result.has_next_page:
        break
    cursor = result.end_cursor

Discover what a connector supports, and what an organization can reach:

organization = agents.AgentOrganization.from_env()
for workspace in organization.list_workspaces():
    print(workspace.workspace_id, workspace.name)

print(connector.inspect().integration_name)

Convert between the Cloud and Agents domains:

from airbyte.cloud import CloudWorkspace

cloud_workspace = CloudWorkspace.from_env()
agent_workspace = agents.AgentWorkspace.from_cloud_workspace(cloud_workspace)
back_to_cloud = agent_workspace.as_cloud_workspace()
  1# Copyright (c) 2026 Airbyte, Inc., all rights reserved.
  2"""PyAirbyte classes and methods for the Airbyte Agents platform.
  3
  4> ## ⚠️ Experimental Interface
  5>
  6> **The Airbyte Agents Python interfaces are experimental.** Class names, method signatures,
  7> and result models may change or be removed without notice between minor versions of
  8> PyAirbyte. Pin an exact PyAirbyte version if you depend on them.
  9
 10Airbyte Agents connectors expose read and write actions on individual entities, executed
 11one action at a time, rather than the batch record replication that `airbyte.cloud`
 12provides. This module is that interface.
 13
 14Airbyte Cloud credentials authenticate against the Agents API, so the `AIRBYTE_CLOUD_*`
 15variables are reused. Set `AIRBYTE_AGENTS_API_URL` to override the Agents API root when
 16connecting through a proxy or local development endpoint.
 17
 18## Usage Examples
 19
 20Read entities from a connector, paging automatically as you iterate:
 21
 22```python
 23from airbyte import agents
 24
 25workspace = agents.AgentWorkspace.from_env()
 26connector = workspace.get_connector("GitHub")  # by ID or name (case insensitive)
 27
 28for issue in connector.iter_entities(
 29    "issues",
 30    api_args={"repository": "airbytehq/PyAirbyte"},  # Passthrough API args
 31):
 32    print(issue["title"])
 33```
 34
 35Fetch a single page instead, when the result's status and metadata are needed:
 36
 37```python
 38result = connector.list_entities(
 39    "issues",
 40    api_args={"repository": "airbytehq/PyAirbyte"},
 41    page_size=50,
 42)
 43print(result.status, result.has_next_page)
 44for entity in result.entities:
 45    print(entity["title"])
 46```
 47
 48Pass `result.end_cursor` back as `cursor` to page through manually:
 49
 50```python
 51cursor = None
 52while True:
 53    result = connector.list_entities(
 54        "issues",
 55        api_args={"repository": "airbytehq/PyAirbyte"},
 56        cursor=cursor,
 57    )
 58    print(len(result.entities))
 59    if not result.has_next_page:
 60        break
 61    cursor = result.end_cursor
 62```
 63
 64Discover what a connector supports, and what an organization can reach:
 65
 66```python
 67organization = agents.AgentOrganization.from_env()
 68for workspace in organization.list_workspaces():
 69    print(workspace.workspace_id, workspace.name)
 70
 71print(connector.inspect().integration_name)
 72```
 73
 74Convert between the Cloud and Agents domains:
 75
 76```python
 77from airbyte.cloud import CloudWorkspace
 78
 79cloud_workspace = CloudWorkspace.from_env()
 80agent_workspace = agents.AgentWorkspace.from_cloud_workspace(cloud_workspace)
 81back_to_cloud = agent_workspace.as_cloud_workspace()
 82```
 83"""
 84
 85from __future__ import annotations
 86
 87from typing import TYPE_CHECKING
 88
 89from airbyte._direct_connectors.models import (
 90    AgentConnectorDetails,
 91    AgentConnectorInfo,
 92    AgentConnectorMetadata,
 93    AgentContextStoreEntity,
 94    AgentContextStoreReadiness,
 95    AgentExecuteResult,
 96    AgentExecutionMetadata,
 97    AgentSkillDocs,
 98    AgentSkillInfo,
 99    AgentSkillList,
100    AgentSkillSection,
101    AgentWorkspaceInfo,
102)
103from airbyte.agents.connectors import AgentConnector
104from airbyte.agents.organizations import AgentOrganization
105from airbyte.agents.skills import AgentSkill
106from airbyte.agents.workspaces import AgentWorkspace
107
108
109# Submodules imported here for documentation reasons: https://github.com/mitmproxy/pdoc/issues/757
110if TYPE_CHECKING:
111    # ruff: noqa: TC004
112    from airbyte.agents import (
113        connectors,
114        organizations,
115        skills,
116        workspaces,
117    )
118
119
120__all__ = [
121    # Submodules
122    "connectors",
123    "organizations",
124    "skills",
125    "workspaces",
126    # Classes
127    "AgentConnector",
128    "AgentConnectorDetails",
129    "AgentConnectorInfo",
130    "AgentConnectorMetadata",
131    "AgentContextStoreEntity",
132    "AgentContextStoreReadiness",
133    "AgentExecuteResult",
134    "AgentExecutionMetadata",
135    "AgentOrganization",
136    "AgentSkill",
137    "AgentSkillDocs",
138    "AgentSkillInfo",
139    "AgentSkillList",
140    "AgentSkillSection",
141    "AgentWorkspace",
142    "AgentWorkspaceInfo",
143]
class AgentConnector:
 35class AgentConnector:
 36    """A connector in an Airbyte Agents workspace.
 37
 38    Get one from `AgentWorkspace.get_connector()` rather than constructing it directly.
 39
 40    ```python
 41    from airbyte import agents
 42
 43    workspace = agents.AgentWorkspace.from_env()
 44    connector = workspace.get_connector("GitHub")  # by ID or name (case insensitive)
 45    result = connector.list_entities("issues", api_args={"repository": "airbytehq/PyAirbyte"})
 46    for entity in result.entities:
 47        print(entity["title"])
 48    ```
 49    """
 50
 51    def __init__(
 52        self,
 53        connector_id: str,
 54        *,
 55        credentials: _AirbyteCredentials,
 56        name: str | None = None,
 57        workspace_id: str | None = None,
 58    ) -> None:
 59        """Initialize an `AgentConnector`. Prefer `AgentWorkspace.get_connector()`."""
 60        self.connector_id = connector_id
 61        """The connector ID."""
 62
 63        self.workspace_id = workspace_id
 64        """The workspace ID."""
 65
 66        self._credentials = credentials
 67        self._name = name
 68        self._details: AgentConnectorDetails | None = None
 69
 70    @property
 71    def name(self) -> str | None:
 72        """The connector name, fetched from the Agents API if not already known."""
 73        if self._name is None:
 74            self._name = self.inspect().name
 75        return self._name
 76
 77    def inspect(self, *, force_refresh: bool = False) -> AgentConnectorDetails:
 78        """Return connector metadata from the Agents API `inspect` endpoint.
 79
 80        The result is cached; pass `force_refresh=True` to fetch it again.
 81        """
 82        if self._details is None or force_refresh:
 83            self._details = AgentConnectorDetails.model_validate(
 84                _api_util.inspect_agent_connector(
 85                    connector_id=self.connector_id,
 86                    credentials=self._credentials,
 87                    organization_id=self._credentials.organization_id,
 88                )
 89            )
 90            self._name = self._details.name or self._name
 91        return self._details
 92
 93    def execute(  # noqa: PLR0913  # Explicit args are the point of this public API.
 94        self,
 95        entity_type: str,
 96        action: AgentAction | str,
 97        api_args: dict[str, Any] | None = None,
 98        *,
 99        select_fields: list[str] | None = None,
100        exclude_fields: list[str] | None = None,
101        page_size: int | None = None,
102        cursor: str | None = None,
103        workspace_id: str | None = None,
104        skip_truncation: bool = True,
105        intent: str | None = None,
106    ) -> AgentExecuteResult:
107        """Execute a single action against one entity type on this connector.
108
109        `entity_type` and `action` are connector-specific, for example `issues` and `list`.
110        Use `inspect()` to see what a connector supports.
111
112        `api_args` holds connector-specific arguments passed through to the connector, for
113        example `{"repository": "airbytehq/PyAirbyte"}`. All other arguments are interpreted
114        by PyAirbyte or by the Agents API itself:
115
116        - `select_fields` and `exclude_fields` prune fields from returned entities.
117        - `page_size` and `cursor` are merged into `api_args` as pagination arguments. Context
118          Store `search` uses `limit` and `cursor`, `sql_select` uses the top-level `cursor`, and
119          direct connector actions use their own pagination arguments in `api_args`. Pass the
120          result cursor according to the action type.
121        - `workspace_id` selects the workspace an action runs against. It defaults to the
122          connector's workspace and is currently sent for `sql_select`, the only action the
123          Agents API scopes by workspace; direct connector actions are scoped by the connector.
124        - `skip_truncation` disables the Agents API's default truncation of large payloads.
125        - `intent` is a free-text description of why the action is being run, which some
126          connectors use to refine results.
127
128        The `download` action is rejected, because it returns a binary stream and PyAirbyte
129        does not yet support streaming responses.
130        """
131        if action in UNSUPPORTED_ACTIONS:
132            raise PyAirbyteInputError(
133                message=f"The {action!r} action is not supported by PyAirbyte.",
134                guidance=(
135                    "This action returns a binary stream instead of JSON, and PyAirbyte does "
136                    "not yet support streaming responses."
137                ),
138                context={"entity_type": entity_type, "action": action},
139            )
140
141        if action not in {*AgentReadAction, *AgentWriteAction}:
142            action_names = ", ".join(
143                member.value for member in (*AgentReadAction, *AgentWriteAction)
144            )
145            raise PyAirbyteInputError(
146                message=f"The {action!r} action is not a valid action name for `execute`.",
147                guidance=f"Use one of: {action_names}.",
148                context={"entity_type": entity_type, "action": action},
149            )
150
151        action_value = action.value if isinstance(action, Enum) else action
152        params = _build_params(api_args=api_args, page_size=page_size, cursor=cursor)
153        if action_value == AgentReadAction.SQL_SELECT.value:
154            resolved_workspace_id = workspace_id or self.workspace_id
155            if (
156                resolved_workspace_id is not None
157                and params.get("workspace_id") is None
158                and params.get("workspace_name") is None
159            ):
160                params.pop("workspace_name", None)
161                params["workspace_id"] = resolved_workspace_id
162        request_body: dict[str, Any] = {
163            "entity": entity_type,
164            "action": action_value,
165            "params": params,
166            "skip_truncation": skip_truncation,
167        }
168        if select_fields is not None:
169            request_body["select_fields"] = select_fields
170        if exclude_fields is not None:
171            request_body["exclude_fields"] = exclude_fields
172        if intent is not None:
173            request_body["intent"] = intent
174
175        return AgentExecuteResult.model_validate(
176            _api_util.execute_agent_connector_action(
177                connector_id=self.connector_id,
178                request_body=request_body,
179                credentials=self._credentials,
180                organization_id=self._credentials.organization_id,
181            )
182        )
183
184    def list_entities(
185        self,
186        entity_type: str,
187        api_args: dict[str, Any] | None = None,
188        **kwargs: Any,  # noqa: ANN401  # Forwarded verbatim to `execute()`.
189    ) -> AgentExecuteResult:
190        """Run the `list` action, which returns a page of entities of `entity_type`."""
191        return self.execute(entity_type, "list", api_args, **kwargs)
192
193    def iter_entities(
194        self,
195        entity_type: str,
196        api_args: dict[str, Any] | None = None,
197        *,
198        limit: int | None = None,
199        **kwargs: Any,  # noqa: ANN401  # Forwarded verbatim to `list_entities()`.
200    ) -> Iterator[dict[str, Any]]:
201        """Yield entities of `entity_type`, following the connector's pagination cursor.
202
203        This is the pagination-free way to read entities: each page is fetched lazily as
204        the caller iterates, so no cursor bookkeeping is needed.
205
206        ```python
207        for issue in connector.iter_entities("issues", {"repository": "airbytehq/PyAirbyte"}):
208            print(issue["title"])
209        ```
210
211        `limit` caps how many entities are yielded in total, which matters for entity types
212        with no natural end. Pass `page_size` to control how many are fetched per request.
213
214        Iteration stops early if the connector reports another page without advancing its
215        cursor, rather than requesting the same page forever.
216
217        Use `list_entities()` instead when a single page is enough, or when the result's
218        `status`, `warning`, or `execution_metadata` are needed.
219        """
220        cursor: str | None = kwargs.pop("cursor", None)
221        yield from _api_util.iter_paged_entities(
222            lambda page_cursor: self.list_entities(
223                entity_type,
224                api_args,
225                cursor=page_cursor,
226                **kwargs,
227            ),
228            limit=limit,
229            cursor=cursor,
230        )
231
232    def search_entities(
233        self,
234        entity_type: str,
235        api_args: dict[str, Any] | None = None,
236        **kwargs: Any,  # noqa: ANN401  # Forwarded verbatim to `execute()`.
237    ) -> AgentExecuteResult:
238        """Run the `search` action, which returns matching entities of `entity_type`."""
239        return self.execute(entity_type, "search", api_args, **kwargs)
240
241    def get_entity(
242        self,
243        entity_type: str,
244        api_args: dict[str, Any] | None = None,
245        **kwargs: Any,  # noqa: ANN401  # Forwarded verbatim to `execute()`.
246    ) -> AgentExecuteResult:
247        """Run the `get` action, which returns a single entity of `entity_type`."""
248        return self.execute(entity_type, "get", api_args, **kwargs)
249
250    def create_entity(
251        self,
252        entity_type: str,
253        api_args: dict[str, Any] | None = None,
254        **kwargs: Any,  # noqa: ANN401  # Forwarded verbatim to `execute()`.
255    ) -> AgentExecuteResult:
256        """Run the `create` action, which creates an entity of `entity_type`."""
257        return self.execute(entity_type, "create", api_args, **kwargs)
258
259    def update_entity(
260        self,
261        entity_type: str,
262        api_args: dict[str, Any] | None = None,
263        **kwargs: Any,  # noqa: ANN401  # Forwarded verbatim to `execute()`.
264    ) -> AgentExecuteResult:
265        """Run the `update` action, which updates an entity of `entity_type`."""
266        return self.execute(entity_type, "update", api_args, **kwargs)
267
268    def delete_entity(
269        self,
270        entity_type: str,
271        api_args: dict[str, Any] | None = None,
272        **kwargs: Any,  # noqa: ANN401  # Forwarded verbatim to `execute()`.
273    ) -> AgentExecuteResult:
274        """Run the `delete` action, which deletes an entity of `entity_type`."""
275        return self.execute(entity_type, "delete", api_args, **kwargs)

A connector in an Airbyte Agents workspace.

Get one from AgentWorkspace.get_connector() rather than constructing it directly.

from airbyte import agents

workspace = agents.AgentWorkspace.from_env()
connector = workspace.get_connector("GitHub")  # by ID or name (case insensitive)
result = connector.list_entities("issues", api_args={"repository": "airbytehq/PyAirbyte"})
for entity in result.entities:
    print(entity["title"])
AgentConnector( connector_id: str, *, credentials: airbyte.cloud._credentials._AirbyteCredentials, name: str | None = None, workspace_id: str | None = None)
51    def __init__(
52        self,
53        connector_id: str,
54        *,
55        credentials: _AirbyteCredentials,
56        name: str | None = None,
57        workspace_id: str | None = None,
58    ) -> None:
59        """Initialize an `AgentConnector`. Prefer `AgentWorkspace.get_connector()`."""
60        self.connector_id = connector_id
61        """The connector ID."""
62
63        self.workspace_id = workspace_id
64        """The workspace ID."""
65
66        self._credentials = credentials
67        self._name = name
68        self._details: AgentConnectorDetails | None = None
connector_id

The connector ID.

workspace_id

The workspace ID.

name: str | None
70    @property
71    def name(self) -> str | None:
72        """The connector name, fetched from the Agents API if not already known."""
73        if self._name is None:
74            self._name = self.inspect().name
75        return self._name

The connector name, fetched from the Agents API if not already known.

def inspect( self, *, force_refresh: bool = False) -> AgentConnectorDetails:
77    def inspect(self, *, force_refresh: bool = False) -> AgentConnectorDetails:
78        """Return connector metadata from the Agents API `inspect` endpoint.
79
80        The result is cached; pass `force_refresh=True` to fetch it again.
81        """
82        if self._details is None or force_refresh:
83            self._details = AgentConnectorDetails.model_validate(
84                _api_util.inspect_agent_connector(
85                    connector_id=self.connector_id,
86                    credentials=self._credentials,
87                    organization_id=self._credentials.organization_id,
88                )
89            )
90            self._name = self._details.name or self._name
91        return self._details

Return connector metadata from the Agents API inspect endpoint.

The result is cached; pass force_refresh=True to fetch it again.

def execute( self, entity_type: str, action: airbyte._direct_connectors.actions.AgentReadAction | airbyte._direct_connectors.actions.AgentWriteAction | str, api_args: dict[str, typing.Any] | None = None, *, select_fields: list[str] | None = None, exclude_fields: list[str] | None = None, page_size: int | None = None, cursor: str | None = None, workspace_id: str | None = None, skip_truncation: bool = True, intent: str | None = None) -> AgentExecuteResult:
 93    def execute(  # noqa: PLR0913  # Explicit args are the point of this public API.
 94        self,
 95        entity_type: str,
 96        action: AgentAction | str,
 97        api_args: dict[str, Any] | None = None,
 98        *,
 99        select_fields: list[str] | None = None,
100        exclude_fields: list[str] | None = None,
101        page_size: int | None = None,
102        cursor: str | None = None,
103        workspace_id: str | None = None,
104        skip_truncation: bool = True,
105        intent: str | None = None,
106    ) -> AgentExecuteResult:
107        """Execute a single action against one entity type on this connector.
108
109        `entity_type` and `action` are connector-specific, for example `issues` and `list`.
110        Use `inspect()` to see what a connector supports.
111
112        `api_args` holds connector-specific arguments passed through to the connector, for
113        example `{"repository": "airbytehq/PyAirbyte"}`. All other arguments are interpreted
114        by PyAirbyte or by the Agents API itself:
115
116        - `select_fields` and `exclude_fields` prune fields from returned entities.
117        - `page_size` and `cursor` are merged into `api_args` as pagination arguments. Context
118          Store `search` uses `limit` and `cursor`, `sql_select` uses the top-level `cursor`, and
119          direct connector actions use their own pagination arguments in `api_args`. Pass the
120          result cursor according to the action type.
121        - `workspace_id` selects the workspace an action runs against. It defaults to the
122          connector's workspace and is currently sent for `sql_select`, the only action the
123          Agents API scopes by workspace; direct connector actions are scoped by the connector.
124        - `skip_truncation` disables the Agents API's default truncation of large payloads.
125        - `intent` is a free-text description of why the action is being run, which some
126          connectors use to refine results.
127
128        The `download` action is rejected, because it returns a binary stream and PyAirbyte
129        does not yet support streaming responses.
130        """
131        if action in UNSUPPORTED_ACTIONS:
132            raise PyAirbyteInputError(
133                message=f"The {action!r} action is not supported by PyAirbyte.",
134                guidance=(
135                    "This action returns a binary stream instead of JSON, and PyAirbyte does "
136                    "not yet support streaming responses."
137                ),
138                context={"entity_type": entity_type, "action": action},
139            )
140
141        if action not in {*AgentReadAction, *AgentWriteAction}:
142            action_names = ", ".join(
143                member.value for member in (*AgentReadAction, *AgentWriteAction)
144            )
145            raise PyAirbyteInputError(
146                message=f"The {action!r} action is not a valid action name for `execute`.",
147                guidance=f"Use one of: {action_names}.",
148                context={"entity_type": entity_type, "action": action},
149            )
150
151        action_value = action.value if isinstance(action, Enum) else action
152        params = _build_params(api_args=api_args, page_size=page_size, cursor=cursor)
153        if action_value == AgentReadAction.SQL_SELECT.value:
154            resolved_workspace_id = workspace_id or self.workspace_id
155            if (
156                resolved_workspace_id is not None
157                and params.get("workspace_id") is None
158                and params.get("workspace_name") is None
159            ):
160                params.pop("workspace_name", None)
161                params["workspace_id"] = resolved_workspace_id
162        request_body: dict[str, Any] = {
163            "entity": entity_type,
164            "action": action_value,
165            "params": params,
166            "skip_truncation": skip_truncation,
167        }
168        if select_fields is not None:
169            request_body["select_fields"] = select_fields
170        if exclude_fields is not None:
171            request_body["exclude_fields"] = exclude_fields
172        if intent is not None:
173            request_body["intent"] = intent
174
175        return AgentExecuteResult.model_validate(
176            _api_util.execute_agent_connector_action(
177                connector_id=self.connector_id,
178                request_body=request_body,
179                credentials=self._credentials,
180                organization_id=self._credentials.organization_id,
181            )
182        )

Execute a single action against one entity type on this connector.

entity_type and action are connector-specific, for example issues and list. Use inspect() to see what a connector supports.

api_args holds connector-specific arguments passed through to the connector, for example {"repository": "airbytehq/PyAirbyte"}. All other arguments are interpreted by PyAirbyte or by the Agents API itself:

  • select_fields and exclude_fields prune fields from returned entities.
  • page_size and cursor are merged into api_args as pagination arguments. Context Store search uses limit and cursor, sql_select uses the top-level cursor, and direct connector actions use their own pagination arguments in api_args. Pass the result cursor according to the action type.
  • workspace_id selects the workspace an action runs against. It defaults to the connector's workspace and is currently sent for sql_select, the only action the Agents API scopes by workspace; direct connector actions are scoped by the connector.
  • skip_truncation disables the Agents API's default truncation of large payloads.
  • intent is a free-text description of why the action is being run, which some connectors use to refine results.

The download action is rejected, because it returns a binary stream and PyAirbyte does not yet support streaming responses.

def list_entities( self, entity_type: str, api_args: dict[str, typing.Any] | None = None, **kwargs: Any) -> AgentExecuteResult:
184    def list_entities(
185        self,
186        entity_type: str,
187        api_args: dict[str, Any] | None = None,
188        **kwargs: Any,  # noqa: ANN401  # Forwarded verbatim to `execute()`.
189    ) -> AgentExecuteResult:
190        """Run the `list` action, which returns a page of entities of `entity_type`."""
191        return self.execute(entity_type, "list", api_args, **kwargs)

Run the list action, which returns a page of entities of entity_type.

def iter_entities( self, entity_type: str, api_args: dict[str, typing.Any] | None = None, *, limit: int | None = None, **kwargs: Any) -> Iterator[dict[str, typing.Any]]:
193    def iter_entities(
194        self,
195        entity_type: str,
196        api_args: dict[str, Any] | None = None,
197        *,
198        limit: int | None = None,
199        **kwargs: Any,  # noqa: ANN401  # Forwarded verbatim to `list_entities()`.
200    ) -> Iterator[dict[str, Any]]:
201        """Yield entities of `entity_type`, following the connector's pagination cursor.
202
203        This is the pagination-free way to read entities: each page is fetched lazily as
204        the caller iterates, so no cursor bookkeeping is needed.
205
206        ```python
207        for issue in connector.iter_entities("issues", {"repository": "airbytehq/PyAirbyte"}):
208            print(issue["title"])
209        ```
210
211        `limit` caps how many entities are yielded in total, which matters for entity types
212        with no natural end. Pass `page_size` to control how many are fetched per request.
213
214        Iteration stops early if the connector reports another page without advancing its
215        cursor, rather than requesting the same page forever.
216
217        Use `list_entities()` instead when a single page is enough, or when the result's
218        `status`, `warning`, or `execution_metadata` are needed.
219        """
220        cursor: str | None = kwargs.pop("cursor", None)
221        yield from _api_util.iter_paged_entities(
222            lambda page_cursor: self.list_entities(
223                entity_type,
224                api_args,
225                cursor=page_cursor,
226                **kwargs,
227            ),
228            limit=limit,
229            cursor=cursor,
230        )

Yield entities of entity_type, following the connector's pagination cursor.

This is the pagination-free way to read entities: each page is fetched lazily as the caller iterates, so no cursor bookkeeping is needed.

for issue in connector.iter_entities("issues", {"repository": "airbytehq/PyAirbyte"}):
    print(issue["title"])

limit caps how many entities are yielded in total, which matters for entity types with no natural end. Pass page_size to control how many are fetched per request.

Iteration stops early if the connector reports another page without advancing its cursor, rather than requesting the same page forever.

Use list_entities() instead when a single page is enough, or when the result's status, warning, or execution_metadata are needed.

def search_entities( self, entity_type: str, api_args: dict[str, typing.Any] | None = None, **kwargs: Any) -> AgentExecuteResult:
232    def search_entities(
233        self,
234        entity_type: str,
235        api_args: dict[str, Any] | None = None,
236        **kwargs: Any,  # noqa: ANN401  # Forwarded verbatim to `execute()`.
237    ) -> AgentExecuteResult:
238        """Run the `search` action, which returns matching entities of `entity_type`."""
239        return self.execute(entity_type, "search", api_args, **kwargs)

Run the search action, which returns matching entities of entity_type.

def get_entity( self, entity_type: str, api_args: dict[str, typing.Any] | None = None, **kwargs: Any) -> AgentExecuteResult:
241    def get_entity(
242        self,
243        entity_type: str,
244        api_args: dict[str, Any] | None = None,
245        **kwargs: Any,  # noqa: ANN401  # Forwarded verbatim to `execute()`.
246    ) -> AgentExecuteResult:
247        """Run the `get` action, which returns a single entity of `entity_type`."""
248        return self.execute(entity_type, "get", api_args, **kwargs)

Run the get action, which returns a single entity of entity_type.

def create_entity( self, entity_type: str, api_args: dict[str, typing.Any] | None = None, **kwargs: Any) -> AgentExecuteResult:
250    def create_entity(
251        self,
252        entity_type: str,
253        api_args: dict[str, Any] | None = None,
254        **kwargs: Any,  # noqa: ANN401  # Forwarded verbatim to `execute()`.
255    ) -> AgentExecuteResult:
256        """Run the `create` action, which creates an entity of `entity_type`."""
257        return self.execute(entity_type, "create", api_args, **kwargs)

Run the create action, which creates an entity of entity_type.

def update_entity( self, entity_type: str, api_args: dict[str, typing.Any] | None = None, **kwargs: Any) -> AgentExecuteResult:
259    def update_entity(
260        self,
261        entity_type: str,
262        api_args: dict[str, Any] | None = None,
263        **kwargs: Any,  # noqa: ANN401  # Forwarded verbatim to `execute()`.
264    ) -> AgentExecuteResult:
265        """Run the `update` action, which updates an entity of `entity_type`."""
266        return self.execute(entity_type, "update", api_args, **kwargs)

Run the update action, which updates an entity of entity_type.

def delete_entity( self, entity_type: str, api_args: dict[str, typing.Any] | None = None, **kwargs: Any) -> AgentExecuteResult:
268    def delete_entity(
269        self,
270        entity_type: str,
271        api_args: dict[str, Any] | None = None,
272        **kwargs: Any,  # noqa: ANN401  # Forwarded verbatim to `execute()`.
273    ) -> AgentExecuteResult:
274        """Run the `delete` action, which deletes an entity of `entity_type`."""
275        return self.execute(entity_type, "delete", api_args, **kwargs)

Run the delete action, which deletes an entity of entity_type.

class AgentConnectorDetails(pydantic.main.BaseModel):
150class AgentConnectorDetails(BaseModel):
151    """Connector metadata returned by the Agents API `inspect` endpoint."""
152
153    model_config = ConfigDict(extra="allow", populate_by_name=True)
154
155    connector_id: str
156    """The connector ID."""
157
158    name: str | None = None
159    """The connector name."""
160
161    workspace_id: str | None = None
162    """The ID of the workspace the connector belongs to."""
163
164    organization_id: str | None = None
165    """The ID of the organization the connector belongs to."""
166
167    source_definition_id: str | None = None
168    """The ID of the underlying Airbyte source definition."""
169
170    integration_name: str | None = Field(default=None, alias="source_definition_name")
171    """Name of the underlying integration, for example `GitHub` or `Snowflake`."""
172
173    docs_skill_id: str | None = None
174    """Skill ID to pass to `AgentWorkspace.get_skill(...).read_docs()` (MCP:
175    `read_agent_skill_docs`) for this connector's usage docs."""
176
177    context_store_readiness: AgentContextStoreReadiness | None = None
178    """Context Store readiness information, when reported."""
179
180    warnings: list[Any] = Field(default_factory=list)
181    """Warnings reported by the Agents API, for example degraded capabilities."""
182
183    @property
184    def context_store_entities(self) -> list[str]:
185        """The entity names this connector can cache in the Context Store.
186
187        Note that this lists Context Store-supported entities specifically. The Agents API
188        does not publish an exhaustive list of executable entity and action pairs, so an
189        entity may be executable via `AgentConnector.execute()` without appearing here.
190        """
191        if self.context_store_readiness is None:
192            return []
193        return [
194            entity.entity
195            for entity in self.context_store_readiness.supported_context_store_entities
196        ]

Connector metadata returned by the Agents API inspect endpoint.

connector_id: str = PydanticUndefined

The connector ID.

name: str | None = None

The connector name.

workspace_id: str | None = None

The ID of the workspace the connector belongs to.

organization_id: str | None = None

The ID of the organization the connector belongs to.

source_definition_id: str | None = None

The ID of the underlying Airbyte source definition.

integration_name: str | None = None

Name of the underlying integration, for example GitHub or Snowflake.

docs_skill_id: str | None = None

Skill ID to pass to AgentWorkspace.get_skill(...).read_docs() (MCP: read_agent_skill_docs) for this connector's usage docs.

context_store_readiness: AgentContextStoreReadiness | None = None

Context Store readiness information, when reported.

warnings: list[typing.Any] = PydanticUndefined

Warnings reported by the Agents API, for example degraded capabilities.

context_store_entities: list[str]
183    @property
184    def context_store_entities(self) -> list[str]:
185        """The entity names this connector can cache in the Context Store.
186
187        Note that this lists Context Store-supported entities specifically. The Agents API
188        does not publish an exhaustive list of executable entity and action pairs, so an
189        entity may be executable via `AgentConnector.execute()` without appearing here.
190        """
191        if self.context_store_readiness is None:
192            return []
193        return [
194            entity.entity
195            for entity in self.context_store_readiness.supported_context_store_entities
196        ]

The entity names this connector can cache in the Context Store.

Note that this lists Context Store-supported entities specifically. The Agents API does not publish an exhaustive list of executable entity and action pairs, so an entity may be executable via AgentConnector.execute() without appearing here.

class AgentConnectorInfo(pydantic.main.BaseModel):
42class AgentConnectorInfo(BaseModel):
43    """Summary information about a connector, as returned by the Agents API."""
44
45    model_config = ConfigDict(extra="allow")
46
47    id: str
48    """The connector ID."""
49
50    name: str | None = None
51    """The connector name, for example `GitHub - <workspace_id>`."""

Summary information about a connector, as returned by the Agents API.

id: str = PydanticUndefined

The connector ID.

name: str | None = None

The connector name, for example GitHub - <workspace_id>.

class AgentConnectorMetadata(pydantic.main.BaseModel):
211class AgentConnectorMetadata(BaseModel):
212    """Connector-reported metadata about a single action's result, including pagination."""
213
214    model_config = ConfigDict(extra="allow")
215
216    has_next_page: bool | None = None
217    """Whether more entities are available after this page, when the connector reports it."""
218
219    end_cursor: str | None = None
220    """The cursor for the next page, when one is available. Pass it as `cursor` for Context Store
221    `search`, or as the connector's own cursor argument in `api_args` for direct connector
222    actions."""

Connector-reported metadata about a single action's result, including pagination.

has_next_page: bool | None = None

Whether more entities are available after this page, when the connector reports it.

end_cursor: str | None = None

The cursor for the next page, when one is available. Pass it as cursor for Context Store search, or as the connector's own cursor argument in api_args for direct connector actions.

class AgentContextStoreEntity(pydantic.main.BaseModel):
54class AgentContextStoreEntity(BaseModel):
55    """An entity that a connector supports caching in the Airbyte Context Store."""
56
57    model_config = ConfigDict(extra="allow")
58
59    entity: str
60    """The entity name, for example `issues`."""
61
62    suggested: bool | None = None
63    """Whether Airbyte suggests caching this entity."""

An entity that a connector supports caching in the Airbyte Context Store.

entity: str = PydanticUndefined

The entity name, for example issues.

suggested: bool | None = None

Whether Airbyte suggests caching this entity.

class AgentContextStoreReadiness(pydantic.main.BaseModel):
66class AgentContextStoreReadiness(BaseModel):
67    """Context Store readiness information for a connector."""
68
69    model_config = ConfigDict(extra="allow")
70
71    supported_context_store_entities: list[AgentContextStoreEntity] = Field(default_factory=list)
72    """The entities this connector can cache in the Context Store."""
73
74    configured_cache_entities: list[dict[str, Any]] = Field(default_factory=list)
75    """The entities currently configured for caching, with their sync status."""

Context Store readiness information for a connector.

supported_context_store_entities: list[AgentContextStoreEntity] = PydanticUndefined

The entities this connector can cache in the Context Store.

configured_cache_entities: list[dict[str, typing.Any]] = PydanticUndefined

The entities currently configured for caching, with their sync status.

class AgentExecuteResult(pydantic.main.BaseModel):
225class AgentExecuteResult(BaseModel):
226    """The result of executing a single action against an Airbyte Agents connector."""
227
228    model_config = ConfigDict(extra="allow")
229
230    status: str
231    """The execution status reported by the Agents API, for example `success`."""
232
233    result: Any = None
234    """The action's payload. Entity-returning actions put a list of entities here."""
235
236    connector_metadata: AgentConnectorMetadata = Field(default_factory=AgentConnectorMetadata)
237    """Connector-reported metadata about the result, including pagination cursors."""
238
239    execution_metadata: AgentExecutionMetadata = Field(default_factory=AgentExecutionMetadata)
240    """Metadata describing how the action was executed."""
241
242    warning: dict[str, Any] | None = None
243    """A warning reported alongside an otherwise successful result."""
244
245    @field_validator("connector_metadata", "execution_metadata", mode="before")
246    @classmethod
247    def _none_to_empty(cls, value: object) -> object:
248        return {} if value is None else value
249
250    @property
251    def entities(self) -> list[dict[str, Any]]:
252        """The result as a list of entities.
253
254        Raises `PyAirbyteInputError` if the action did not return a list of entities. Use
255        `result` for actions whose payload is not a list of entities.
256        """
257        if not isinstance(self.result, list):
258            raise PyAirbyteInputError(
259                message="This action did not return a list of entities.",
260                guidance="Use the `result` attribute to read non-entity result payloads.",
261                context={"result_type": type(self.result).__name__},
262            )
263
264        invalid_types = sorted(
265            {type(entity).__name__ for entity in self.result if not isinstance(entity, dict)}
266        )
267        if invalid_types:
268            raise PyAirbyteInputError(
269                message="This action returned a list that is not a list of entities.",
270                guidance="Use the `result` attribute to read non-entity result payloads.",
271                context={"unexpected_item_types": invalid_types},
272            )
273        return self.result
274
275    @property
276    def has_next_page(self) -> bool:
277        """Whether the connector reported more entities after this page."""
278        return bool(self.connector_metadata.has_next_page)
279
280    @property
281    def end_cursor(self) -> str | None:
282        """The cursor for the next page, or `None` when there is no next page."""
283        return self.connector_metadata.end_cursor

The result of executing a single action against an Airbyte Agents connector.

status: str = PydanticUndefined

The execution status reported by the Agents API, for example success.

result: Any = None

The action's payload. Entity-returning actions put a list of entities here.

connector_metadata: AgentConnectorMetadata = PydanticUndefined

Connector-reported metadata about the result, including pagination cursors.

execution_metadata: AgentExecutionMetadata = PydanticUndefined

Metadata describing how the action was executed.

warning: dict[str, typing.Any] | None = None

A warning reported alongside an otherwise successful result.

entities: list[dict[str, typing.Any]]
250    @property
251    def entities(self) -> list[dict[str, Any]]:
252        """The result as a list of entities.
253
254        Raises `PyAirbyteInputError` if the action did not return a list of entities. Use
255        `result` for actions whose payload is not a list of entities.
256        """
257        if not isinstance(self.result, list):
258            raise PyAirbyteInputError(
259                message="This action did not return a list of entities.",
260                guidance="Use the `result` attribute to read non-entity result payloads.",
261                context={"result_type": type(self.result).__name__},
262            )
263
264        invalid_types = sorted(
265            {type(entity).__name__ for entity in self.result if not isinstance(entity, dict)}
266        )
267        if invalid_types:
268            raise PyAirbyteInputError(
269                message="This action returned a list that is not a list of entities.",
270                guidance="Use the `result` attribute to read non-entity result payloads.",
271                context={"unexpected_item_types": invalid_types},
272            )
273        return self.result

The result as a list of entities.

Raises PyAirbyteInputError if the action did not return a list of entities. Use result for actions whose payload is not a list of entities.

has_next_page: bool
275    @property
276    def has_next_page(self) -> bool:
277        """Whether the connector reported more entities after this page."""
278        return bool(self.connector_metadata.has_next_page)

Whether the connector reported more entities after this page.

end_cursor: str | None
280    @property
281    def end_cursor(self) -> str | None:
282        """The cursor for the next page, or `None` when there is no next page."""
283        return self.connector_metadata.end_cursor

The cursor for the next page, or None when there is no next page.

class AgentExecutionMetadata(pydantic.main.BaseModel):
199class AgentExecutionMetadata(BaseModel):
200    """Metadata describing how an Agents connector action was executed."""
201
202    model_config = ConfigDict(extra="allow")
203
204    connector_instance_id: str | None = None
205    """The connector instance that served the request."""
206
207    execution_time_ms: int | None = None
208    """The server-side execution time, in milliseconds."""

Metadata describing how an Agents connector action was executed.

connector_instance_id: str | None = None

The connector instance that served the request.

execution_time_ms: int | None = None

The server-side execution time, in milliseconds.

class AgentOrganization:
 77class AgentOrganization:
 78    """An organization on the Airbyte Agents platform.
 79
 80    ```python
 81    from airbyte import agents
 82
 83    organization = agents.AgentOrganization.from_env()
 84    workspace = organization.get_workspace("my-workspace")  # by ID or name (case insensitive)
 85    ```
 86    """
 87
 88    def __init__(
 89        self,
 90        *,
 91        organization_id: str | None = None,
 92        client_id: str | SecretString | None = None,
 93        client_secret: str | SecretString | None = None,
 94        bearer_token: str | SecretString | None = None,
 95        public_api_root: str | None = None,
 96        config_api_root: str | None = None,
 97    ) -> None:
 98        """Initialize an `AgentOrganization`.
 99
100        Credentials fall back to the `AIRBYTE_CLOUD_*` environment variables when they are
101        not passed explicitly. API roots default to the `AIRBYTE_CLOUD_API_URL` /
102        `AIRBYTE_CLOUD_CONFIG_API_URL` environment variables (public Airbyte Cloud when unset);
103        custom roots require `AIRBYTE_AGENTS_API_URL`. The organization ID is optional: the
104        Agents API infers it when the credentials belong to exactly one organization.
105        """
106        self._credentials = _AirbyteCredentials.from_auth(
107            organization_id=organization_id,
108            client_id=client_id,
109            client_secret=client_secret,
110            bearer_token=bearer_token,
111            public_api_root=public_api_root,
112            config_api_root=config_api_root,
113            # Mirrors `CloudWorkspace.__init__`: any explicit credential disables env
114            # fallback, since an env bearer token plus explicit client creds is rejected
115            # as mutually exclusive auth.
116            env_vars=not (client_id or client_secret or bearer_token),
117        )
118
119        self.organization_id: str | None = self._credentials.organization_id
120        """The organization ID, when known."""
121
122    @classmethod
123    def from_env(cls, organization_id: str | None = None) -> AgentOrganization:
124        """Create an `AgentOrganization` from the `AIRBYTE_CLOUD_*` environment variables."""
125        return cls(organization_id=organization_id)
126
127    def list_workspaces(self) -> list[AgentWorkspace]:
128        """List the workspaces visible to these credentials in the Agents API."""
129        return [
130            self._workspace_from_info(info)
131            for info in (
132                AgentWorkspaceInfo.model_validate(record)
133                for record in _api_util.list_agent_workspaces(
134                    credentials=self._credentials,
135                    organization_id=self.organization_id,
136                )
137            )
138        ]
139
140    def get_workspace(
141        self,
142        id_or_name: str | None = None,
143        /,
144        *,
145        workspace_id: str | None = None,
146        name: str | None = None,
147    ) -> AgentWorkspace:
148        """Get a workspace in this organization, by ID or by name.
149
150        Pass a single positional value to look the workspace up by either its ID or its
151        name, or name the argument to be explicit.
152
153        Lookup by an explicit `workspace_id` does not call the Agents API. Every other form
154        lists the organization's workspaces and matches on ID first, then on an exact name,
155        ignoring case.
156        """
157        lookup = _resolve_workspace_lookup(
158            id_or_name,
159            workspace_id=workspace_id,
160            name=name,
161        )
162
163        if lookup.workspace_id and not lookup.name:
164            return self._workspace_from_info(AgentWorkspaceInfo(id=lookup.workspace_id))
165
166        workspaces = self.list_workspaces()
167        if lookup.workspace_id:
168            id_matches = [
169                workspace
170                for workspace in workspaces
171                if workspace.workspace_id == lookup.workspace_id
172            ]
173            if id_matches:
174                return id_matches[0]
175
176        name_lower = (lookup.name or "").lower()
177        matches = [
178            workspace
179            for workspace in workspaces
180            if workspace.name and workspace.name.lower() == name_lower
181        ]
182        if not matches:
183            raise AirbyteError(
184                message="No workspace found with the given ID or name.",
185                guidance="Use `list_workspaces()` to see the available workspaces.",
186                context={"name": lookup.name},
187            )
188        if len(matches) > 1:
189            raise AirbyteError(
190                message="Multiple workspaces matched the given name.",
191                guidance="Pass `workspace_id` instead of `name`.",
192                context={"name": lookup.name, "match_count": len(matches)},
193            )
194        return matches[0]
195
196    def as_cloud_organization(self) -> CloudOrganization:
197        """Return this organization as an `airbyte.cloud.CloudOrganization`.
198
199        Every Agents organization is also a Cloud organization, so this conversion needs no
200        API call. It requires a known organization ID, and raises `PyAirbyteInputError`
201        when the ID is unknown.
202        """
203        if not self.organization_id:
204            raise PyAirbyteInputError(
205                message="Organization ID is required to convert to a `CloudOrganization`.",
206                guidance=(
207                    "Provide `organization_id`, or set the `AIRBYTE_CLOUD_ORGANIZATION_ID` "
208                    "environment variable."
209                ),
210            )
211        return CloudOrganization(
212            organization_id=self.organization_id,
213            client_id=self._credentials.client_id,
214            client_secret=self._credentials.client_secret,
215            bearer_token=self._credentials.bearer_token,
216            public_api_root=self._credentials.public_api_root,
217            config_api_root=self._credentials.config_api_root,
218        )
219
220    @classmethod
221    def from_cloud_organization(
222        cls,
223        cloud_organization: CloudOrganization,
224    ) -> AgentOrganization:
225        """Return a Cloud organization as an `AgentOrganization`.
226
227        Whether the organization can actually execute connector actions depends on its
228        Airbyte Agents subscription, which is only knowable per workspace. Use
229        `AgentWorkspace.from_cloud_workspace()` for an authoritative eligibility check.
230
231        Raises `AirbyteAgentsUnavailableError` when the Cloud organization uses non-public Cloud
232        API roots unless `AIRBYTE_AGENTS_API_URL` is set; the Cloud API roots are carried over so
233        the token exchange stays on the same deployment.
234        """
235        credentials = cloud_organization._credentials  # noqa: SLF001  # Same-domain conversion.
236        _api_util.check_public_cloud_api_roots(credentials)
237        return cls(
238            organization_id=cloud_organization.organization_id,
239            client_id=credentials.client_id,
240            client_secret=credentials.client_secret,
241            bearer_token=credentials.bearer_token,
242            public_api_root=credentials.public_api_root,
243            config_api_root=credentials.config_api_root,
244        )
245
246    def _workspace_from_info(self, info: AgentWorkspaceInfo) -> AgentWorkspace:
247        """Build an `AgentWorkspace` from workspace info, reusing these credentials."""
248        return AgentWorkspace(
249            workspace_id=info.id,
250            organization_id=info.organization_id or self.organization_id,
251            name=info.name,
252            client_id=self._credentials.client_id,
253            client_secret=self._credentials.client_secret,
254            bearer_token=self._credentials.bearer_token,
255            public_api_root=self._credentials.public_api_root,
256            config_api_root=self._credentials.config_api_root,
257        )

An organization on the Airbyte Agents platform.

from airbyte import agents

organization = agents.AgentOrganization.from_env()
workspace = organization.get_workspace("my-workspace")  # by ID or name (case insensitive)
AgentOrganization( *, organization_id: str | None = None, client_id: str | airbyte.secrets.SecretString | None = None, client_secret: str | airbyte.secrets.SecretString | None = None, bearer_token: str | airbyte.secrets.SecretString | None = None, public_api_root: str | None = None, config_api_root: str | None = None)
 88    def __init__(
 89        self,
 90        *,
 91        organization_id: str | None = None,
 92        client_id: str | SecretString | None = None,
 93        client_secret: str | SecretString | None = None,
 94        bearer_token: str | SecretString | None = None,
 95        public_api_root: str | None = None,
 96        config_api_root: str | None = None,
 97    ) -> None:
 98        """Initialize an `AgentOrganization`.
 99
100        Credentials fall back to the `AIRBYTE_CLOUD_*` environment variables when they are
101        not passed explicitly. API roots default to the `AIRBYTE_CLOUD_API_URL` /
102        `AIRBYTE_CLOUD_CONFIG_API_URL` environment variables (public Airbyte Cloud when unset);
103        custom roots require `AIRBYTE_AGENTS_API_URL`. The organization ID is optional: the
104        Agents API infers it when the credentials belong to exactly one organization.
105        """
106        self._credentials = _AirbyteCredentials.from_auth(
107            organization_id=organization_id,
108            client_id=client_id,
109            client_secret=client_secret,
110            bearer_token=bearer_token,
111            public_api_root=public_api_root,
112            config_api_root=config_api_root,
113            # Mirrors `CloudWorkspace.__init__`: any explicit credential disables env
114            # fallback, since an env bearer token plus explicit client creds is rejected
115            # as mutually exclusive auth.
116            env_vars=not (client_id or client_secret or bearer_token),
117        )
118
119        self.organization_id: str | None = self._credentials.organization_id
120        """The organization ID, when known."""

Initialize an AgentOrganization.

Credentials fall back to the AIRBYTE_CLOUD_* environment variables when they are not passed explicitly. API roots default to the AIRBYTE_CLOUD_API_URL / AIRBYTE_CLOUD_CONFIG_API_URL environment variables (public Airbyte Cloud when unset); custom roots require AIRBYTE_AGENTS_API_URL. The organization ID is optional: the Agents API infers it when the credentials belong to exactly one organization.

organization_id: str | None

The organization ID, when known.

@classmethod
def from_env( cls, organization_id: str | None = None) -> AgentOrganization:
122    @classmethod
123    def from_env(cls, organization_id: str | None = None) -> AgentOrganization:
124        """Create an `AgentOrganization` from the `AIRBYTE_CLOUD_*` environment variables."""
125        return cls(organization_id=organization_id)

Create an AgentOrganization from the AIRBYTE_CLOUD_* environment variables.

def list_workspaces(self) -> list[AgentWorkspace]:
127    def list_workspaces(self) -> list[AgentWorkspace]:
128        """List the workspaces visible to these credentials in the Agents API."""
129        return [
130            self._workspace_from_info(info)
131            for info in (
132                AgentWorkspaceInfo.model_validate(record)
133                for record in _api_util.list_agent_workspaces(
134                    credentials=self._credentials,
135                    organization_id=self.organization_id,
136                )
137            )
138        ]

List the workspaces visible to these credentials in the Agents API.

def get_workspace( self, id_or_name: str | None = None, /, *, workspace_id: str | None = None, name: str | None = None) -> AgentWorkspace:
140    def get_workspace(
141        self,
142        id_or_name: str | None = None,
143        /,
144        *,
145        workspace_id: str | None = None,
146        name: str | None = None,
147    ) -> AgentWorkspace:
148        """Get a workspace in this organization, by ID or by name.
149
150        Pass a single positional value to look the workspace up by either its ID or its
151        name, or name the argument to be explicit.
152
153        Lookup by an explicit `workspace_id` does not call the Agents API. Every other form
154        lists the organization's workspaces and matches on ID first, then on an exact name,
155        ignoring case.
156        """
157        lookup = _resolve_workspace_lookup(
158            id_or_name,
159            workspace_id=workspace_id,
160            name=name,
161        )
162
163        if lookup.workspace_id and not lookup.name:
164            return self._workspace_from_info(AgentWorkspaceInfo(id=lookup.workspace_id))
165
166        workspaces = self.list_workspaces()
167        if lookup.workspace_id:
168            id_matches = [
169                workspace
170                for workspace in workspaces
171                if workspace.workspace_id == lookup.workspace_id
172            ]
173            if id_matches:
174                return id_matches[0]
175
176        name_lower = (lookup.name or "").lower()
177        matches = [
178            workspace
179            for workspace in workspaces
180            if workspace.name and workspace.name.lower() == name_lower
181        ]
182        if not matches:
183            raise AirbyteError(
184                message="No workspace found with the given ID or name.",
185                guidance="Use `list_workspaces()` to see the available workspaces.",
186                context={"name": lookup.name},
187            )
188        if len(matches) > 1:
189            raise AirbyteError(
190                message="Multiple workspaces matched the given name.",
191                guidance="Pass `workspace_id` instead of `name`.",
192                context={"name": lookup.name, "match_count": len(matches)},
193            )
194        return matches[0]

Get a workspace in this organization, by ID or by name.

Pass a single positional value to look the workspace up by either its ID or its name, or name the argument to be explicit.

Lookup by an explicit workspace_id does not call the Agents API. Every other form lists the organization's workspaces and matches on ID first, then on an exact name, ignoring case.

def as_cloud_organization(self) -> airbyte.cloud.CloudOrganization:
196    def as_cloud_organization(self) -> CloudOrganization:
197        """Return this organization as an `airbyte.cloud.CloudOrganization`.
198
199        Every Agents organization is also a Cloud organization, so this conversion needs no
200        API call. It requires a known organization ID, and raises `PyAirbyteInputError`
201        when the ID is unknown.
202        """
203        if not self.organization_id:
204            raise PyAirbyteInputError(
205                message="Organization ID is required to convert to a `CloudOrganization`.",
206                guidance=(
207                    "Provide `organization_id`, or set the `AIRBYTE_CLOUD_ORGANIZATION_ID` "
208                    "environment variable."
209                ),
210            )
211        return CloudOrganization(
212            organization_id=self.organization_id,
213            client_id=self._credentials.client_id,
214            client_secret=self._credentials.client_secret,
215            bearer_token=self._credentials.bearer_token,
216            public_api_root=self._credentials.public_api_root,
217            config_api_root=self._credentials.config_api_root,
218        )

Return this organization as an airbyte.cloud.CloudOrganization.

Every Agents organization is also a Cloud organization, so this conversion needs no API call. It requires a known organization ID, and raises PyAirbyteInputError when the ID is unknown.

@classmethod
def from_cloud_organization( cls, cloud_organization: airbyte.cloud.CloudOrganization) -> AgentOrganization:
220    @classmethod
221    def from_cloud_organization(
222        cls,
223        cloud_organization: CloudOrganization,
224    ) -> AgentOrganization:
225        """Return a Cloud organization as an `AgentOrganization`.
226
227        Whether the organization can actually execute connector actions depends on its
228        Airbyte Agents subscription, which is only knowable per workspace. Use
229        `AgentWorkspace.from_cloud_workspace()` for an authoritative eligibility check.
230
231        Raises `AirbyteAgentsUnavailableError` when the Cloud organization uses non-public Cloud
232        API roots unless `AIRBYTE_AGENTS_API_URL` is set; the Cloud API roots are carried over so
233        the token exchange stays on the same deployment.
234        """
235        credentials = cloud_organization._credentials  # noqa: SLF001  # Same-domain conversion.
236        _api_util.check_public_cloud_api_roots(credentials)
237        return cls(
238            organization_id=cloud_organization.organization_id,
239            client_id=credentials.client_id,
240            client_secret=credentials.client_secret,
241            bearer_token=credentials.bearer_token,
242            public_api_root=credentials.public_api_root,
243            config_api_root=credentials.config_api_root,
244        )

Return a Cloud organization as an AgentOrganization.

Whether the organization can actually execute connector actions depends on its Airbyte Agents subscription, which is only knowable per workspace. Use AgentWorkspace.from_cloud_workspace() for an authoritative eligibility check.

Raises AirbyteAgentsUnavailableError when the Cloud organization uses non-public Cloud API roots unless AIRBYTE_AGENTS_API_URL is set; the Cloud API roots are carried over so the token exchange stays on the same deployment.

class AgentSkill:
27class AgentSkill:
28    """A skill on the Airbyte Agents platform.
29
30    Get one from `AgentWorkspace.get_skill()` or `AgentWorkspace.list_skills()` rather than
31    constructing it directly.
32    """
33
34    def __init__(
35        self,
36        skill_id: str,
37        *,
38        credentials: _AirbyteCredentials,
39        workspace_id: str | None = None,
40        info: AgentSkillInfo | None = None,
41    ) -> None:
42        """Initialize an `AgentSkill`. Prefer `AgentWorkspace.get_skill()`."""
43        self.skill_id = skill_id
44        """The skill ID."""
45
46        self._credentials = credentials
47        self._workspace_id = workspace_id
48        self._info = info
49
50    @property
51    def info(self) -> AgentSkillInfo:
52        """The skill's metadata, fetched from the Agents API if not already known."""
53        if self._info is None:
54            self._info = self.read_docs().metadata
55        return self._info
56
57    @property
58    def title(self) -> str | None:
59        """The human-readable skill title."""
60        return self.info.title
61
62    @property
63    def kind(self) -> str | None:
64        """The skill category, for example `static` or `connector_source`."""
65        return self.info.kind
66
67    def read_docs(self, *, section: str | None = None) -> AgentSkillDocs:
68        """Read this skill's docs, optionally scoped to a single section.
69
70        Omit `section` for metadata, guidance, and the outline of available sections, or
71        pass an exact section `id` from the outline to read that section.
72        """
73        docs = AgentSkillDocs.model_validate(
74            _api_util.read_agent_skill_docs(
75                skill_id=self.skill_id,
76                credentials=self._credentials,
77                organization_id=self._credentials.organization_id,
78                workspace_id=self._workspace_id,
79                section=section,
80            )
81        )
82        if self._info is None:
83            self._info = docs.metadata
84        return docs

A skill on the Airbyte Agents platform.

Get one from AgentWorkspace.get_skill() or AgentWorkspace.list_skills() rather than constructing it directly.

AgentSkill( skill_id: str, *, credentials: airbyte.cloud._credentials._AirbyteCredentials, workspace_id: str | None = None, info: AgentSkillInfo | None = None)
34    def __init__(
35        self,
36        skill_id: str,
37        *,
38        credentials: _AirbyteCredentials,
39        workspace_id: str | None = None,
40        info: AgentSkillInfo | None = None,
41    ) -> None:
42        """Initialize an `AgentSkill`. Prefer `AgentWorkspace.get_skill()`."""
43        self.skill_id = skill_id
44        """The skill ID."""
45
46        self._credentials = credentials
47        self._workspace_id = workspace_id
48        self._info = info

Initialize an AgentSkill. Prefer AgentWorkspace.get_skill().

skill_id

The skill ID.

info: AgentSkillInfo
50    @property
51    def info(self) -> AgentSkillInfo:
52        """The skill's metadata, fetched from the Agents API if not already known."""
53        if self._info is None:
54            self._info = self.read_docs().metadata
55        return self._info

The skill's metadata, fetched from the Agents API if not already known.

title: str | None
57    @property
58    def title(self) -> str | None:
59        """The human-readable skill title."""
60        return self.info.title

The human-readable skill title.

kind: str | None
62    @property
63    def kind(self) -> str | None:
64        """The skill category, for example `static` or `connector_source`."""
65        return self.info.kind

The skill category, for example static or connector_source.

def read_docs( self, *, section: str | None = None) -> AgentSkillDocs:
67    def read_docs(self, *, section: str | None = None) -> AgentSkillDocs:
68        """Read this skill's docs, optionally scoped to a single section.
69
70        Omit `section` for metadata, guidance, and the outline of available sections, or
71        pass an exact section `id` from the outline to read that section.
72        """
73        docs = AgentSkillDocs.model_validate(
74            _api_util.read_agent_skill_docs(
75                skill_id=self.skill_id,
76                credentials=self._credentials,
77                organization_id=self._credentials.organization_id,
78                workspace_id=self._workspace_id,
79                section=section,
80            )
81        )
82        if self._info is None:
83            self._info = docs.metadata
84        return docs

Read this skill's docs, optionally scoped to a single section.

Omit section for metadata, guidance, and the outline of available sections, or pass an exact section id from the outline to read that section.

class AgentSkillDocs(pydantic.main.BaseModel):
132class AgentSkillDocs(BaseModel):
133    """Documentation for a single skill, as returned by the Agents API."""
134
135    model_config = ConfigDict(extra="allow")
136
137    metadata: AgentSkillInfo
138    """Metadata for the requested skill."""
139
140    outline: list[AgentSkillSection] = Field(default_factory=list)
141    """The sections available for this skill."""
142
143    section_id: str | None = None
144    """The requested section ID, or `None` for the default docs response."""
145
146    content: list[dict[str, Any]] = Field(default_factory=list)
147    """Rendered docs content blocks, such as headings, paragraphs, and code blocks."""

Documentation for a single skill, as returned by the Agents API.

metadata: AgentSkillInfo = PydanticUndefined

Metadata for the requested skill.

outline: list[AgentSkillSection] = PydanticUndefined

The sections available for this skill.

section_id: str | None = None

The requested section ID, or None for the default docs response.

content: list[dict[str, typing.Any]] = PydanticUndefined

Rendered docs content blocks, such as headings, paragraphs, and code blocks.

class AgentSkillInfo(pydantic.main.BaseModel):
78class AgentSkillInfo(BaseModel):
79    """Summary information about a skill, as returned by the Agents API."""
80
81    model_config = ConfigDict(extra="allow")
82
83    id: str
84    """The skill ID. Pass it to `read_skill_docs` to read this skill's docs."""
85
86    kind: str | None = None
87    """The skill category, for example `static` or `connector_source`."""
88
89    title: str | None = None
90    """The human-readable skill title."""
91
92    summary: str | None = None
93    """A short summary of what the skill documents."""
94
95    tags: list[str] = Field(default_factory=list)
96    """Search and categorization tags for the skill."""
97
98    warnings: list[Any] = Field(default_factory=list)
99    """Non-fatal issues reported while building or reading the skill's docs."""

Summary information about a skill, as returned by the Agents API.

id: str = PydanticUndefined

The skill ID. Pass it to read_skill_docs to read this skill's docs.

kind: str | None = None

The skill category, for example static or connector_source.

title: str | None = None

The human-readable skill title.

summary: str | None = None

A short summary of what the skill documents.

tags: list[str] = PydanticUndefined

Search and categorization tags for the skill.

warnings: list[typing.Any] = PydanticUndefined

Non-fatal issues reported while building or reading the skill's docs.

class AgentSkillList(pydantic.main.BaseModel):
102class AgentSkillList(BaseModel):
103    """A page of skills, as returned by the Agents API."""
104
105    model_config = ConfigDict(extra="allow")
106
107    data: list[AgentSkillInfo]
108    """The skills on this page."""
109
110    next_cursor: str | None = None
111    """The cursor to pass as `cursor` to fetch the next page, when one is available."""

A page of skills, as returned by the Agents API.

data: list[AgentSkillInfo] = PydanticUndefined

The skills on this page.

next_cursor: str | None = None

The cursor to pass as cursor to fetch the next page, when one is available.

class AgentSkillSection(pydantic.main.BaseModel):
114class AgentSkillSection(BaseModel):
115    """A section of a skill's docs, as listed in the docs outline."""
116
117    model_config = ConfigDict(extra="allow")
118
119    id: str
120    """The section ID. Pass it as `section` to read this section."""
121
122    title: str | None = None
123    """The human-readable section title."""
124
125    summary: str | None = None
126    """A short summary of the section content."""
127
128    available: bool = True
129    """Whether this section can currently be read."""

A section of a skill's docs, as listed in the docs outline.

id: str = PydanticUndefined

The section ID. Pass it as section to read this section.

title: str | None = None

The human-readable section title.

summary: str | None = None

A short summary of the section content.

available: bool = True

Whether this section can currently be read.

class AgentWorkspace:
 35class AgentWorkspace:
 36    """A workspace on the Airbyte Agents platform.
 37
 38    Airbyte Cloud credentials authenticate against the Agents API, so this class reads the
 39    same `AIRBYTE_CLOUD_*` environment variables as `airbyte.cloud.CloudWorkspace`.
 40
 41    ```python
 42    from airbyte import agents
 43
 44    workspace = agents.AgentWorkspace.from_env()
 45    for connector in workspace.list_connectors():
 46        print(connector.name)
 47    ```
 48    """
 49
 50    def __init__(
 51        self,
 52        *,
 53        workspace_id: str | None = None,
 54        organization_id: str | None = None,
 55        name: str | None = None,
 56        client_id: str | SecretString | None = None,
 57        client_secret: str | SecretString | None = None,
 58        bearer_token: str | SecretString | None = None,
 59        public_api_root: str | None = None,
 60        config_api_root: str | None = None,
 61    ) -> None:
 62        """Initialize an `AgentWorkspace`.
 63
 64        Credentials fall back to the `AIRBYTE_CLOUD_*` environment variables when they are
 65        not passed explicitly. API roots default to the `AIRBYTE_CLOUD_API_URL` /
 66        `AIRBYTE_CLOUD_CONFIG_API_URL` environment variables (public Airbyte Cloud when unset);
 67        custom roots require `AIRBYTE_AGENTS_API_URL`.
 68        """
 69        credentials = _AirbyteCredentials.from_auth(
 70            workspace_id=workspace_id,
 71            organization_id=organization_id,
 72            client_id=client_id,
 73            client_secret=client_secret,
 74            bearer_token=bearer_token,
 75            public_api_root=public_api_root,
 76            config_api_root=config_api_root,
 77            # Mirrors `CloudWorkspace.__init__`: any explicit credential disables env
 78            # fallback, since an env bearer token plus explicit client creds is rejected
 79            # as mutually exclusive auth.
 80            env_vars=not (client_id or client_secret or bearer_token),
 81        )
 82        if not credentials.workspace_id:
 83            raise PyAirbyteInputError(
 84                message="Workspace ID is required.",
 85                guidance=(
 86                    "Provide `workspace_id`, or set the `AIRBYTE_CLOUD_WORKSPACE_ID` "
 87                    "environment variable."
 88                ),
 89            )
 90
 91        self._credentials = credentials
 92
 93        self.workspace_id: str = credentials.workspace_id
 94        """The workspace ID."""
 95
 96        self.organization_id: str | None = credentials.organization_id
 97        """The organization ID, sent to the Agents API when it is known."""
 98
 99        self.name: str | None = name
100        """The workspace name, when known. Use `get_info()` to fetch it from the API."""
101
102    @classmethod
103    def from_env(
104        cls,
105        workspace_id: str | None = None,
106        *,
107        organization_id: str | None = None,
108    ) -> AgentWorkspace:
109        """Create an `AgentWorkspace` from the `AIRBYTE_CLOUD_*` environment variables.
110
111        The variables used are `AIRBYTE_CLOUD_BEARER_TOKEN` or the
112        `AIRBYTE_CLOUD_CLIENT_ID` and `AIRBYTE_CLOUD_CLIENT_SECRET` pair, along with
113        `AIRBYTE_CLOUD_WORKSPACE_ID` and `AIRBYTE_CLOUD_ORGANIZATION_ID`.
114        """
115        return cls(workspace_id=workspace_id, organization_id=organization_id)
116
117    def get_info(self) -> AgentWorkspaceInfo:
118        """Fetch this workspace from the Agents API.
119
120        A successful call is authoritative proof that the workspace is reachable through
121        the Agents API with these credentials.
122        """
123        return AgentWorkspaceInfo.model_validate(
124            _api_util.get_agent_workspace(
125                workspace_id=self.workspace_id,
126                credentials=self._credentials,
127                organization_id=self.organization_id,
128            )
129        )
130
131    def list_connectors(self) -> list[AgentConnector]:
132        """List the connectors configured in this workspace."""
133        return [
134            AgentConnector(
135                connector_id=info.id,
136                name=info.name,
137                credentials=self._credentials,
138                workspace_id=self.workspace_id,
139            )
140            for info in (
141                AgentConnectorInfo.model_validate(record)
142                for record in _api_util.list_agent_connectors(
143                    workspace_id=self.workspace_id,
144                    credentials=self._credentials,
145                    organization_id=self.organization_id,
146                )
147            )
148        ]
149
150    def list_skills(self) -> list[AgentSkill]:
151        """List all skills available to this workspace, following pagination."""
152        return [
153            AgentSkill(
154                skill_id=info.id,
155                credentials=self._credentials,
156                workspace_id=self.workspace_id,
157                info=info,
158            )
159            for info in _skills.iter_skills(
160                credentials=self._credentials,
161                workspace_id=self.workspace_id,
162            )
163        ]
164
165    def get_skill(self, skill_id: str) -> AgentSkill:
166        """Get a skill by ID, without calling the Agents API."""
167        return AgentSkill(
168            skill_id,
169            credentials=self._credentials,
170            workspace_id=self.workspace_id,
171        )
172
173    def read_skill_docs(
174        self,
175        skill_id: str,
176        *,
177        section: str | None = None,
178    ) -> AgentSkillDocs:
179        """Read a skill's docs, optionally scoped to a single section.
180
181        Omit `section` for metadata, guidance, and the outline of available sections, or
182        pass an exact section `id` from the outline to read that section. Connector usage
183        docs use the `docs_skill_id` reported by `AgentConnector.inspect()`.
184        """
185        return self.get_skill(skill_id).read_docs(section=section)
186
187    def get_connector(
188        self,
189        id_or_name: str | None = None,
190        /,
191        *,
192        id: str | None = None,  # noqa: A002  # Shadows `id` deliberately, as a short alias.
193        connector_id: str | None = None,
194        name: str | None = None,
195    ) -> AgentConnector:
196        """Get a connector in this workspace, by ID or by name.
197
198        Pass a single positional value to look the connector up by either its ID or its
199        name, or name the argument to be explicit: `id` and `connector_id` are synonyms,
200        so pass whichever reads better.
201
202        Lookup by an explicit ID does not call the Agents API. Every other form lists the
203        workspace's connectors and matches on ID first, then on an exact name, then on a
204        unique substring, so `name="GitHub"` finds a connector named
205        `GitHub - <workspace_id>`. Name matching is case-insensitive.
206        """
207        lookup = _resolve_connector_lookup(
208            id_or_name,
209            id=id,
210            connector_id=connector_id,
211            name=name,
212        )
213
214        if lookup.connector_id and not lookup.name:
215            return AgentConnector(
216                connector_id=lookup.connector_id,
217                credentials=self._credentials,
218                workspace_id=self.workspace_id,
219            )
220
221        connectors = self.list_connectors()
222        if lookup.connector_id:
223            id_matches = [
224                connector
225                for connector in connectors
226                if connector.connector_id == lookup.connector_id
227            ]
228            if id_matches:
229                return id_matches[0]
230
231        name_lower = (lookup.name or "").lower()
232        matches = [
233            connector
234            for connector in connectors
235            if connector.name and connector.name.lower() == name_lower
236        ] or [
237            connector
238            for connector in connectors
239            if connector.name and name_lower in connector.name.lower()
240        ]
241        if not matches:
242            raise AirbyteError(
243                message="No connector found with the given ID or name.",
244                guidance="Use `list_connectors()` to see the available connectors.",
245                context={"lookup": lookup.name, "workspace_id": self.workspace_id},
246            )
247        if len(matches) > 1:
248            raise AirbyteError(
249                message="Multiple connectors matched the given name.",
250                guidance="Pass `connector_id`, or a name that matches only one connector.",
251                context={
252                    "name": lookup.name,
253                    "matched_names": [connector.name for connector in matches],
254                },
255            )
256        return matches[0]
257
258    def as_cloud_workspace(self) -> CloudWorkspace:
259        """Return this workspace as an `airbyte.cloud.CloudWorkspace`.
260
261        Every Agents workspace is also a Cloud workspace, so this conversion always
262        succeeds without calling either API.
263        """
264        return CloudWorkspace(
265            workspace_id=self.workspace_id,
266            client_id=self._credentials.client_id,
267            client_secret=self._credentials.client_secret,
268            bearer_token=self._credentials.bearer_token,
269            api_root=self._credentials.public_api_root,
270            config_api_root=self._credentials.config_api_root,
271        )
272
273    @classmethod
274    def from_cloud_workspace(
275        cls,
276        cloud_workspace: CloudWorkspace,
277        *,
278        organization_id: str | None = None,
279        verify: bool = True,
280    ) -> AgentWorkspace:
281        """Return a Cloud workspace as an `AgentWorkspace`.
282
283        Cloud workspace IDs are also Agents workspace IDs, but not every Cloud workspace is
284        reachable through the Agents API: the organization needs an Airbyte Agents
285        subscription. By default this is verified by fetching the workspace from the Agents
286        API, which raises `AirbyteError` when it is not eligible. Pass `verify=False` to
287        skip that call.
288
289        Raises `AirbyteAgentsUnavailableError` when the Cloud workspace uses non-public Cloud API
290        roots unless `AIRBYTE_AGENTS_API_URL` is set; the Cloud API roots are carried over so the
291        token exchange stays on the same deployment.
292        """
293        _api_util.check_public_cloud_api_roots(
294            cloud_workspace._credentials,  # noqa: SLF001  # Same-domain conversion.
295        )
296        workspace = cls(
297            workspace_id=cloud_workspace.workspace_id,
298            organization_id=organization_id,
299            client_id=cloud_workspace.client_id,
300            client_secret=cloud_workspace.client_secret,
301            bearer_token=cloud_workspace.bearer_token,
302            public_api_root=cloud_workspace._credentials.public_api_root,  # noqa: SLF001
303            config_api_root=cloud_workspace._credentials.config_api_root,  # noqa: SLF001
304        )
305        if verify:
306            workspace.get_info()
307        return workspace

A workspace on the Airbyte Agents platform.

Airbyte Cloud credentials authenticate against the Agents API, so this class reads the same AIRBYTE_CLOUD_* environment variables as airbyte.cloud.CloudWorkspace.

from airbyte import agents

workspace = agents.AgentWorkspace.from_env()
for connector in workspace.list_connectors():
    print(connector.name)
AgentWorkspace( *, workspace_id: str | None = None, organization_id: str | None = None, name: str | None = None, client_id: str | airbyte.secrets.SecretString | None = None, client_secret: str | airbyte.secrets.SecretString | None = None, bearer_token: str | airbyte.secrets.SecretString | None = None, public_api_root: str | None = None, config_api_root: str | None = None)
 50    def __init__(
 51        self,
 52        *,
 53        workspace_id: str | None = None,
 54        organization_id: str | None = None,
 55        name: str | None = None,
 56        client_id: str | SecretString | None = None,
 57        client_secret: str | SecretString | None = None,
 58        bearer_token: str | SecretString | None = None,
 59        public_api_root: str | None = None,
 60        config_api_root: str | None = None,
 61    ) -> None:
 62        """Initialize an `AgentWorkspace`.
 63
 64        Credentials fall back to the `AIRBYTE_CLOUD_*` environment variables when they are
 65        not passed explicitly. API roots default to the `AIRBYTE_CLOUD_API_URL` /
 66        `AIRBYTE_CLOUD_CONFIG_API_URL` environment variables (public Airbyte Cloud when unset);
 67        custom roots require `AIRBYTE_AGENTS_API_URL`.
 68        """
 69        credentials = _AirbyteCredentials.from_auth(
 70            workspace_id=workspace_id,
 71            organization_id=organization_id,
 72            client_id=client_id,
 73            client_secret=client_secret,
 74            bearer_token=bearer_token,
 75            public_api_root=public_api_root,
 76            config_api_root=config_api_root,
 77            # Mirrors `CloudWorkspace.__init__`: any explicit credential disables env
 78            # fallback, since an env bearer token plus explicit client creds is rejected
 79            # as mutually exclusive auth.
 80            env_vars=not (client_id or client_secret or bearer_token),
 81        )
 82        if not credentials.workspace_id:
 83            raise PyAirbyteInputError(
 84                message="Workspace ID is required.",
 85                guidance=(
 86                    "Provide `workspace_id`, or set the `AIRBYTE_CLOUD_WORKSPACE_ID` "
 87                    "environment variable."
 88                ),
 89            )
 90
 91        self._credentials = credentials
 92
 93        self.workspace_id: str = credentials.workspace_id
 94        """The workspace ID."""
 95
 96        self.organization_id: str | None = credentials.organization_id
 97        """The organization ID, sent to the Agents API when it is known."""
 98
 99        self.name: str | None = name
100        """The workspace name, when known. Use `get_info()` to fetch it from the API."""

Initialize an AgentWorkspace.

Credentials fall back to the AIRBYTE_CLOUD_* environment variables when they are not passed explicitly. API roots default to the AIRBYTE_CLOUD_API_URL / AIRBYTE_CLOUD_CONFIG_API_URL environment variables (public Airbyte Cloud when unset); custom roots require AIRBYTE_AGENTS_API_URL.

workspace_id: str

The workspace ID.

organization_id: str | None

The organization ID, sent to the Agents API when it is known.

name: str | None

The workspace name, when known. Use get_info() to fetch it from the API.

@classmethod
def from_env( cls, workspace_id: str | None = None, *, organization_id: str | None = None) -> AgentWorkspace:
102    @classmethod
103    def from_env(
104        cls,
105        workspace_id: str | None = None,
106        *,
107        organization_id: str | None = None,
108    ) -> AgentWorkspace:
109        """Create an `AgentWorkspace` from the `AIRBYTE_CLOUD_*` environment variables.
110
111        The variables used are `AIRBYTE_CLOUD_BEARER_TOKEN` or the
112        `AIRBYTE_CLOUD_CLIENT_ID` and `AIRBYTE_CLOUD_CLIENT_SECRET` pair, along with
113        `AIRBYTE_CLOUD_WORKSPACE_ID` and `AIRBYTE_CLOUD_ORGANIZATION_ID`.
114        """
115        return cls(workspace_id=workspace_id, organization_id=organization_id)

Create an AgentWorkspace from the AIRBYTE_CLOUD_* environment variables.

The variables used are AIRBYTE_CLOUD_BEARER_TOKEN or the AIRBYTE_CLOUD_CLIENT_ID and AIRBYTE_CLOUD_CLIENT_SECRET pair, along with AIRBYTE_CLOUD_WORKSPACE_ID and AIRBYTE_CLOUD_ORGANIZATION_ID.

def get_info(self) -> AgentWorkspaceInfo:
117    def get_info(self) -> AgentWorkspaceInfo:
118        """Fetch this workspace from the Agents API.
119
120        A successful call is authoritative proof that the workspace is reachable through
121        the Agents API with these credentials.
122        """
123        return AgentWorkspaceInfo.model_validate(
124            _api_util.get_agent_workspace(
125                workspace_id=self.workspace_id,
126                credentials=self._credentials,
127                organization_id=self.organization_id,
128            )
129        )

Fetch this workspace from the Agents API.

A successful call is authoritative proof that the workspace is reachable through the Agents API with these credentials.

def list_connectors(self) -> list[AgentConnector]:
131    def list_connectors(self) -> list[AgentConnector]:
132        """List the connectors configured in this workspace."""
133        return [
134            AgentConnector(
135                connector_id=info.id,
136                name=info.name,
137                credentials=self._credentials,
138                workspace_id=self.workspace_id,
139            )
140            for info in (
141                AgentConnectorInfo.model_validate(record)
142                for record in _api_util.list_agent_connectors(
143                    workspace_id=self.workspace_id,
144                    credentials=self._credentials,
145                    organization_id=self.organization_id,
146                )
147            )
148        ]

List the connectors configured in this workspace.

def list_skills(self) -> list[AgentSkill]:
150    def list_skills(self) -> list[AgentSkill]:
151        """List all skills available to this workspace, following pagination."""
152        return [
153            AgentSkill(
154                skill_id=info.id,
155                credentials=self._credentials,
156                workspace_id=self.workspace_id,
157                info=info,
158            )
159            for info in _skills.iter_skills(
160                credentials=self._credentials,
161                workspace_id=self.workspace_id,
162            )
163        ]

List all skills available to this workspace, following pagination.

def get_skill(self, skill_id: str) -> AgentSkill:
165    def get_skill(self, skill_id: str) -> AgentSkill:
166        """Get a skill by ID, without calling the Agents API."""
167        return AgentSkill(
168            skill_id,
169            credentials=self._credentials,
170            workspace_id=self.workspace_id,
171        )

Get a skill by ID, without calling the Agents API.

def read_skill_docs( self, skill_id: str, *, section: str | None = None) -> AgentSkillDocs:
173    def read_skill_docs(
174        self,
175        skill_id: str,
176        *,
177        section: str | None = None,
178    ) -> AgentSkillDocs:
179        """Read a skill's docs, optionally scoped to a single section.
180
181        Omit `section` for metadata, guidance, and the outline of available sections, or
182        pass an exact section `id` from the outline to read that section. Connector usage
183        docs use the `docs_skill_id` reported by `AgentConnector.inspect()`.
184        """
185        return self.get_skill(skill_id).read_docs(section=section)

Read a skill's docs, optionally scoped to a single section.

Omit section for metadata, guidance, and the outline of available sections, or pass an exact section id from the outline to read that section. Connector usage docs use the docs_skill_id reported by AgentConnector.inspect().

def get_connector( self, id_or_name: str | None = None, /, *, id: str | None = None, connector_id: str | None = None, name: str | None = None) -> AgentConnector:
187    def get_connector(
188        self,
189        id_or_name: str | None = None,
190        /,
191        *,
192        id: str | None = None,  # noqa: A002  # Shadows `id` deliberately, as a short alias.
193        connector_id: str | None = None,
194        name: str | None = None,
195    ) -> AgentConnector:
196        """Get a connector in this workspace, by ID or by name.
197
198        Pass a single positional value to look the connector up by either its ID or its
199        name, or name the argument to be explicit: `id` and `connector_id` are synonyms,
200        so pass whichever reads better.
201
202        Lookup by an explicit ID does not call the Agents API. Every other form lists the
203        workspace's connectors and matches on ID first, then on an exact name, then on a
204        unique substring, so `name="GitHub"` finds a connector named
205        `GitHub - <workspace_id>`. Name matching is case-insensitive.
206        """
207        lookup = _resolve_connector_lookup(
208            id_or_name,
209            id=id,
210            connector_id=connector_id,
211            name=name,
212        )
213
214        if lookup.connector_id and not lookup.name:
215            return AgentConnector(
216                connector_id=lookup.connector_id,
217                credentials=self._credentials,
218                workspace_id=self.workspace_id,
219            )
220
221        connectors = self.list_connectors()
222        if lookup.connector_id:
223            id_matches = [
224                connector
225                for connector in connectors
226                if connector.connector_id == lookup.connector_id
227            ]
228            if id_matches:
229                return id_matches[0]
230
231        name_lower = (lookup.name or "").lower()
232        matches = [
233            connector
234            for connector in connectors
235            if connector.name and connector.name.lower() == name_lower
236        ] or [
237            connector
238            for connector in connectors
239            if connector.name and name_lower in connector.name.lower()
240        ]
241        if not matches:
242            raise AirbyteError(
243                message="No connector found with the given ID or name.",
244                guidance="Use `list_connectors()` to see the available connectors.",
245                context={"lookup": lookup.name, "workspace_id": self.workspace_id},
246            )
247        if len(matches) > 1:
248            raise AirbyteError(
249                message="Multiple connectors matched the given name.",
250                guidance="Pass `connector_id`, or a name that matches only one connector.",
251                context={
252                    "name": lookup.name,
253                    "matched_names": [connector.name for connector in matches],
254                },
255            )
256        return matches[0]

Get a connector in this workspace, by ID or by name.

Pass a single positional value to look the connector up by either its ID or its name, or name the argument to be explicit: id and connector_id are synonyms, so pass whichever reads better.

Lookup by an explicit ID does not call the Agents API. Every other form lists the workspace's connectors and matches on ID first, then on an exact name, then on a unique substring, so name="GitHub" finds a connector named GitHub - <workspace_id>. Name matching is case-insensitive.

def as_cloud_workspace(self) -> airbyte.cloud.CloudWorkspace:
258    def as_cloud_workspace(self) -> CloudWorkspace:
259        """Return this workspace as an `airbyte.cloud.CloudWorkspace`.
260
261        Every Agents workspace is also a Cloud workspace, so this conversion always
262        succeeds without calling either API.
263        """
264        return CloudWorkspace(
265            workspace_id=self.workspace_id,
266            client_id=self._credentials.client_id,
267            client_secret=self._credentials.client_secret,
268            bearer_token=self._credentials.bearer_token,
269            api_root=self._credentials.public_api_root,
270            config_api_root=self._credentials.config_api_root,
271        )

Return this workspace as an airbyte.cloud.CloudWorkspace.

Every Agents workspace is also a Cloud workspace, so this conversion always succeeds without calling either API.

@classmethod
def from_cloud_workspace( cls, cloud_workspace: airbyte.cloud.CloudWorkspace, *, organization_id: str | None = None, verify: bool = True) -> AgentWorkspace:
273    @classmethod
274    def from_cloud_workspace(
275        cls,
276        cloud_workspace: CloudWorkspace,
277        *,
278        organization_id: str | None = None,
279        verify: bool = True,
280    ) -> AgentWorkspace:
281        """Return a Cloud workspace as an `AgentWorkspace`.
282
283        Cloud workspace IDs are also Agents workspace IDs, but not every Cloud workspace is
284        reachable through the Agents API: the organization needs an Airbyte Agents
285        subscription. By default this is verified by fetching the workspace from the Agents
286        API, which raises `AirbyteError` when it is not eligible. Pass `verify=False` to
287        skip that call.
288
289        Raises `AirbyteAgentsUnavailableError` when the Cloud workspace uses non-public Cloud API
290        roots unless `AIRBYTE_AGENTS_API_URL` is set; the Cloud API roots are carried over so the
291        token exchange stays on the same deployment.
292        """
293        _api_util.check_public_cloud_api_roots(
294            cloud_workspace._credentials,  # noqa: SLF001  # Same-domain conversion.
295        )
296        workspace = cls(
297            workspace_id=cloud_workspace.workspace_id,
298            organization_id=organization_id,
299            client_id=cloud_workspace.client_id,
300            client_secret=cloud_workspace.client_secret,
301            bearer_token=cloud_workspace.bearer_token,
302            public_api_root=cloud_workspace._credentials.public_api_root,  # noqa: SLF001
303            config_api_root=cloud_workspace._credentials.config_api_root,  # noqa: SLF001
304        )
305        if verify:
306            workspace.get_info()
307        return workspace

Return a Cloud workspace as an AgentWorkspace.

Cloud workspace IDs are also Agents workspace IDs, but not every Cloud workspace is reachable through the Agents API: the organization needs an Airbyte Agents subscription. By default this is verified by fetching the workspace from the Agents API, which raises AirbyteError when it is not eligible. Pass verify=False to skip that call.

Raises AirbyteAgentsUnavailableError when the Cloud workspace uses non-public Cloud API roots unless AIRBYTE_AGENTS_API_URL is set; the Cloud API roots are carried over so the token exchange stays on the same deployment.

class AgentWorkspaceInfo(pydantic.main.BaseModel):
24class AgentWorkspaceInfo(BaseModel):
25    """Summary information about a workspace, as returned by the Agents API."""
26
27    model_config = ConfigDict(extra="allow")
28
29    id: str
30    """The workspace ID."""
31
32    name: str | None = None
33    """The workspace name."""
34
35    organization_id: str | None = None
36    """The ID of the organization the workspace belongs to."""
37
38    status: str | None = None
39    """The workspace status, for example `active`."""

Summary information about a workspace, as returned by the Agents API.

id: str = PydanticUndefined

The workspace ID.

name: str | None = None

The workspace name.

organization_id: str | None = None

The ID of the organization the workspace belongs to.

status: str | None = None

The workspace status, for example active.