> ## 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 for most services; for GitHub, organization tools narrowed by agent tools and repositories. |
| **MCP servers** | Any remote server that speaks the Model Context Protocol. | A token you store in a vault. See [Connect MCP servers](/recursion/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 TD
  add["Admin adds the app and picks its tools"] --> active["Admin signs in: connection active"]
  active --> grant["App granted on the agent"]
  grant --> session["Session calls the allowed tools"]
  session --> transcript["Every call and result in the transcript"]
```

<Note>
  Organization users, developers, and admins can view the **Integrations** page, the catalog, and connections. Adding an app, signing in to it, reconnecting or checking it, changing its tools, disconnecting it, and granting apps to an agent need the organization developer or admin role. Console messages that say "ask an admin" mean anyone with the developer or admin role. The billing role has no access to integrations. See [Organizations and roles](/recursion/organizations-and-roles). 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.
</Note>

## The catalog

The built-in catalog has 164 apps, grouped by name below. The console always shows the current catalog, which can grow. Search it from **Add integration** on the **Integrations** page.

<AccordionGroup>
  <Accordion title="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
  </Accordion>

  <Accordion title="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
  </Accordion>

  <Accordion title="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
  </Accordion>

  <Accordion title="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
  </Accordion>

  <Accordion title="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
  </Accordion>
</AccordionGroup>

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. An app that cannot be configured is listed for reference and isn't offered under **Add integration**. The console shows whether its sign-in uses OAuth, a token or key, or either.

## Connect an app

Add the app with the tools agents may use, then 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. On 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>

The app's row now 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, so next, [grant the app to an agent](#grant-apps-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.

## Choose which tools agents may use

Each connection carries a **tool allow-list**: the tools the organization permits agents to use, chosen on the **Integrations** page. An agent can't call a tool that isn't on the list. In standalone Managed Agents, you can narrow this list when assigning the connection to an agent.

* **Start with read-only.** **Select read-only** is the safest starting point. Add write tools only for tasks that need them.
* **The organization list is the ceiling.** Agents inherit all enabled tools unless you select a smaller set for an assignment. An agent's selection never adds tools outside the organization list.
* **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.

<Accordion title="How tools are classified">
  | 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.
</Accordion>

### Change an app's tools

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.

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

<Accordion title="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**.
</Accordion>

## Grant apps to an agent

A connection does nothing until an agent is granted it.

<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="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": "<model-id>",
        "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" }
  ]
}
```

Grants live on the agent version in `built_in_integrations`, so every change publishes a new version. Running sessions keep the version they started with, and [automations](/recursion/automations#keep-the-agent-version-current) keep their pinned version, so a grant you remove still applies to an automation's runs until you move it to the new version.

<Accordion title="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` to inherit the connection's current allow-list. To narrow access, supply 1 to 200 tool names from that list. New sessions intersect the saved selection with the current organization list, so removing a tool at the organization level removes it from agents too.
  * 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](/recursion/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.
</Accordion>

### 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.

<Note>
  Built-in apps need a way out of the sandbox. The agent calls a built-in app's tools with commands inside the sandbox. Recursion allows the hosts a built-in app needs, so its tools work when **Internet access** is **Enabled** or limited to specific hosts. With **No access**, calls to the app fail with a network error. MCP servers, native integrations' MCP tools, `web_search`, and `web_fetch` run outside the sandbox and don't need that. See [Control network access](/recursion/environments#control-network-access).
</Note>

## 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.
```

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. A call to an app you didn't grant, or to a tool that isn't on the allow-list, 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.

<Warning>
  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.
</Warning>

## Test 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 **Test connection**. **Test connection** 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
}
```

When `ok` and `token_minted` are both `true`, 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](#reconnect-an-app).

<Accordion title="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": "usr_01a0867d1b0875be9f9a9938ffb90971",
        "usage": { "agents": 2 },
        "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**.
</Accordion>

## 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.

<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
}
```

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`. Connecting the app again starts from the catalog and creates a new connection, so agents must be granted the new one.

`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.

## Connection states

```mermaid theme={"theme":"css-variables"}
stateDiagram-v2
  [*] --> pending: app added
  pending --> active: signed in
  active --> revoked: sign-in stops working
  revoked --> active: reconnect
  pending --> [*]: disconnect
  active --> [*]: disconnect
  revoked --> [*]: disconnect
```

A new connection starts **Pending setup**, becomes **Active** once someone signs in, and moves to **Revoked** if the sign-in stops working.

<Accordion title="What each state means">
  | 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.
</Accordion>

## 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.

| Service (`id`) | What agents get | Access levels | Where you grant it |
| - | - | - | - |
| GitHub (`github`) | GitHub's MCP tools, called outside the sandbox. No GitHub credential is placed in the sandbox. See [GitHub access](/recursion/github). | Organization tool selection, then an agent subset for all or up to 500 repositories | On the agent, or through a vault |
| Slack (`slack`) | Bot tools for reading channels and threads, looking up users, and posting messages, plus Slack events for [automations](/recursion/automations). | `trigger_thread` (originating thread only), `workspace_tools` (the installed bot's reachable channels) | On the agent, or 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, and LaunchDarkly fix the access level when you connect (`preset_fixed_at_authorization`); to change it, reconnect with the new level. GitHub tools are selected on the connection and narrowed per agent. Slack levels are chosen per grant. An existing Slack installation can be granted either level without reconnecting.

<Accordion title="Test, reconnect, or disconnect a native connection">
  Testing, reconnecting, and disconnecting work the same for native and built-in connections; see [Test a connection](#test-a-connection), [Reconnect an app](#reconnect-an-app), and [Disconnect an app](#disconnect-an-app). A native connection's test also reports `resource_count`, and `missing_permissions` the service hasn't granted yet. For GitHub, `resource_count` is the number of repositories in [the repository list](#list-github-repositories-and-pull-requests). Disconnecting GitHub doesn't uninstall the app from your GitHub account.
</Accordion>

### 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**. For GitHub, see [Connect GitHub](/recursion/github#connect-github) for the existing-installation flow.
  </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>

The connection appears in `listIntegrationConnections` with `state: "active"`. Connecting grants nothing to any agent; next, [grant it to an agent](#grant-a-native-integration-to-an-agent). Connecting GitHub or Slack also adds an [event source](/recursion/automations#receive-events) that automations can listen to.

<Accordion title="Start a connection from the API">
  `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. For a new GitHub installation, send `{"option":"install"}` to open GitHub's App installation page; omit `option` to connect an existing installation. Send `connection_id` to re-authorize an existing connection in place, keeping its id so agents that use it keep working.
</Accordion>

### Grant a native integration to an agent

Access is saved with the agent version, so saving it creates a new version.

<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 services that offer one. For GitHub, select the agent's tools and keep **All authorized repositories** or pick repositories.
    4. Click **Use this access**. For services other than GitHub, the checkbox named for the connection's MCP tools controls whether sessions get them.
    5. Click **Save new version**.
  </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": "<model-id>",
        "system": "Track the release and report blocked work.",
        "nativeIntegrations": [
          {"connectionId": "31e6c9a4-2d75-48b0-a1f3-7c5e9d2b6a84", "permission": "write", "tools": ["get_file_contents", "create_pull_request"], "resources": ["acme/api"]},
          {"connectionId": "a8d2f4c1-6b3e-4f97-8c20-5e1d7b9a3f46", "permission": "read"}
        ]
      }'
    ```
  </Tab>
</Tabs>

Sessions already running keep the access they started with, and automations keep their version until you move them, so removed access still applies to their runs until then. See [Keep the agent version current](/recursion/automations#keep-the-agent-version-current). A new version replaces the whole definition, so send every field you want to keep.

<Accordion title="nativeIntegrations fields and MCP tools">
  | `nativeIntegrations[]` field | Rules |
  | - | - |
  | `connectionId` | Required. An active connection in your organization, listed once. |
  | `permission` | Required for compatibility. For standalone GitHub, the server derives `read` or `write` from `tools`; other services use their access levels. |
  | `tools` | Standalone GitHub only. The agent's subset of the connection's enabled MCP tools. |
  | `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`. |

  `disabled_integration_mcp_providers` lists services whose MCP tools sessions don't get while the grant still works for everything else. GitHub can't be listed there: its MCP tools are how a GitHub grant works, so `github` in the list is ignored. On a new version, leaving it out turns every service's MCP tools on. See [Tools](/recursion/tools#withhold-integration-tools).
</Accordion>

### Grant through a vault

Grant a connection per session through a [vault](/recursion/vaults) with an `integration` credential. Use it when different sessions of one agent need different accounts or repositories.

```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. For Slack, the agent's **Integrations** section grants `trigger_thread` or `workspace_tools` access without a vault credential; a vault grant is also available when access must vary by session.

