---
title: Connect an account
description: "Save a credential once as an account, from the app you are configuring or through an agent, then link it to any app that needs that provider, for yourself or for everyone who uses the app."
---

An **account** is one saved set of credentials for one **provider**. You connect
it once. Every app that needs that provider can use it, and none of them holds
a second copy.

There are two kinds of credential, and the provider decides which it accepts:

- **Secrets**: fields you paste, such as an API token, or an email and a key.
- **OAuth**: browser sign-in or a client ID and secret for machine-to-machine access.
  Executor stores and renews the resulting tokens.

## From the dashboard

Every account is connected for an app. Choose **Connect new account** at the end of a provider's list on the app's
**Accounts** tab, then choose **Connect** to start browser sign-in. If the provider
needs credentials, enter them in the same dialog. The connection dialog contains the
sign-in methods supported by the provider.

The dialog uses the provider fields already loaded with the app. Opening it does
not create a connection or fetch the fields again. A connection is created when
you submit; retries in that dialog reuse the same request.

OAuth setup is checked in the background and reused across forms. If Executor can
use a saved client or register one automatically, the dialog can connect without
asking for client details.
If your own client is required, its fields appear immediately. A failed check offers
**Retry** without guessing which setup to show. Client IDs and secrets stay on the
server during these checks.

The provider's code declares the grant, endpoints, scopes, and authentication
method. Open **Advanced** below Connect to review **Required permissions**.
The dialog asks only for a client ID
and, when required, a client secret. For browser sign-in, copy the fixed redirect
URL into the OAuth app's settings. Machine-to-machine connections complete in the
dialog and need no browser redirect.

If saved OAuth client details are wrong, open **Advanced** below the Connect button,
choose **Change** next to OAuth client, and enter the replacement. Executor saves manual client details
only after a successful sign-in. A failed replacement keeps the previous client
and account. If browser sign-in rejects the client, **Update client details**
opens the form again.

You name an account after it connects. Executor names each new account
`Default`, then `Default 2`, and so on when that name is already in use for the
provider. Once the account is saved, or browser sign-in returns, a **Name this
account** dialog offers to rename it to something such as “Work” or “Personal.”
Closing it keeps the default. Reconnecting keeps the existing name. You can also
rename an account later from its menu on the app's **Accounts** tab, or with **Edit
details** on the **Accounts** page.

Completing a connection saves the account and links it to this app. There is
no separate step. Browser sign-in returns to the app's Accounts tab;
cancelling or failing sign-in offers a direct way back to the app.

The Accounts tab has two sections. **Your accounts** ("Linked for you") holds
the accounts you linked for your own calls. **App accounts** ("Linked for
everyone") holds the accounts the app uses for every caller and its background
work; it appears only when the app requires accounts of its own.

Each requirement lists every saved account of its provider you can use. A
single-account requirement shows a radio button per account: selecting one
links it and replaces the account linked before, which stays saved in
**Accounts**. A `.many()` requirement shows checkboxes: checking one links it,
unchecking one unlinks it, and the app uses every checked account. **Connect new
account** at the end of the list adds an account and links it. Each change is
saved immediately. Members who can view an app but not use it cannot change its
links.

Only people who can edit the app (organization owners and admins, and the app's
creator; on local, the owner) change **App accounts**, and only with accounts
they can use. Everyone else sees that section greyed out and read-only: which
account fills each requirement and whether it works, without the account's
email, username or other details from the provider. The same account may be
linked in both sections.

Unlinking keeps the saved account available to other apps. When no app uses it
any more, Executor offers to delete it. The global **Accounts** page lists saved
accounts and the apps that use them; it does not connect accounts or change
their credentials.

Local has the same Accounts tab. See "Which accounts a call uses" below.

## From an agent

An agent can start the same flow. It must never ask you for a secret in chat,
and must never read a token out of your files. It asks Executor for a connection
link and gives you the link; you finish in your browser.

<CodeGroup>

```js Hosted
return await tools.executor.appAccounts.userConnect({
  path: { organization: "<organization-id>", app: "<app-id>", requirement: "vercel" },
});
```

```js Local
return await tools.executor.appAccounts.userConnect({
  path: { app: "<app-id>", requirement: "vercel" },
  body: {},
});
```

</CodeGroup>

Neither request names an account owner: hosted saves the account under your
organization and local under the app's owner.

Every request names the app requirement it connects for. Use
`appAccounts.userConnect` for a user requirement and `appAccounts.appConnect`
for one of the app's own requirements; on hosted, only people who can edit the
app can start the second. To replace the credentials of an account the app already
uses, add its ID as `account` in the body. The account keeps its ID, name and
links.

Check whether you finished:

<CodeGroup>

```js Hosted
const connection = await tools.executor.accounts.connection({
  path: { organization: "<organization-id>", connection: "<connection-id>" },
});
return connection.state;
```

```js Local
const connection = await tools.executor.accountConnections.get({
  path: { connection: "<connection-id>" },
});
return connection.state;
```

</CodeGroup>

`Completed` means the account is saved and linked to the requirement, for you
or for everyone, in the same transaction. If the app's requirement changed while
the request was open, such as moving between user and app accounts, the request
fails with `AccountConnectionTargetChanged` rather than saving the account. If
you can no longer link the account in that scope, it fails with
`AccountConnectionRefused`.

A pending request expires after thirty minutes. Issue a new one if it does.

An agent manages links with the same five tools in each scope,
`appAccounts.user.*` for your own links and `appAccounts.app.*` for the app's.
Local takes the same paths without `organization`:

- `list({ path: { organization, app } })` lists the links.
- `get({ path: { organization, app, requirement } })` lists the requirement's
  linked accounts, then the other accounts you could link.
- `link({ path: { organization, app, requirement, account } })` links an
  account. For a single-account requirement it replaces the one before.
- `unlink({ path: { organization, app, requirement, account } })` unlinks it.
- `connect({ path: { organization, app, requirement } })` connects a new account,
  as above.

Linking fails with `RequirementMismatch` when the app has no such requirement in
that scope or the account's provider differs. Changing app links needs edit
access to the app.

## Which accounts a call uses

Calls never choose accounts. Every call, schedule and page runs with the
accounts linked to the app: for each of your requirements, the one account you
linked to a single-account requirement and every account you linked to a
`.many()` requirement; for each of the app's own requirements, the accounts
linked for everyone. A single-account requirement with no account fails with
`AccountRequired`, which says whether you need to link one or someone who can
edit the app does. A tool input that passes `$accounts` fails with
`AccountChoiceUnsupported`.

To use a different account, link it on the app's **Accounts** tab. To use two
accounts of one provider side by side, copy the app and link one to each copy.

## Changing and removing an account

You can rename an account, replace its credentials, and remove it. Replace credentials
from an app that uses the account: open its menu on the app's **Accounts** tab and choose
**Reconnect** or **Update credentials**. A **Needs sign-in** account also has a
**Reconnect** button there. Replacing credentials keeps the same account, so every app
linked to it keeps working.

Removing an account removes your links to it. An account linked to an app for
everyone cannot be removed until someone who can edit the app unlinks it
(`AccountLinkedToApp`). An app left with no account
for a requirement exposes no tools until you link or connect another. Executor
does not substitute a different account.
