Skip to content
Beeuni
English
Esc
↑↓navigate↵open⌘Jpreview
On this page

Connectors

How connectors and connections give agents scoped access to external tools.

A connector links a project to an external tool or service. The agent calls it as a tool. Beeuni brokers each call, so the sandbox never holds the connector credential.

You declare most connectors in beeuni.yaml. See the manifest reference for every field. Beeuni declares channel and computer connectors when you connect a chat platform or a machine.

Browse and add a connector

Open Customize → Connectors. All is the default catalog, with Beeuni apps and channels, MCP servers, and API integrations. Search for an app or filter by connection type.

Select an app, choose a connection method, and select Install. For a server or API outside the catalog, use New → Add a custom connector. The catalog is available to every project; no feature flag is required.

Connectors, connections, and accounts

A connector defines the actions an agent can call and how to reach the service. It does not grant Beeuni permissions: the agent’s manifest grant must list the connector slug, and its role must allow the call. See One vocabulary, two bindings. A connector contains:

  • a project-unique slug
  • a display name
  • a provider app
  • connector policies

A connection is one account — one authorized identity — for the connector: a credential, an OAuth grant, or an API key. A connector can hold several accounts side by side. Every account uses the connector’s policies. Each account’s owner_type decides who may run a call as it:

  • project — a shared account. Reachable by anyone the connector is granted to, human or service account (agent, trigger).
  • member — a private account, owned by one project member (owner_id). Reachable only by that member, and only in a private session. A service account (an agent or a trigger acting unattended) can never run as a member’s private account.

Use named rule sets to give different agents different tool permissions on the same connector. Create another profile only when you need a different connection method or different base rules.

Install a connector

  1. Open Customize → Connectors and find the app on All.
  2. Select Install. Beeuni opens the connector and its Add account dialog.
  3. Only you is selected by default. Choose a shared audience if other people or unattended sessions need the account.
  4. Sign in to the app, or enter its API key.

Installing requires permission to change project connectors. On an installed connector, members can add their own accounts from Accounts. Installing an app already in the project opens its existing connector.

If a custom MCP server requests sign-in, Add account opens when its authentication scheme is configured or detected. If tools cannot load, the connector remains saved and shows the error with Retry. Saved OAuth application credentials are reused on retry.

A single shared account is the connector’s default. Adding a shared account reuses the sole unsigned-in shared placeholder when one exists. An account remains private unless you choose to share it.

Manage a connector

Each connector shows the tabs that apply to it. Tools and Settings require permission to change the project’s connectors.

  • Accounts — the logins this connector can run as. Add account asks for a name and who can use it. Use Share on an existing account to change its audience.
  • Tools — which tools the agent may call, and which need approval.
  • Agents — which agents can use the connector and the rule set each agent uses. Channel and computer connectors do not show this tab.
  • Triggers — event triggers for this connector. This tab appears when the connector provides events.
  • Settings — the connection details, and Remove connector. Computer connectors do not show this tab.

Installing an app that is already in the project opens its existing connector. Use Add account to connect another account.

When an agent needs an account, it posts a Connect card in the chat. The card opens a dialog, with the name the agent suggests already filled in and the audience it asked for preselected. Beeuni creates a new account, never re-authorizes an existing one, and tells the agent the new account’s name so it can run calls as that account.

Choose who can use a shared account

A shared account is usable by everyone in the project until you narrow it. Select Share on the account card. The dialog is the same Grant access dialog used for agents. It lists who can use the account now, and you add:

  • Everyone in the project — the default.
  • Groups — every member of each group.
  • People — one member each. Pick only yourself to keep the account to you.
  • Agents — every session of that agent, triggers and shared sessions included. Anyone who can run the agent can use the account through it.

Nothing changes until you select Save. Removing the last group or person makes the account usable by everyone again; the dialog states the result before you save.

