airbyte.agents.organizations

Airbyte Agents organizations.

⚠️ 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.

  1# Copyright (c) 2026 Airbyte, Inc., all rights reserved.
  2"""Airbyte Agents organizations.
  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"""
 10
 11from __future__ import annotations
 12
 13from typing import TYPE_CHECKING, NamedTuple
 14
 15from airbyte.agents import _api_util
 16from airbyte.agents.models import AgentWorkspaceInfo
 17from airbyte.agents.workspaces import AgentWorkspace
 18from airbyte.cloud._credentials import _AirbyteCredentials
 19from airbyte.cloud.organizations import CloudOrganization
 20from airbyte.exceptions import AirbyteError, PyAirbyteInputError
 21
 22
 23if TYPE_CHECKING:
 24    from airbyte.secrets.base import SecretString
 25
 26
 27class _WorkspaceLookup(NamedTuple):
 28    """What to look a workspace up by, once the lookup arguments have been validated.
 29
 30    Both fields are set when the caller passed a positional value that could be either an
 31    ID or a name, in which case an ID match takes precedence over a name match.
 32    """
 33
 34    workspace_id: str | None
 35    name: str | None
 36
 37
 38def _resolve_workspace_lookup(
 39    id_or_name: str | None,
 40    /,
 41    *,
 42    workspace_id: str | None,
 43    name: str | None,
 44) -> _WorkspaceLookup:
 45    """Validate workspace lookup arguments and return what to look the workspace up by.
 46
 47    Exactly one of `workspace_id`, `name`, or the positional `id_or_name` is required. A
 48    blank value is rejected rather than treated as an omitted argument.
 49    """
 50    all_args = {"id_or_name": id_or_name, "workspace_id": workspace_id, "name": name}
 51
 52    blank_args = sorted(
 53        key for key, value in all_args.items() if value is not None and not value.strip()
 54    )
 55    if blank_args:
 56        raise PyAirbyteInputError(
 57            message="Workspace lookup arguments cannot be blank.",
 58            guidance="Omit the argument entirely, or pass a non-blank value.",
 59            context={"blank_args": blank_args},
 60        )
 61
 62    provided = sorted(key for key, value in all_args.items() if value)
 63    if len(provided) != 1:
 64        raise PyAirbyteInputError(
 65            message="Exactly one workspace lookup argument is required.",
 66            guidance="Pass a workspace ID or name positionally, or as `workspace_id` or `name`.",
 67            context={"provided": provided},
 68        )
 69
 70    if id_or_name:
 71        return _WorkspaceLookup(workspace_id=id_or_name, name=id_or_name)
 72
 73    return _WorkspaceLookup(workspace_id=workspace_id, name=name)
 74
 75
 76class AgentOrganization:
 77    """An organization on the Airbyte Agents platform.
 78
 79    ```python
 80    from airbyte import agents
 81
 82    organization = agents.AgentOrganization.from_env()
 83    workspace = organization.get_workspace("my-workspace")  # by ID or name (case insensitive)
 84    ```
 85    """
 86
 87    def __init__(
 88        self,
 89        *,
 90        organization_id: str | None = None,
 91        client_id: str | SecretString | None = None,
 92        client_secret: str | SecretString | None = None,
 93        bearer_token: str | SecretString | None = None,
 94    ) -> None:
 95        """Initialize an `AgentOrganization`.
 96
 97        Credentials fall back to the `AIRBYTE_CLOUD_*` environment variables when they are
 98        not passed explicitly. The organization ID is optional: the Agents API infers it
 99        when the credentials belong to exactly one organization.
100        """
101        self._credentials = _AirbyteCredentials.from_auth(
102            organization_id=organization_id,
103            client_id=client_id,
104            client_secret=client_secret,
105            bearer_token=bearer_token,
106            # Mirrors `CloudWorkspace.__init__`: any explicit credential disables env
107            # fallback, since an env bearer token plus explicit client creds is rejected
108            # as mutually exclusive auth.
109            env_vars=not (client_id or client_secret or bearer_token),
110        )
111
112        self.organization_id: str | None = self._credentials.organization_id
113        """The organization ID, when known."""
114
115    @classmethod
116    def from_env(cls, organization_id: str | None = None) -> AgentOrganization:
117        """Create an `AgentOrganization` from the `AIRBYTE_CLOUD_*` environment variables."""
118        return cls(organization_id=organization_id)
119
120    def list_workspaces(self) -> list[AgentWorkspace]:
121        """List the workspaces visible to these credentials in the Agents API."""
122        return [
123            self._workspace_from_info(info)
124            for info in (
125                AgentWorkspaceInfo.model_validate(record)
126                for record in _api_util.list_agent_workspaces(
127                    credentials=self._credentials,
128                    organization_id=self.organization_id,
129                )
130            )
131        ]
132
133    def get_workspace(
134        self,
135        id_or_name: str | None = None,
136        /,
137        *,
138        workspace_id: str | None = None,
139        name: str | None = None,
140    ) -> AgentWorkspace:
141        """Get a workspace in this organization, by ID or by name.
142
143        Pass a single positional value to look the workspace up by either its ID or its
144        name, or name the argument to be explicit.
145
146        Lookup by an explicit `workspace_id` does not call the Agents API. Every other form
147        lists the organization's workspaces and matches on ID first, then on an exact name,
148        ignoring case.
149        """
150        lookup = _resolve_workspace_lookup(
151            id_or_name,
152            workspace_id=workspace_id,
153            name=name,
154        )
155
156        if lookup.workspace_id and not lookup.name:
157            return self._workspace_from_info(AgentWorkspaceInfo(id=lookup.workspace_id))
158
159        workspaces = self.list_workspaces()
160        if lookup.workspace_id:
161            id_matches = [
162                workspace
163                for workspace in workspaces
164                if workspace.workspace_id == lookup.workspace_id
165            ]
166            if id_matches:
167                return id_matches[0]
168
169        name_lower = (lookup.name or "").lower()
170        matches = [
171            workspace
172            for workspace in workspaces
173            if workspace.name and workspace.name.lower() == name_lower
174        ]
175        if not matches:
176            raise AirbyteError(
177                message="No workspace found with the given ID or name.",
178                guidance="Use `list_workspaces()` to see the available workspaces.",
179                context={"name": lookup.name},
180            )
181        if len(matches) > 1:
182            raise AirbyteError(
183                message="Multiple workspaces matched the given name.",
184                guidance="Pass `workspace_id` instead of `name`.",
185                context={"name": lookup.name, "match_count": len(matches)},
186            )
187        return matches[0]
188
189    def as_cloud_organization(self) -> CloudOrganization:
190        """Return this organization as an `airbyte.cloud.CloudOrganization`.
191
192        Every Agents organization is also a Cloud organization, so this conversion needs no
193        API call. It requires a known organization ID, and raises `PyAirbyteInputError`
194        when the ID is unknown.
195        """
196        if not self.organization_id:
197            raise PyAirbyteInputError(
198                message="Organization ID is required to convert to a `CloudOrganization`.",
199                guidance=(
200                    "Provide `organization_id`, or set the `AIRBYTE_CLOUD_ORGANIZATION_ID` "
201                    "environment variable."
202                ),
203            )
204        return CloudOrganization(
205            organization_id=self.organization_id,
206            client_id=self._credentials.client_id,
207            client_secret=self._credentials.client_secret,
208            bearer_token=self._credentials.bearer_token,
209            public_api_root=self._credentials.public_api_root,
210            config_api_root=self._credentials.config_api_root,
211        )
212
213    @classmethod
214    def from_cloud_organization(
215        cls,
216        cloud_organization: CloudOrganization,
217    ) -> AgentOrganization:
218        """Return a Cloud organization as an `AgentOrganization`.
219
220        Whether the organization can actually execute connector actions depends on its
221        Airbyte Agents subscription, which is only knowable per workspace. Use
222        `AgentWorkspace.from_cloud_workspace()` for an authoritative eligibility check.
223
224        Raises `PyAirbyteInputError` when the Cloud organization uses non-public Cloud API
225        roots, since an `AgentOrganization` cannot carry them.
226        """
227        credentials = cloud_organization._credentials  # noqa: SLF001  # Same-domain conversion.
228        _api_util.check_public_cloud_api_roots(credentials)
229        return cls(
230            organization_id=cloud_organization.organization_id,
231            client_id=credentials.client_id,
232            client_secret=credentials.client_secret,
233            bearer_token=credentials.bearer_token,
234        )
235
236    def _workspace_from_info(self, info: AgentWorkspaceInfo) -> AgentWorkspace:
237        """Build an `AgentWorkspace` from workspace info, reusing these credentials."""
238        return AgentWorkspace(
239            workspace_id=info.id,
240            organization_id=info.organization_id or self.organization_id,
241            name=info.name,
242            client_id=self._credentials.client_id,
243            client_secret=self._credentials.client_secret,
244            bearer_token=self._credentials.bearer_token,
245        )
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    ) -> None:
 96        """Initialize an `AgentOrganization`.
 97
 98        Credentials fall back to the `AIRBYTE_CLOUD_*` environment variables when they are
 99        not passed explicitly. The organization ID is optional: the Agents API infers it
100        when the credentials belong to exactly one organization.
101        """
102        self._credentials = _AirbyteCredentials.from_auth(
103            organization_id=organization_id,
104            client_id=client_id,
105            client_secret=client_secret,
106            bearer_token=bearer_token,
107            # Mirrors `CloudWorkspace.__init__`: any explicit credential disables env
108            # fallback, since an env bearer token plus explicit client creds is rejected
109            # as mutually exclusive auth.
110            env_vars=not (client_id or client_secret or bearer_token),
111        )
112
113        self.organization_id: str | None = self._credentials.organization_id
114        """The organization ID, when known."""
115
116    @classmethod
117    def from_env(cls, organization_id: str | None = None) -> AgentOrganization:
118        """Create an `AgentOrganization` from the `AIRBYTE_CLOUD_*` environment variables."""
119        return cls(organization_id=organization_id)
120
121    def list_workspaces(self) -> list[AgentWorkspace]:
122        """List the workspaces visible to these credentials in the Agents API."""
123        return [
124            self._workspace_from_info(info)
125            for info in (
126                AgentWorkspaceInfo.model_validate(record)
127                for record in _api_util.list_agent_workspaces(
128                    credentials=self._credentials,
129                    organization_id=self.organization_id,
130                )
131            )
132        ]
133
134    def get_workspace(
135        self,
136        id_or_name: str | None = None,
137        /,
138        *,
139        workspace_id: str | None = None,
140        name: str | None = None,
141    ) -> AgentWorkspace:
142        """Get a workspace in this organization, by ID or by name.
143
144        Pass a single positional value to look the workspace up by either its ID or its
145        name, or name the argument to be explicit.
146
147        Lookup by an explicit `workspace_id` does not call the Agents API. Every other form
148        lists the organization's workspaces and matches on ID first, then on an exact name,
149        ignoring case.
150        """
151        lookup = _resolve_workspace_lookup(
152            id_or_name,
153            workspace_id=workspace_id,
154            name=name,
155        )
156
157        if lookup.workspace_id and not lookup.name:
158            return self._workspace_from_info(AgentWorkspaceInfo(id=lookup.workspace_id))
159
160        workspaces = self.list_workspaces()
161        if lookup.workspace_id:
162            id_matches = [
163                workspace
164                for workspace in workspaces
165                if workspace.workspace_id == lookup.workspace_id
166            ]
167            if id_matches:
168                return id_matches[0]
169
170        name_lower = (lookup.name or "").lower()
171        matches = [
172            workspace
173            for workspace in workspaces
174            if workspace.name and workspace.name.lower() == name_lower
175        ]
176        if not matches:
177            raise AirbyteError(
178                message="No workspace found with the given ID or name.",
179                guidance="Use `list_workspaces()` to see the available workspaces.",
180                context={"name": lookup.name},
181            )
182        if len(matches) > 1:
183            raise AirbyteError(
184                message="Multiple workspaces matched the given name.",
185                guidance="Pass `workspace_id` instead of `name`.",
186                context={"name": lookup.name, "match_count": len(matches)},
187            )
188        return matches[0]
189
190    def as_cloud_organization(self) -> CloudOrganization:
191        """Return this organization as an `airbyte.cloud.CloudOrganization`.
192
193        Every Agents organization is also a Cloud organization, so this conversion needs no
194        API call. It requires a known organization ID, and raises `PyAirbyteInputError`
195        when the ID is unknown.
196        """
197        if not self.organization_id:
198            raise PyAirbyteInputError(
199                message="Organization ID is required to convert to a `CloudOrganization`.",
200                guidance=(
201                    "Provide `organization_id`, or set the `AIRBYTE_CLOUD_ORGANIZATION_ID` "
202                    "environment variable."
203                ),
204            )
205        return CloudOrganization(
206            organization_id=self.organization_id,
207            client_id=self._credentials.client_id,
208            client_secret=self._credentials.client_secret,
209            bearer_token=self._credentials.bearer_token,
210            public_api_root=self._credentials.public_api_root,
211            config_api_root=self._credentials.config_api_root,
212        )
213
214    @classmethod
215    def from_cloud_organization(
216        cls,
217        cloud_organization: CloudOrganization,
218    ) -> AgentOrganization:
219        """Return a Cloud organization as an `AgentOrganization`.
220
221        Whether the organization can actually execute connector actions depends on its
222        Airbyte Agents subscription, which is only knowable per workspace. Use
223        `AgentWorkspace.from_cloud_workspace()` for an authoritative eligibility check.
224
225        Raises `PyAirbyteInputError` when the Cloud organization uses non-public Cloud API
226        roots, since an `AgentOrganization` cannot carry them.
227        """
228        credentials = cloud_organization._credentials  # noqa: SLF001  # Same-domain conversion.
229        _api_util.check_public_cloud_api_roots(credentials)
230        return cls(
231            organization_id=cloud_organization.organization_id,
232            client_id=credentials.client_id,
233            client_secret=credentials.client_secret,
234            bearer_token=credentials.bearer_token,
235        )
236
237    def _workspace_from_info(self, info: AgentWorkspaceInfo) -> AgentWorkspace:
238        """Build an `AgentWorkspace` from workspace info, reusing these credentials."""
239        return AgentWorkspace(
240            workspace_id=info.id,
241            organization_id=info.organization_id or self.organization_id,
242            name=info.name,
243            client_id=self._credentials.client_id,
244            client_secret=self._credentials.client_secret,
245            bearer_token=self._credentials.bearer_token,
246        )

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)
 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    ) -> None:
 96        """Initialize an `AgentOrganization`.
 97
 98        Credentials fall back to the `AIRBYTE_CLOUD_*` environment variables when they are
 99        not passed explicitly. The organization ID is optional: the Agents API infers it
100        when the credentials belong to exactly one organization.
101        """
102        self._credentials = _AirbyteCredentials.from_auth(
103            organization_id=organization_id,
104            client_id=client_id,
105            client_secret=client_secret,
106            bearer_token=bearer_token,
107            # Mirrors `CloudWorkspace.__init__`: any explicit credential disables env
108            # fallback, since an env bearer token plus explicit client creds is rejected
109            # as mutually exclusive auth.
110            env_vars=not (client_id or client_secret or bearer_token),
111        )
112
113        self.organization_id: str | None = self._credentials.organization_id
114        """The organization ID, when known."""

