Activate Eve in Teams (Bot Framework)
This is the durable path for putting a Genesis bot inside Microsoft Teams. The bot is installed as a real Teams app — it has its own avatar, its own@-mention, and its own credentials. Messages it sends are attributed to the
bot, not to the human admin who set up the integration. Users can @-mention
it in channels and DM it directly. This is the same model used by Microsoft
Copilot, Otter, Notion, and every other AI bot in Teams.
This page describes the Bot Framework path. There are two other Teams
integration paths in Genesis with different tradeoffs:
- Microsoft Graph / delegated OAuth (
teams_tools) — sends messages as the connected human user, not as a bot. See Microsoft Teams. - Power Automate webhooks (
teams_webhook_tools) — outbound-only alerts and reports, attributed to the Workflow. See Teams Webhook Setup.
@-mentions, channel install events, or Adaptive Card action callbacks.Prerequisites
- Azure subscription with permission to create Bot resources. The free Azure tier is sufficient — Bot Service has an F0 (Free) pricing tier.
- Microsoft 365 admin for the tenant where the bot will be used. Required for sideloading the Teams app manifest. If you’re testing in your own dev tenant, the Microsoft 365 Developer Program gives a free renewable tenant with 25 user licenses.
- Genesis instance reachable from the public internet over HTTPS.
Bot Framework Service POSTs activities to a public URL; localhost is not
reachable. This is automatic only for genuinely public hosts such as Railway
or Cloud Run. SPCS ingress is protected by Snowflake SSO and requires the
SPCS inbound relay.
For local development, expose port 8083 (or whichever port your stack uses)
via ngrok or similar —
ngrok http 8083gives anhttps://*.ngrok-free.devURL pointing at your local Genesis.
Architecture
bot_servicing row. When a Teams user messages the
bot, Microsoft Bot Framework POSTs the activity (with a Microsoft-signed JWT)
to POST /api/teams/messages/<bot_id> on your Genesis instance (or to the
public SPCS inbound relay, which forwards the same JWT in
X-Teams-Bot-Authorization while authenticating to Genesis with its confined
Snowflake service-user credential). Genesis validates the JWT, dispatches to
the bot’s agent loop, and posts the response back through Bot Framework via
continue_conversation.
On Snowpark Container Services, approve and assign Teams Bot Framework
external access from Config → Microsoft Teams before bots can validate
inbound JWTs or send replies. See
Activate Genesis Data Agents On Teams.
Step 1: Create the Azure Bot resource
In the Azure Portal:- Create a resource → search for Azure Bot → Create.
- Fill in Bot handle (any unique-in-Azure string, e.g.
genesis-eve-prod). - Pick a Resource group (create one if needed).
- Data residency: Global (regional only matters for compliance-bound deployments).
- Pricing tier: F0 (Free) — the free tier is fine for dev and most production use.
- Type of App:
- Single Tenant — the bot only works in your tenant. Simplest. Use this for internal Genesis instances and dev tenants.
- Multi Tenant — the bot works across customer tenants. Choose this only
if you’re distributing Genesis as a multi-tenant SaaS. Requires that the
Microsoft App ID be configured for
AzureADMultipleOrgsaudience.The Azure Portal’s Bot creation wizard hides Multi Tenant as a quick option as of mid-2026. To create a multi-tenant Azure Bot, you must first create the App Registration manually in Entra ID with audience “Accounts in any organizational directory,” then choose Use existing app registration in the Bot creation flow.
- Creation type: Create new Microsoft App ID — don’t reuse an existing App Registration intended for the delegated-OAuth Graph path; the credential rotation cadence and audit story is cleaner with a dedicated app reg.
- Service management reference: leave blank.
- Click Review + create → Create. Provisioning takes 30-90 seconds.
Step 2: Capture credentials
After provisioning, open the new Bot resource → Settings → Configuration. Capture:- Microsoft App ID — the GUID at the top of the Configuration page. This
becomes
teams_app_id. - App Tenant ID — also on the Configuration page. This becomes
teams_app_tenant_id. - Bot Type — confirm it matches what you chose in Step 1.6 (Single Tenant
or Multi Tenant). This becomes
teams_app_type.
teams_app_password):
- Click Manage Password next to the App ID. This takes you to the underlying Entra App Registration’s Certificates & secrets blade.
- Azure auto-creates a secret when the Bot resource is provisioned, but
the unmasked value is only shown at creation time — once the page
refreshes, only
La_*****…is visible. Delete the auto-created secret (trash icon), then click + New client secret:- Description:
genesis-<bot-handle> - Expires: 180 days (rotate per your security policy)
- Click Add
- Description:
- Immediately copy the Value column (the click-to-copy button next to
it). The value disappears on the next page load. Store it directly into
your secret manager /
bot_servicingrow in the next step — do not paste it into chat, tickets, or terminal scrollback.
Step 3: Enable the Teams channel
In the same Bot resource → Settings → Channels:- Find Microsoft Teams in the Available Channels list → click it.
- Accept the Microsoft Channel Publication Terms.
- Pick Microsoft Teams Commercial (or Microsoft Teams Government if your customer is on the Government cloud).
- Click Apply. A “Microsoft Teams channel enabled” notification confirms.
Step 4: Set the messaging endpoint
In the same Bot resource → Settings → Configuration:- Messaging endpoint:
<your-genesis-public-host>is your Genesis instance’s HTTPS hostname (e.g.genesis.your-company.com, or the ngrok URL during dev).<bot_id>is the Genesis bot’s id (case-sensitive — usuallyEve).
- Leave Enable Streaming Endpoint unchecked.
- Schema Transformation Version V1.3 is fine.
- Click Apply — a “Saved Configuration” notification confirms.
Step 5: Configure the bot in Genesis
In the Genesis dashboard, open Agents → your bot (e.g. Eve) → the Teams tab and enter the Bot Framework identity from Step 2:- App ID — the Microsoft App (client) ID.
- Client Secret — the client secret value (on later edits, leave this blank to keep the stored secret).
- Tenant ID — your Azure AD tenant (required for a SingleTenant app).
- App Type — SingleTenant or MultiTenant (match your app registration).
- Toggle Activate on Teams on, then click Save.
Deactivated agents do not receive new Teams messages. If an agent is
deactivated in Agents Center and Skills,
Genesis evicts its Teams adapter until it is reactivated.
The Activate on Teams toggle is the per-bot on/off switch. To temporarily
disable the Teams surface for a bot without losing its configuration, untoggle
it and restart Genesis. To disable Teams across all bots globally, set the
config field
teams.enabled to false (or the equivalent env var
GENESIS_TEAMS_ENABLED=false, which still works). That global switch is a
kill-switch: inbound POST /api/teams/messages/{bot_id} traffic and
teams_bot_action SEND_PROACTIVE are refused on the next request — you do
not need to wait for a session rebuild.Note that GENESIS_TEAMS_ENABLED supplies the default for teams.enabled,
so it applies only while that field has never been set explicitly. If someone
has saved teams.enabled through the config UI or API, the stored value wins
over the env var — use the override GENESIS__TEAMS__ENABLED=false if you
need the switch to take effect regardless of stored config.Checking whether a bot’s Teams adapter is really running
Saving credentials is not the same as having a live adapter — registration also requires Activate on Teams, a non-empty App ID, and a successful background runtime refresh after save (usually within a few seconds). To see the actual state for one bot:accepting_inbound: false means inbound Teams messages for that bot are being
rejected with 404 — check globally_enabled (server-wide teams.enabled)
and adapter_registered (per-bot adapter in the live registry). When
adapter_registered is false, reason tells you which registration
prerequisite is missing.
Step 6: Download the Teams app package
In the same Teams tab for the bot, click Download Teams app package. Genesis renders a Teams app.zip — ready to sideload — built from the bot’s
configured Bot Framework App ID.
The package contains:
manifest.json— the Teams app declaration (bot id, scopes, command list)color.png+outline.png— bot icons (default solid-color placeholders)
The Teams app
id (the GUID Teams uses to track installs) is a deterministic
uuid5 derived from the bot id, so re-downloading never creates a “new” app
from the customer’s perspective.Step 7: Sideload the manifest into your tenant
Open Teams Admin Center:- Manage apps → Actions (top right) → Upload new app →
Upload → pick the
.zipfrom Step 6. - A “New app added” confirmation appears. The app’s status will be “Available to: Everyone (org-wide default)” — meaning anyone in your tenant can install it from the Teams Apps catalog.
- If your tenant policy blocks custom apps, you’ll see an error. Go to Teams apps → Setup policies → Global → Upload custom apps and enable it, then retry.
Channel thread following and RSC consent
The generated app package requests the resource-specific consent (RSC) permissionChannelMessage.Read.Group. A team owner must consent to this
permission when the app is installed in a team or updated. Tenant policy can
prevent a team owner from granting it; in that case, ask your Microsoft 365
administrator to allow or approve the app.
Existing installations do not gain this permission automatically. Download a
new app package from Genesis, re-upload it in Teams Admin Center, and update the
team installation so a team owner can review and consent to the new permission.
Until that happens, the bot continues to receive only explicitly mentioned
channel messages, so untagged thread follow-ups cannot reach Genesis.
Because RSC is granted at install time, an in-place update does not always
re-trigger the consent prompt. If untagged follow-ups still do not arrive after
updating, remove the app from the team and add it again — a fresh install
reliably requests consent.
The failure is silent from inside Teams: a bot that never received a message and
a bot that received one and correctly ignored it both look identical. Genesis
logs tell them apart — an ignored message records
reason=not_directed_to_bot, whereas a message Microsoft never delivered leaves
no log line at all.
For operators who have enabled http.rate_limit.enabled (it is off by default),
RSC increases inbound /api/teams request volume because unengaged channel
messages still reach Genesis before being ignored. If that traffic should not
consume the general HTTP rate-limit budget, add /api/teams to the existing
configurable ignore-prefix list.
Step 8: Install the bot in a Teams chat or channel
In Teams Desktop (or Teams Web):- Click Apps in the left rail.
- Search for the bot name (e.g.
Eve). It appears under “Built for your org.” - Click Add — choose Personal for a 1:1 DM with the bot (simplest first smoke), or Add to a team / chat for group conversations.
- Once added, type a message. The bot responds within a few seconds.
Verifying end-to-end
A successful install + message produces this trace in the Genesis server log:bot_id mismatch
between Azure config and bot_servicing). The ngrok inspector at
http://127.0.0.1:4040 shows every request.
If the message reaches Genesis but Eve doesn’t respond, check:
/api/teams/healthreturnsregistered_bots > 0(the per-bot adapter must be registered).- The bot’s
bot_servicingrow hasteams_active='Y'AND a non-nullteams_app_id. - The bot’s session bootstrapped successfully — look for
STARTUP_PHASE create_app_sessions elapsed_s(success) versusFailed to create agent sessions(failure; commonly an unrelated LLM config issue).
What this path supports today
- ✅ User
@-mentions the bot in a channel or DMs it — bot responds in-thread - ✅ Messages are attributed to the bot principal (not a human admin)
- ✅ Channel install / uninstall events recorded in
teams_installations - ✅ Adaptive Card content rendered correctly when posted from agent tools
- ✅ Eve’s full agent loop (skills, tool calls, multi-turn context)
- ✅ Accepted Teams messages create Requests rows automatically by default (
ticket_automation.auto_track_chat_requests); channel reply chains reuse one Request, follow-ups attach by deterministic thread/quote correlation, and each row deep-links back to the originating Teams message when Genesis can build ateams.microsoft.commessage URL - ⚠️ Adaptive Card
Action.Execute(Approve/Reject buttons) — code path exists but needs a real card-sending agent flow to smoke - ⚠️ Proactive sends (
teams_bot_actionSEND_PROACTIVE) — requires conversation reference persistence (supported in both SQLAlchemy and legacy metadata modes; the bot must have received at least one message since the reference was stored), andteams.enabledmust betrueserver-wide. Useteams_bot_tools/LIST_REACHABLEto see which conversations are currently reachable.
Common pitfalls
Related
- Microsoft Teams (Graph connection) —
the delegated-OAuth path used by
teams_tools. - Teams Webhook Setup —
Power Automate / Workflow webhook path used by
teams_webhook_tools. - Microsoft Bot Framework documentation
- Teams app manifest schema

