โ†
AI Agent Builders & Citizen Developers
Proficient ยท M5 ยท lesson 5 of 34 ยท queued
Preview โ€” browse every lesson free. Enroll to mark lessons complete, open partner links and save your progress. Login & enroll โ†’
Designing the Human's UI: Slack, Teams, and Embedded Approvals
๐Ÿ“–
now learning

Designing the Human's UI: Slack, Teams, and Embedded Approvals

15 min

The L2 chapter on approve/edit/reject design covered the why and the basic anatomy. This lesson is for builders who have shipped an approval card, watched the data, and noticed something inconvenient: the card is technically functional and operationally broken. Reviewers are missing the field changes. Edit buttons are being clicked, the modal opens, and the edits being saved are minor typos when the agent's underlying claim was wrong. The card looks correct in design review and degrades on real traffic. The cause is almost always one of three things: the card asks the human to decide without giving them everything they need; the card buries the most important signal below the fold; or the card has no override-reason field, so the agent never learns from rejection. This lesson is how to build the production-grade approval surface, in Slack Block Kit and Microsoft Teams Adaptive Cards, with the under-30-seconds-to-decide budget intact and every component the reviewer actually needs. We will also look at embedded approvals โ€” inside Salesforce, Notion, Linear โ€” because Slack is not always the right surface.

The Five Things the Card Must Contain

An L2-grade card has six elements (context line, draft preview, risk signal, three buttons, attribution, timeout). An L3-grade card has those six plus five non-negotiables that turn the card from "notification with buttons" into "decision surface." A card missing any of these will leak quality in ways the dashboard cannot show.

1. The draft, but specifically: the diff if the agent is editing an existing artifact

If the agent is composing a new artifact (drafting a fresh email, drafting a fresh Notion doc), the draft preview is enough. If the agent is modifying an existing artifact โ€” updating a Salesforce field, editing an existing email thread reply, changing a contract clause โ€” the card must show the diff. Old value on the left, new value on the right, the change in the middle. Reviewers cannot reason about a field change without seeing what is changing. They will skip the change every time.

The diff is rendered inside the card body using Block Kit's section blocks with Mrkdwn formatting and a fixed-width font (```) for code-like diffs, or inside Teams Adaptive Cards as a FactSet with old/new values. The visual is what carries the comprehension; the labels are secondary.

2. The source(s) the agent used to construct the draft

Every claim in the draft links back to the source the agent retrieved. For a customer-email draft, the source is the customer's prior conversation, the relevant policy doc, and (if applicable) the CRM record. For a refund proposal, the source is the order record, the refund policy, and the customer's interaction history. The card surfaces the source as clickable references: [1] Order #4882-A ยท [2] Refund Policy v3.2 ยท [3] Customer thread. The reviewer can click and see the actual passage that drove the agent's decision. Without sources, the reviewer is asked to evaluate a confident assertion with no way to check it.

3. The agent's confidence and the alternatives it considered

The agent's own confidence on this draft expressed as a calibrated score (e.g. "high / medium / low" or a numerical 0.0โ€“1.0) and โ€” critically โ€” at least one alternative the agent considered and rejected. "I went with a $50 refund because the order was charged successfully and the customer reports the product was damaged. The alternative was no refund (no damage photo attached) which I rejected because the customer's account is in good standing and Section 4.2 of the refund policy allows refund without proof of damage for orders under $100." Showing the alternative is what lets the reviewer's brain disagree, because now the reviewer has two options to compare instead of one option to either accept or reject in a vacuum.

4. An override reason field on the edit and reject paths

When the reviewer edits the draft or rejects the proposed action, they are forced to provide a one-sentence reason. Not optional. Not "you can if you want." A required text field captures: "Why did you change this?" or "Why are you rejecting?" The field is short (max 280 chars), required, and feeds into the eval set (Lesson 3 of this chapter). Without the field, you have an approve/edit/reject card that loses 80% of its signal โ€” the override is logged but the meaning of the override is lost.

5. Single-click commit on the approve path

Approve is a button. One click. No confirmation dialog. The reviewer has already done the work of evaluating; do not punish them with a "are you sure?" modal that adds friction and trains them to click through any modal. Edit opens a modal; Reject opens the override-reason field; Approve commits. The asymmetry is intentional โ€” the high-friction paths are edit and reject because those are the ones where reasoning matters.