A narrowed account follows the private-account rules, with its audience in place of the owner:

  • A person in the audience can use it, and only in a private session.
  • An agent in the audience can use it in every one of its sessions.
  • Any other agent, and a trigger of any other agent, never uses it.
  • A session that binds it cannot be shared, unless the account is shared with that session’s own agent.
  • Only a person changes who can use an account. An agent session gets 403.

A person outside the audience does not see the account. A person who manages the project’s connections still sees it, marked Not shared with you, so they can change who can use it.

Share your own private account

Your own Only you account has a Share button too. Sharing it turns it into a shared account for the people and groups you pick (you stay on it unless you remove yourself), or for everyone in the project. From then on it is a shared account: people who manage the project’s connections can manage it too.

  • Sharing needs project.connector.connections.manage, the same right as adding a shared account. Without it the button is disabled and says why.
  • Beeuni writes the grants first and turns the account shared in one update, so it is never open to the whole project in between.
  • A shared account of the same name on the connector blocks it (409): rename one first.
POST /projects/{projectId}/connections/{connectionId}/share
     {"principals": [{"principal_type": "group", "principal_id": "<group-id>"}]}

An empty principals list shares it with everyone. From the CLI: beeuni connectors connections share <connection-id> --group <group-id> (also --user <email|id>, repeatable, or --everyone).

Each audience entry is one connection object assignment in the grant store:

POST   /accounts/{accountId}/iam/assignments
       {"principal_type": "group", "principal_id": "<group-id>",
        "role_key": "agent-user", "scope_type": "project", "scope_id": "<project-id>",
        "object_type": "connection", "object_id": "<connection-id>"}
DELETE /accounts/{accountId}/iam/assignments/{assignmentId}

From the CLI, the same writes are:

beeuni access grant --group <group-id> --connection <connection-id>
beeuni access grant --everyone --connection <connection-id>
beeuni access revoke <assignment-id>
beeuni connectors connections ls   # the WHO CAN USE column
  • principal_type is user, group, or project (everyone in the project; principal_id is the project id).
  • Granting or revoking one needs project.connector.connections.manage.
  • Only a shared account can be narrowed through these grants. A private account, or an id that is not a connection in this project, answers 404; share your own private account with the share route above instead.
  • GET /projects/{projectId}/connections returns each shared account’s grants in shared_with, and usable: false on an account the caller sees only to manage it. The session picker offers only usable accounts.

Which identity an account acts as

An account’s label is a name chosen before authorization. It does not say which login was used. connected_as does: the email, login, or display name the provider reports for the authorized account.

  • Beeuni reads it when a managed authorization finalizes: first the connected account’s display name, then the toolkit’s “who am I” tool (Gmail, Google Calendar, Google Drive, Linear, GitHub, Slack, Notion, Outlook, Microsoft Teams, HubSpot, Jira, Asana, Figma, Airtable, Salesforce, Trello, Dropbox, Calendly). Google Docs and Google Sheets expose no such tool, so their connected_as stays null.
  • An account still carrying a default label (Private connection, Project connection, or the connector name) is relabelled to that identity. A label someone chose is never replaced.
  • The connect page shows “Connected as …” the moment the account lands, so a shared account authorized with a personal login is visible right away.
  • connected_as is on every connection (GET /projects/{projectId}/connections), on the accounts list, in beeuni connectors accounts <slug> and beeuni connectors connections ls (the CONNECTED AS column), and on the account rows in Customize → Connectors.

Rename an account

Rename an account without re-authorizing it:

PUT /projects/{projectId}/connections/{connectionId}/label   {"label": "Support inbox"}
beeuni connectors connections rename <connection-id> Support inbox

In the dashboard, open the connector, then ⋯ → Rename on the account row. Only the label changes. The authorized account, its owner, its default flag, and the provider state stay as they are. The same gate applies as for disconnect: your own private account, or project.connector.connections.manage for a shared one.

