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

# Connections

> One API over every third-party app an account has connected — list them, run their tools, call their APIs directly, and gate every write behind an approval.

**A connection is a third-party account your Crevio account has linked — a Gmail inbox, a Notion workspace, a Slack team, an ad account.** The Connections API is one surface over all of them: the same endpoints list, inspect, run, rename, and disconnect every family, and the transport underneath never appears in the contract.

Under the hood a connection is one of several things — an OAuth app connection, a remote MCP server, a social or ads channel — but you address all of them the same way, by a single unified id.

| Endpoint                         | Purpose                                                                                     |
| -------------------------------- | ------------------------------------------------------------------------------------------- |
| `GET /connections`               | Searchable catalog of everything connectable, each flagged with whether you've connected it |
| `GET /connections/connected`     | Only what this account has connected                                                        |
| `POST /connections/connect`      | Mint a hosted OAuth link to send a user through                                             |
| `GET /connections/{id}/tools`    | The tools a connection exposes, each with its approval policy                               |
| `POST /connections/{id}/execute` | Run one of those tools                                                                      |
| `POST /connections/{id}/proxy`   | Call the app's own API directly, for endpoints no tool covers                               |
| `PATCH /connections/{id}`        | Give the connection a nickname                                                              |
| `DELETE /connections/{id}`       | Disconnect it                                                                               |

## The unified id

Every connection carries one id, returned by `GET /connections` and `GET /connections/connected`. **Treat it as opaque** — store it and pass it back verbatim wherever an endpoint takes `{id}`. Don't parse it or build one yourself; its internal shape is not part of the contract.

Alongside it, `kind` tells you which family the connection belongs to — an app connection, a remote MCP server, a social or ads channel, or a native integration. Compare that value if you need to branch, but you rarely do: listing, inspecting, and disconnecting work identically across families. Only `execute` and `proxy` care, and they tell you when you've reached for the wrong one.

## Connecting an app

`POST /connections/connect` mints a **hosted** OAuth link — no frontend SDK required. Send the user to `redirect_url`; when they come back, the connection shows up in `GET /connections/connected`.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.crevio.co/v1/connections/connect \
    -H "Authorization: Bearer YOUR_API_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{ "toolkit": "gmail", "callback_url": "https://yourapp.com/connected" }'
  ```

  ```typescript SDK theme={null}
  import { Crevio } from "@crevio/sdk";

  const crevio = new Crevio({ apiKey: process.env["CREVIO_API_KEY"] ?? "" });

  const link = await crevio.connections.connect({
    toolkit: "gmail",
    callbackUrl: "https://yourapp.com/connected",
  });
  // → send the user to link.redirectUrl
  ```
</CodeGroup>

<Note>
  `proxy` and the rename endpoint are new; the TypeScript SDK picks them up on its next generated release. Call them over REST in the meantime — every other connection operation is already available as `crevio.connections.*`.
</Note>

Pass `scope: "user"` to make the connection **personal** to the calling user rather than shared with the whole account. Personal connections are invisible to everyone else on the account — use them when each teammate links their own inbox, and the default (team) when the business links its one Notion.

## What's connected

```bash theme={null}
curl https://api.crevio.co/v1/connections/connected \
  -H "Authorization: Bearer YOUR_API_TOKEN"
```

```json theme={null}
{
  "object": "list",
  "data": [
    {
      "object": "connection",
      "id": "<connection_id>",
      "kind": "<family>",
      "label": "work gmail",
      "status": "active",
      "disconnectable": true,
      "callable": true,
      "connected_at": "2026-08-01T09:12:44Z"
    }
  ],
  "has_more": false
}
```

| Field            | Meaning                                                                          |
| ---------------- | -------------------------------------------------------------------------------- |
| `id`             | Opaque handle — what every other endpoint takes as `{id}`                        |
| `kind`           | The connection's family. Opaque; compare it, don't parse it                      |
| `label`          | What to show a human — the nickname if one is set, otherwise the app's own label |
| `status`         | `active`, or `error` when the upstream grant is gone or broken                   |
| `callable`       | Whether an agent can act through this connection at all                          |
| `disconnectable` | Whether `DELETE` applies                                                         |

<Note>
  **`status: "error"` does not mean the connection is gone.** The record survives — along with its approval policies and its place in your UI — so the user can reconnect the same app rather than setting it up from scratch. Calls through it fail until they do. Surface it as "needs reconnecting", not "disconnected".
</Note>

## Running a tool

Most apps expose a fixed set of actions. `GET /connections/{id}/tools` lists them with the approval policy each one resolves to for this account:

```json theme={null}
{
  "object": "connection_tool",
  "name": "gmail-send-email",
  "label": "Send Email",
  "description": "Send an email from your Gmail account",
  "policy": "always_ask",
  "default_policy": "always_ask"
}
```

`policy` is what will actually happen; `default_policy` is what Crevio classified the tool as before any account override. Both are one of:

| Policy         | Behavior on `execute`             |
| -------------- | --------------------------------- |
| `always_allow` | Runs immediately, `200`           |
| `always_ask`   | Freezes a pending approval, `202` |
| `off`          | Refused, `403 tool_disabled`      |

Reads generally classify as `always_allow` and writes as `always_ask`. Account owners override any tool from the dashboard's approval settings.

```bash theme={null}
curl -X POST https://api.crevio.co/v1/connections/$CONNECTION_ID/execute \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "tool": "gmail-send-email", "arguments": { "to": "sam@example.com", "subject": "Hi" } }'
```

`arguments` is passed through to the provider verbatim — Crevio does not reshape it. Get the exact field names from the tool's own schema rather than guessing.

### When a call needs approval

A `202` is not an error. It means the action was **frozen intact**, and the response body is the [Approval](/docs/developer/api-reference/introduction) that's now waiting:

```json theme={null}
{
  "object": "approval",
  "id": "iapr_9fQ2xk",
  "status": "pending",
  "action_type": "connections.execute",
  "title": "Send Email on work gmail",
  "app_slug": "gmail",
  "tool_name": "gmail-send-email"
}
```

Resolve it with `POST /approvals/{id}/approve` or `/deny`. Approving replays the exact frozen request — you don't re-send it yourself, and re-sending it creates a second approval rather than executing the first.

## Calling an app's API directly

Tools cover the common actions. When the thing you need isn't among them — an unusual endpoint, a field mask, a filter the action can't express — `POST /connections/{id}/proxy` makes an **authenticated HTTP request to the app's own API** on the connection's behalf.

```bash theme={null}
curl -X POST https://api.crevio.co/v1/connections/$CONNECTION_ID/proxy \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "endpoint": "/gmail/v1/users/me/messages?q=is:unread newer_than:1d",
    "method": "GET"
  }'