The L3-grade card has: a diff (if editing), the sources, the agent's confidence + at least one rejected alternative, a required override-reason field on edit and reject, and a single-click approve path. Missing any one of these and the card looks healthy in dashboard but leaks signal in production.

Slack Block Kit: The Production Pattern

Slack Block Kit has been the canonical interactive card format since 2019 and remains the default surface for human-in-the-loop approvals in 2026. Block Kit is JSON. Every card is a payload posted to a channel or DM via the Slack Web API (chat.postMessage) and updated in-place after a button click via chat.update. The interaction loop is: post the card, user clicks a button, Slack POSTs an action payload to your endpoint, your endpoint updates the card to show the decision state.

The full payload, annotated

The production-grade approval card is six Block Kit blocks in a single message:

  1. Header block. {"type": "header", "text": {"type": "plain_text", "text": "Refund approval: Order #4882-A ($50)"}}. The header is the one-line gist. Reviewer reads this in 0.5s and knows what they're being asked to decide.
  2. Context block (risk signal). {"type": "context", "elements": [{"type": "mrkdwn", "text": ":large_yellow_circle: Confidence: medium ยท 1 risk flag: customer first refund this quarter"}]}. Color-coded emoji + structured signal. The reviewer's eye snaps to the yellow circle.
  3. Section block (draft). The full proposed action in a code block. For refunds, that includes the amount, the order reference, and a one-paragraph rationale. For emails, the subject and full body truncated to 1,200 chars with a "view full" overflow option.
  4. Section block (sources). Three or four clickable links to the source documents/records. {"type": "section", "text": {"type": "mrkdwn", "text": "*Sources:* ยท ยท "}}.
  5. Section block (alternative considered). {"type": "section", "text": {"type": "mrkdwn", "text": "*Alternative considered:* Decline refund (no damage photo) โ€” rejected because customer's account is in good standing and ยง4.2 allows refund <$100 without proof."}}.
  6. Actions block (three buttons). {"type": "actions", "elements": [{"type": "button", "text": {"type": "plain_text", "text": "Approve"}, "style": "primary", "value": "approve_4882a"}, {"type": "button", "text": {"type": "plain_text", "text": "Edit"}, "value": "edit_4882a"}, {"type": "button", "text": {"type": "plain_text", "text": "Reject"}, "style": "danger", "value": "reject_4882a"}]}.

The interactive modal for Edit

When the reviewer clicks Edit, Slack opens a modal via views.open. The modal contains the editable fields (refund amount, rationale) pre-populated with the agent's draft, plus the required override-reason field. The reviewer modifies, types one sentence about why, and clicks "Save and approve." The card in the channel is then updated via chat.update to show "Approved by @sarah.k (edited) ยท refund: $35 (was $50) ยท reason: customer is a multi-year regular, generosity adjustment."

The interactive modal for Reject

Reject opens a smaller modal with just the required override-reason field. Reviewer types one sentence ("policy ยง4.2 requires damage photo for refunds over $25 and none was attached"), clicks Confirm Reject. Card updates to "Rejected by @sarah.k ยท reason: ยง4.2 requires damage photo."

The persisted decision state on the card

After approval, the buttons are removed and replaced with a context block: {"type": "context", "elements": [{"type": "mrkdwn", "text": ":white_check_mark: *Approved by @sarah.k* at 14:32 ยท executed at 14:32:04 ยท "}]}. The original draft + sources + alternative stay visible above; the buttons are replaced with the outcome. This is the audit trail surface โ€” anyone scrolling the channel later sees the full story without needing to click into a logs system.

Routing the card: named recipient, not channel-broadcast

The card is posted to the named reviewer as a DM, with an optional channel mirror in #approvals-audit as read-only. Named DM is what creates accountability; channel broadcast diffuses it. If the named reviewer doesn't act within the timeout, the card escalates: a follow-up DM to the backup reviewer (named in code, typically the reviewer's manager) and a channel ping. Default timeouts: 30 minutes for medium-stakes, 4 hours for low-stakes, 5 minutes for incident-grade.

Microsoft Teams Adaptive Cards: The Production Pattern

