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

# Connections

> Let an agent act through third-party apps — OAuth or API key — without ever touching the token.

<Info>**Available now.** The connection layer is a first-party Vetta capability ("Passport").</Info>

A connection authorizes an identity to act inside a third-party app — helpdesks, issue trackers, email, chat, CRMs, docs, and over a thousand more — over OAuth or a scoped API key. Once connected, the agent reaches the app's tools through Vetta's connection tooling and **never handles the raw credential**; Vetta stores it, injects it at call time, and refreshes managed OAuth tokens server-side.

**There is no list of supported apps to check against.** The catalogue is read live, so the first step of any setup is a search:

```bash CLI theme={"system"}
vetta identity connections apps --search helpdesk
# -> zendesk · Zendesk · managed_oauth/oauth · 12 operations
```

## Two objects

Every connection is built from two objects. Keeping them separate is what lets one authorized app serve many identities under different policies.

* **Auth config** — *how* an app authenticates. It pins the auth method (managed OAuth, your own OAuth app, or an API key), the **scopes**, any custom app credentials, and — the part that matters most — the **operations a connection may call**. Anything not pinned does not exist to the agent. One auth config per app is typically shared across the org.
* **Connected account** — a **per-identity** instance of that auth config: the actual authorization for one identity, with its own lifecycle, status, and token. This is what a `connection_call` runs against.

```text theme={"system"}
auth config (connector: "zendesk", scopes, pinned tools)
   └── connected account  → identity "ava"    (active)
   └── connected account  → identity "sales"  (initiated)
```

## Connecting

An end user authorizes through a hosted **Connect Link**. There is no non-interactive path: OAuth needs the user's consent, and an API-key connector asks for the key on the same hosted form so it never travels through a Vetta request body. Either way the result is a connected account bound to one identity.

<Steps>
  <Step title="Find the app">
    Search the catalogue, then read the one you want whole — it tells you which auth methods work, the operations pinned by default, and anything connecting will still ask for.

    ```bash CLI theme={"system"}
    vetta identity connections apps --search helpdesk
    vetta identity connections app zendesk
    ```
  </Step>

  <Step title="Create the auth config (once per app)">
    Managed OAuth needs no credentials of your own; bring a custom OAuth app or API key when you need it. Pin the operations with `--tool` to narrow the surface below the app's default set.

    ```bash CLI theme={"system"}
    vetta identity connections config-add --connector zendesk --auth managed_oauth
    ```
  </Step>

  <Step title="Start a connection">
    Initiate a connected account for an identity. Vetta returns a hosted **Connect Link** the end user opens to authorize — see the caveat on `connect_link` below about whose domain serves it.

    ```bash CLI theme={"system"}
    vetta identity connect --identity idn_... --auth-config ac_... --field subdomain=acme
    # -> the hosted authorization page for this connected account
    ```

    ```bash cURL theme={"system"}
    curl https://api.vetta.sh/v1/connections \
      -H "Authorization: Bearer $VETTA_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{ "identity": "idn_...", "auth_config_id": "ac_...", "fields": { "subdomain": "acme" } }'
    # -> { "id": "ca_8f2c", "status": "initiated",
    #      "connect_link": "https://…", "connect_link_expires_at": "…" }
    ```
  </Step>

  <Step title="Authorize">
    The user completes OAuth (or submits the API key) at the Connect Link and is returned to your dashboard, which reads the account and says what happened. The connected account moves from `initiated` to `active` on its own.

    <Note>**Poll, do not wait for a webhook.** `GET /v1/connections/{id}` reconciles against the provider on read, so re-reading the connection *is* the refresh. There is no `connection.*` webhook yet, so a program that needs to know when a person finished authorizing polls this route.</Note>
  </Step>

  <Step title="Use it">
    The identity's agent lists the tools available on the connected account and invokes them.

    ```bash CLI theme={"system"}
    vetta identity connections list --identity ava
    ```
  </Step>
</Steps>

## The agent can start this itself

The four steps above are the operator's path. An agent has the same flow as three tools, offered to
every session whose agent has an identity — so "connect our Zendesk" is something you can say to the
agent rather than something you have to go and do for it.

| Tool                  | What it does                                                                                                                                                                                                                                       |
| --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `connections.search`  | Searches the catalogue by name, category or what an app does, and returns each app's id. There are over a thousand apps and the list changes without a release of ours, so there is nothing to memorize — the agent searches.                      |
| `connections.connect` | Starts an authorization for this agent's identity and returns the **Connect Link**. The agent hands that link to the person; it cannot authorize on their behalf. Reuses the org's existing auth config for the app rather than creating a second. |
| `connections.status`  | Reads whether the person finished. `active` means the account's tools are offered from the next turn on.                                                                                                                                           |

<Note>**The consent screen is the approval.** `connections.connect` produces a URL and nothing else: no credential exists and no tool is gained until a human opens it and authorizes at the third party. If you want a gate earlier than that, these register as ordinary tool names, so `"connections.connect": { "permission": "ask" }` in the agent's `tools.configs` parks the session for approval like any other tool.</Note>