Initialize an AgentOrganization.

Credentials fall back to the AIRBYTE_CLOUD_* environment variables when they are not passed explicitly. 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:
116    @classmethod
117    def from_env(cls, organization_id: str | None = None) -> AgentOrganization:
118        """Create an `AgentOrganization` from the `AIRBYTE_CLOUD_*` environment variables."""
119        return cls(organization_id=organization_id)

Create an AgentOrganization from the AIRBYTE_CLOUD_* environment variables.

def list_workspaces(self) -> list[airbyte.agents.AgentWorkspace]:
121    def list_workspaces(self) -> list[AgentWorkspace]:
122        """List the workspaces visible to these credentials in the Agents API."""
123        return [
124            self._workspace_from_info(info)
125            for info in (
126                AgentWorkspaceInfo.model_validate(record)
127                for record in _api_util.list_agent_workspaces(
128                    credentials=self._credentials,
129                    organization_id=self.organization_id,
130                )
131            )
132        ]

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) -> airbyte.agents.AgentWorkspace:
134    def get_workspace(
135        self,
136        id_or_name: str | None = None,
137        /,
138        *,
139        workspace_id: str | None = None,
140        name: str | None = None,
141    ) -> AgentWorkspace:
142        """Get a workspace in this organization, by ID or by name.
143
144        Pass a single positional value to look the workspace up by either its ID or its
145        name, or name the argument to be explicit.
146
147        Lookup by an explicit `workspace_id` does not call the Agents API. Every other form
148        lists the organization's workspaces and matches on ID first, then on an exact name,
149        ignoring case.
150        """
151        lookup = _resolve_workspace_lookup(
152            id_or_name,
153            workspace_id=workspace_id,
154            name=name,
155        )
156
157        if lookup.workspace_id and not lookup.name:
158            return self._workspace_from_info(AgentWorkspaceInfo(id=lookup.workspace_id))
159
160        workspaces = self.list_workspaces()
161        if lookup.workspace_id:
162            id_matches = [
163                workspace
164                for workspace in workspaces
165                if workspace.workspace_id == lookup.workspace_id
166            ]
167            if id_matches:
168                return id_matches[0]
169
170        name_lower = (lookup.name or "").lower()
171        matches = [
172            workspace
173            for workspace in workspaces
174            if workspace.name and workspace.name.lower() == name_lower
175        ]
176        if not matches:
177            raise AirbyteError(
178                message="No workspace found with the given ID or name.",
179                guidance="Use `list_workspaces()` to see the available workspaces.",
180                context={"name": lookup.name},
181            )
182        if len(matches) > 1:
183            raise AirbyteError(
184                message="Multiple workspaces matched the given name.",
185                guidance="Pass `workspace_id` instead of `name`.",
186                context={"name": lookup.name, "match_count": len(matches)},
187            )
188        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:
190    def as_cloud_organization(self) -> CloudOrganization:
191        """Return this organization as an `airbyte.cloud.CloudOrganization`.
192
193        Every Agents organization is also a Cloud organization, so this conversion needs no
194        API call. It requires a known organization ID, and raises `PyAirbyteInputError`
195        when the ID is unknown.
196        """
197        if not self.organization_id:
198            raise PyAirbyteInputError(
199                message="Organization ID is required to convert to a `CloudOrganization`.",
200                guidance=(
201                    "Provide `organization_id`, or set the `AIRBYTE_CLOUD_ORGANIZATION_ID` "
202                    "environment variable."
203                ),
204            )
205        return CloudOrganization(
206            organization_id=self.organization_id,
207            client_id=self._credentials.client_id,
208            client_secret=self._credentials.client_secret,
209            bearer_token=self._credentials.bearer_token,
210            public_api_root=self._credentials.public_api_root,
211            config_api_root=self._credentials.config_api_root,
212        )

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:
214    @classmethod
215    def from_cloud_organization(
216        cls,
217        cloud_organization: CloudOrganization,
218    ) -> AgentOrganization:
219        """Return a Cloud organization as an `AgentOrganization`.
220
221        Whether the organization can actually execute connector actions depends on its
222        Airbyte Agents subscription, which is only knowable per workspace. Use
223        `AgentWorkspace.from_cloud_workspace()` for an authoritative eligibility check.
224
225        Raises `PyAirbyteInputError` when the Cloud organization uses non-public Cloud API
226        roots, since an `AgentOrganization` cannot carry them.
227        """
228        credentials = cloud_organization._credentials  # noqa: SLF001  # Same-domain conversion.
229        _api_util.check_public_cloud_api_roots(credentials)
230        return cls(
231            organization_id=cloud_organization.organization_id,
232            client_id=credentials.client_id,
233            client_secret=credentials.client_secret,
234            bearer_token=credentials.bearer_token,
235        )

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 PyAirbyteInputError when the Cloud organization uses non-public Cloud API roots, since an AgentOrganization cannot carry them.