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

# Connectors & connected accounts

> A connector is a third-party integration a user connects once — Gmail, Microsoft 365, GitHub, Adobe, Slack, Stripe, Notion, … A connected account is that user's live credential row. Tool agents then act *as that user*: google-gmail reads the mailbox of whoever owns the ozk_ key that made the …


A **connector** is a third-party integration a user connects **once** — Gmail, Microsoft 365, GitHub, Adobe, Slack, Stripe, Notion, … A **connected account** is that user's live credential row. Tool agents then act *as that user*: `google-gmail` reads the mailbox of whoever owns the `ozk_` key that made the call. Nothing about the credential ever appears in the API.

Three things are easy to confuse:

| Thing | What it is | Where |
|---|---|---|
| **Tool agent** (`google-gmail`) | code that calls a provider's API on behalf of a user | tools / agents planes |
| **Connector** (`google`, `github`, …) | the OAuth app or API-key slot a user connects | Studio → Integrations |
| **Connector proxy provider** (`brave-search`, `firecrawl`, …) | a metered pass-through to a provider's REST API with your own or the platform's key | `api.plungeai.com/v1/connect/…` (7.5) |

## 7.1 Two credential tiers

| Tier | How the user connects | Examples | Stored as |
|---|---|---|---|
| **OAuth** | clicks *Continue with <Provider>* in Studio, approves at the provider, lands back connected | Google Workspace, Microsoft 365, Adobe, GitHub, GitLab, Slack, Salesforce, HubSpot, Bitbucket, Shopify | encrypted access + refresh token; refreshed server-side when `tokensExpire` |
| **API key / PAT** | pastes a key in the connector dialog | Anthropic, OpenAI, Stripe, Notion (internal token), GitLab PAT, Linear, Jira, Datadog, … | encrypted key; **verified live before storage** (`core/core-secrets/api-key-verify.ts` probes the provider — a rejected key is never saved) |

Both land in `user_credentials` on the compliance database, AES-256-GCM encrypted with per-scope keys; every read is audited (`core/core-secrets/CLAUDE.md`). Agents never receive raw tokens from callers — the platform injects them at run time, which is why `params` names such as `api_key` or `token` are stripped by contract validation.

## 7.2 How a connected account flows into an API call

```
your app ──ozk_ key──► One API ──user_id──► engine ──► google-gmail agent
                                                          │ resolveApiKey(google, user)
                                                          ▼
                                            user_credentials row?  yes → call Gmail as the user
                                                                   no  → outcome: needs_connection
```

Without a connection the agent answers with a structured refusal (`success: false`, `outcome: needs_connection`) and the tools plane returns `424 connection_required`:

```json
{ "error": { "code": "connection_required", "message": "Connect your Google account in Studio → Integrations to use google-gmail", "outcome": "needs_connection", "connect": { "provider": "google", "method": "oauth", "link": "POST /v1/connections/google/link" } } }
```