<Note>**Tools arrive on the next turn.** The session resolves its connected accounts when a turn starts, so an account authorized mid-conversation is usable on the turn after it reads `active` — which is what `connections.status` is for.</Note>

## Lifecycle

A connected account moves through a small set of states. Managed OAuth tokens are **refreshed server-side**, so an `active` account keeps working without your involvement.

<Steps>
  <Step title="initiated">Connect Link issued; awaiting the user's authorization.</Step>
  <Step title="active">Authorized and usable. Tokens refresh automatically in the background.</Step>
  <Step title="failed">Authorization or a refresh failed. `status_reason` carries the cause (denied scope, expired grant, revoked app).</Step>
  <Step title="disconnected">Revoked by the user or by you. The stored credential is dropped and calls are refused.</Step>
</Steps>

## Calling tools

The agent works through Vetta's connection tools, never the raw credential:

* **`connection_search`** — discover the tools available on a connected account.
* **`connection_call`** — invoke a specific tool with arguments.
* **`manage_connections`** — a meta-tool for listing connected accounts, checking status, and starting a new connection from inside a run.

```jsonc theme={"system"}
// what the agent emits, conceptually:
connection_call({
  connection: "conn_8f2c",   // a connected account
  tool: "send_message",
  args: { to: "customer@example.com", subject: "Refund", body: "Done." }
})
```

## Governance

The **auth config's pinned tools are the first governance layer, and they are enforced at the provider**: an operation the config was not pinned to is refused before it is dispatched. Above that, which apps an identity may connect and which of the pinned tools are callable is controlled by [policy](/docs/identity/policies): `open`, an **allowlist**, or a **blocklist**, plus per-connection approval on sensitive actions. Connecting a new app is approval-gated by default. Every call is priced against your [budget](/docs/concepts/budgets) and drawn from the [organization balance](/docs/platform/billing).

## Pricing

Connection usage is metered per event. These are the rates you are charged:

| Meter                   | Rate                           |
| ----------------------- | ------------------------------ |
| Managed tool call       | \$0.0003 per `connection_call` |
| Connected account       | \$0.10 each (managed account)  |
| Trigger / webhook event | \$0.003 per event              |

A run that discovers and sends one message costs a few managed tool calls — on the order of \$0.001 — plus the one-time \$0.10 for the connected account. Spend appears as connection line items in the [agent's breakdown](/docs/concepts/budgets) and the [organization ledger](/docs/platform/billing).

## Field reference

<ParamField path="connector" type="string" required>
  The app to connect, by its catalogue id — e.g. `zendesk`, `linear`, `intercom`. Search `GET /v1/connections/apps` for it; there is no fixed set. The five category words that used to be the only accepted values (`email`, `chat`, `tracker`, `crm`, `docs`) still resolve, onto the apps they always meant.
</ParamField>

<ParamField path="tools" type="string[]">
  The operations a connection made from this auth config may call. Omitted, the app's own featured set is pinned. This is a boundary, not a filter — anything outside it is refused at the provider.
</ParamField>

<ParamField path="fields" type="object">
  Values the app needs before authorization can start — an account subdomain, a region, a workspace. Read them from the app or the auth config (`connect_fields`); leave them out and the hosted page asks the person instead.
</ParamField>

<ParamField path="auth" type="string" default="managed_oauth">
  Auth method on the **auth config**: `managed_oauth` (Vetta's OAuth app), `oauth` (your own OAuth app credentials), or `api_key`.
</ParamField>

<ParamField path="scopes" type="string[]">
  Scopes requested at authorization. Narrower scopes reduce what a connected account can do.
</ParamField>

<ParamField path="identity" type="string" required>
  The identity a **connected account** is bound to. One auth config can back many identities.
</ParamField>

<ParamField path="status" type="string">
  Connected-account state: `initiated`, `active`, `failed`, or `disconnected`.
</ParamField>

<ParamField path="status_reason" type="string">
  Present on `failed` — the cause (denied scope, expired grant, revoked app).
</ParamField>

<ParamField path="connect_link" type="string">
  The hosted authorization URL the end user opens to authorize. Present while `initiated`, and it expires — read `connect_link_expires_at` and mint a new one rather than caching it.

  <Warning>**This page is not on a Vetta domain today.** The hosted authorization page is served by the connection provider, on the provider's own host, and it carries the provider's name in the page title, the heading and a "Secured by" badge. Serving it from `connect.vetta.sh` is not offered on the current plan; the provider's own white-labelling covers a logo, an app title and a theme — set in their dashboard, with no API — and removes the badge only when you bring your own OAuth app for that connector. Treat the connect link as a page you hand to an end user, not as part of your product's surface, until this changes.</Warning>
</ParamField>

<Card title="Next: the vault" icon="key" href="/docs/identity/vault">
  Secrets the agent can use but never see.
</Card>
