> ## Documentation Index
> Fetch the complete documentation index at: https://docs.labelbox.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Integrations

> Connect your organization to 164+ apps, choose which tools agents may use in each one, and grant apps to agents so sessions can work in them.

Integrations let agents work in the apps your team already uses. An admin connects an app once for the whole organization and chooses which of its tools agents may use. Then you grant the app to any agent, and its sessions can read and act in that app within those limits.

There are three ways to give an agent access to another service:

| Kind                      | What it covers                                                                                     | How access is controlled                                                                 |
| ------------------------- | -------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
| **Built-in integrations** | 164+ apps, from Salesforce and Zendesk to Snowflake and Jira, picked from a catalog.               | An admin chooses each app's tools once. Agents get only those tools.                     |
| **Native integrations**   | GitHub, Slack, Jira, Confluence, Loom, and LaunchDarkly, connected by signing in to them directly. | An access level, and for GitHub a set of repositories, granted per agent or per session. |
| **MCP servers**           | Any remote server that speaks the Model Context Protocol.                                          | A token you store in a vault. See [Connect MCP servers](/managed-agents/mcp-servers).    |

Most of this page covers built-in integrations. [Native integrations](#native-integrations) and [MCP servers](#mcp-servers) are at the end.

```mermaid theme={"theme":"css-variables"}
flowchart LR
  admin["Admin"] -->|"adds the app and picks its tools"| connection["Connection: pending"]
  connection -->|"admin signs in to the app"| active["Connection: active"]
  active -->|"granted on the agent"| agent["Agent version"]
  agent -->|"session starts"| session["Session"]
  session -->|"short-lived access to the allowed tools"| app["The app"]
  session --> transcript["Transcript: every tool call and result"]
```

## Before you begin

What you can do depends on your organization role. See [Organizations and roles](/managed-agents/organizations-and-roles).

| Action                                                       | Who can do it                              |
| ------------------------------------------------------------ | ------------------------------------------ |
| View the **Integrations** page, the catalog, and connections | Organization users, developers, and admins |
| Add an app, sign in to it, reconnect it, or check it         | Organization developers and admins         |
| Change the tools agents may use in an app                    | Organization developers and admins         |
| Disconnect an app                                            | Organization developers and admins         |
| Grant apps to an agent                                       | Organization developers and admins         |

The billing role has no access to integrations. Console messages that say "ask an admin" mean anyone with the developer or admin role.

You also need an account in the app you're connecting, with the access you want agents to have. Agents act in the app as that account, so use one whose permissions fit the work.

The samples on this page use cURL for the integration operations, which the TypeScript SDK doesn't wrap, and TypeScript for agent operations. The TypeScript samples assume the `rl` client from [Client setup](/managed-agents/api#client-setup).

## The catalog

These are the 164 apps in the built-in catalog, grouped by name. The console always shows the current catalog, which can grow. Search it from **Add integration** on the **Integrations** page, or read it through the API with [`listBuiltInIntegrationCatalog`](#read-the-catalog-through-the-api).

### A–C (34 apps)

ActiveCampaign, Adobe PDF Services, Ahrefs, Airtable, Amadeus, Amazon S3, Amplitude, Anaplan, Apollo, Arize, Articulate Reach 360, Asana, Ashby, Attio, Avalara, Aviationstack, Axiom, BambooHR, Basecamp, bioRxiv, Bitly, Box, Brex, Calendly, Clay, ClickUp, ClinicalTrials.gov, Cloudflare, CMS Coverage, Coda, ComplianceQuest, Confluence, Contentful, Crustdata

### D–G (29 apps)

Databricks, Datadog, Deel, Discord, DoorDash, Dropbox, Duffel, Dynamics 365 Sales, Exa, Expensify, Figma, Firecrawl, Fireflies, FireHydrant, FreshBooks, Freshdesk, Freshservice, Front, Gamma, GitLab, Goldcast, Gong, Google BigQuery, Google Maps, Google Tasks, Grafana, Granola, Greenhouse, Guru

### H–M (22 apps)

Hex, HiBob, HubSpot, Intercom, Jenkins, Jira, Jira Data Center & Server, Jira Service Management, Kintone, Klaviyo, Linear, Looker, Lucidchart, Luma, Make, Metabase, Microsoft Excel, Microsoft Planner, Microsoft Teams, Microsoft Teams GCC High, Miro, Monday.com

### N–R (30 apps)

n8n, Nextech Practice+, Nooks, Notion, NPI Registry, Okta, OneDrive, OneDrive GCC High, OneNote, Oracle HCM, Oracle Sales Cloud, Oracle SCM, Ordinal, Oura, Outlook, PagerDuty, PayPal, Peec AI, Pendo, Pipedrive, Plaud, PostHog, PubMed, Pylon, Quartr, Ramp, ReadMe, Render, Reo.dev, Rootly

### S–Z (49 apps)

Sabre, Salesforce, Sanity, SAP SuccessFactors, SendGrid, Sentry, ServiceNow, SharePoint, SharePoint GCC High, Shopify, Sigma Computing, SmartAdvocate, SmartRecruiters, Smartsheet, Snowflake, Sprout Social, Square, Statsig, Straker, Stripe, Supabase, TeamCity, Teamwork.com, TinyFish, Trello, TripAdvisor, UKG Pro, UKG Pro WFM, Unify, Unily, Vercel, Vestaboard, VisualPing, Warp, Weather, Webflow, WHOOP, Wikipedia, Wix, WordPress, Workday, X, Yelp, YouTrack, Zendesk, Zendesk Sell, Zoho CRM, Zoho Desk, Zoom

When your organization can connect Jira or Confluence as a [native integration](#native-integrations), **Add integration** offers the native connection for that service instead of the built-in one.

## Connect an app

Connecting an app happens in two parts: you add it with the tools agents may use, then you sign in to it. Connecting is done in the console. The API can read, check, change, and disconnect connections, but it can't add one.

<Steps>
  <Step title="Open Add integration">
    In the sidebar, click **Integrations**. Under **Integration connections**, click **Add integration**.
  </Step>

  <Step title="Pick the app">
    Search for the app and click it. An app you already added shows **Already added**. Each app can be added once per organization; to change it later, use its row.
  </Step>

  <Step title="Choose its tools">
    The **Configure** drawer lists every tool the app offers. Each one is marked **Read-only.**, **Changes service data.**, or **Destructive: updates or deletes service data.** Tick the tools agents may use, or use **Select read-only**, **Select all**, or **Clear**. See [Choose which tools agents may use](#choose-which-tools-agents-may-use).
  </Step>

  <Step title="Review and create">
    Click **Review selection**. The review shows how many tools you picked and how many of them can change or delete data in the app. Click **Create 1 connection**.
  </Step>

  <Step title="Sign in to the app">
    The app appears in the table with the state **Pending setup** and the account **Not connected yet**. Click **Connect** on its row. You're sent to the app's sign-in page. Sign in with the account agents should act as, and approve access.
  </Step>

  <Step title="Confirm the connection">
    You come back to **Integrations**, which shows **Finishing connection…** and then **Connected. Assign access in the agent's Integrations section.**
  </Step>
</Steps>

**What success means:** the app's row shows the state **Active**, the **Account** column shows the app's name, and the tool count appears beside it, for example "6 tools, 2 can change data". Only an active connection can be granted to an agent.

Each sign-in started from **Connect** is single-use and short-lived. If you close the sign-in page or take too long, nothing is connected and the row stays **Pending setup**; click **Connect** again.

### Connection states

```mermaid theme={"theme":"css-variables"}
stateDiagram-v2
  [*] --> pending: app added with its tools
  pending --> active: sign-in completes
  active --> revoked: sign-in no longer works
  revoked --> active: reconnect
  active --> active: reconnect or change tools
  pending --> [*]: disconnect
  active --> [*]: disconnect
  revoked --> [*]: disconnect
```

| State     | Console label     | What it means                                                                                                                          |
| --------- | ----------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| `pending` | **Pending setup** | Added, but nobody has signed in to the app yet. Agents can't use it.                                                                   |
| `active`  | **Active**        | Signed in and usable. Agents granted the app can use its allowed tools.                                                                |
| `revoked` | **Revoked**       | The sign-in stopped working, for example because the account's access was removed in the app. Agents can't use it until you reconnect. |

A connection moves to **Revoked** when a session or a check finds that the app no longer accepts the sign-in. A temporary problem reaching the app doesn't change the state.

## Choose which tools agents may use

Each connection carries a **tool allow-list**: the tools that every agent using the app may call. It's one decision for the whole organization, made on the **Integrations** page. An agent can't call a tool that isn't on the list, and it can't narrow the list further for itself.

Every tool in the catalog has a classification:

| Console label                                     | Classification                           | What the tool can do              |
| ------------------------------------------------- | ---------------------------------------- | --------------------------------- |
| **Read-only.**                                    | `read_only: true`, `destructive: false`  | Read data in the app.             |
| **Changes service data.**                         | `read_only: false`, `destructive: false` | Create or change data in the app. |
| **Destructive: updates or deletes service data.** | `destructive: true`                      | Update or delete data in the app. |

A tool marked both read-only and destructive is treated as destructive.

Choose tools with these rules in mind:

* **Start with read-only.** **Select read-only** is the safest starting point. Add write tools only for tasks that need them.
* **The list is shared.** Every agent granted the app gets the same tools. If one agent needs to write and another shouldn't, give the writing agent an MCP server or a separate account instead.
* **The list can't be empty.** A connection always names at least one tool, and at most 200.
* **Changes reach agents on their own.** New sessions always use the new list. A session that's already running may pick it up when its access is next renewed, so don't count on a running session keeping the old list.

### Change an app's tools

<Tabs>
  <Tab title="Console">
    1. In the sidebar, click **Integrations**.
    2. Open the app's row menu and click **Tools**.
    3. Tick or clear tools. The list applies to every agent using the app.
    4. Click **Save tools**. The console confirms that the app's tools were updated for every agent using it.
  </Tab>

  <Tab title="cURL">
    ```bash theme={"theme":"css-variables"}
    curl -X PATCH 'https://api.recursion.labelbox.com/managed-agents/v1/integrations/connections/3c9e1f72-5a4b-4d08-9e6c-1b7f2a8d0e45' \
      -H "Authorization: Bearer $RECURSION_API_KEY" \
      -H 'Content-Type: application/json' \
      -d '{
        "resources": ["get_ticket", "search_tickets", "add_ticket_comment"],
        "permission": "write"
      }'
    ```
  </Tab>
</Tabs>

`updateIntegrationConnectionSelection` replaces the whole allow-list. `resources` names tools from the catalog, from 1 to 200 of them. `permission` sets which tools the list may hold:

* `read`, the default when you omit it: every tool must be read-only. A tool that isn't is refused.
* `write`: any tool in the catalog. This is what the console uses.

Each tool is stored at its own level, `read` for a read-only tool and `write` for anything else, whatever preset you send. A `200` response is the connection. Some fields are left out here.

```json theme={"theme":"css-variables"}
{
  "connection_id": "3c9e1f72-5a4b-4d08-9e6c-1b7f2a8d0e45",
  "external_id": "zendesk",
  "account_login": "Zendesk",
  "account_type": "Zendesk",
  "state": "active",
  "resource_selection": "selected",
  "permissions": {
    "add_ticket_comment": "write",
    "get_ticket": "read",
    "search_tickets": "read"
  },
  "updated_at": "2026-09-25T18:04:11Z"
}
```

The tool names in these examples are illustrative. Use the names the catalog lists for the app.

Changing tools doesn't change the connection's state, and it isn't allowed on a revoked connection; reconnect it first.

### When the catalog changes

The catalog can reclassify or remove a tool after you pick it. A tool you saved as read-only that's no longer classified read-only, or a tool that's no longer in the catalog, blocks the whole app: sessions can't use it until the tool is removed from the list.

The **Tools** drawer shows these tools with a warning and a **Remove** button. Click it, then **Save tools**. Through the API, send the list again without them.

### Read the catalog through the API

`listBuiltInIntegrationCatalog` returns the whole catalog, sorted by app name. Pass `search`, up to 200 characters, to match app names, slugs, and tool names without regard to case. The catalog is the same for every organization and is refreshed about daily. It's listed under **Integrations** in the endpoint reference.

```json theme={"theme":"css-variables"}
{
  "refreshed_at": "2026-09-25T06:00:00Z",
  "connectors": [
    {
      "slug": "zendesk",
      "name": "Zendesk",
      "description": "Customer service and support ticketing.",
      "auth_types": ["OAuth2"],
      "configurable": true,
      "tools": [
        { "name": "add_ticket_comment", "description": "Add a comment to a ticket.", "read_only": false, "destructive": false },
        { "name": "delete_ticket", "description": "Delete a ticket.", "read_only": false, "destructive": true },
        { "name": "get_ticket", "description": "Get one ticket.", "read_only": true, "destructive": false }
      ]
    }
  ]
}
```

An app with `configurable: false` is listed for reference and isn't offered under **Add integration**. `auth_types` says how the app's sign-in page authenticates: `OAuth2`, `Secrets` (you paste a token or key), or both.

## Grant apps to an agent

A connection does nothing until an agent is granted it. Grants live on the agent version in `built_in_integrations`, so every change publishes a new version and running sessions keep the version they started with.

<Tabs>
  <Tab title="Console">
    1. In the sidebar, click **Agents**, then open the agent.
    2. On **Configuration**, find the **Integrations** section.
    3. Click **Add integration access**, search for the app, and click **Select**. A connection that isn't active shows **Reconnect first** instead.
    4. Repeat for each app, and click **Remove** to take one away.
    5. Click **Save new version**.
  </Tab>

  <Tab title="TypeScript">
    ```typescript theme={"theme":"css-variables"}
    const agent = await rl.managedAgents.createAgent({
      body: {
        name: 'Support triage',
        model: 'anthropic/claude-sonnet-5',
        system:
          'You triage new support tickets. Read each ticket, find the customer account, and add an internal comment with a suggested priority.',
        built_in_integrations: [{ connection_id: '3c9e1f72-5a4b-4d08-9e6c-1b7f2a8d0e45' }],
      },
    });
    ```
  </Tab>

  <Tab title="cURL">
    ```bash theme={"theme":"css-variables"}
    curl -X POST 'https://api.recursion.labelbox.com/managed-agents/v1/agents' \
      -H "Authorization: Bearer $RECURSION_API_KEY" \
      -H 'Content-Type: application/json' \
      -d '{
        "name": "Support triage",
        "model": "anthropic/claude-sonnet-5",
        "system": "You triage new support tickets. Read each ticket, find the customer account, and add an internal comment with a suggested priority.",
        "built_in_integrations": [{ "connection_id": "3c9e1f72-5a4b-4d08-9e6c-1b7f2a8d0e45" }]
      }'
    ```
  </Tab>
</Tabs>

A `200` response is the agent. It lists the granted connections. Some fields are left out here.

```json theme={"theme":"css-variables"}
{
  "agent_id": "8d2f4b61-0c3e-4a97-b5d8-7e1a9c6f3b20",
  "latest_agent_version_id": "f4a7c2e9-1b3d-4e68-9a05-6c8e2d7b1f39",
  "name": "Support triage",
  "built_in_integrations": [
    { "connection_id": "3c9e1f72-5a4b-4d08-9e6c-1b7f2a8d0e45" }
  ]
}
```

The rules for `built_in_integrations`:

* Each entry names a connection by `connection_id`. Get ids from `listIntegrationConnections`, or from the row on the **Integrations** page.
* Every connection must be a built-in integration in your organization, be **Active**, and have at least one tool on its allow-list.
* An agent can name at most 25 entries, counted before duplicates are removed. A repeated id is kept once, and the order you send is kept.
* Leave out `tools`. It's reserved, and a non-empty value is refused. The agent always gets the connection's full allow-list.
* The list is part of the definition, so send it on every `createAgentVersion`. Leaving it out publishes a version with no apps. See [Update rules](/managed-agents/agents#update-rules).
* Built-in integrations aren't vault grants. They never appear under an agent's credential access, and `default_vault_ids` and `credential_refs` don't affect them.

### What a session receives

When a session starts, it keeps the list of apps from its agent version for its whole life. For each app, the session gets:

* **Access to the allowed tools only.** The agent can call the tools on the connection's allow-list and nothing else in that app.
* **Short-lived, scoped access.** Access is limited to the apps the agent was granted, expires within an hour, and renews on its own while the session runs. You never handle a token. The access is available to commands in the sandbox, so treat it like anything else the agent can read: it works only for the granted apps and allowed tools, and only until it expires.
* **A skill for using apps.** Sessions of an agent with built-in integrations also load a skill that shows the agent how to find and call app tools. It takes one of the session's 100 skill slots, so if the agent's own skills already fill all 100, the last one is left out.

An app whose connection is no longer active when the session starts is left out of that session. The session still starts.

## How agents use apps in a session

Agents already know how to find the right tool in each granted app, check the inputs it needs, and call it. You don't list tools in the prompt. Describe the outcome you want, and name the app when it matters:

```text theme={"theme":"css-variables"}
Read the Zendesk tickets opened since yesterday. For each one, find the customer's
account in Salesforce and add an internal comment on the ticket with a suggested
priority. Don't change ticket status.
```

In the session:

* Each call to an app appears in the transcript as a command the agent runs, with the app's result below it. The **Tools** tab counts every call.
* The agent can't reach an app you didn't grant or a tool that isn't on the allow-list. A call to anything else fails.
* If an app's sign-in stops working, calls to that app fail and the connection becomes **Revoked** on the **Integrations** page until you reconnect it.

A write or destructive tool on the allow-list can change real data in the app. Say in the task what the agent may and may not change, and keep write tools off the list when the work is read-only.

## Check a connection

A check tries a fresh sign-in with the app, the same way a session does, and reports whether it worked. Use it after connecting, or when an agent can't reach an app.

<Tabs>
  <Tab title="Console">
    1. In the sidebar, click **Integrations**.
    2. Open the app's row menu and click **Check**. **Check** appears only for an active connection.
    3. Read the **Connection check** column: **Connection check passed.**, or the reason it failed.
  </Tab>

  <Tab title="cURL">
    ```bash theme={"theme":"css-variables"}
    curl -X POST 'https://api.recursion.labelbox.com/managed-agents/v1/integrations/connections/3c9e1f72-5a4b-4d08-9e6c-1b7f2a8d0e45/probe' \
      -H "Authorization: Bearer $RECURSION_API_KEY"
    ```
  </Tab>
</Tabs>

`probeIntegrationConnection` answers `200` even when the check fails, so read `ok`, not the status code.

```json theme={"theme":"css-variables"}
{
  "ok": true,
  "state": "active",
  "token_minted": true,
  "resource_count": 3
}
```

**What success means:** `ok` and `token_minted` are both `true`, so a session using the app will get access. For a built-in integration, `resource_count` is the number of tools on the allow-list. A failed check returns `ok: false` and a `failure` reason. If the app no longer accepts the sign-in, `state` comes back `revoked`; reconnect the app.

## List and read connections

`listIntegrationConnections` returns every connection in your organization, native and built-in, including pending and revoked ones. Disconnected connections are left out. `getIntegrationConnection` returns one.

```bash theme={"theme":"css-variables"}
curl 'https://api.recursion.labelbox.com/managed-agents/v1/integrations/connections' \
  -H "Authorization: Bearer $RECURSION_API_KEY"
```

This example shows one built-in connection. Some fields are left out here.

```json theme={"theme":"css-variables"}
{
  "connections": [
    {
      "connection_id": "3c9e1f72-5a4b-4d08-9e6c-1b7f2a8d0e45",
      "external_id": "zendesk",
      "account_login": "Zendesk",
      "account_type": "Zendesk",
      "state": "active",
      "resource_selection": "selected",
      "permissions": { "add_ticket_comment": "write", "get_ticket": "read", "search_tickets": "read" },
      "connected_by": "cku41m9qz0002x8l5d7e3f1ab",
      "usage": { "agents": 2, "automations": 0 },
      "created_at": "2026-09-25T17:41:02Z",
      "updated_at": "2026-09-25T18:04:11Z"
    }
  ]
}
```

For a built-in connection:

* `external_id` is the app's slug from the catalog, and `account_type` is its name. `account_login` is the app's name too, followed by the sign-in method when the app names one.
* `permissions` is the tool allow-list, each tool with its level.
* `usage.agents` counts the agents whose current version grants the app.
* `provider` identifies the kind of connection. Every built-in connection shares one provider, which `listIntegrationProviders` lists as **Built-in integrations**.

## Reconnect an app

Reconnect when a connection is **Revoked**, or to sign in to the app again. Reconnecting keeps the same `connection_id`, so the agents granted the app and its tool allow-list stay as they are.

1. In the sidebar, click **Integrations**.
2. Click **Reconnect** on the app's row. For an active app, it's in the row menu.
3. In the **Reconnect** dialog, click **Continue to sign-in**.
4. Sign in to the app and approve access.

Signing in with a different account changes the account every agent using the app acts as. To keep two accounts separate, disconnect the app and add it again instead.

## Disconnect an app

Disconnecting removes the app's sign-in and stops all access through it. Every agent granted the app loses it from its next session. Connecting the app again starts from the catalog and creates a new connection, so agents must be granted the new one.

<Tabs>
  <Tab title="Console">
    1. In the sidebar, click **Integrations**.
    2. Open the app's row menu and click **Disconnect**.
    3. In the **Disconnect** dialog, click **Disconnect**.
  </Tab>

  <Tab title="cURL">
    ```bash theme={"theme":"css-variables"}
    curl -X DELETE 'https://api.recursion.labelbox.com/managed-agents/v1/integrations/connections/3c9e1f72-5a4b-4d08-9e6c-1b7f2a8d0e45' \
      -H "Authorization: Bearer $RECURSION_API_KEY"
    ```
  </Tab>
</Tabs>

```json theme={"theme":"css-variables"}
{
  "connection_id": "3c9e1f72-5a4b-4d08-9e6c-1b7f2a8d0e45",
  "deleted": true,
  "grants_revoked": 0
}
```

`grants_revoked` counts vault grants that used the connection, which applies to native integrations. It's `0` for a built-in integration, because those are granted on the agent. Agents that still name a disconnected connection show it as **Unavailable connection** in their **Integrations** section. Remove it and save a new version; a version that still names it returns `404 not_found` on `built_in_integrations`.

## Native integrations

Native integrations connect a service by signing in to it directly. Each session gets a short-lived token limited to the access you grant, and the connection's own permissions at the service are always the ceiling.

```mermaid theme={"theme":"css-variables"}
flowchart LR
  connect["Connect an account on Integrations"] --> grant["Grant access: on the agent, or through a vault"]
  grant --> session["Each session"]
  session --> token["Short-lived token for that session"]
  token --> mcp["The service's MCP tools, called outside the sandbox"]
  token --> sandbox["GitHub: git and gh in the sandbox"]
```

| Service (`id`)                                          | What agents get                                                                                                                          | Access levels                                              | Where you grant it               |
| ------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------- | -------------------------------- |
| GitHub (`github`)                                       | Authenticated `git` and `gh` in the sandbox, and GitHub's MCP tools. See [GitHub access](/managed-agents/github).                        | `read`, `write`, `full`, for all or up to 500 repositories | On the agent, or through a vault |
| Slack (`slack`)                                         | Tools that read and reply in the Slack thread that started the session, and Slack events for [automations](/managed-agents/automations). | `trigger_thread`                                           | Through a vault                  |
| Jira (`jira`), Confluence (`confluence`), Loom (`loom`) | The service's MCP tools.                                                                                                                 | `read`, `write`                                            | On the agent, or through a vault |
| LaunchDarkly (`launchdarkly`)                           | LaunchDarkly's MCP tools.                                                                                                                | `reader`, `writer`, `observability`                        | On the agent, or through a vault |

`listIntegrationProviders` returns the same list, with `configured: true` for each service your organization can connect. Jira, Confluence, Loom, LaunchDarkly, and Slack fix the access level when you connect (`preset_fixed_at_authorization`); to change it, reconnect with the new level. GitHub's level is chosen per grant.

### Connect a service

<Steps>
  <Step title="Start the connection">
    In the sidebar, click **Integrations**, click **Add integration**, and click the service. The **Connect** drawer opens. If it shows **Access level**, choose it now. Click **Continue**.
  </Step>

  <Step title="Approve at the service">
    Sign in and approve the request. For GitHub, choose the organization or user account to install the app on, and the repositories it can reach. For Slack, choose the workspace.
  </Step>

  <Step title="Choose an account if asked">
    If the service returns more than one account you can connect, the console shows **Choose an account**. Pick one and click **Continue**.
  </Step>

  <Step title="Confirm">
    Back on **Integrations**, the console shows **Connected. Assign access in the agent's Integrations section.** The row's **State** is **Active**.
  </Step>
</Steps>

**What success means:** the connection appears in `listIntegrationConnections` with `state: "active"`. Connecting grants nothing to any agent. Connecting GitHub or Slack also adds an [event source](/managed-agents/automations#receive-events) that automations can listen to.

To start from code, `startIntegrationInstall` returns the service's authorization `url` and a single-use `state`. Open the `url` in a browser where the same person is signed in to the console; after approval the console completes the connection. The `state` expires after 15 minutes.

```bash theme={"theme":"css-variables"}
curl -X POST 'https://api.recursion.labelbox.com/managed-agents/v1/integrations/jira/install' \
  -H "Authorization: Bearer $RECURSION_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"option": "read"}'
```

```json theme={"theme":"css-variables"}
{
  "url": "https://auth.atlassian.com/authorize?client_id=...&state=...",
  "state": "4c1f0e9a2b7d4e6f8a3c5b1d9e7f2a60"
}
```

`option` is the access level for a service that fixes it at connect time. Send `connection_id` to re-authorize an existing connection in place, keeping its id so agents that use it keep working.

### Grant a native integration to an agent

Access is saved with the agent version, so saving it creates a new version. Sessions already running keep the access they started with.

<Tabs>
  <Tab title="Console">
    1. Open the agent. On **Configuration**, find **Integrations** and click **Add integration access**.
    2. Click **Select** next to the connection. A connection that isn't active shows **Reconnect first**.
    3. In **Configure access**, choose the **Access level**. For GitHub, keep **All authorized repositories** or clear it and pick repositories.
    4. Click **Use this access**. The checkbox named for the connection's MCP tools controls whether sessions get them.
    5. Click **Save new version**.
  </Tab>

  <Tab title="TypeScript">
    ```typescript theme={"theme":"css-variables"}
    await rl.managedAgents.createAgentVersion({
      agent_id: '5f0c2a1e-8b7d-4c3a-9e21-6d4f0b9a7c55',
      body: {
        base_agent_version_id: 'c1a94e07-2f6b-4d18-b3a5-0e7d8c6f4a21',
        name: 'Release coordinator',
        model: 'anthropic/claude-sonnet-4-5',
        system: 'Track the release and report blocked work.',
        nativeIntegrations: [
          { connectionId: '31e6c9a4-2d75-48b0-a1f3-7c5e9d2b6a84', permission: 'write', resources: ['acme/api'] },
          { connectionId: 'a8d2f4c1-6b3e-4f97-8c20-5e1d7b9a3f46', permission: 'read' },
        ],
        disabled_integration_mcp_providers: ['github'],
      },
    });
    ```
  </Tab>

  <Tab title="cURL">
    ```bash theme={"theme":"css-variables"}
    curl -X POST 'https://api.recursion.labelbox.com/managed-agents/v1/agents/5f0c2a1e-8b7d-4c3a-9e21-6d4f0b9a7c55/versions' \
      -H "Authorization: Bearer $RECURSION_API_KEY" \
      -H 'Content-Type: application/json' \
      -d '{
        "base_agent_version_id": "c1a94e07-2f6b-4d18-b3a5-0e7d8c6f4a21",
        "name": "Release coordinator",
        "model": "anthropic/claude-sonnet-4-5",
        "system": "Track the release and report blocked work.",
        "nativeIntegrations": [
          {"connectionId": "31e6c9a4-2d75-48b0-a1f3-7c5e9d2b6a84", "permission": "write", "resources": ["acme/api"]},
          {"connectionId": "a8d2f4c1-6b3e-4f97-8c20-5e1d7b9a3f46", "permission": "read"}
        ],
        "disabled_integration_mcp_providers": ["github"]
      }'
    ```
  </Tab>
</Tabs>

| `nativeIntegrations[]` field | Rules                                                                                                                                       |
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `connectionId`               | Required. An active connection in your organization, listed once.                                                                           |
| `permission`                 | Required. One of the service's access levels. For a service that fixed its level at connect time, it must match.                            |
| `resources`                  | GitHub only. Leave it out to reach every repository the installation reaches, including ones added later, or list 1 to 500 as `owner/name`. |

A new version replaces the whole definition, so send every field you want to keep. `disabled_integration_mcp_providers` lists services whose MCP tools sessions don't get while the grant still works for everything else; a new agent defaults to `["github"]`. On a new version, leaving it out turns every service's MCP tools on. See [Tools](/managed-agents/tools#withhold-integration-tools).

### Grant through a vault

Grant a connection per session through a [vault](/managed-agents/vaults) with an `integration` credential. Use it when different sessions of one agent need different accounts or repositories, and for Slack, which is always granted this way. In the console, grant it from the agent's or session's **Credential access**.

```bash theme={"theme":"css-variables"}
curl -X POST 'https://api.recursion.labelbox.com/managed-agents/v1/vaults/26d4b1a8-73c9-4f60-8a15-9e2c7b5d4f31/credentials' \
  -H "Authorization: Bearer $RECURSION_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
    "credential_type": "integration",
    "integration_connection_id": "31e6c9a4-2d75-48b0-a1f3-7c5e9d2b6a84",
    "integration_permission": "read",
    "integration_resources": ["acme/api"]
  }'
```

Adding the same connection to the same vault again replaces the earlier grant. If `integration_permission` is left out, the service's least privileged level applies.

**How grants combine in a session:**

* A session gets the agent version's `nativeIntegrations` plus any `integration` credentials in its granted vaults.
* Each connection is used once. If the agent and a vault both grant it, the agent's grant wins. Among vaults, the first in `vault_ids` wins.
* Several connections to one service, such as two GitHub organizations, all work in one session.
* A grant whose connection isn't active is skipped, and the session starts without it.

### List GitHub repositories and pull requests

For a GitHub connection, list the repositories it can reach and their pull requests, for example to fill a repository picker. Results come from GitHub, 100 per page, with the 1-based `page` query parameter.

```bash theme={"theme":"css-variables"}
curl 'https://api.recursion.labelbox.com/managed-agents/v1/integrations/connections/31e6c9a4-2d75-48b0-a1f3-7c5e9d2b6a84/repositories?page=1' \
  -H "Authorization: Bearer $RECURSION_API_KEY"
curl 'https://api.recursion.labelbox.com/managed-agents/v1/integrations/connections/31e6c9a4-2d75-48b0-a1f3-7c5e9d2b6a84/repositories/acme%2Fapi/pull-requests?state=open&page=1' \
  -H "Authorization: Bearer $RECURSION_API_KEY"
```

```json theme={"theme":"css-variables"}
{
  "repositories": [
    { "id": 812345671, "name": "api", "full_name": "acme/api", "private": true, "default_branch": "main" }
  ],
  "total_count": 12,
  "page": 1
}
```

The repository can be `name`, `owner/name` (URL-encoded as `owner%2Fname`), or end in `.git`. `state` is `open` (the default), `closed`, or `all`. Use `full_name` values in `resources`.

**Checking, reconnecting, and disconnecting** work the same for native and built-in connections; see [Check a connection](#check-a-connection), [Reconnect an app](#reconnect-an-app), and [Disconnect an app](#disconnect-an-app). A native connection's check also reports `resource_count`, such as how many repositories it reaches, and `missing_permissions` the service hasn't granted yet. Disconnecting GitHub doesn't uninstall the app from your GitHub account.

## MCP servers

For a service that isn't in the catalog, or when you want the token kept out of the sandbox entirely, add its MCP server to the agent. The token lives in a vault and is attached to requests outside the sandbox. See [Connect MCP servers](/managed-agents/mcp-servers) and [Vaults](/managed-agents/vaults).

## What can go wrong

| Symptom or code                                                                                  | Cause                                                                                                                                                 | Fix                                                                                                                      |
| ------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| An app isn't in **Add integration**                                                              | You already added it (it shows **Already added**), it's listed with `configurable: false`, or your organization connects it as a native integration.  | Use its existing row, pick another app, or add the native connection.                                                    |
| **Add integration** is missing                                                                   | Your role can view integrations but not add them.                                                                                                     | Ask someone with the developer or admin role.                                                                            |
| "The vendor sign-in was closed before it finished. Nothing was connected."                       | You left the app's sign-in page.                                                                                                                      | Click **Connect** on the row again.                                                                                      |
| "the vendor sign-in could not be confirmed"                                                      | The sign-in link or its code expired, or was already used.                                                                                            | Click **Connect** on the row again, and finish the sign-in within a few minutes.                                         |
| The row stays **Pending setup**                                                                  | Nobody finished signing in to the app.                                                                                                                | Click **Connect** on the row.                                                                                            |
| The row shows **Revoked**, or the agent says the app must be reconnected                         | The app no longer accepts the sign-in.                                                                                                                | [Reconnect the app](#reconnect-an-app).                                                                                  |
| **Reconnect first** in the agent's **Add integration access**                                    | The connection isn't active.                                                                                                                          | Connect or reconnect it on **Integrations**.                                                                             |
| The **Tools** drawer warns that stored tools are no longer read-only or no longer in the catalog | The catalog changed after you chose the tools. Sessions can't use the app until they're removed.                                                      | Click **Remove**, then **Save tools**.                                                                                   |
| The agent says a tool isn't available                                                            | The tool isn't on the app's allow-list, or the app isn't granted to the agent.                                                                        | Add the tool on **Integrations**, or grant the app and start a new session.                                              |
| Check reports "the provider could not mint a token for this connection"                          | The app refused a fresh sign-in, or couldn't be reached.                                                                                              | Check again. If `state` is `revoked`, reconnect.                                                                         |
| `400 invalid_request` on `resources`                                                             | The list is empty, has more than 200 tools, names a tool the app doesn't have, or has a tool that isn't read-only under the `read` preset.            | Send 1 to 200 tool names from the catalog, or use `permission: "write"`.                                                 |
| `400 invalid_request` on `permission`                                                            | The preset isn't `read` or `write`.                                                                                                                   | Send `read`, `write`, or leave it out.                                                                                   |
| `400 invalid_request` on `connection_id` when changing tools                                     | The connection is revoked.                                                                                                                            | Reconnect it first.                                                                                                      |
| `422 provider_selection_unsupported`                                                             | The connection is a native integration, whose access is set per agent.                                                                                | Set the access level on the agent instead.                                                                               |
| `400 invalid_request` on `built_in_integrations`                                                 | More than 25 apps, `tools` isn't empty, an entry has no `connection_id`, or a connection isn't active, has no tools, or isn't a built-in integration. | Read `message`, then fix that entry.                                                                                     |
| `404 not_found` on `built_in_integrations` or `connection_id`                                    | The connection doesn't exist, was disconnected, or belongs to another organization.                                                                   | List connections and use a current id.                                                                                   |
| `403 forbidden`                                                                                  | Your role can't make this change.                                                                                                                     | See [Organizations and roles](/managed-agents/organizations-and-roles).                                                  |
| `503 built_in_catalog_unavailable`                                                               | The catalog couldn't be loaded.                                                                                                                       | Retry. The console shows **Retry**.                                                                                      |
| `503 built_in_integrations_unconfigured`                                                         | The app catalog couldn't be reached for your organization.                                                                                            | Contact support.                                                                                                         |
| `503 integrations_unconfigured`                                                                  | The integration service couldn't be reached.                                                                                                          | Retry later. If it continues, contact support.                                                                           |
| "This organization has to approve the app before it can be connected."                           | A GitHub organization requires an owner to approve new apps.                                                                                          | After an owner approves, start the connection again.                                                                     |
| "This install link is no longer valid." (`400` on `state`)                                       | The `state` was already used, is older than 15 minutes, or belongs to another person.                                                                 | Start the connection again.                                                                                              |
| `400 invalid_request` on `nativeIntegrations` or `integration_permission`                        | A connection is listed twice, the level isn't one the service offers, or it differs from the level fixed at connect time.                             | Use a level from `permissions`, or reconnect with the level you need.                                                    |
| `400 invalid_request` on `integration_resources`                                                 | Resources for a service other than GitHub, or more than 500 repositories.                                                                             | Leave `resources` out, or name fewer repositories.                                                                       |
| `429 github_rate_limited` or `503 github_unavailable` listing repositories                       | GitHub is limiting requests or unavailable.                                                                                                           | Retry later.                                                                                                             |
| Slack tools are missing in a session                                                             | The session didn't start from a Slack thread, or the Slack connection isn't granted through a vault.                                                  | Start it from a Slack [automation](/managed-agents/automations), and grant the connection through **Credential access**. |

For all error codes, see [Errors](/managed-agents/errors).

## Limits

| Limit                                | Value                                                                |
| ------------------------------------ | -------------------------------------------------------------------- |
| Connections per app                  | 1 per organization                                                   |
| Tools on one app's allow-list        | 1 to 200                                                             |
| Tool name                            | Up to 128 characters                                                 |
| Apps granted to one agent            | 25                                                                   |
| Catalog `search`                     | Up to 200 characters                                                 |
| Session access to an app             | Expires within an hour, and renews on its own while the session runs |
| Skill slots a session with apps uses | 1 of 100                                                             |

See [Limits](/managed-agents/limits) for every other limit.

## Next steps

<CardGroup cols={2}>
  <Card title="Agents" href="/managed-agents/agents">
    Publish agent versions and keep grants across updates.
  </Card>

  <Card title="Connect MCP servers" href="/managed-agents/mcp-servers">
    Add any remote MCP server with a token from a vault.
  </Card>

  <Card title="Security" href="/managed-agents/security">
    See what reaches the sandbox for each kind of access.
  </Card>

  <Card title="Sessions" href="/managed-agents/sessions">
    Start a session and follow its tool calls.
  </Card>
</CardGroup>
