> ## Documentation Index
> Fetch the complete documentation index at: https://docs.genesiscomputing.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Activate Eve in Teams (Bot Framework)

> Install Genesis bots as first-class Microsoft Teams apps via the Bot Framework — bot-attributed messages, @-mentions, channel install events, and Adaptive Card callbacks. End-to-end setup runbook.

# 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.

<Note>
  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](/docs/Setup/Microsoft-Teams).
  * **Power Automate webhooks** (`teams_webhook_tools`) — outbound-only
    alerts and reports, attributed to the Workflow. See
    [Teams Webhook Setup](/docs/Workspaces/Microsoft-Teams/Teams-Webhook-Setup).

  Use the Bot Framework path when you need two-way conversation, native
  `@`-mentions, channel install events, or Adaptive Card action callbacks.
</Note>

## 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](https://developer.microsoft.com/en-us/microsoft-365/dev-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](/docs/Workspaces/Microsoft-Teams/Activate-Genbots-On-Teams).
  For local development, expose port 8083 (or whichever port your stack uses)
  via [ngrok](https://ngrok.com/) or similar — `ngrok http 8083` gives an
  `https://*.ngrok-free.dev` URL pointing at your local Genesis.

## Architecture

```
+---------+      +----------------------+      +----------------+
| Teams   |----->| Microsoft Bot        |----->| Genesis        |
| user    |      | Framework Service    |      | (FastAPI :8083)|
+---------+      | (Azure, Microsoft-   |      |                |
                 |  signs JWTs)         |      | POST /api/     |
                 +----------------------+      | teams/messages |
                       ^                       | /{bot_id}      |
                       |                       +-------+--------+
                       |                               |
                       |   continue_conversation       |
                       +-------------------------------+
```

For each Genesis bot you want available in Teams, you create **one** Azure Bot
resource. The Azure Bot has a Microsoft App ID and a client secret. Genesis
stores those on the bot's `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`.

<Note>
  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](/docs/Workspaces/Microsoft-Teams/Activate-Genbots-On-Teams).
</Note>

<Warning>
  On Snowpark Container Services, the relay reaches Genesis through an
  application-role grant held by its Snowflake role. That grant belongs to the
  Genesis application object rather than to the account, so when the application
  object is dropped and recreated — as a reinstall does — the grant goes with it
  while the relay's user and role survive untouched. Teams then fails for every
  message, in a way that looks like a Genesis fault rather than a missing grant.

  Every identity signal stays healthy while this is broken — the service user is
  enabled, the key is valid, Snowflake login succeeds, and the relay's `/health`
  still reports `token_state` `valid` — so the relay authenticates and then
  receives 403s. Check the grant before anything else:
  `SHOW GRANTS TO ROLE <relay_role>` returning nothing means it must be
  re-applied. See
  [Activate Genesis Data Agents On Teams](/docs/Workspaces/Microsoft-Teams/Activate-Genbots-On-Teams).
</Warning>

## Step 1: Create the Azure Bot resource

