> 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