Connect Rat Things to Microsoft Teams#
Rat Things separates the immediate Teams acknowledgement from the potentially long-running agent job:
@Rat Things <prompt>
-> immediate: "Rat Things request received. I'll reply when session <id> completes a turn."
-> asynchronous Session Turn
-> terminal result delivered through the configured Teams egress
Microsoft gives an outgoing webhook five seconds to return its synchronous response. The delayed result therefore cannot come from that HTTP response after the connection closes.
Session boundaries#
- A verified Teams event resolves an owned Agent/environment source binding.
- The same tenant, sender and provider thread reuse a Session. Another sender or thread has independent Session history.
- A message during active work steers the current Turn; later input starts a new Turn. Durable input receipts prevent duplicate provider deliveries from adding the same input twice.
- The binding's authenticated creator owns the Session. Provider sender identity remains separate from resource ownership.
The Teams gateway must preserve the trusted provider tenant, sender and root-thread reference. Display text and tags are prompts, not authority or Session identifiers. See provider bindings for configuration.
Choose a connection mode#
| Mode | Available in this repository | Terminal result | Best use |
|---|---|---|---|
| Outgoing webhook + Workflow | Yes | Posts to a configured chat or channel, not necessarily the originating thread | Initial tenant integration and controlled deployments |
| Teams bot + threaded gateway | Gateway contract only | Replies to the originating activity | Production conversational experience after the gateway is implemented |
The LocalStack suite validates the complete threaded-gateway envelope, including the original
conversationId and activityId. It does not validate Microsoft identity, bot installation, or
live tenant delivery.
Prerequisites#
- A deployed Rat Things AWS stack and permission to update its Terraform configuration.
- Permission to manage apps for the target Teams team. Tenant policy must permit outgoing webhooks.
- AWS CLI access to the deployment account and permission to manage the intended Secrets Manager secrets.
- A public Teams channel for the current outgoing-webhook ingress. Outgoing webhooks do not support
private channels, personal chat, or general messages without an
@mention. - For Workflow egress, permission to create a Teams Workflow and at least one co-owner for operational continuity.
Use a separate webhook, Workflow, and set of secrets for every Rat Things environment.
1. Bootstrap the ingress secret and deploy#
The Teams callback URL exists only when the Teams route is enabled, while Teams reveals the real HMAC secret only after the outgoing webhook is created. Bootstrap the AWS secret first, deploy the route, and replace the secret value after Teams supplies it.
Create a Secrets Manager secret containing a temporary random value. This pipeline keeps the value out of shell arguments and command history:
export RAT_THINGS_TEAMS_SECRET_ID="rat-things/dev/teams-outgoing"
openssl rand -base64 32 \
| jq -Rn '{hmac_secret: input}' \
| aws secretsmanager create-secret \
--name "$RAT_THINGS_TEAMS_SECRET_ID" \
--secret-string file:///dev/stdin
Record the returned ARN and configure infra/terraform.tfvars:
teams_outgoing_webhook_secret_arn = "arn:aws:secretsmanager:REGION:ACCOUNT:secret:rat-things/dev/teams-outgoing-SUFFIX"
teams_webhook_enabled = true
# Start with the simple bridge. Configure its URL secret in the next section.
teams_delivery_mode = "workflow"
Do not put the HMAC value itself in Terraform, .env, a command-line argument, or source control.
Package and deploy the stack using the normal deployment flow:
npm ci
npm run package
terraform -chdir=infra init
terraform -chdir=infra plan -out=teams.tfplan
terraform -chdir=infra apply teams.tfplan
Read the callback URL from Terraform:
terraform -chdir=infra output -json webhook_urls | jq -r '.teams'
The expected path ends in /webhooks/teams.
2. Create the Teams outgoing webhook#
In Microsoft Teams:
Open the target team, select More options (...), and choose Manage team.
Open Apps. Under Upload an app, select Create an outgoing webhook.
Set the name to
Rat Things. This becomes the name users mention.Paste the Terraform-reported HTTPS callback URL, add a description and optional icon, and select Create.
Copy the HMAC security token from the confirmation dialog. Treat it as a password; Teams issues a unique token for this webhook.
In AWS Secrets Manager, replace the bootstrap secret value with this JSON shape:
{"hmac_secret":"<base64 HMAC token from Teams>"}
The Lambda resolves the secret on each invocation, so changing only the secret value does not require another Terraform apply. Rat Things verifies HMAC-SHA256 over the exact raw request body before parsing the activity.
3. Configure terminal-result delivery#
Option A: Teams Workflow bridge#
This is the connection mode that can be completed with the current repository.
In the destination Teams chat or channel, open Workflows.
Choose Post to a channel when a webhook request is received (or the equivalent chat template), select the destination, and create the Workflow.
Because the current notifier authenticates with the secret callback URL rather than a Microsoft bearer token, select the trigger's Anyone authentication option. If tenant policy prohibits anonymous triggers, use the bot/gateway design below instead of weakening that policy.
Add at least one co-owner. Workflows belong to users and can otherwise become orphaned.
Copy the generated callback URL and store it in a separate Secrets Manager secret using this shape:
{"url":"<Teams Workflow callback URL>"}Configure its ARN and apply Terraform:
teams_delivery_mode = "workflow" teams_workflow_url_secret_arn = "arn:aws:secretsmanager:REGION:ACCOUNT:secret:rat-things/dev/teams-workflow-SUFFIX"
The callback URL contains a credential and must never be committed or logged. The notifier sends an Adaptive Card with the run status, response, and run ID. The card appears in the Workflow's selected destination; it is not guaranteed to be a reply to the original mention.
Option B: exact delayed replies through a Teams bot#
Use this mode only after deploying an authenticated AWS-hosted Teams bot gateway. The gateway itself is not included yet; Rat Things currently provides and tests its internal delivery contract.
The Teams-facing implementation must:
- Register a single-tenant Azure Bot or supported managed-identity bot, enable its Microsoft Teams channel, and install the corresponding Teams app in every permitted destination.
- Receive and authenticate the original activity as that same bot identity. A different bot cannot safely assume an outgoing webhook's conversation reference is authorized for it.
- Retain the activity's trusted
serviceUrl,conversation.id, andid, bound to the tenant and installed bot identity. - Expose a private or strongly authenticated AWS gateway endpoint that accepts Rat Things'
reply-to-activityenvelope. Do not expose this as an unsigned public relay. - Acquire a Bot Connector access token and send the terminal activity to
/v3/conversations/{conversationId}/activities/{activityId}using the original trustedserviceUrl. Enforce tenant allowlists and idempotency by run ID.
After that gateway exists, store its trusted AWS URL in Secrets Manager:
{"url":"https://<private-or-authenticated-gateway>/teams/replies"}
Then configure and apply:
teams_delivery_mode = "threaded-gateway"
teams_reply_gateway_url_secret_arn = "arn:aws:secretsmanager:REGION:ACCOUNT:secret:rat-things/dev/teams-reply-gateway-SUFFIX"
Named Workflow routes are intentionally rejected in threaded-gateway mode. See
Channel adapters for the versioned envelope.
4. Session continuity and delivery#
A valid signed activity is resolved through an owned Agent binding and receives an input receipt. Activities from the same sender in the same Teams thread continue that sender's Session. Repeated provider deliveries reuse the reserved occurrence.
The notification stage reads a saved terminal root Turn. Workflow mode sends an Adaptive Card to the configured destination; threaded mode sends the reply envelope to the configured gateway. Delivery fencing prevents a provider retry from repeating an already confirmed notification. An uncertain external acknowledgement remains uncertain until it is reconciled.
Contributor harness instructions are in testing/README.md.
Troubleshooting#
| Symptom | Check |
|---|---|
| Teams reports that the webhook is unavailable | Callback URL is the current stack output, the route is deployed, and tenant policy allows outgoing webhooks |
401 invalid_signature |
Secret value is the exact base64 token from this outgoing-webhook installation; the raw request was not rewritten by a proxy |
| No immediate acknowledgement | API Gateway/Lambda latency stayed under Teams' five-second deadline and durable S3/DynamoDB/SQS submission succeeded |
| Acknowledgement appears but no terminal card | The Session has a saved terminal root Turn, the Workflow URL secret is current, delivery outbox retries are healthy, and the flow is enabled |
Workflow returns 401 or 403 |
Trigger authentication matches the notifier; the shipped adapter does not attach a Microsoft user bearer token |
| Result appears in the wrong place | Workflow mode targets its configured destination; use a completed bot gateway for exact-thread delivery |
| Gateway receives the envelope but Teams rejects it | Bot is installed, token audience and tenant are correct, and the stored serviceUrl and conversation reference belong to that bot identity |