In the [Azure Portal](https://portal.azure.com):

1. **Create a resource** → search for **Azure Bot** → **Create**.
2. Fill in **Bot handle** (any unique-in-Azure string, e.g. `genesis-eve-prod`).
3. Pick a **Resource group** (create one if needed).
4. **Data residency**: Global (regional only matters for compliance-bound
   deployments).
5. **Pricing tier**: **F0 (Free)** — the free tier is fine for dev and most
   production use.
6. **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 `AzureADMultipleOrgs` audience.
     <Note>
       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.
     </Note>
7. **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.
8. **Service management reference**: leave blank.
9. 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`.

To get the **client secret** (which becomes `teams_app_password`):

1. Click **Manage Password** next to the App ID. This takes you to the
   underlying Entra App Registration's **Certificates & secrets** blade.
2. 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**
3. **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_servicing` row 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**:

1. Find **Microsoft Teams** in the Available Channels list → click it.
2. Accept the Microsoft Channel Publication Terms.
3. Pick **Microsoft Teams Commercial** (or **Microsoft Teams Government** if
   your customer is on the Government cloud).
4. Click **Apply**. A "Microsoft Teams channel enabled" notification confirms.

## Step 4: Set the messaging endpoint

In the same Bot resource → **Settings → Configuration**:

* **Messaging endpoint**:
  ```
  https://<your-genesis-public-host>/api/teams/messages/<bot_id>
  ```
  * `<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 — usually `Eve`).
* 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**.

<Warning>
  Saving stores the credentials. Genesis refreshes the bot's Teams adapter
  asynchronously in the background — you do **not** need a full Genesis restart
  for credential changes to take effect. Allow a few seconds after save before
  testing inbound messages. If inbound traffic still fails JWT validation with
  `Invalid AppId passed on token`, confirm the App ID and tenant match the Azure
  registration and check `/api/teams/bot/{bot_id}/runtime`.
</Warning>

<Note>
  Deactivated agents do not receive new Teams messages. If an agent is
  deactivated in [Agents Center and Skills](/docs/User-Guide/Agents-Center-And-Skills),
  Genesis evicts its Teams adapter until it is reactivated.
</Note>

After the restart, the server log shows the adapter registered:

```text theme={null}
Teams adapter registered for bot Eve (app_id=…)
```

and the public health endpoint reports the registered count:

```bash theme={null}
curl https://<your-genesis-public-host>/api/teams/health
# → {"status":"ok","registered_bots":1}
```

<Note>
  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.
</Note>

### 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:

```bash theme={null}
curl -H "Authorization: Bearer <token>" \
  https://<your-genesis-public-host>/api/teams/bot/<bot_id>/runtime
# → {"adapter_registered": true, "globally_enabled": true,
#    "accepting_inbound": true, "decision": "registered",
#    "reason": "ok", ...}
```

`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)

<Warning>
  Set (and save) the bot's Teams credentials in **Step 5 before you download**.
  The package embeds the bot's **App ID**, so if you download it while the bot
  is still pointed at a different App ID, the sideloaded app's tokens won't
  match the bot and every message will 401. Re-download after any App ID change.
</Warning>

<Note>
  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.
</Note>

## Step 7: Sideload the manifest into your tenant

Open [Teams Admin Center](https://admin.teams.microsoft.com/policies/manage-apps):

1. **Manage apps** → **Actions** (top right) → **Upload new app** →
   **Upload** → pick the `.zip` from Step 6.
2. 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.
3. 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)
permission `ChannelMessage.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.

<Warning>
  This permission means Microsoft delivers every message from channels where
  the app is installed to Genesis. Genesis receives all of those channel
  messages, including messages in threads where the bot was not mentioned. It
  only acts on a channel thread after the bot has been `@`-mentioned in that
  thread, and `stop` ends that engagement.
</Warning>

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.

<Warning>
  **Updating the catalog app does nothing for a team running a sideloaded
  copy.** If the same bot was ever uploaded straight into a team as a custom
  app, that team keeps its own copy of the package with its own permissions.
  Publishing a new version to the org catalog leaves it untouched, and untagged
  messages keep not arriving even though Teams Admin Center shows the new
  version and the permission.

  Check for this when Teams Admin Center shows the app installed for **no one**
  while the bot is clearly present in a channel, or when several entries with
  the same bot name appear in the `@`-mention autocomplete. Remove every copy
  from the team, then install the catalog app once so a team owner consents to
  it.
</Warning>

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](https://teams.microsoft.com)):

1. Click **Apps** in the left rail.
2. Search for the bot name (e.g. `Eve`). It appears under "Built for your
   org."
3. Click **Add** — choose **Personal** for a 1:1 DM with the bot (simplest
   first smoke), or **Add to a team / chat** for group conversations.
4. 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:

```text theme={null}
TeamsBotOsInputAdapter enqueued message bot=<bot_id> tenant=… conv=… text_len=N
POST /api/teams/messages/<bot_id> -> 202 in 0.0Ns
… (LLM call) …
MESSAGE PROCESSING COMPLETE: <bot_id>/teams::<bot_id>::<tenant>::<conv>
```

If the activity arrives at ngrok but doesn't reach Genesis, that's a routing
problem (typically: messaging endpoint URL wrong, or `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/health` returns `registered_bots > 0` (the per-bot adapter
  must be registered).
* The bot's `bot_servicing` row has `teams_active='Y'` AND a non-null
  `teams_app_id`.
* The bot's session bootstrapped successfully — look for
  `STARTUP_PHASE create_app_sessions elapsed_s` (success) versus
  `Failed 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](/docs/User-Guide/Work-Center) 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 a `teams.microsoft.com` message 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_action` `SEND_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), and `teams.enabled` must be `true` server-wide. Use
  `teams_bot_tools` / `LIST_REACHABLE` to see which conversations are currently
  reachable.