A rename is refused with 400 for me, project, or a UUID-shaped label, because --account resolves those before labels. It is refused with 409 when another account of the same owner already has that label, compared case-insensitively.

Connect a different account

A connect link for a slot that already holds an active account does not re-authorize it. The provider reuses the account, and the connect page shows Already connected. To use a different login, add a new account on the connector, make it the default, then disconnect the old one.

A call that names no account resolves to the caller’s own default private account first, then the project’s default shared account. Every call result echoes which account it ran as (account: { connection_id, label, owner_type }), so the transcript always shows the identity a tool call used. List the accounts a connector can be called as, default first, with GET /connectors/projects/{projectId}/connectors/{slug}/accounts.

Providers

A connector uses one provider type:

  • openapi, postman, graphql, http — direct API connectors
  • mcp — a remote MCP server over HTTP or SSE
  • channel — a chat platform connection
  • computer — each member’s connected machine is one account

See Slack and channels and Computers for the managed provider flows.

App availability

The connector catalogue shows the apps currently available to connect. Some apps require additional authentication setup and may not appear until that setup is complete.

Authentication and policy

A connection authenticates with:

  • Managed OAuth, a channel install, or a native OAuth2 grant
  • an API key or token entered through the dashboard

Beeuni encrypts connection data and resolves it server-side for each tool call. The agent requests an action. Beeuni attaches the credential, checks the agent grant and connector-connection policy, calls the external API, and returns the result.

Connector policies belong to the connector. A connection cannot override them. Project guardrails apply above connector-connection policies.

By default, an unmatched connector action runs without approval. Set policy.default_mode: risk to require approval for unmatched write and destructive actions. Set sensitive: true to make require_approval the connector’s unmatched-action default, including reads. Explicit project or connector-connection rules still apply first.

Approve one governed call

require_approval creates one decision for one connector call. The Connector returns 202 pending_approval with approval_url, approval_summary, and execution_id. It does not keep an HTTP request open.

Share approval_url with any teammate. The URL identifies the request but does not grant authority. The page requires a signed-in Beeuni account. Beeuni then verifies that the account can access and approve actions in the project.

The approval page shows the redacted parameters that the connector will receive. A caller can add approval_context (CLI: --reason) to describe the effect. Use it when the parameters are only references: gmail.send_draft takes a draft id, so the agent states the recipient, subject, and body. The page shows this text as an unverified description from the agent. Check it against the parameters before approving. Beeuni never sends approval_context to the provider.

Approve or deny the call once. Add an optional message to the decision. The message goes back to the agent with the decision, so a deny can say what to do instead.

Every pending call also appears in Review → Needs you for the project. Open Review from the sidebar; its count shows how many items wait for you. A call opens in the same approval view as its approval link, so you see the connector, the parameters, and the agent’s description before you decide.

When the call comes from a Slack or Teams session, Beeuni posts an approval card in that thread. The card shows the agent’s description, the parameters, and Approve / Deny. In Slack, Reply… opens a box for a message to the agent; in Teams, the card has the message box inline. Only a project manager or the person who started the session can decide. A decision made anywhere (the thread, Beeuni, or the approval page) replaces the card’s buttons with the outcome, and the agent continues in the same thread. Beeuni sends the decision back into the session through a durable callback. An approval applies only to the exact request digest. A changed recipient, subject, body, channel, URL, or other parameter requires a new decision.

Open the session’s Audit panel to use the same parameter view. Historical entries remain read-only. There is no session-wide approval option. Use an explicit always_run policy only when a connector action must run unattended.

Connect with OAuth

Open the project's Connectors page

Open the project. Select Connectors, then select the app.

Select the account scope

Select Project for a shared account, reachable by anyone the connector is granted to. Select User for a private account, owned by you and reachable only by you, only in a private session.

Complete authorization

Complete the OAuth flow. Beeuni stores the connected account as a connection.

Connect an MCP server that uses OAuth 2.1