For organizations on the Microsoft stack, the equivalent surface is Adaptive Cards in Teams. Adaptive Cards are also JSON, also support interactive elements, and have been Microsoft's standard for chat-based UIs since 2018. The 2026 version is Adaptive Cards 1.6.

The mapping from Block Kit to Adaptive Cards

The components map closely but with different names:

  • Block Kit header โ†’ Adaptive Card TextBlock with size: Large, weight: Bolder.
  • Block Kit context (risk signal) โ†’ Adaptive Card ColumnSet with a colored icon (Image element) and a TextBlock.
  • Block Kit section (draft) โ†’ Adaptive Card TextBlock with wrap: true and fontType: Monospace for code-like content.
  • Block Kit links (sources) โ†’ Adaptive Card actions of type Action.OpenUrl rendered inline as text.
  • Block Kit actions block (three buttons) โ†’ Adaptive Card actions array with Action.Submit for approve/reject and Action.ShowCard for the inline edit form.

The inline edit form

Adaptive Cards' Action.ShowCard is the underutilized feature that makes the L3-grade card sing in Teams. Clicking Edit expands an inline sub-card with editable Input.Text / Input.Number fields pre-populated with the draft, plus a required Input.Text for the override reason. The reviewer never leaves the conversation; the edit form expands in place, and Submit collapses it back into the approval-confirmed state. This is the cleanest UX in either platform.

The full Adaptive Card payload pattern

The structure is a top-level AdaptiveCard with body (the read-only elements) and actions (the buttons). Body contains: header TextBlock, risk-signal ColumnSet, draft TextBlock, sources ColumnSet, alternative-considered TextBlock. Actions contains: Action.Submit "Approve" (style: positive), Action.ShowCard "Edit" (expanding to the inline form), Action.Submit "Reject" (style: destructive). The Reject action's data field carries the action ID; clicking it triggers a follow-up dialog for the override reason via Teams' task module.

Teams-specific gotchas

Teams Adaptive Cards have rendering inconsistencies across desktop, mobile, and web. The fixes that matter in 2026:

  • Mobile breakpoints. Test on Teams mobile (iOS and Android). The mobile renderer truncates long TextBlocks more aggressively. Set maxLines explicitly with a "see more" toggle action for the draft body if it exceeds 8 lines.
  • Image rendering for risk badges. Use inline emoji or unicode glyphs over external Image URLs; external images sometimes fail to load on corporate networks and the card renders without the risk signal.
  • Sequential submit. Avoid multiple Action.Submit calls that depend on order โ€” Teams does not guarantee delivery order; design the flow as discrete actions, not chained.
  • Power Platform connector vs. graph-API direct. Power Automate's "Post adaptive card and wait for response" is the no-code path; for higher volume, post directly via Microsoft Graph's chatMessage API for better latency and rate-limit control.

Embedded Approvals Inside the Tool

Slack and Teams are the standard surfaces. They are not always the right surface. The right surface is wherever the reviewer was already going to be when the approval is needed. For some workflows that is Salesforce, Notion, Linear, or the company's own internal app. Embedded approvals reduce the friction further than even a great Slack card.

When embedded beats chat

Three signals push you toward embedded approvals over chat:

  • The reviewer's primary workplace is the tool. A sales ops manager lives in Salesforce. A product manager lives in Linear. An editor lives in Notion. Sending them to Slack pulls them out of the surface where they were working; an embedded approval keeps them in place.
  • The decision context is the record itself. "Approve this contract amendment" makes more sense inside the contract document than as a Slack DM. The reviewer sees the document, the embedded card sits alongside, the approval happens in context.
  • The audit trail belongs to the tool. A change to a Salesforce opportunity should be auditable inside Salesforce's history view, not in a Slack channel. Embedded approvals keep the audit trail co-located with the artifact.

Salesforce embedded approvals (Agentforce / Lightning Apps)

Salesforce in 2026 supports Agentforce-native approval surfaces inside the Lightning experience. The agent proposes a field change; an Agentforce-rendered card appears on the record page with Approve/Edit/Reject. The card is the same shape as the Slack equivalent โ€” draft (diff), sources (linked Salesforce records), agent confidence, alternative considered, override reason on edit/reject. The reviewer approves inside the record; the audit log is written to the record's history.

Notion / Coda / Confluence embedded approvals

