Metadata-Version: 2.5
Name: composio-client
Version: 2.0.0
Summary: The official Python library for the Composio API, generated from the v3.1 OpenAPI spec with hey-api
Project-URL: Homepage, https://github.com/ComposioHQ/composio-client
Project-URL: Repository, https://github.com/ComposioHQ/composio-client
Author: Composio
License-Expression: Apache-2.0
License-File: LICENSE
Keywords: api,client,composio,tool_router
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3
Classifier: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: httpx>=0.27
Requires-Dist: pydantic<3,>=2.9
Requires-Dist: typing-extensions>=4.12
Description-Content-Type: text/markdown

# composio-client for Python

Sync and async clients for the Composio v3.1 API, with typed request fields and Pydantic responses.
Python 3.10 or newer is required.

Upgrading from Stainless 1.43 requires explicit authentication and the
other changes in the [migration guide](https://github.com/ComposioHQ/composio-client/blob/main/python/MIGRATION.md).

## Install the client

```bash
pip install composio-client
```

With `uv`:

```bash
uv add composio-client
```

## Quickstart

```python
from composio_client import Composio, ProjectAuth

client = Composio(auth=ProjectAuth("ak_..."))

session = client.tool_router.session.create(user_id="user@example.com")
result = client.tool_router.session.execute(
    session.session_id,
    tool_slug="GITHUB_CREATE_ISSUE",
    arguments={"owner": "composiohq", "repo": "composio", "title": "Example"},
)
```

The checked-in OpenAPI snapshot supplies all 114 operations across the sync and async clients.
Operations marked internal are under `client._internal` and carry no compatibility promise.

## Authenticate and scope the client

`auth` is required and says which Composio credential the client sends:

| `auth` | Sends |
| --- | --- |
| `NoAuth()` | No Composio credential. `x-api-key` and its siblings are then ordinary headers you may set |
| `ProjectAuth(key)` | `x-api-key` on every request |
| `UserAuth(key)` | `x-user-api-key` on every request |
| `OrganizationAuth(key)` | `x-org-api-key` on every request |
| `AutoAuth(project=, user=, organization=)` | One of the configured keys per operation, chosen from the operation's declared security schemes, falling back to the project key |

A fixed mode never consults the operation table. A request carries at most one credential, and
only to the configured API origin.

A credential is a key or a function returning one. The function is called once per logical call
and its key is reused across that call's retries and same-origin redirects. `AsyncComposio` also
accepts an `async` function:

```python
client = Composio(auth=UserAuth(lambda: vault.read("composio-user-key")))
```

`scope` routes every request to an organization, a project, or both, sent as `x-org-id` and
`x-project-id`:

```python
from composio_client import Composio, Scope, UserAuth

client = Composio(auth=UserAuth("uak_..."), scope=Scope(organization_id="org_...", project_id="pr_..."))
```

A header a configured owner writes cannot also come through generic headers: with `auth` other
than `NoAuth()`, `x-api-key`, `x-user-api-key` and `x-org-api-key` are refused in
`default_headers`, `COMPOSIO_CUSTOM_HEADERS`, a raw call's `headers=` and `request_options`
headers; with `scope` set, so are `x-org-id` and `x-project-id`. Use `with_options(auth=...)` or
`with_options(scope=...)` to derive a client that sends something else. `with_options` re-runs every
constructor check; `scope=None` there clears the scope.

The few operations that declare `x-user-api-key` or `x-org-id` as a parameter (for example
`client._internal.org.consumer.project.resolve(x_user_api_key=..., x_org_id=...)`) keep their keywords. The
key you pass replaces the configured `auth` for that call, and an `x_org_id` that disagrees with the
configured `scope` fails before anything is sent.

`client.auth_mode` and `client.scope` say how a client is configured. No attribute reads a
credential back; keep your own copy of a key you need.

## Configure from the environment

The constructor reads nothing from the process. `from_env` reads the mapping you hand it:

```python
import os

client = Composio.from_env(os.environ, environment="staging")
```

| Variable | Becomes |
| --- | --- |
| `COMPOSIO_API_KEY`, `COMPOSIO_USER_API_KEY`, `COMPOSIO_ORG_API_KEY` | `auth`: one set is that fixed mode, several are `AutoAuth`, none is `NoAuth()` |
| `COMPOSIO_BASE_URL` | `base_url` |
| `COMPOSIO_CUSTOM_HEADERS` | `default_headers`, held to the reserved-header rule |
| `COMPOSIO_ORG_ID`, `COMPOSIO_PROJECT_ID` | Not read: `scope` is always an explicit argument |

A keyword argument replaces the whole field it names: `auth=NoAuth()` means no credential whatever
the mapping holds, and `default_headers=` replaces `COMPOSIO_CUSTOM_HEADERS` rather than merging
into it.

## Identify the calling product

A product built on the client passes a `ClientIdentity` instead of setting headers by hand:

```python
from composio_client import ClientIdentity, Composio, ProjectAuth

client = Composio(
    auth=ProjectAuth("ak_..."),
    identity=ClientIdentity(product="composio", version="0.9.0", source="PYTHON_SDK", framework="openai"),
)
```

`product` and `version` are sent as `x-client-provenance` and `x-client-version` (`version` also
as `x-sdk-version`); `source`, `framework` and `runtime` as `x-source`, `x-framework` and
`x-runtime`. The library headers (`x-client-library`, `x-client-library-version`,
`x-client-language`) are the package's own and are refused in `default_headers`,
`COMPOSIO_CUSTOM_HEADERS` and `request_options` headers; once `identity` is set, so is every
identity header. A refused name raises `ComposioError`. `ClientIdentity` is frozen and validates
that every value is non-empty printable ASCII.

## Use the async client

```python
import asyncio

from composio_client import AsyncComposio, ProjectAuth


async def main() -> None:
    async with AsyncComposio(auth=ProjectAuth("ak_...")) as client:
        session = await client.tool_router.session.create(user_id="user@example.com")
        print(session.session_id)


asyncio.run(main())
```

## Read raw response metadata

Every released resource keeps the Stainless raw and streaming accessors:

```python
from composio_client import Composio, ProjectAuth

client = Composio(auth=ProjectAuth("ak_..."))

response = client.tool_router.session.with_raw_response.retrieve("sess_123")
print(response.status_code)
session = response.parse()

with client.tool_router.session.with_streaming_response.retrieve("sess_123") as response:
    print(response.headers)
```

The async resource exposes the corresponding async response objects and context managers.

## Retries and timeouts

GET, HEAD, OPTIONS, PUT and DELETE calls retry up to twice on connection errors, timeouts and
retryable HTTP responses. POST and PATCH are sent once by default, including tool execution.
A client-level `max_retries` only applies to idempotent methods. A call that is safe to repeat
opts in with `request_options={"max_retries": 2}` (or `options=RequestOptions(max_retries=2)`
on a raw request). An `idempotency_key` only sends a header; server deduplication is not guaranteed,
and the header does not opt a write into retries. This applies to both sync and async clients.

## Handle API errors

The exception hierarchy matches the Stainless package. Existing `except` blocks keep working:

```python
from composio_client import Composio, NotFoundError, ProjectAuth

client = Composio(auth=ProjectAuth("ak_..."))

try:
    client.tool_router.session.retrieve("missing")
except NotFoundError as error:
    print(error.status_code)
```

## Execution results

The session methods `execute`, `execute_meta` and `proxy_execute` return their
`SessionExecuteResult`, `SessionExecuteMetaResult` and `SessionProxyExecuteResult` unions.
Each union includes a legacy `*Response` class, a `*BlockedResponse`, and an
`*InputRequiredResponse`. A result does not always have `data`: handle blocked and input-required
results before reading the legacy fields.

For `execute`, narrow the return value with `isinstance`:

```python
from composio_client.types.tool_router.session_execute_response import (
    SessionExecuteBlockedResponse,
    SessionExecuteInputRequiredResponse,
    SessionExecuteResult,
)


def summarize(result: SessionExecuteResult) -> str:
    if isinstance(result, SessionExecuteBlockedResponse):
        return result.message
    if isinstance(result, SessionExecuteInputRequiredResponse):
        return f"Input required: {', '.join(result.input_requests)}"
    return result.error or "Execution completed"
```

`SessionExecuteResponse` remains a Pydantic class for completed/failed and legacy results, so existing
constructors and subclasses keep working. Its `result_type` may be `None` for a legacy fixture or
response. Use `SessionExecuteResult` when annotating the method's whole return value. The meta and
proxy methods follow the same naming rule. This distinction is part of the 2.0 public contract.

## Migrate pre-release owned-client calls

Earlier owned-client builds accepted aggregate `body`, `query`, and `headers` values. Those calls
remain valid during the transition:

```python
client.tool_router.session.create({"user_id": "user@example.com"})
client.tool_router.session.config_history("sess_123", query={"limit": 25})
```

Prefer the flat Stainless form in new code:

```python
client.tool_router.session.create(user_id="user@example.com")
client.tool_router.session.config_history("sess_123", limit=25)
```

See [the Python migration guide](https://github.com/ComposioHQ/composio-client/blob/main/python/MIGRATION.md) for compatibility details and the stricter credential
transport rules.

## Develop the Python package

Generate from the repository root:

```bash
pnpm generate:python
pnpm codegen:check
```

Run Python checks from `python/`:

```bash
uv run ruff check
uv run ruff format --check
uv run mypy src scripts
uv run pytest
uv run python scripts/check_stainless_compatibility.py
uv build
uv run python scripts/check_stainless_compatibility.py --wheel dist/*.whl
```

The normal compatibility check is offline. It reads the committed `1.43.0` fixture and does not
install or call the Stainless client.

## Contributing and security

See [contributor guidance](https://github.com/ComposioHQ/composio-client/blob/main/CONTRIBUTING.md)
and [the security policy](https://github.com/ComposioHQ/composio-client/blob/main/SECURITY.md).
This package is licensed under [Apache-2.0](https://github.com/ComposioHQ/composio-client/blob/main/LICENSE).