Beeuni implements the MCP authorization specification. Open the connector, select Add credential, then select the OAuth 2.0 tab. Beeuni probes the server and reads its metadata:

  1. The unauthenticated probe returns 401 with WWW-Authenticate: Bearer resource_metadata="…".
  2. Beeuni reads the protected resource metadata (RFC 9728) at that URL, or at /.well-known/oauth-protected-resource.
  3. Beeuni reads the authorization server metadata (RFC 8414 or OpenID Connect discovery) for the first authorization server the resource names.

When that server advertises registration_endpoint, Beeuni registers itself as an OAuth client (RFC 7591) and shows one button: Connect <server>. You create no application, and you copy no client ID or secret. Beeuni then runs Authorization Code with PKCE (S256) and binds the token to the server with the resource parameter (RFC 8707).

Register this redirect URI when a server needs one in advance:

https://api.beeuni.ai/v1/connectors/oauth2/callback

When the server publishes endpoints but no registration_endpoint, Beeuni prefills the authorization URL, token URL, and scopes. Enter the client ID of an app you create with that provider. When the server publishes no metadata, enter every field.

Beeuni keeps the MCP session: it runs initialize and notifications/initialized on demand, then sends Mcp-Session-Id on later calls. A server that answers without a session never sees the handshake.

Beeuni binds each connection to the authorization server that issued it. When the callback carries an iss parameter (RFC 9207), Beeuni rejects it unless it matches the recorded issuer — a code minted by a different server is refused before it is redeemed.

Authorize from the CLI

To authorize from a terminal or an agent session, declare the connector in beeuni.yaml, then run the authorization command:

connectors:
  - slug: read-ai
    name: Read AI
    provider: mcp
    url: 'https://api.read.ai/mcp'
    auth:
      type: bearer
beeuni connectors authorize read-ai --json

The command creates the connection, runs the discovery chain, registers Beeuni as a client when the server supports RFC 7591, and returns the URL to approve:

{
  "connection_id": "7b1a16b2-...",
  "registered": true,
  "scopes": ["openid", "offline_access", "mcp:execute", "meeting:read"],
  "authorization_url": "https://authn.read.ai/oauth2/auth?response_type=code&...",
  "expires_at": "2026-08-19T14:48:26.345Z"
}

An agent returns authorization_url to the person it is working with. After they approve, the agent confirms:

beeuni connectors authorize read-ai --status

The command exits non-zero while the status is error. Use --scope to narrow what is requested. Use --client-id and --client-secret for a server that does not support dynamic client registration.

Beeuni refetches the connector’s tool catalog as soon as authorization completes, so the connector leaves the error state without a manual sync.

Connect a direct API with OAuth2

Direct connectors support:

  • client credentials
  • authorization code with PKCE
  • device authorization
  • dynamic client registration (RFC 7591)

For client credentials, enter the token URL, client ID, scopes, and client secret. You can use client_secret_basic, client_secret_post, client_secret_jwt, or private_key_jwt token-endpoint authentication.

For authorization code, enter the authorization URL and token URL. For device authorization, enter the device-authorization URL and token URL. An RFC 8414 discovery URL can provide these endpoints.

Beeuni encrypts the OAuth2 configuration and tokens. It refreshes access tokens before expiry, and it stores each rotated refresh token. Revoking the connection blocks the next connector call.

For Microsoft Graph, use:

https://login.microsoftonline.com/{tenant_id}/oauth2/v2.0/token
https://graph.microsoft.com/.default

For direct SharePoint REST calls, use the SharePoint resource scope:

https://{tenant}.sharepoint.com/.default

Connect with an API key

Declare the connector

connectors:
  - slug: stripe-read
    name: Stripe read access
    provider: openapi
    spec: 'https://raw.githubusercontent.com/stripe/openapi/master/openapi/spec3.json'
    auth:
      type: bearer
    policies:
      - match: 'get_*'
        action: always_run
      - match: '*'
        action: block