<Accordion title="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.
</Accordion>

### 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. For an installation on selected repositories, the list is the installation's own repositories, the same list the console's repository pickers show. 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/api/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 pull-request path names the repository owner and repository name separately. `state` is `open` (the default), `closed`, or `all`. Use `full_name` values in `resources`.

## 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](/recursion/mcp-servers) and [Vaults](/recursion/vaults).

## What can go wrong

The most common problems:

* **The row stays Pending setup, or the sign-in couldn't be confirmed.** Nobody finished signing in, or the sign-in link expired or was already used. Click **Connect** on the row again, and finish the sign-in within a few minutes.
* **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).
* **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.
* **A built-in app's tool fails with a network error.** The environment has **No access** to the internet. Set its **Internet access** to **Enabled**, or limit it to specific hosts.

<Accordion title="Every integration error">
  | 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. |
  | **Test connection** reports "the provider could not mint a token for this connection" | The app refused a fresh sign-in, or couldn't be reached. | Run **Test connection** again. If `state` is `revoked`, reconnect. |
  | A built-in app's tool list can't be saved | The list is empty, has more than 200 tools, names a tool the app doesn't have, or includes a write tool under **Read only** access. | Choose 1 to 200 tools from the catalog, or select **Read and write** access. |
  | A revoked app's tools can't be changed | The connection is no longer active. | Reconnect it first. |
  | A shared tool list isn't available | 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, more than 200 selected tools, a blank or no longer enabled tool name, an entry with no `connection_id`, or a connection that 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](/recursion/organizations-and-roles). |
  | The built-in catalog doesn't load | The catalog is temporarily unavailable. | Click **Retry**. |
  | Built-in apps aren't available | 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. |
  | `400 invalid_request`: "repository … is owned by …, but this connection is for …" | A GitHub repository in `resources` or `integration_resources` belongs to a different owner than the connection's account. | Name repositories of the connection's own account, or use the connection for that owner. |
  | A built-in app's tool fails with a network error | The environment has **No access** to the internet. | Set the environment's **Internet access** to **Enabled**, or limit it to specific hosts. |
  | `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 Slack connection isn't granted on the agent or through an accessible vault, or `trigger_thread` was granted but the session didn't start from that Slack installation's thread. | Grant `workspace_tools` for manual, scheduled, or other event-triggered sessions. Use `trigger_thread` for a Slack-origin session. |

  For all error codes, see [Errors](/recursion/errors).
</Accordion>

Limits on connections per app, allow-list size, tool names, apps granted to one agent, catalog search, and session access to an app are in [Limits](/recursion/limits).

## Next steps

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

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

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

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