## Common pitfalls

| Symptom | Cause | Fix |
| - | - | - |
| Teams Admin Center upload fails with "The property packageName is not allowed in the manifest schema" | Older manifest template | Upgrade Genesis — current versions emit a manifest without `packageName`, then re-download the app package. |
| `/api/teams/messages/<bot_id>` returns 401 in your dev env | Auth0 wrapper intercepting Bot Framework JWTs | The middleware already has a bypass for this path. If you've added custom auth in front, mirror it. |
| User sends one message, sees several identical replies in Teams | An older adapter posted every in-progress snapshot as a separate message | Upgrade Genesis — current versions send only the completed response. |
| Eve responds in the web UI but not in Teams, no log activity | Adapter not registered for the bot | Check `teams_active='Y'`, `teams_app_id` non-null, restart Genesis. Confirm `/api/teams/health` shows `registered_bots > 0`. |
| Eve responds but with the wrong attribution | The Azure Bot's App ID doesn't match what's in `bot_servicing.teams_app_id` | Confirm both match; rotate the secret if the values diverged. |
| `/api/teams/messages/<bot_id>` returns 503 | Transient failure fetching Microsoft Bot Framework signing metadata (JWKS) | Confirm **Config → Microsoft Teams** external access is assigned on SPCS; check egress and retry. Bot Framework retries 503s. |
| Activity reaches Genesis but returns 403 | Inbound `serviceUrl` host is not on the allowlist | Add the host to `teams.allowed_service_url_hosts` in **Config → Advanced Settings**, or verify the activity's `serviceUrl` is genuine Bot Framework traffic. |
| On SPCS: the relay authenticates to Snowflake normally but Genesis returns 403 for every activity | The relay role's application-role grant is gone — it is scoped to the application object, so recreating that object (a reinstall) takes the grant with it, leaving the identity itself looking healthy | Run `SHOW GRANTS TO ROLE <relay_role>`; if it returns nothing, re-run the relay grants. See [Activate Genesis Data Agents On Teams](/docs/Workspaces/Microsoft-Teams/Activate-Genbots-On-Teams). |
| Free-tier ngrok URL changes on restart | ngrok free tier rotates the subdomain | Update the Azure Bot's messaging endpoint each time ngrok restarts. For stable URLs use ngrok's reserved-domain feature (paid) or a real public hostname. |

## Related

* [Microsoft Teams (Graph connection)](/docs/Setup/Microsoft-Teams) —
  the delegated-OAuth path used by `teams_tools`.
* [Teams Webhook Setup](/docs/Workspaces/Microsoft-Teams/Teams-Webhook-Setup) —
  Power Automate / Workflow webhook path used by `teams_webhook_tools`.
* [Microsoft Bot Framework documentation](https://learn.microsoft.com/en-us/azure/bot-service/)
* [Teams app manifest schema](https://learn.microsoft.com/en-us/microsoftteams/platform/resources/schema/manifest-schema)
