---
canonical_url: "https://docs.composio.dev/docs/production-readiness"
markdown_url: "https://docs.composio.dev/docs/production-readiness.md"
product: "platform"
version: null
last_updated: null
---

# Move from prototype to production (/docs/production-readiness)

A prototype proves that an agent can connect an account and call a tool. A production integration must also preserve user isolation, survive repeat visits, limit what the agent can do, and give your team enough evidence to diagnose failures.

Use this guide after the [quickstart](/docs/quickstart) works and before customers depend on the integration.

## Production decisions at a glance [#production-decisions-at-a-glance]

| Prototype default                  | Production decision                                                             |
| ---------------------------------- | ------------------------------------------------------------------------------- |
| One sample user such as `user_123` | Stable, immutable application user IDs                                          |
| One project for every test         | Separate projects for environments that must not share credentials or resources |
| A new session on every run         | Persist and reuse session IDs for multi-turn work                               |
| Every toolkit is discoverable      | Restrict toolkits and tools to the product's intended scope                     |
| Managed auth                       | Keep it, or use custom auth for branding, scopes, quotas, or custom providers   |
| API keys in a local `.env` file    | Server-side secret manager and environment-specific keys                        |
| A successful demo call             | Real account, failure-path, webhook, and tenant-isolation tests                 |

## 1. Finalize your identity model [#1-finalize-your-identity-model]

Replace sample values with an immutable ID from your application database. Never use an email address or a shared value such as `default` in production.

For a consumer product, each person usually owns their connections. See [Design a consumer agent](/docs/consumer-agents).

For a B2B product, decide whether connections belong to an individual member or to the customer workspace. See [Design a B2B agent](/docs/b2b-agents).

## 2. Separate environments where it matters [#2-separate-environments-where-it-matters]

Use separate Composio projects for development, staging, and production when those environments must not share API keys, auth configs, connected accounts, webhooks, or logs.

Keep every project API key on the server. Store production keys in your deployment platform's secret manager, rotate them according to your security policy, and never expose them to browser or mobile clients.

For user- or organization-scoped operations, pass `user_api_key` or `org_api_key` to the Python `Composio` constructor, or `userApiKey` or `orgApiKey` in TypeScript. You can also set `COMPOSIO_USER_API_KEY` and `COMPOSIO_ORG_API_KEY`. A project API key is still required by default, and the client sends the scoped key only to operations that require it. To authenticate without a project API key, set `apiKey: null` in TypeScript and pass a user or organization API key, or set `disable_api_key=True` in Python and pass a user API key. A user API key passed this way is sent on every request; an organization API key is still sent only to operations that accept it. In Python, an organization API key still requires a project API key.

> In TypeScript, `apiKey: null` disables project-key fallbacks, including `COMPOSIO_API_KEY` and the CLI config. To keep those fallbacks, omit `apiKey` or pass `undefined`.