Document-edit agents (auto-summarize a meeting, propose a doc update) embed approvals as inline comments or callouts inside the document. The agent's proposed edit is visible as a tracked change; the Approve action accepts the change; Reject discards; Edit opens an inline editor. Notion AI and Coda's AI Assist have shipped this pattern at scale through 2025โ€“2026.

Linear / Jira embedded approvals

For agents that file tickets, route bugs, or propose ticket updates, the approval lives as an inline comment on the ticket with action buttons. The ticket assignee (or a designated approver) sees the proposal in-context, approves, and the change is applied. Linear's API supports custom comment renderers; Jira's Forge platform supports the same pattern via app extensions.

Internal-app embedded approvals

For organizations with internal admin tools (Retool, Internal.io, custom-built dashboards), the embedded approval is a component inside the admin surface. The reviewer's daily workflow is the admin app; the approval appears there. This is the highest-leverage embed for high-volume operations work because the reviewer never has to leave their primary surface.

The 30-Second Budget and How the Card Honors It

The under-30-seconds constraint is what makes or breaks the card. A card that takes 90 seconds to evaluate destroys the agent's leverage ratio and trains reviewers to skim. A card that compresses the decision to 12 seconds keeps the leverage and keeps the reviewer engaged. The work of the card design is collapsing the decision time while preserving the comprehension.

The eye-path of a fast review

The well-designed card guides the reviewer's eye through a specific path:

  1. Header (0โ€“1 second). Reviewer sees what kind of decision this is. "Refund approval: $50."
  2. Risk signal (1โ€“2 seconds). Color and badge tell the reviewer how much attention this card warrants. Green = skim. Yellow = read. Red = read carefully.
  3. Draft preview (3โ€“10 seconds). Reviewer reads the proposed action or scans the diff.
  4. Sources, if needed (10โ€“18 seconds). Reviewer clicks one source link if anything in the draft looks off.
  5. Alternative considered (18โ€“22 seconds). Reviewer reads the alternative; this is where they reason about whether the agent made the right call.
  6. Decision (22โ€“28 seconds). Reviewer clicks Approve, opens Edit, or opens Reject.

