# Connected apps API

> The apps each user connects through a sign-in link, and the apps they may choose from.

Send `Authorization: Bearer vk_test_...` on every call.

## `GET /v1/apps`

Its own page: [GET /v1/apps](https://vestra.ai/developers/reference/api/connected-apps/get-v1-apps.md)

The apps your users may connect, after your allowlist.

Your API key, scope `runtime` or `all`. On the card: [`connect`](https://vestra.ai/developers/connect.md). Every call here is a checked sample.

### Request

Query:

- `limit` (`integer`): How many items per page, 1 to 100. Default 20.
- `cursor` (`string`): Where to go on from: the `next_cursor` of the page before, unchanged. Absent: the first page.
- `search` (`string`): Only apps whose name holds these words.
- `category` (`string`): Only apps of this category.
- `deployment_id` (`string`): Only the apps this deployment allows (its `apps`), by its `dep_` id or name. Absent: the company default's.

### Answers 200

- `data` (`object[]`): The items on this page.
  - `object` (`"app"`): Always `app`.
  - `slug` (`string`): The app's name in calls: what `app` takes when you connect it. Example: `gmail`.
  - `name` (`string`): The app's name, as people read it. Example: `Gmail`.
  - `description` (`string`): What the app is, in a line.
  - `category` (`string | null`): What kind of app it is, such as `email`; null when it has none.
  - `logo_url` (`string`): The app's logo, an image URL.
  - `connectable` (`boolean`): false when Vestra cannot connect it yet.
  - `no_auth` (`boolean`): true when it needs no sign-in: its tools just run.
- `next_cursor` (`string | null`): Send it as `cursor` for the next page; null on the last page.

### Code

**TypeScript**

```ts
const catalog = await vestra.connect.catalog();
```

**Python**

```python
catalog = vestra.connect.catalog()
```

**CLI**

```bash
vestra connect --catalog
```

**HTTP**

```bash
# Get a sign-in link for one app. It is for one user: send them its url.
curl "$VESTRA_BASE_URL/v1/connections" \
  -H "Authorization: Bearer $VESTRA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "user_id": "traveller-42", "app": "gmail" }'

# List their apps. You can also list the catalog your users may pick from.
curl "$VESTRA_BASE_URL/v1/connections?user_id=traveller-42" \
  -H "Authorization: Bearer $VESTRA_API_KEY"

curl "$VESTRA_BASE_URL/v1/apps?search=calendar" \
  -H "Authorization: Bearer $VESTRA_API_KEY"

# Disconnect an app. user_id says whose connection it is.
curl -X DELETE "$VESTRA_BASE_URL/v1/connections/con_7hs?user_id=traveller-42" \
  -H "Authorization: Bearer $VESTRA_API_KEY"
```

**MCP**

```json
{
  "method": "tools/call",
  "params": {
    "name": "connect",
    "arguments": { "user": "traveller-42", "app": "gmail" }
  }
}
```

## `POST /v1/connections`

Its own page: [POST /v1/connections](https://vestra.ai/developers/reference/api/connected-apps/post-v1-connections.md)

Get a connect link for one app, for one user (user_id) or the company's own Bash. After consent the user lands on your return_url (or your first allowed one, or our own page), and a webhook fires. The link carries the id of the connection it makes.

Your API key, scope `runtime` or `all`. On the card: [`connect`](https://vestra.ai/developers/connect.md). Every call here is a checked sample.

### Request

Headers:

- `Idempotency-Key` (`string`): Send the same key to retry this call safely for 24 hours.

Body:

- `user_id` (`string`): Whose Bash: your id for the user. Absent: your company's own Bash. Example: `traveller-42`.
- `app` (`string`, required): The app to connect, by its slug from GET /v1/apps. Example: `gmail`.
- `return_url` (`string (URL)`): Where the user lands after they sign in; it must match one of your allowed return URLs. Absent: your first allowed return URL, or our own page. Limits: at most 2048 characters. Example: `https://app.example.com/connected`.
- `deployment_id` (`string`): The deployment whose `apps` decide whether this app is allowed, by its `dep_` id or name. Absent: the company default's. Limits: 1 to 128 characters.

### Answers 200 or 201

- `object` (`"connect_link"`): Always `connect_link`.
- `id` (`string | null`): The connection this link makes (`con_`), to read with GET /v1/connections/{connection_id}; null only when there is none to make (an app with no sign-in).
- `status` (`"pending" | "connected"`): `pending`: send the user to `url`. `connected`: they already had this app, or it needs no sign-in.
- `url` (`string | null`): The sign-in link: send it to your user, or open it yourself for your company's own Bash. Null when there is nothing to sign in to.
- `connection` (`object | null`): The connection when it is already made; null while it is pending.
  - `object` (`"connection"`): Always `connection`.
  - `id` (`string`): The connection's id. Example: `con_8f2k`.
  - `user_id` (`string | null`): Your id for the user; null for your company's own Bash.
  - `app` (`string`): The app, by its slug. Example: `gmail`.
  - `status` (`"active" | "pending" | "initializing" | "expired" | "revoked" | "inactive" | "failed"`): `active`: Bash can use it. `pending` or `initializing`: the sign-in is not finished. `expired`, `revoked`, `inactive` or `failed`: it no longer works, and the user connects it again.
  - `label` (`string | null`): The account's label, usually its email; null when the app gives none.
  - `created_at` (`string`): When it was connected, as an ISO 8601 date and time in UTC.
- `expires_at` (`string`): When the link stops working, as an ISO 8601 date and time in UTC.

### Code

**TypeScript**

```ts
const link = await vestra.user('traveller-42').connect('gmail');
```

**Python**

```python
link = vestra.user("traveller-42").connect("gmail")
```

**CLI**

```bash
vestra connect gmail --user traveller-42
```

**HTTP**

```bash
# Get a sign-in link for one app. It is for one user: send them its url.
curl "$VESTRA_BASE_URL/v1/connections" \
  -H "Authorization: Bearer $VESTRA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "user_id": "traveller-42", "app": "gmail" }'

# List their apps. You can also list the catalog your users may pick from.
curl "$VESTRA_BASE_URL/v1/connections?user_id=traveller-42" \
  -H "Authorization: Bearer $VESTRA_API_KEY"

curl "$VESTRA_BASE_URL/v1/apps?search=calendar" \
  -H "Authorization: Bearer $VESTRA_API_KEY"

# Disconnect an app. user_id says whose connection it is.
curl -X DELETE "$VESTRA_BASE_URL/v1/connections/con_7hs?user_id=traveller-42" \
  -H "Authorization: Bearer $VESTRA_API_KEY"
```

**MCP**

```json
{
  "method": "tools/call",
  "params": {
    "name": "connect",
    "arguments": { "user": "traveller-42", "app": "gmail" }
  }
}
```

## `GET /v1/connections`

Its own page: [GET /v1/connections](https://vestra.ai/developers/reference/api/connected-apps/get-v1-connections.md)

The connected apps of one user (user_id), or of the company's own Bash.

Your API key, scope `runtime` or `all`. On the card: [`connect`](https://vestra.ai/developers/connect.md). Every call here is a checked sample.

### Request

Query:

- `limit` (`integer`): How many items per page, 1 to 100. Default 20.
- `cursor` (`string`): Where to go on from: the `next_cursor` of the page before, unchanged. Absent: the first page.
- `user_id` (`string`): Whose Bash: your id for the user. Absent: your company's own Bash.

### Answers 200

- `data` (`object[]`): The items on this page.
  - `object` (`"connection"`): Always `connection`.
  - `id` (`string`): The connection's id. Example: `con_8f2k`.
  - `user_id` (`string | null`): Your id for the user; null for your company's own Bash.
  - `app` (`string`): The app, by its slug. Example: `gmail`.
  - `status` (`"active" | "pending" | "initializing" | "expired" | "revoked" | "inactive" | "failed"`): `active`: Bash can use it. `pending` or `initializing`: the sign-in is not finished. `expired`, `revoked`, `inactive` or `failed`: it no longer works, and the user connects it again.
  - `label` (`string | null`): The account's label, usually its email; null when the app gives none.
  - `created_at` (`string`): When it was connected, as an ISO 8601 date and time in UTC.
- `next_cursor` (`string | null`): Send it as `cursor` for the next page; null on the last page.

### Code

**TypeScript**

```ts
const connections = await vestra.connect.list();
```

**Python**

```python
connections = vestra.connect.list()
```

**CLI**

```bash
vestra connect
```

**HTTP**

```bash
# Get a sign-in link for one app. It is for one user: send them its url.
curl "$VESTRA_BASE_URL/v1/connections" \
  -H "Authorization: Bearer $VESTRA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "user_id": "traveller-42", "app": "gmail" }'

# List their apps. You can also list the catalog your users may pick from.
curl "$VESTRA_BASE_URL/v1/connections?user_id=traveller-42" \
  -H "Authorization: Bearer $VESTRA_API_KEY"

curl "$VESTRA_BASE_URL/v1/apps?search=calendar" \
  -H "Authorization: Bearer $VESTRA_API_KEY"

# Disconnect an app. user_id says whose connection it is.
curl -X DELETE "$VESTRA_BASE_URL/v1/connections/con_7hs?user_id=traveller-42" \
  -H "Authorization: Bearer $VESTRA_API_KEY"
```

**MCP**

```json
{
  "method": "tools/call",
  "params": {
    "name": "connect",
    "arguments": { "user": "traveller-42", "app": "gmail" }
  }
}
```

## `GET /v1/connections/{connection_id}`

Its own page: [GET /v1/connections/{connection_id}](https://vestra.ai/developers/reference/api/connected-apps/get-v1-connections-connection-id.md)

One connection's status. user_id names whose it is.

Your API key, scope `runtime` or `all`. On the card: [`connect`](https://vestra.ai/developers/connect.md). Over HTTP it is made from the contract: fill in each {…} and every value.

### Request

Path:

- `connection_id` (`string`, required): The connected app's id (`con_...`).

Query:

- `user_id` (`string`): Whose Bash: your id for the user. Absent: your company's own Bash.

### Answers 200

- `object` (`"connection"`): Always `connection`.
- `id` (`string`): The connection's id. Example: `con_8f2k`.
- `user_id` (`string | null`): Your id for the user; null for your company's own Bash.
- `app` (`string`): The app, by its slug. Example: `gmail`.
- `status` (`"active" | "pending" | "initializing" | "expired" | "revoked" | "inactive" | "failed"`): `active`: Bash can use it. `pending` or `initializing`: the sign-in is not finished. `expired`, `revoked`, `inactive` or `failed`: it no longer works, and the user connects it again.
- `label` (`string | null`): The account's label, usually its email; null when the app gives none.
- `created_at` (`string`): When it was connected, as an ISO 8601 date and time in UTC.

### Code

**TypeScript**

```ts
const connection = await vestra.user('traveller-42').connect.get('con_7hs');
```

**Python**

```python
connection = vestra.user("traveller-42").connect.get("con_7hs")
```

**HTTP**

```bash
curl "$VESTRA_BASE_URL/v1/connections/{connection_id}" \
  -H "Authorization: Bearer $VESTRA_API_KEY"
```

**CLI, MCP**: No sample in your language for this route, so here it is over HTTP.

## `DELETE /v1/connections/{connection_id}`

Its own page: [DELETE /v1/connections/{connection_id}](https://vestra.ai/developers/reference/api/connected-apps/delete-v1-connections-connection-id.md)

Disconnect. user_id names whose it is.

Your API key, scope `runtime` or `all`. On the card: [`connect`](https://vestra.ai/developers/connect.md). Every call here is a checked sample.

### Request

Path:

- `connection_id` (`string`, required): The connected app's id (`con_...`).

Query:

- `user_id` (`string`): Whose Bash: your id for the user. Absent: your company's own Bash.

### Answers 200

- `object` (`"connection"`): Always `connection`.
- `id` (`string`): The disconnected connection's id.
- `deleted` (`true`): Always true: Bash can no longer use the app for this user.

### Code

**TypeScript**

```ts
await vestra.user('traveller-42').connect.remove('con_7hs');
```

**Python**

```python
vestra.user("traveller-42").connect.remove("con_7hs")
```

**CLI**

```bash
vestra connect --remove con_7hs --user traveller-42
```

**HTTP**

```bash
# Get a sign-in link for one app. It is for one user: send them its url.
curl "$VESTRA_BASE_URL/v1/connections" \
  -H "Authorization: Bearer $VESTRA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "user_id": "traveller-42", "app": "gmail" }'

# List their apps. You can also list the catalog your users may pick from.
curl "$VESTRA_BASE_URL/v1/connections?user_id=traveller-42" \
  -H "Authorization: Bearer $VESTRA_API_KEY"

curl "$VESTRA_BASE_URL/v1/apps?search=calendar" \
  -H "Authorization: Bearer $VESTRA_API_KEY"

# Disconnect an app. user_id says whose connection it is.
curl -X DELETE "$VESTRA_BASE_URL/v1/connections/con_7hs?user_id=traveller-42" \
  -H "Authorization: Bearer $VESTRA_API_KEY"
```

**MCP**

```json
{
  "method": "tools/call",
  "params": {
    "name": "connect",
    "arguments": { "user": "traveller-42", "app": "gmail" }
  }
}
```

## `GET /v1/connect/return`

Its own page: [GET /v1/connect/return](https://vestra.ai/developers/reference/api/connected-apps/get-v1-connect-return.md)

Hosted by us. The app sends the user here after consent; we record it and forward them to your return_url. You never call it.

No key: anyone may call it. On the card: [`connect`](https://vestra.ai/developers/connect.md). Over HTTP it is made from the contract: fill in every value.

### Request

Nothing to send but the route.

### Answers 200

Its answer is a protocol of its own: its line's page says more.

### Code

**HTTP**

```bash
curl "$VESTRA_BASE_URL/v1/connect/return"
```

**TypeScript, Python, CLI, MCP**: No sample in your language for this route, so here it is over HTTP.