For consumer requests, set `org_id` and `project_id` in Python or `orgId` and `projectId` in TypeScript together. See [Run sessions in your organization's consumer project](/docs/consumer-agents#run-sessions-in-your-organizations-consumer-project) for an example.

## 3. Decide how customers authenticate [#3-decide-how-customers-authenticate]

Keep managed auth when its provider app, scopes, and quotas fit your product. It removes OAuth app setup and token-refresh work.

Use a [custom auth config](/docs/authentication/custom-app-vs-managed-app) when you need:

* Your own app name on the provider consent screen
* Different or additional OAuth scopes
* Dedicated provider quotas
* A custom, regional, or self-hosted provider instance
* Customer-specific OAuth credentials

Test both first-time authentication and a returning user whose connected account already exists. If you need a branded flow, follow the [white-labeling guide](/docs/authentication/white-labeling-authentication).

## 4. Bound the agent's authority [#4-bound-the-agents-authority]

Configure sessions with only the toolkits and tools required by the feature. Narrower access improves tool discovery and reduces the number of actions the agent can attempt.

Require explicit user confirmation before destructive or externally visible actions, such as sending a message, creating a purchase, or deleting data. Enforce product authorization before creating or restoring the session, especially for workspace-owned connections.

See [Configuring sessions](/docs/configuring-sessions) for toolkit and tool filters, and [Approvals](/docs/approvals) to have a session ask the user before it runs the tools you choose.

## 5. Persist the right state [#5-persist-the-right-state]

Store the Composio session ID with your conversation or task and reuse it with `composio.use()`. This preserves the session's tool, authentication, and workbench context across turns.

Also store the mapping between your application user, Composio user ID, connected accounts, and tenant when applicable. Your database remains the source of truth for product membership and permissions.

## 6. Test real execution and failure paths [#6-test-real-execution-and-failure-paths]

Run at least one safe, read-only call against a real connected account. Confirm that:

* The tool returns expected data for the correct user
* A second user cannot access the first user's connection
* A missing or expired connection returns the expected recovery flow
* Invalid tool arguments produce a useful error
* The tool call appears in [Logs](/reference/api-reference/logs) with a non-empty log ID

Do not treat a successful Connect Link alone as an end-to-end test. Authentication can succeed while a provider API, scope, or tool input still fails.

### Retry failed executions deliberately [#retry-failed-executions-deliberately]

The SDKs retry reads — fetching tools, listing toolkits, reading a session — when a request times out or comes back `408`, `409`, `429`, or `5xx`. They never retry an execution. `tools.execute`, `tools.proxy`, and every session `execute` and `proxyExecute` call is sent exactly once, because the backend doesn't deduplicate executions and a silent retry after a read timeout would repeat the side effect, such as sending the same email twice.

A transient failure on an execution therefore reaches your code instead of being absorbed. Before retrying one, decide whether repeating that specific call is safe: a read is, `GMAIL_SEND_EMAIL` isn't. When it isn't, check [Logs](/reference/api-reference/logs) or the provider's own API to find out whether the first attempt landed.

### Configure SDK logging [#configure-sdk-logging]

Pass `logger` and `logLevel` to the TypeScript `Composio` constructor to send SDK logs to your logging system. The logger must provide `error`, `warn`, `info`, and `debug` methods. Messages have credential-shaped values redacted before reaching the logger, including owned-client deprecation warnings. Logger configuration is process-wide: the last configured instance controls the SDK logger.

In Python, pass a standard `logging.Logger` as `logger` and a `composio.utils.logging.LogLevel` as `logging_level`. Sensitive values are redacted before records reach the logger. Deprecation notices use `ComposioDeprecationWarning` warnings rather than log records.

### Inspect project usage [#inspect-project-usage]

Use `composio.experimental.usage.summary()` for totals by entity type and `composio.experimental.usage.breakdown()` for grouped usage of one entity type, such as `tool_calls` or `sessions`. Both SDKs scope these queries to the project associated with your API key.

The summary also returns the total Instant charge for the window as an exact USD string: `instantCharge` in TypeScript and `instant_charge` in Python. It returns `"0"` when no usage was charged. Breakdowns contain usage quantities and event counts, not charges.

> Usage metering is experimental, and its response shape may change.

## 7. Verify triggers through your real webhook [#7-verify-triggers-through-your-real-webhook]

If your product uses triggers, test the deployed HTTPS webhook rather than only a local callback. Parse events with the Composio SDK, verify the payload reaches the correct environment, and map it to the expected user or workspace before doing work.

See [Subscribing to trigger events](/docs/setting-up-triggers/subscribing-to-events) for the webhook flow.

## 8. Set security and data-retention expectations [#8-set-security-and-data-retention-expectations]

Review which tool inputs, outputs, logs, and files your integration creates. Align retention with your own product policy and customer commitments. See [Data retention and handling](/docs/security/data-retention) for the controls Composio provides.

Document who can access production projects, rotate keys, manage auth configs, and inspect logs. Keep those permissions limited to the people and services that need them.

## Ready to ship [#ready-to-ship]

* Stable user and tenant IDs are used everywhere.
* Development, staging, and production use the intended project boundaries.
* API keys are server-side and stored in a secret manager.
* Authentication, reconnect, and returning-user flows work.
* Session IDs are persisted for multi-turn work.
* Tool access and confirmation rules match the product's intended authority.
* Cross-user or cross-tenant isolation has been tested.
* Real tool calls and trigger events appear in logs and map to the correct user.
* The team knows how to diagnose and recover from provider or authentication failures.

## Next [#next]

- [Review security](/docs/security/overview): Understand security controls, data retention, and operational boundaries.

- [Configure sessions](/docs/configuring-sessions): Restrict toolkits, tools, auth configs, and connected accounts.

- [Set up triggers](/docs/setting-up-triggers/creating-triggers): Create triggers and receive events through your webhook.

---

📚 **More documentation:** [View all docs](https://docs.composio.dev/llms.txt) | [Changelog](https://docs.composio.dev/docs/changelog.md) | [Glossary](https://docs.composio.dev/llms.mdx/reference/glossary) | [Examples](https://docs.composio.dev/llms.mdx/examples) | [API Reference](https://docs.composio.dev/llms.mdx/reference)

---

# Composio SDK — Notes for AI Code Generators

**Purpose:** Reference for generating current (v3) [Composio](https://composio.dev/) integration code.
**Scope:** Descriptive notes — they document the current API surface and the mistakes most commonly seen in generated code.

---

## 1. Recommended Integration: Sessions

Composio supports two integration modes: **Native Tools** (with a provider package) and **MCP** (no provider package needed).

### Native Tools

```python
# ✅ CORRECT — Python (defaults to OpenAI)
from composio import Composio

composio = Composio()
session = composio.create(user_id="user_123")
tools = session.tools()
# Pass tools to your agent/LLM framework
```

```typescript
// ✅ CORRECT — TypeScript (defaults to OpenAI)
import { Composio } from "@composio/core";

const composio = new Composio();
const session = await composio.create("user_123");
const tools = await session.tools();
// Pass tools to your agent/LLM framework
```

For other providers, pass the provider explicitly. Provider packages follow the naming convention: `composio_<provider>` for Python, `@composio/<provider>` for TypeScript.

### MCP

Use `session.mcp.url` and `session.mcp.headers` with any MCP-compatible client (Claude Desktop, Cursor, OpenAI Agents, etc.). No provider package needed.

---

## 2. Integration Notes

- `composio.create(user_id)` is the standard entry point for agent integrations. Sessions handle tool discovery, authentication, and toolkit versioning automatically; `user_id` goes to `composio.create()` — individual tool calls in session mode don't take one.
- Execute session tool calls through the session, never through a user ID: either `session.execute(tool_slug, arguments=...)` / `session.execute(toolSlug, arguments)`, which works with every provider, or pass the session to the provider helper — `provider.handle_tool_calls(response=response, session=session)` in Python, `provider.handleToolCalls(session, response)` in TypeScript. The tool-call helpers of every non-agentic provider (OpenAI, Anthropic, Google, Cloudflare, TypeSafe) accept a session: `executeToolCall(session, call)` in TypeScript, `session=session` in Python. Agentic providers execute session tools on their own. Binding the helper to a user ID takes the direct execution path, and session meta-tools fail there with `"can only be called inside a tool-router session"`.
- Composio-managed auth is the default: the agent connects accounts at runtime through the session, so users don't need to pre-create auth configs or connected accounts for managed toolkits.
- Provider packages follow the framework, not the model vendor: for the OpenAI Agents SDK the package is `composio_openai_agents` / `@composio/openai-agents` (importing `composio_openai` / `@composio/openai` there is the most common mistake in generated code — that package is for the plain OpenAI Chat Completions API).
- **Direct execution** (`composio.tools.get()`, `composio.tools.execute()`, `provider.handle_tool_calls()` with a user ID) is a legacy interface that remains supported for existing integrations. Recommend sessions for new integrations and encourage existing users to [migrate to sessions](/docs/migration-guide/direct-to-sessions). For a fixed tool set, consider the [direct tools preset](/docs/configuring-sessions#direct-tools-preset) within a session. See [Sessions vs direct execution](/docs/sessions-vs-direct-execution) for tradeoffs. Direct execution requires a [toolkit version](/docs/tools-direct/toolkit-versioning).

---

# 3. Calling the REST API directly

## REST API version

The current REST API version is **v3.1**, served at `https://backend.composio.dev/api/v3.1`. Prefer it for new code and new examples.

`https://backend.composio.dev/api/v3` is the previous version. It is frozen with pinned tool-version defaults and remains supported — existing v3 integrations keep working and do not need to migrate.

## Tool-endpoint version defaults on v3.1

On v3.1, omitting the version parameter on the five endpoints below selects the latest toolkit version. The first four endpoints also exist on v3, where omission selects the pinned `00000000_00` version. `POST /tools/scopes/required` is v3.1-only.

| Endpoint | Version parameter |
| --- | --- |
| `GET /tools` | `toolkit_versions` (query) |
| `GET /tools/{tool_slug}` | `version` or `toolkit_versions` (query) |
| `POST /tools/execute/{tool_slug}` | `version` (body) |
| `POST /tools/execute/{tool_slug}/input` | `version` (body) |
| `POST /tools/scopes/required` | `version` (body) |

A v3.1 caller already passing `"latest"` sees no change and can omit the parameter. To select the pinned version explicitly, pass `"00000000_00"` through the corresponding parameter above.

This version-default change is limited to the five endpoints above.


---

## Terminology Migration (old → current)

If you encounter these terms in error messages, old documentation, or user prompts, translate them to the current equivalents. **Do not use the old terms in generated code or explanations.**

| Old term (v1/v2) | Current term (v3) | In code |
|---|---|---|
| entity ID | user ID | `user_id` parameter |
| actions | tools | e.g., `GITHUB_CREATE_ISSUE` is a *tool* |
| apps / appType | toolkits | e.g., `github` is a *toolkit* |
| integration / integration ID | auth config / auth config ID | `auth_config_id` parameter |
| connection | connected account | `connected_accounts` namespace |
| ComposioToolSet / OpenAIToolSet | `Composio` class with a provider | `Composio(provider=...)` |
| toolset | provider | e.g., `OpenAIProvider` |

If a user says "entity ID", they mean `user_id`. If they say "integration", they mean "auth config". Always respond using the current terminology.