The manifest still accepts an authorization_strategy key for backward compatibility. Beeuni ignores it: set the owner (shared or private) per connection instead, when you add its credential.

Merge the change request

Beeuni reads the manifest from the default branch. The connector becomes active after the change request merges.

Add the connection

Open the connector and set its credential. Beeuni stores the value encrypted. It does not write the value to the manifest.

Grant an agent access

Add the connector slug to the agent’s connectors field:

agents:
  release-bot:
    connectors: [stripe-read]

Omit connectors, and the agent gets none. Merge the change before it takes effect.

connectors_required is deprecated. The manifest still accepts it, but it has no effect. A missing connector account does not prevent a session from starting. Access is checked when the agent calls the connector. An agent granted stripe-read can start its turn, but a call without a usable account is denied with instructions for connecting one. See When a call is denied.

Select an account

Two independent ways to pick which account a call runs as — pin one for the whole session, or name one on a single call.

Pin a session-wide default

A session can override the connector’s default account for every call it makes:

{
  "connector_bindings": {
    "stripe-read": {
      "connection_id": "00000000-0000-4000-8000-000000000000"
    }
  }
}

The binding key is the connector slug. The value is an account (connection) this session’s caller is entitled to reach.

Use GET /projects/{projectId}/sessions/{sessionId}/scope to read the effective binding. Use PUT on the same path to replace it. The replacement applies to the next tool call without restarting the session.

Name an account on one call

List the accounts a connector can be called as — default first — with:

GET /connectors/projects/{projectId}/connectors/{slug}/accounts
{
  "connector": "stripe-read",
  "accounts": [
    { "connection_id": "...", "label": "Shared workspace", "owner_type": "project", "is_default": true, "connected_as": "billing@example.com" },
    { "connection_id": "...", "label": "My Stripe", "owner_type": "member", "is_default": false, "connected_as": null }
  ]
}

Then name one on the call, by connection id or by label (case-insensitive), or with the two selector words me (your own default private account) and project (the project’s default shared account):

beeuni connectors call stripe-read get_balance '{}' --account me
beeuni connectors call stripe-read get_balance '{}' --account "Shared workspace"

A call that names no account resolves to the caller’s own default private account first, then the project’s default shared account — unchanged from before a connector could hold more than one account. A named account that does not resolve is denied, never silently substituted onto a different one (see When a call is denied). Every successful call result echoes which account it ran as: account: { connection_id, label, owner_type }.

Use a connector in a session

Inside a session, use the Connector CLI:

beeuni connectors ls
beeuni connectors accounts stripe-read
beeuni connectors call stripe-read <action> '<json-args>' [--account <id|label|me|project>]

connectors lists the connectors in scope. accounts lists the accounts this caller may call stripe-read as — default first, shared before private within each group. call runs one action, as the named --account or, when omitted, the same default resolution used before a connector could hold more than one account.

Large results

A list or search call can return more than a model can read: tool output above ~50 KB is truncated. Save the result to a file and work through it with code:

beeuni connectors call linear graphql.issues '{"first":250}' --out /workspace/output/issues.json
# → { "ok": true, "account": …, "saved_to": "/workspace/output/issues.json",
#     "bytes": 93037, "shape": { "issues": { "nodes": "array(250) of {id,title,…}",
#     "pageInfo": { "hasNextPage": true, "endCursor": "…" } } } }
jq '.data.issues.nodes[] | {id, title}' /workspace/output/issues.json

--out writes the full JSON result, creating parent directories. Stdout gets the call envelope without data, plus saved_to, bytes, and shape: the outline of data (keys, array lengths, and every pageInfo). The exit code is unchanged.

Attach files

A call never carries file bytes in its arguments. Stage the file, then put its reference in the arguments. The gateway reads the bytes server-side and writes them into the request.

beeuni connectors call microsoft-graph.sendmail @mail.json \
  --attach /workspace/artifacts/report.pdf