`connect` is the way in: the connector (`provider`), how it connects (`method`: `oauth`, `api_key` or `wallet`) and the route that starts it ([7.3](#73-how-your-testers-connect-an-account)). Every `424` from the tools, agents, workflows and Agent Call routes carries it when the agent is connection-tier; the Markdown and text wires print it as `connect: POST /v1/connections/<provider>/link` under the execution footer. `credential_required` (`needs_api_key`) carries it the same way.

The same run **with** a connection returns `200 { ok: true, content }` — live example from 2026-09-18 with a Microsoft 365 connection on the calling account (`microsoft-todo`, operation `list_lists`, request id `00000000-0000-4000-8000-000000000003`).

Which agents need what: the `credential` field on every card (`GET /v1/tools/<id>`, the lists, `GET /v1/discovery/search`) — `{ "tier": "platform" }` or `{ "tier": "connection", "provider", "method" }`; the **Credential** column in [the tool catalog](/one-api/tools#65-the-catalog-by-category-live-2026-09-18) is that field. The rule of thumb: `google-*` needs the `google` connector (except the key-based tool APIs `google-books`, `google-translate`, `google-places-agent`, `google-cloud-iot-agent`), `microsoft-*` needs Microsoft 365 (connector id `azure`), `adobe-*` Adobe (not `adobe-pdf-services`), `gitlab-agent` GitLab, `stripe-agent` a Stripe key; search/scrape/document tools run on platform keys.

## 7.3 How your testers connect an account

Connecting is an API call. The `ozk_` key that makes the call owns the connection, and no Studio login is needed:

| | Call | Answer |
|---|---|---|
| **Bring a key** | `POST /v1/connections/:provider` · `{ "api_key", "api_secret"?, "on_behalf_of"? }` | `201 { provider, on_behalf_of, status: "connected" }`; `400 credential_rejected` when the provider refuses the key (providers with a live probe — GitLab today — are checked before anything is stored); `404 unknown_provider` |
| **Send a hosted link** | `POST /v1/connections/:provider/link` · `{ "on_behalf_of"?, "return_url"? }` | `201 { url, expires_at }` |
| **List** | `GET /v1/connections?on_behalf_of=` | `200 { connections: [ { provider, on_behalf_of, method, status, connected_at } ] }` — status only; no token, key or hint is ever returned |
| **Disconnect** | `DELETE /v1/connections/:provider?on_behalf_of=` | `204`; `404 connection_not_found` |

The hosted link is `https://studio.plungeai.com/connect/<token>`: signed, **single-use**, valid **15 minutes**, and it works without a Studio session — send it to the person who owns the account. The page opens with who is asking — your Studio name, else your email — and says that your agents will act on the account on their behalf until they disconnect it. For an OAuth provider the page is *Continue with <Provider>* → approve at the provider → back to your `return_url` (an `https` URL you gave, with `?connected=<provider>` added) or, without one, a confirmation page. Where Studio has no OAuth app for the provider the page is a key form: the pasted key is checked and stored the same way as `POST /v1/connections/:provider`. A tampered or expired link is `404`, a used one `410`.

```bash
curl -s -X POST "https://api.plungeai.com/v1/connections/google/link" \
  -H "Authorization: Bearer $PLUNGE_API_KEY" -H "Content-Type: application/json" \
  -d '{"return_url":"https://example.com/done"}'
```

**Your customers (`on_behalf_of`).** Name a customer with any id you own — 1 to 128 characters of `A-Z a-z 0-9 _ . -` (no `..`, no `:`) — and the connection is that customer's, not yours: it is stored under your key and your customer id, so two developers who use the same id never collide, `GET /v1/connections` shows only the account you ask for (yours, or the one `on_behalf_of` names) and nobody can reach a customer of another key. An empty `on_behalf_of` is refused, never read as your own account.

To run an agent as a customer, send the same id on the call: `POST /v1/execute` with `on_behalf_of: cust_42` next to `agent:`. A connection-tier agent then uses the account connected under that id (and answers `424` with `connect` when that customer has none) — never yours; the run, its billing and a saved agent stay yours, and the result is returned to you as usual. It is a wire key, like `format`: it never reaches the agent as a field. It is accepted on `POST /v1/execute` only: the per-id doors (`/v1/tools/{id}/execute`, `/v1/agents/{id}/execute`, `/v1/workflows/{id}/execute` and their stream variants) answer `400 invalid_body` rather than quietly running as you.

Provider ids are the connector ids of [7.4](#74-the-connector-catalog) — the `credential.provider` of a card or the `connect.provider` of a `424` names the one to use (`google`, `azure` for Microsoft 365, `github`, `slack`, `stripe`, …).

The same connection can be made in **Ocean Studio** (`https://studio.plungeai.com`), where a signed-in user connects their own account in a browser:

1. Studio → **Integrations** → **Browse connectors** → pick the provider.
2. OAuth connector: **Continue with <Provider>** → approve → back in Studio as *Connected*. API-key connector: paste the key → it is probed live → *Connected* (or an inline "rejected" error).
3. Verify with the API: run the agent with the same user's `ozk_` key — the `needs_connection` outcome disappears.

The Studio endpoints behind those buttons (session cookie, not API key — listed so you know what happens, not for you to call): `GET /api/connections` (yours), `GET /api/connections/providers` (public: every OAuth provider and its scopes), `GET /api/auth/oauth/:provider/start`, `POST /api/connections/api-key` (`400` when the live probe rejects the key), `DELETE /api/connections/:provider`. Special flows exist for the Circle agent wallet (`/api/connections/circle-agent-wallet/init|verify`), retailer logins such as Amazon (`/api/connections/retailer/connect`) and QR/browser logins such as WhatsApp (`/api/connections/browser-session/*`) — those connect in Studio only.

```bash
curl -s "https://studio.plungeai.com/api/connections/providers"
```

Redirect URIs and OAuth app registration are platform-side — testers never configure anything at the provider.

## 7.4 The connector catalog

The registry lists **802 connector cards** (`GET /v1/discovery/search?kind=connectors&limit=100`, page with `offset` until `total`; live 2026-09-29). Every card carries `connector.auth` — never null:

| `connector.auth` | Meaning | Count |
|---|---|---|
| `oauth_static` | a platform-registered OAuth app; user clicks Continue | 46 |
| `api_key` | user pastes a key/PAT | 83 |
| `oauth_dcr` | an MCP server the user connects with Dynamic Client Registration (see [MCP plane](/one-api/mcp)) | 519 |
| `none` | platform-key or public provider — nothing to connect. Also what a card reads when its source declared no auth (57 of the 154 today: 35 community mirrors and 22 legacy tool cards) | 154 |

```bash
curl -s "https://api.plungeai.com/v1/discovery/search?kind=connectors&limit=3" \
  -H "Authorization: Bearer $PLUNGE_API_KEY"
```

```bash
curl -s "https://api.plungeai.com/v1/discovery/cards/connector/slack" \
  -H "Authorization: Bearer $PLUNGE_API_KEY"
```

**OAuth apps registered on the platform today** (client id wired in the Studio worker, `wrangler-templates/template.wrangler.ocean.jsonc` ↔ `core/core-secrets/oauth-providers.ts`): `google` (Workspace: Drive, Gmail, Calendar, Contacts, Tasks, Docs, Sheets, Slides, Analytics, Search Console, Merchant Center, Business Profile, YouTube; `google-youtube` also connects YouTube on its own), `azure` (Microsoft 365: OneDrive, Outlook, Calendar, Teams, SharePoint, To Do, Planner, OneNote, Office files), `adobe` (Creative Cloud, Express, Photoshop, Lightroom, Stock, Experience Platform, Journey Optimizer), `github`, `gitlab`, `bitbucket`, `slack`, `salesforce`, `hubspot`, `shopify`. Provider entries also exist for `sonos`, `spotify`, `tesla`, `facebook`, `linkedin`, `twitter`, `reddit`, `tiktok`, `pinterest`, `snapchat`, `intuit`, `square`, `zoom`, `sentry`, `notion` — their cards show in Studio, but the OAuth app is not registered yet, so *Continue with …* bounces with `oauth_not_configured`; connect those with an API key where the card offers one.

By category (ids as the registry spells them):

| Group | Connectors |
|---|---|
| **AI & ML** | `openai`, `anthropic`, `huggingface`, `ibm` |
| **Google** | `google` (Workspace umbrella), `google-drive`, `google-gmail`, `google-calendar`, `google-contacts`, `google-tasks`, `google-youtube` |
| **Microsoft** | `azure` (Microsoft 365 umbrella), `microsoft-onedrive`, `microsoft-outlook-mail`, `microsoft-outlook-calendar`, `microsoft-teams`, `microsoft-sharepoint`, `microsoft-365` |
| **Adobe** | `adobe`, `adobe-express`, `adobe-photoshop`, `adobe-lightroom`, `adobe-stock`, `adobe-cc-files`, `adobe-aep`, `adobe-ajo`, `adobe-workfront` |
| **Developer & cloud** | `github`, `gitlab`, `bitbucket`, `postman`, `aws`, `cloudflare`, `vercel`, `netlify`, `supabase`, `databricks`, `snowflake` |
| **Observability** | `sentry`, `datadog`, `grafana-mcp-server`, `honeycomb`, `incident-io`, `logrocket-mcp`, `jam` |
| **Communication** | `slack`, `discord`, `telegram`, `twilio`, `sendgrid`, `mailchimp`, `resend`, `whatsapp`, `whatsapp-personal` |
| **Productivity & PM** | `notion`, `airtable`, `asana`, `jira`, `linear`, `monday`, `clickup`, `trello`, `basecamp`, `teamwork`, `craft`, `mem`, `roam-research`, `ticktick`, … (28 productivity + 19 project-management cards) |
| **CRM, sales & marketing** | `salesforce`, `hubspot`, `pipedrive`, `zoho`, `close`, `helpscout`, `intercom`, `attio`, `apollo-io`, `klaviyo`, `lemlist`, `outreach`, `salesloft`, `zoominfo`, … (49 marketing-and-sales cards) |
| **Payments & finance** | `stripe`, `paypal`, `square`, `plaid`, `xero`, `circle`, `circle-agent-wallet`, `x402-wallet`, `intuit`, `intuit-quickbooks`, `intuit-payments`, `intuit-payroll`, `intuit-time-tracking`, `airwallex` |
| **Data & analytics** | `amplitude`, `mixpanel`, `segment`, `looker`, `alpha-vantage` |
| **Search & scraping (platform-key providers)** | `brave-search`, `exa`, `firecrawl`, `crawl4ai`, `browserbase`, `scrapingbee`, `serpapi`, `serper`, `perplexity-search`, `news-api`, `nyt`, `arxiv`, `wikipedia`, `product-hunt`, `weather`, `wolfram-alpha` |
| **Social** | `facebook`, `instagram`, `threads`, `linkedin`, `twitter`, `reddit`, `pinterest`, `snapchat`, `tiktok`, `youtube`, `bereal`, `clubhouse` |
| **Commerce & travel** | `shopify`, `amazon`, `target-app`, `shop`, `instacart`, `expedia`, `booking`, `tripadvisor`, `uber`, `viator`, `kiwi-com`, `amadeus`, … |
| **Legal, security, research, lifestyle** | 13 legal, 15 security, 8 research and 11 lifestyle MCP-server connectors (`oauth_dcr` / `none`) |

## 7.5 The connector proxy — `/v1/connect/:provider/proxy/*`

For providers you would otherwise call directly, the inference gateway offers a **metered pass-through** (`inference/gateway/src/routes/connector-proxy.ts`): one `ozk_` key, one bill, transparent bytes in both directions (streaming included, no body transformation).

| | |
|---|---|
| **Key** | your `ozk_…` platform key (Dashboard → One API → Keys, `https://dashboard.plungeai.com`); a legacy `sk-conn-…` key is still accepted until it is revoked and replaced |
| **Route** | `ANY https://api.plungeai.com/v1/connect/<provider_id>/proxy/<upstream path>` — the path after `/proxy/` is appended to the provider's `base_url` |
| **Mode** (from the `connector_providers` rate card) | `byok` — your own upstream token connected for the org is used, you pay `byok_routing_fee`; `platform` — the platform's key is used, you pay `price_per_call`; `both` — your token if connected, else the platform's |
| **No connection, provider is BYOK-only** | `402` "connection required" |
| **Unknown or inactive provider** | `404 model_not_found` / `400 provider_unavailable` |
| **Limits** | per-provider `rate_limit_rpm`; org spend caps and guardrails apply exactly as on the models plane |
| **Auth failures** | `401 invalid_api_key`; a suspended or closed account is `401 Account suspended` |

Example (one metered provider call, so it carries no `# verify` marker):

```bash
curl "https://api.plungeai.com/v1/connect/brave-search/proxy/res/v1/web/search?q=cloudflare+workers" \
  -H "Authorization: Bearer $PLUNGE_API_KEY"
```

Each call is metered as a `connector_requests` row and appears in the Dashboard next to your inference spend. Prefer the tools plane when you want the agent's judgement (result formatting, pagination, outcome vocabulary); use the proxy when you want the provider's raw API with one key and one bill.
