Deliver approvals in Teams or email and resume the agent from a callback¶
Draft outline: not validated
Drafted with AI from official sources on 16 Oct 2026. Do not use on a live system until it is written, dry-run by someone other than the author and signed off (planned 2026-12-03). Author: James · dry-run: Prasanna · sign-off: Sivaraj. Work items: AGT-05-T05, AGT-05-T06, AGT-05-T07, AGT-05-T08.
1. Summary¶
When approvers work in Teams or Outlook and should not need a custom app.
Risk: High (forged callbacks could approve actions). Typical duration: 2-4 days.
2. When to use and when not to¶
Use it when: When approvers work in Teams or Outlook and should not need a custom app.
Do not use it when: to be written by the author.
3. What you must know first¶
- Power Automate approvals support first to respond, everyone must approve, custom responses and sequential types
- The Standard approvals connector has throttling limits per flow
- Universal Actions use Action.Execute so one card works in Teams and Outlook
- The callback must check that an interrupt is still pending before resuming
- Guest users cannot act on approvals from Outlook email
4. Prerequisites and access¶
- Power Automate or a Teams bot registration
- Callback endpoint with HTTPS and signature verification
- Approver groups in Entra ID
- Audit table
5. Steps¶
Outline of the steps; the author adds the commands and a check after each one.
- On interrupt, store an approval record with thread_id and payload
- Trigger the flow or bot to send the card to the approver group
- Sign the callback payload and verify it at the endpoint
- Check the pending interrupt and approver authorisation
- Write the audit record, then resume with Command(resume=...)
- Update the card to show the outcome
- Test duplicate clicks and late clicks after timeout
Draft snippet: Approval callback that verifies, audits and resumes the graph
from fastapi import FastAPI, Header, HTTPException
from langgraph.types import Command
from pydantic import BaseModel
from agent_app.runtime import audit_log, graph, verify_signature
app = FastAPI()
class Decision(BaseModel):
thread_id: str
approval_id: str
type: str # approve | edit | reject
approver: str
comment: str | None = None
edited_args: dict | None = None
@app.post("/approvals/callback")
def approval_callback(body: Decision, x_signature: str = Header(...)) -> dict:
if not verify_signature(body.model_dump_json(), x_signature):
raise HTTPException(status_code=401, detail="bad signature")
cfg = {"configurable": {"thread_id": body.thread_id}}
snapshot = graph.get_state(cfg)
if not snapshot.interrupts:
raise HTTPException(status_code=409, detail="no pending approval; already decided or expired")
audit_log(body) # write before resuming so the decision is never lost
graph.invoke(Command(resume=body.model_dump()), cfg)
return {"status": "resumed", "approval_id": body.approval_id}
Snippets to add:
snippets/services/approvals/callback.py: FastAPI approval callback (python)snippets/services/approvals/card.json: Adaptive Card with Action.Execute (json)snippets/services/approvals/flow.json: Power Automate flow export (json)
6. Verify it works¶
A second engineer sends a sandbox approval to a test Teams channel, approves, rejects and double-clicks; pass when each resumes once, the forged callback is refused and every decision is audited.
7. Known issues and fixes¶
| Symptom | Likely cause | Fix |
|---|---|---|
| Approver clicks twice and action runs twice | Callback not idempotent | Return 409 when no interrupt is pending |
| Approvals fail to create in bursts | Connector throttling | Queue requests and respect connector limits |
| External approver cannot respond | Guest user via Outlook | Route guests to the portal or a bot card |
8. Monitoring and alerts¶
| Signal | Alert when | Action |
|---|---|---|
| Callback signature failures | Any | Security reviews the source |
| Approval creation failures | More than 3 in 10 minutes | Check connector throttling and flow run history |
9. AWS and open-source equivalents¶
On AWS use Step Functions with a task token callback and SNS or Slack for notification. Open-source teams can use a Slack or email webhook with the same callback design.
10. Validation record¶
Pending. Planned dry-run scenario: A second engineer sends a sandbox approval to a test Teams channel, approves, rejects and double-clicks; pass when each resumes once, the forged callback is refused and every decision is audited.