```

| Field      | Notes                                                                                       |
| ---------- | ------------------------------------------------------------------------------------------- |
| `endpoint` | A path relative to the app's own API base, or an absolute URL on the app's domain. Required |
| `method`   | `GET` (default), `POST`, `PUT`, `PATCH`, `DELETE`                                           |
| `body`     | Sent verbatim. Ignored on `GET`                                                             |
| `headers`  | Extra headers forwarded upstream                                                            |

<Warning>
  **Never send a credential.** The connected account's token is injected server-side and is never held by Crevio nor exposed to the caller. `Authorization`, `Host`, `Cookie`, `Accept-Encoding`, `Content-Length`, and `Connection` are stripped from `headers` — an `Authorization` you set yourself would fight the injected credential rather than authenticating anything.
</Warning>

### Reading the response

```json theme={null}
{
  "object": "connection_proxy_response",
  "connection": "<connection_id>",
  "endpoint": "/gmail/v1/users/me/messages?q=is:unread newer_than:1d",
  "status": 200,
  "truncated": false,
  "body": { "messages": [] }
}
```

**`status` is the app's status code, not Crevio's.** A `200` from this endpoint with `"status": 404` in the body means the call succeeded and the app answered "not found" — that's an answer to read, not a failure to retry. Crevio's own HTTP status only reports whether the request could be made at all.

`truncated: true` means the app's response exceeded 256 KB and `body` was cut. Paginate or narrow the query rather than working from a partial payload.

### Governance

The proxy is gated exactly like a tool call, keyed on the **method**: reads run now, writes freeze a `202` approval, and an account override still wins. That's what stops the raw-request path from being a way around the approval a named tool would have asked for.

<Tip>
  **Prefer a real tool when one exists.** It validates its inputs and reads far better in the approval card a human has to decide on. Reach for the proxy when nothing covers the endpoint.
</Tip>

### What can't be proxied

| Family                | Response                                                                      |
| --------------------- | ----------------------------------------------------------------------------- |
| Remote MCP servers    | `409 use_execute` — they speak tools, not HTTP. Use `/tools` and `/execute`   |
| Social & ads channels | `409 use_native_endpoints` — operate them through `/v1/socials` and `/v1/ads` |

## Naming a connection

With two Gmails linked, "work gmail" and "support gmail" is what makes a connection addressable by a human — and what an agent echoes back when it asks which account to act on.

```bash theme={null}
curl -X PATCH https://api.crevio.co/v1/connections/$CONNECTION_ID \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "nickname": "work gmail" }'
```

The nickname is up to 60 characters and becomes the connection's `label`. Send an empty string to clear it and fall back to the provider's own label. Only app connections can be renamed — every other family takes its name from a record the provider owns, and a rename there would be overwritten on the next sync (`422 not_renameable`).

## Disconnecting

```bash theme={null}
curl -X DELETE https://api.crevio.co/v1/connections/$CONNECTION_ID \
  -H "Authorization: Bearer YOUR_API_TOKEN"
```

Revokes upstream where the provider supports it and prunes the local record either way.

## Errors

Beyond the [standard error codes](/docs/developer/guides/errors), these are specific to connections:

| Status | Code                     | Meaning                                                                      |
| ------ | ------------------------ | ---------------------------------------------------------------------------- |
| `403`  | `tool_disabled`          | The account turned this tool — or raw requests to this connection — off      |
| `404`  | —                        | No connection with that id on this account                                   |
| `409`  | `use_execute`            | MCP connections are driven through `/execute`                                |
| `409`  | `use_native_endpoints`   | Social and ads connections have typed `/v1` endpoints                        |
| `409`  | `onboarding_incomplete`  | The account is still in setup; third-party integrations aren't available yet |
| `422`  | `not_callable`           | This connection can't be acted on                                            |
| `422`  | `not_renameable`         | Only app connections carry a nickname                                        |
| `502`  | `execution_failed`       | The call reached the app and the app failed                                  |
| `503`  | `feature_not_configured` | Connections aren't configured for this workspace                             |

<CardGroup cols={2}>
  <Card title="Approvals" icon="circle-check" href="/docs/developer/api-reference/introduction">
    Resolve the pending actions that gated writes freeze.
  </Card>

  <Card title="Agents overview" icon="robot" href="/docs/developer/guides/agents-overview">
    How connections become capabilities an agent can use on a schedule.
  </Card>
</CardGroup>
