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

# Multiple Accounts: Gateway Selection Protocol

> How agents pick between several connected accounts of one app: the 409 disambiguation response, the connection id header, and the retry contract.

A project can connect several accounts of the same app — two Gmail inboxes, a personal and an org GitHub — and an agent can hold [grants](/docs/guides/agent-access) for any subset of them. That raises a question the gateway can't answer alone: when an agent calls `gmail.googleapis.com`, **which** account should serve the request?

This page documents the protocol that resolves it. The exchange happens on the agent's ordinary proxied traffic, not on the management API.

## The flow

```mermaid theme={null}
sequenceDiagram
    participant A as Agent
    participant G as Gateway
    participant P as Provider API

    A->>G: GET /gmail/v1/users/me/messages
    G-->>A: 409 multiple_connections + choices
    A->>G: same request + x-onecli-connection-id: conn_9f2c1b
    G->>P: request with conn_9f2c1b's credentials
    P-->>G: 200
    G-->>A: 200 + x-onecli-connections header
```

1. The agent sends a normal API request through the gateway.
2. If more than one granted account could serve it, the gateway answers `409` with the list of choices instead of guessing.
3. The agent retries the **identical** request with the `x-onecli-connection-id` header naming one choice.
4. The gateway injects that account's credentials and forwards.

When only one granted account matches, none of this happens — the request forwards directly.

## The 409 responses

Two variants, same retry contract. **Same app, several accounts:**

```json theme={null}
{
  "error": "multiple_connections",
  "message": "Multiple connections exist for this provider. Specify which one to use with the x-onecli-connection-id header.",
  "connections": [
    {
      "id": "conn_9f2c1b",
      "label": "Support inbox",
      "provider": "gmail",
      "display_name": "Gmail"
    },
    {
      "id": "conn_4a77d0",
      "label": "Founders",
      "provider": "gmail",
      "display_name": "Gmail"
    }
  ],
  "header": "x-onecli-connection-id",
  "example": "x-onecli-connection-id: conn_9f2c1b"
}
```

**Different apps matching the same request** (for example, two integrations that both serve a shared API host):

```json theme={null}
{
  "error": "multiple_providers",
  "message": "Multiple app integrations are connected that can handle this API request. If you can determine the correct provider from context, specify it using the x-onecli-connection-id header. Otherwise, ask the user which provider to use.",
  "connections": [
    {
      "id": "conn_18c3e5",
      "label": null,
      "provider": "google-drive",
      "display_name": "Google Drive"
    },
    {
      "id": "conn_b02f91",
      "label": null,
      "provider": "google-docs",
      "display_name": "Google Docs"
    }
  ],
  "header": "x-onecli-connection-id",
  "example": "x-onecli-connection-id: conn_18c3e5"
}
```

Both carry `x-should-retry: false`: retrying **unchanged** will not succeed. The fix is adding the header — pick a `connections[].id` (use `label` and `display_name` to decide, or ask the user) and resend.

## Retrying with a choice

```bash theme={null}
curl https://gmail.googleapis.com/gmail/v1/users/me/messages \
  -H "x-onecli-connection-id: conn_9f2c1b"
```

The header names a connection for **this request only** — there is no sticky selection. Agents typically remember the id for a task and send it on every related call.

## Stale ids: the 404

If the header names a connection that no longer exists (deleted, or the grant was revoked), the gateway answers `404` with the current choices — note there is no `example` field on this one:

```json theme={null}
{
  "error": "connection_not_found",
  "message": "Connection 'conn_old123' was not found or has been removed. Choose from the available connections.",
  "connections": [
    {
      "id": "conn_9f2c1b",
      "label": "Support inbox",
      "provider": "gmail",
      "display_name": "Gmail"
    }
  ],
  "header": "x-onecli-connection-id"
}
```

Re-pick from `connections` and retry.

## Discovering accounts up front

Successfully forwarded responses advertise the accounts that could serve that request in the `x-onecli-connections` response header — a JSON array of the same choice objects. An agent can read it from any successful call and start sending `x-onecli-connection-id` before ever hitting a 409.

## Per-account permissions

Grants and policy decisions are **per account**. The same request can be blocked on one connection and allowed on another — for example, `send_email` set to allow on the support inbox but ask on the founders account. The gateway evaluates the selected account's grant, so switching the header can change the verdict. The per-account view lives at `GET /v1/policy/effective-app-permissions?provider=gmail&connectionId=conn_9f2c1b`.

## In the SDK

The [Node SDK](/docs/sdks/node) exports the header names and a typed parser for these bodies (`parseGatewayError`, `CONNECTION_ID_HEADER`, `CONNECTIONS_HEADER`), so agent-side code can implement the retry without string-matching.

## Related

* [Gateway errors](/docs/api-reference/gateway-errors) — every error body the gateway returns on proxied traffic
* [Agent access](/docs/guides/agent-access) — granting accounts to agents
