Skip to main content
An interface is an adapter between AgentOS and a surface where users live. It receives events from Slack, Telegram, WhatsApp, or other channels, maps them onto AgentOS’s run and session model, and routes responses back to the right thread, channel, or user. The agent doesn’t change. The same Agent instance answers a Slack DM, a Telegram message, and an AG-UI browser stream. Memory carries across all of them, scoped per (user_id, session_id).

Available interfaces

Two categories. Chat surfaces meet humans where they already are. Protocol surfaces are how other systems talk to your agent.

Chat surfaces

InterfaceUse caseSetup
SlackTeam chat, DMs, channel mentions, thread sessionsSlack
TelegramPersonal assistants, mobile chatTelegram
WhatsAppCustomer support, mobile chat with E2E encryption supportWhatsApp
DiscordCommunity servers, gaming, custom commands. Runs in its own process via agno.integrations.discord.Discord

Protocol surfaces

InterfaceUse caseSetup
A2AOther agents talk to yours over a standardized agent-to-agent protocolA2A
AG-UIBrowser clients consuming SSE streams of run outputAG-UI

Setup

Each interface registers its own routes on the FastAPI app. Slack lands events at /slack/events. Telegram at /telegram/webhook. The agent=... parameter tells the interface which agent to dispatch incoming messages to.
from agno.os import AgentOS
from agno.os.interfaces.slack import Slack
from agno.os.interfaces.telegram import Telegram

agent_os = AgentOS(
    agents=[agent],
    db=db,
    interfaces=[
        Slack(agent=agent, token="xoxb-...", signing_secret="..."),
        Telegram(agent=agent, token="bot-token"),
    ],
)
If your AgentOS has multiple agents, you can wire each interface to a different one — Slack to your customer support agent, Telegram to a personal assistant — or wire several interfaces to the same agent.

Credentials at a glance

Per-interface setup pages have the full OAuth flows, scope lists, and webhook configuration. The summary:
InterfaceNeeds
SlackBot token (xoxb-...), signing secret, OAuth scopes for the events you handle
TelegramBot token from @BotFather
WhatsAppBusiness API token, verify token, phone number ID
DiscordBot token, application ID
A2AInbound JWTs validated by AgentOS auth
AG-UIAgentOS JWT in the SSE request

Sessions per surface

Every interface maps surface state to AgentOS sessions, so a conversation in Slack carries forward like any other session — the next reply in the same thread reuses the same session and history, no re-mentioning required.
InterfaceSession IDUser ID
SlackThread timestampSlack user ID
TelegramChat IDTelegram user ID
WhatsAppPhone numberPhone number
DiscordChannel + threadDiscord user ID
A2ACaller-suppliedCaller-supplied
AG-UIBrowser sessionJWT subject

Resolving Slack user IDs to names

By default Slack hands you opaque user IDs like U07ABCXYZ. If your agent’s responses or memory are clearer when grounded in human names, set resolve_user_identity=True on the Slack interface:
Slack(agent=agent, token=..., signing_secret=..., resolve_user_identity=True)
The interface looks up each user ID against users.info and passes the resolved name through to the agent. Off by default because it costs an extra Slack API call per message and isn’t always wanted.

One agent, many surfaces

A single agent can answer on every surface at once. Memory follows the user across surfaces — assuming you can map their Slack ID to the same user_id the AG-UI client passes:
agent_os = AgentOS(
    agents=[support_agent],
    db=db,
    interfaces=[
        Slack(agent=support_agent, token=..., signing_secret=...),
        Telegram(agent=support_agent, token=...),
        WhatsApp(agent=support_agent, token=..., verify_token=...),
        AGUI(agent=support_agent),
    ],
)
The same questions the agent answered in Slack last week show up in its memory when the user opens the AG-UI widget on your website. The agent doesn’t know which surface a request came from. It just sees a user_id, a session_id, and a message.

Conditional registration

Don’t register interfaces you don’t have credentials for:
interfaces = []

if SLACK_TOKEN and SLACK_SIGNING_SECRET:
    interfaces.append(Slack(agent=agent, token=SLACK_TOKEN, signing_secret=SLACK_SIGNING_SECRET))

if TELEGRAM_TOKEN:
    interfaces.append(Telegram(agent=agent, token=TELEGRAM_TOKEN))

agent_os = AgentOS(agents=[agent], db=db, interfaces=interfaces)
The Scout, Dash, and Coda tutorials use this pattern. Slack only loads when both env vars are set, so dev runs without credentials don’t crash.

Custom interfaces and one-off webhooks

The interface API is small. Subclass BaseInterface, register routes on the FastAPI app via setup, dispatch incoming messages to the agent. See BaseInterface for the full surface. For one-off webhooks (a CRM event, a GitHub action, a custom dashboard), don’t write an interface. Add a route directly to the FastAPI app:
app = agent_os.get_app()

@app.post("/webhooks/stripe")
async def handle_stripe(event: dict):
    response = await agent.arun(f"Process Stripe event: {event}", user_id="system")
    return {"ok": True, "response": response.content}
A custom interface is for surfaces you’ll reuse across agents and OS instances. A direct route is for one-off integrations.

Next

Scheduling →