--attach stages the file and appends a reference to the action’s attachments array. The array is found in the action’s input schema: attachments for the Email channel, body.message.attachments for Microsoft Graph sendMail. Name it with --attach-path body.message.attachments when the schema is unusual. Large JSON arguments go in a file (@mail.json) or on stdin (-).

To place a file yourself, stage it first:

beeuni connectors upload /workspace/artifacts/report.pdf --connector microsoft-graph
# → { "attachment_id": "…", "ref": { "$beeuni_attachment": "…" }, … }

Put ref anywhere in the arguments. The gateway replaces it by position:

Position of ref Replaced with
An element of an attachments array whose items are objects The provider’s attachment item. The item schema selects the shape: Microsoft Graph fileAttachment (name + contentBytes or @odata.type), Postmark (Name + Content), Mailjet (Filename + Base64Content), SendGrid and Resend (filename + content), Brevo (name + content). Any other item shape is refused with attachment_item_shape_unknown.
A string field, e.g. contentBytes or a GitHub file content The file’s base64.
The Email channel’s attachments[] A signed download URL for the Email provider.

Rules:

  • Files come from /workspace/output, /workspace/artifacts, /workspace/reports, or /workspace/deliverables. Symbolic links and hard links are refused. Copy a file there first.
  • One call carries up to 20 files and 25 MiB in total. The upstream API can set a lower limit: Microsoft Graph documents 3 MB as the largest file one sendMail request can attach.
  • A staged file is private to the session that staged it, used once, and deleted after 24 hours. A failed call releases it for a retry.
  • The audit record, the approval preview, and the model see only the reference, never the bytes. An upstream error that echoes the file has the base64 removed.
  • Managed connectors do not accept staged files (connector_attachments_unsupported).

The MCP server offers the same flow: call takes attachment_files (and an optional attachment_path), and upload_attachment returns a ref. The SDK equivalent is connectors.uploadAttachment(bytes, { filename, contentType, connector }), which returns ref.

Call results

POST /connectors/projects/{projectId}/call

These fields describe the result of a connector call.

Field On Value
data 200 The raw upstream answer, unchanged.
output 200 The normalized tool payload. Read output ?? data.
binding 200, 500, 429, 503 The connector binding that ran the call. null when the action did not resolve.
upstream_status 200, 500, 429, 503 The upstream HTTP status. null when no upstream answered.
upstream_error 200 A tool failure inside a 2xx response: MCP isError: true, a JSON-RPC error, or GraphQL errors without data. HTTP remains 200, but the body has ok: false, status: "tool_error", and the tool’s message in error. upstream_error carries the same message for older clients.
retry_after_seconds 429, 503 The upstream’s Retry-After, in seconds, when it sent one. The response also carries the Retry-After header.

The HTTP status of a failed call:

  • An upstream 429 answers 429. An upstream 503 answers 503. Wait retry_after_seconds (or back off) and call again.
  • Every other upstream failure answers 500 with the cause in reason.
  • An upstream answer larger than 64 MiB answers 500 with reason: "upstream_response_too_large: …". Beeuni stops reading at the limit. Ask for a smaller page or a filter.
  • An upstream that does not answer within the deadline answers 500 with reason: "upstream_timeout: …". The deadline is 60 seconds by default. Set a client timeout above it, so the gateway always answers first.

A connector call is not idempotent. Beeuni does not deduplicate a repeated call. After upstream_timeout or a client-side abort, the call may have run upstream: check its effect before you repeat a write (a sent email, a created record).

Typed calls from code

beeuni connectors types writes a TypeScript declaration file for every action you can call. Each action gets its argument type from its input schema and its result type from its output schema. The result type describes the call’s output field.

beeuni connectors types --out src/beeuni-connectors.d.ts
beeuni connectors types --connector linear,github --out src/beeuni-connectors.d.ts

Include the file in your tsconfig.json. Then call through callAction:

import { createBeeuni } from '@beeuni/sdk';

const connectors = createBeeuni({ backendUrl, getToken }).project(projectId).connectors;
const result = await connectors.callAction('linear', 'list_issues', { team: 'CORE' });
if (result.ok) console.log(result.output?.issues.length);

The connector handle reads the same file, and run returns the typed output itself or throws a typed error (see Connectors from code):

const issues = await createBeeuni({ backendUrl, getToken })
  .project(projectId)
  .connector('linear')
  .run('list_issues', { team: 'CORE' });

callAction(slug, action, args, options) sends the same request as call('<slug>.<action>', args, options) and returns the same result. Only the types differ: a missing or unknown argument is a compile error, and output has the generated type. An action that is not in the file accepts any object and returns output: unknown.

  • OpenAPI, HTTP, MCP and GraphQL connectors publish an output schema when their source declares one. A GraphQL result is typed as an object.
  • Managed connectors publish no output schema. Their result is unknown. The CLI names them after it writes the file.
  • Each $defs entry becomes one named type (BeeuniConnectorDef<n>_<name>), so a recursive schema stays finite. A schema that cannot compile (a union that contains itself) or whose types pass 256 KB for one action becomes unknown.
  • Re-run the command after you add a connector or it syncs a new version.
  • The catalog returns output schemas only on request: GET /connectors/projects/{projectId}/catalog?include_output_schemas=true adds outputSchema to each action (null when none is stored).

When a call is denied

A denied call answers { ok: false, status: "denied", reason, hint }. reason is a stable code; hint says what to do.

reason HTTP Meaning Also in the body
connector_not_assigned 403 The running agent’s connectors: grant in beeuni.yaml does not include this connector. agent, granted (the list or "all"), manifest_revision and manifest_commit (the manifest the grant came from).
connector_not_connected 403 No usable account for this call. Either the connector has no account at all for this caller (no stored credential, or the app was never authorized — beeuni connectors ls shows it as needs_auth), or --account named one that did not resolve. connect_url — a hosted link to authorize an account, when the caller can mint one — when nothing is connected. requested_account and available_accounts — the name that did not resolve and the names that were — when --account named one.
connector_disabled 403 The connector is declared but disabled. —
connector_not_found 404 No connector with this slug is declared in the project. —
action_not_found 404 The connector has no such action. —

For an unconnected connector, connect_url provides a link to connect an account. The session can start before an account is connected (see Grant an agent access). The agent returns the link unchanged; the web transcript renders it as a Connect button. The link authorizes a private account for the human who opens it by default — connecting a shared account is an explicit choice (beeuni connectors connect stripe-read --owner project, gated on project.connector.write).

The channel connector that created a session (Slack, Teams, email) is always callable from that session, whatever the agent’s grant says, so the agent can always report in the thread it was asked from.

A session’s grant follows beeuni.yaml: a change to the running agent’s connectors: (including beeuni connectors add --apply) applies on the next call. The grant records which manifest blob and commit it came from; a read that would replace it from the same blob is confirmed by a second read first, and a read from an older commit never replaces it.

Slack and Microsoft Teams connect from the dashboard the same way as OAuth apps. Connecting Slack writes a channel connector to beeuni.yaml for you. See Slack & channels.

Add an MCP server by URL

Select Customize → Connectors → New → MCP server, paste an HTTPS server URL and choose who may use it. Only you is selected by default. Select Connect and complete sign-in. Supported apps can open this flow directly from Install. A server requiring a pasted credential opens credential entry instead. If sign-in fails, the connector stays saved so you can retry from its page.

Approval expiry

A pending call expires after 24 hours unless the project sets a different approvals.ttl in its manifest. Its approval link expires at the same time. The call leaves Review → Needs you, and its chat card shows Expired. Resolving it through the API returns 409 with code: "APPROVAL_EXPIRED". Expiry does not resume the session; the agent must make a new call to request approval again.

Was this page helpful?