If any of those steps takes longer than its budget, the card is leaking quality. The most common failure points are step 3 (draft is too long, reviewer skims) and step 5 (alternative is missing or not specific, reviewer can't reason).

Compression techniques that preserve comprehension

  • Pre-summarize the draft. If the draft is a long customer email, prepend a one-sentence summary: "Apologizing for late shipment, offering $20 credit, asking customer to confirm new ETA." The summary is what the reviewer reads; the full body is read only if the summary raises a question.
  • Calibrated risk signal. The badge color is set by the agent's own confidence and the risk flags that fired. Not by every signal. A reviewer who sees red 80% of the time stops responding to red.
  • Sources inline, not as a sidebar. The source links are part of the card, not a separate panel. The reviewer can click one without navigating away.
  • Alternative as a single sentence. Not a paragraph. Not a table. One sentence: "Alternative: no refund, rejected because ยง4.2 allows refund under $100 without proof."

The Override-Reason Field: Treat It Like Training Data

The override-reason field on edit/reject is the single most underutilized component of the approval card. Most cards have no field. Cards that have a field have it as optional. Cards that have it as required let it be free-text with no structure, so the data is unusable for downstream training.

The structured override-reason pattern

Make the override-reason field a structured input with a category dropdown plus a short free-text. The dropdown has 4โ€“6 named categories:

  • Wrong amount / value โ€” the agent's numeric or quantitative choice was wrong.
  • Wrong tone / phrasing โ€” the content was approximately right but needed style adjustment.
  • Missing context โ€” the agent didn't have or didn't use information that would have changed the decision.
  • Policy violation โ€” the agent's proposed action violated a documented policy.
  • Wrong recipient / scope โ€” the agent was acting on the wrong target.
  • Other (free-text required) โ€” for cases that don't fit the categories.

The structured category plus the free-text means you can build a dashboard that says: "of the 47 overrides this week, 28 were 'wrong tone,' 11 were 'missing context,' 5 were 'wrong amount,' 3 were 'policy violation.'" Now the agent improvement work has a target. Without the structure, you have free-text that nobody reads.

Why required (not optional) matters

Optional fields are filled in 5โ€“15% of the time. Required fields are filled 100% of the time. The data quality differential is the entire training signal. The 30-second budget can absorb the 3 seconds of an override-reason field; what it cannot absorb is no feedback loop at all.

What to do with the override data โ€” the bridge to Lesson 3

Every captured override becomes a candidate row in the eval set. Reviewer edited the agent's $50 refund to $35 because "customer is a multi-year regular, generosity adjustment"? That's a new eval case: customer with N-year tenure โ†’ refund recommendation should account for tenure. The structured category plus the free-text is enough signal to build a categorized eval set and to retrain on the patterns. We dig into the feedback-loop mechanics in Lesson 3 of this chapter.

Story: the Card Redesign That Cut TTA and Raised Override Quality

A 90-person SaaS company in March 2026 had a customer-success agent that drafted renewal-risk follow-up emails. The first version of the card was a standard Slack approve/edit/reject: header, draft body, three buttons. Median TTA was 41 seconds. Approval rate was 78%. Override rate was 22% but the overrides were mostly minor tone tweaks; the underlying customer-risk classification the agent was making didn't change.

The CS leader pulled 30 of the rejected drafts and noticed something. In 19 of the 30, the agent had used a wrong-segment classification (e.g., treating a Series A customer like a Series C). The reviewers were editing the email tone but not flagging the segment error, because the segment error wasn't visible on the card. The agent's "this is a high-risk Series A renewal" reasoning was buried in a tool-call log nobody saw.

The team redesigned the card with the L3-grade structure:

  • Header gained the segment. "Renewal follow-up: Acme Co. (Series A, $84K ARR, risk: high)" โ€” now the segment is the first thing the reviewer sees.
  • Sources block added. Customer CRM record, prior interaction thread, the contract โ€” three clickable links.
  • Alternative considered added. "Alternative: standard 30-day check-in (rejected because two open tickets unresolved in past 14 days)." Now the reviewer can see what the agent ruled out.
  • Override-reason field structured. Category dropdown (wrong segment, wrong tone, missing context, etc.) plus required one-sentence note.

After 60 days on the redesigned card:

  • Median TTA: 41 seconds โ†’ 24 seconds. The card got faster despite having more content. Because the structure guided the eye and the summary was at the top, reviewers spent less time reading the full body.
  • Approval rate: 78% โ†’ 71%. The rate went down because reviewers now caught segment errors they were missing before โ€” overrides became more accurate, not less.
  • Override-reason categorization showed 60% of overrides were "wrong segment classification." That single insight drove a model fine-tune on segment classification that cut wrong-segment overrides from 60% of overrides to 18% over the next eval cycle.

The card redesign was the fix. The mode (Approve) was right; the surface design had been leaving the segment context invisible. The story illustrates why the five non-negotiables matter โ€” they are not aesthetic preferences; they are the difference between a card that captures signal and a card that loses it.

Anti-Patterns: Cards That Look Fine and Are Broken

The "everything is yellow" card

The risk badge is set to yellow on every card because "we want reviewers to pay attention to everything." Within two weeks, reviewers ignore the badge entirely. The badge must be calibrated โ€” green on confident routine cases, yellow on flagged ones, red on rare risk fires. If everything is yellow, nothing is yellow.

The "we'll add the sources later" card

The card ships without source links. The reviewer is asked to trust the agent's claims without verification. Approval rates are high until something goes wrong publicly, then trust collapses and the program is paused. The fix is to ship with sources on day one โ€” clickable links into the CRM, the policy doc, the prior thread. The Perplexity citation pattern transferred to operator workflows.

The "free-text override only" card

The override field is a free-text textarea with no structure. Reviewers type "wrong" or "fix tone" or nothing. The aggregate is unparseable. After two months, the team has a thousand override records and zero usable training signal. The fix is the structured category dropdown.

The "approve with confirmation" card

Clicking Approve opens a "are you sure?" dialog. The first 50 cards, reviewers click Confirm thoughtfully. By card 200, they click Confirm reflexively. The confirmation dialog has trained them to ignore confirmations. Approve is one click; Edit and Reject are two clicks. The asymmetry pre-attentions the cases where reasoning is happening.

The "channel broadcast" card

The card is posted to #approvals for any team member to grab. First two weeks, two senior people grab everything. By week three, both senior people get bored or busy and stop grabbing. Cards pile up. The team adds a "claim" button to fix it. Now the bottleneck is "who is the named owner of this card" which is what the named-DM routing was for in the first place. The fix is named DM routing with channel mirror as audit-only.

The "100-line draft" card

The agent drafts a long-form artifact (a 6-paragraph customer email, a full QBR section) and the card shows the entire draft inline. Reviewers scroll, then skim, then approve. The fix is to pre-summarize at the top: one sentence of "what this draft does" before the full body. Reviewers read the summary; if it raises a question, they read the body.

Implementation Platforms and the 2026 Choice

The card payload is the easy part. The harder part is the surrounding plumbing: posting the card at the right moment, handling the button click, updating the card with the decision, capturing the override into a downstream eval store. The 2026 choice has narrowed.

The four platforms operators ship on

  • n8n. The Slack node and Teams node both support interactive card posting and webhook callbacks. The flow is: agent node produces draft โ†’ Slack node posts card โ†’ flow waits โ†’ webhook receives action โ†’ conditional branches on decision. Self-hosted or cloud; the same flow ships either way.
  • Lindy. Native human-approval step with a Slack/Teams surface. The card layout is templated; less control than n8n but faster to ship. Good for teams that want the approval without writing JSON.
  • LangGraph + a custom Slack app. The LangGraph state machine pauses on a "human approval" node; a custom Slack app posts the card and writes the resulting decision back to LangGraph state. Most control, most code; usual choice for engineering-heavy teams.
  • Salesforce Agentforce. Native approval surface inside Lightning experience for Salesforce-resident agents. Lowest friction for Salesforce-centric ops; least portable.

The decision factors

Three factors:

  1. Where do your reviewers live? Slack-first orgs use Slack. Microsoft-first orgs use Teams. Salesforce-first ops orgs use embedded Salesforce.
  2. How much customization do you need? Templated cards (Lindy, Agentforce) ship fast. Custom Block Kit JSON in n8n or a code-first LangGraph integration gives you complete control over layout, branching, and override-reason capture.
  3. What is the downstream destination of the override data? If you're feeding Braintrust / LangSmith / Langfuse, you need a webhook step that writes the structured override-reason into the eval store. Both n8n and the LangGraph approach handle this cleanly. Templated platforms usually require a follow-up integration.

Key Takeaways

  • The L3-grade card has five non-negotiables: a diff (when editing), source links, agent confidence + at least one rejected alternative, a required structured override-reason field, and single-click approve.
  • Slack Block Kit is the canonical Slack surface. Six blocks: header, risk-signal context, draft section, sources, alternative considered, three-button actions. Modal for edit; modal for reject; persisted decision state after action.
  • Teams Adaptive Cards 1.6 is the equivalent. Action.ShowCard is the underutilized feature for inline edit forms. Test mobile rendering; prefer inline emoji over external images.
  • Embedded approvals beat chat when: reviewer lives in the tool (Salesforce, Notion, Linear), the decision context is the record itself, and the audit trail belongs to the tool.
  • The 30-second budget has a known eye-path: header (1s) โ†’ risk signal (2s) โ†’ draft (10s) โ†’ sources (18s) โ†’ alternative (22s) โ†’ decision (28s). Each step has a budget; missing time at one step means the card is leaking.
  • The override-reason field is the most-skipped component. Required, structured (4โ€“6 category dropdown + free-text), feeds the eval set. Without it, you have audit logs but no training signal.
  • Pre-attention via color works when calibrated. Green-yellow-red on actual risk signal, not on every card. Reviewers spend 3โ€“4x more time on red badges than green.
  • The card-redesign story: adding the segment to the header, sources, alternative considered, and structured override reason dropped TTA from 41s to 24s while raising override quality and exposing a 60%-of-overrides classification problem.
  • Five common anti-patterns: everything-is-yellow, no-sources, free-text-override-only, approve-with-confirmation, channel-broadcast. Each looks fine in design and leaks in production.
  • Four shipping platforms in 2026: n8n (Block Kit JSON), Lindy (templated), LangGraph + custom Slack app (code-first), Salesforce Agentforce (embedded). Choose by where reviewers live and how much customization you need.