> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.astropods.com/agent-connections/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.astropods.com/_mcp/server.
# Let your agent use a user's connections
A connection is a third-party account, such as GitHub, that a person has linked to Astropods. When you declare a connection in `astropods.yml`, each user who chats with your agent decides whether to let it use their account. The agent then acts as that user, for example by opening a pull request in their name, instead of sharing one static token across everyone.
By the end of this guide, your agent asks for a GitHub connection, the user allows it, and the agent calls GitHub as that user.
## Before you start
* An agent project you can run with `ast project start`. [Your first project](/get-started) sets one up.
* You are signed in with [`ast login`](/cli/top-level#login).
* Users link the provider on their personal account first, under **Settings** > **Connectors**. The consent card in chat also offers a **Connect** button.
## Supported providers
| Provider | `provider` value |
| -------- | ---------------- |
| GitHub | `github` |
| Postman | `postman` |
| Box | `box` |
Pushing a spec with any other `provider` value fails validation.
## How it works
User
Allows in chat
Your agent
Asks for a token
Provider
Acts as the user
1. Your spec lists the connections the agent needs.
2. Before the first message of a new chat, the user sees a consent card. It shows each provider, your reason, and the scopes.
3. The user allows it for a duration they pick: until they revoke it, 24 hours, 7 days, or 30 days.
4. During a turn, your agent requests an access token for that user and calls the provider with it.
Astropods never stores the provider token. The agent gets a token only for a user who allowed it and is chatting now.
## Declare connections
Add a `connections` list to `astropods.yml`:
**`astropods.yml`**
```yaml title="astropods.yml"
connections:
- provider: github
scopes: [repo, read:org]
required: true
reason: Open pull requests in your repositories
- provider: postman
required: false
reason: Link collections to the pull requests it opens
```
| Field | Required | Description |
| ---------- | -------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `provider` | Yes | The provider to connect. Each provider appears once. |
| `reason` | Yes | Shown on the consent card. 120 characters or fewer. |
| `scopes` | No | Scopes your agent needs. Each is a single word with no spaces. |
| `required` | No | When `true`, chat stays blocked until the user allows the connection. When `false`, the user can skip it. Defaults to `true`. |
Run `ast spec validate` to check the file before you push. Connections are separate from [`integrations`](/known-integrations), which are labels for the Agent Card.
## Use a connection in your agent
Create a `ConnectionClient` once, then call `getToken` with the provider and the ID of the user in the current turn. Your adapter passes that ID as `userId` in the stream options.
#### TypeScript
```ts
import { ConnectionClient, ConnectionError } from "@astropods/adapter-core/connections";
const connections = new ConnectionClient();
async function openPullRequest(userId: string) {
try {
const { accessToken } = await connections.getToken("github", userId);
return await fetch("https://api.github.com/user", {
headers: { authorization: `Bearer ${accessToken}` },
});
} catch (err) {
if (err instanceof ConnectionError) {
return `I can't use your GitHub account yet (${err.code}).`;
}
throw err;
}
}
```
#### Python
```python
from astropods_adapter_core.connections import ConnectionClient, ConnectionTokenError
connections = ConnectionClient()
async def open_pull_request(user_id: str):
try:
token = await connections.get_token_async("github", user_id)
except ConnectionTokenError as err:
return f"I can't use your GitHub account yet ({err.code})."
return token
```
Install the extra first: `pip install "astropods-adapter-core[connections]"`.
The client reads its credentials from the environment Astropods sets for your agent. It reuses a token until about a minute before the token expires.
## Handle refusals
A refused request carries a `code`:
| Code | Meaning | What your agent can do |
| ----------------------- | ------------------------------------------------------------------------------ | -------------------------------------------------------------- |
| `not_consented` | The user never allowed this provider, revoked it, or it expired. | Ask the user to start a new chat and allow it. |
| `not_active` | The user has not messaged the agent in web chat in the last 15 minutes. | Retry after the user's next message. |
| `not_connected` | The user disconnected the provider. | Ask the user to reconnect under **Settings** > **Connectors**. |
| `needs_reauthorization` | The provider needs the user to sign in again, or a requested scope is missing. | Ask the user to reconnect. |
| `unavailable` | The request failed or the server could not be reached. | Retry later. |
## Test locally
Run the agent with `ast dev`. When the spec declares `connections`, the CLI opens a dev session and the agent calls the provider as you, from your own personal account. Local runs skip the consent card. Connect the provider under **Settings** > **Connectors** first.
## Manage access
Users review what they have allowed in **Settings** > **Connectors** > **Agent access**. **Revoke access** takes effect on the agent's next token request. Disconnecting a provider on a personal account revokes every agent's access to it.
As the agent's owner, you see how many people allowed each connection under **Configure** > **Access**. You never see who.
## What users see
When a user opens a new chat with an agent that declares connections they haven't allowed, a consent card appears above the message box. Here `` stands for your agent's name.
| Part of the card | What it shows |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Title | "Allow `` to use your accounts" |
| Subtitle | "`` acts as you, with the permissions listed, while you chat with it." |
| One row per connection | The provider's icon and name, your `reason`, and your `scopes`. A **Required** label marks required connections. |
| Row action | A checkbox when the provider is connected. Required rows stay checked. When it isn't connected, a **Connect** button, such as **Connect GitHub**, or **Reconnect** if the provider needs a new sign-in. |
| **Allow for** | **Until I revoke** (default), **24 hours**, **7 days**, or **30 days**. It applies to every checked row. |
| Buttons | **Allow access** saves the choice. **Not now** closes the card. |
While the card is open, the message box is disabled and reads "Allow access to chat with ``".
If a required connection is still not allowed after **Not now**, the card is replaced by a notice: "`` needs access to your accounts before you can chat". Its **Review access** button reopens the card. If only optional connections are left, **Not now** enables the message box.
| Situation | What the user sees |
| -------------------------------- | ------------------------------------------------------------------------- |
| Provider not connected | **Connect** on the row. After sign-in, the user returns to the same chat. |
| No personal account | "Needs a personal account" on the row. |
| Allow with nothing connected | "Connect an account first, then allow access." |
| Saving fails | "Couldn't save your choice. Try again." |
| Status can't load | "Couldn't load account access", with **Try again**. |
| Every connection already allowed | No card. The chat starts as usual. |
## What to expect
* The connection always comes from the user's personal account, even when the agent belongs to a team account. A connection made on a team account does not count.
* Token requests succeed only in Astropods web chat. Agents that serve their own UI and Slack chats get `not_active`.
* When a new version adds a scope the user has not seen, the consent card appears again.
* Background work with no open chat can't fetch a token.
## Next steps
* [Managing secrets](/secrets): store a shared, account-wide credential instead
* [Known integrations](/known-integrations): label what your agent talks to
> Declare the accounts your agent needs in astropods.yml and fetch an access token for the person chatting