Approval Card
An agent asks its owner to approve a risky action — who asks, what, on which records, why, until when — with one Approve / Decline for the whole request.
- Status
- Since
0.25.28- Accessibility pattern
- labelled region, described buttons, announced outcome
Last updated
Claude Code via Manoj, agentwants to move 2 tasks to Trash
- #12 Update launch specMarketing
- #14 Draft copyMarketing
“These duplicate #10 and #11.”
You can restore them from Trash for 30 days.
Requested Expires
Install
Configure registry credentials first using the Quickstart. Before copying this item or any transitive registry dependency, verify the signed manifest and save the exact bytes for Approval Card before installation. Retain the digest printed by this command.
pnpm exec vegastack-design verify approval-cardInstall with the pinned shadcn CLI. Bare shadcn does not verify VegaStack signature or integrity.
pnpm dlx shadcn@4.21.0 add @vegastack/approval-cardRun the exact offline --post-write command printed by the preflight, using its saved item path and independently retained --expected-integrity digest. Use the saved preflight for every transitive registry dependency too; stop on any mismatch before using the components.
The same command installs the registry items it composes: @vegastack/button, @vegastack/person-avatar, @vegastack/relative-time, @vegastack/use-announcer.
Usage
import { ApprovalCard } from "@/components/ui/approval-card";
<ApprovalCard
actor={{ name: "Claude Code", kind: "agent", via: "Manoj" }}
verb={
<>
wants to <strong>move 2 tasks to Trash</strong>
</>
}
items={tasks.map((t) => ({
id: t.id,
title: t.title,
href: t.href,
meta: t.space,
}))}
reason="These duplicate #10 and #11."
reassurance="You can restore them from Trash for 30 days."
requestedAt={request.createdAt}
expiresAt={request.expiresAt}
status={request.status}
onApprove={() => approve(request.id)}
onDecline={() => decline(request.id)}
/>;The host owns the request: it passes status back after a decision, and an onApprove or
onDecline that returns a promise keeps its button busy (a rejection shows its message under the
card). The actor is a Person with kind: "agent", so it reads exactly as the agent does in
comments, activity and pickers: the purple bot, "Claude Code via Manoj" and the "Agent" label.
Scope
One decision covers the whole request — all or nothing. List the first few records in items
and count the rest in moreCount ("and 3 more"). Put it above a record's description, in an inbox
detail sheet, or in a dialog; it fills its container.
Examples
Claude Code via Manoj, agentwants to move 2 tasks to Trash
- #12 Update launch specMarketing
- #14 Draft copyMarketing
“These duplicate #10 and #11.”
You can restore them from Trash for 30 days.
Requested Expires
Outcomes
approved, declined, expired, cancelled and already (answered on another device) replace
the buttons with a line; statusLine swaps in your own words ("Approved by you · just now"). A
viewer who canDecide={false} sees whose approval it waits for.
Claude Code via Manoj, agentwants to move 2 tasks to Trash
- #12 Update launch specMarketing
- #14 Draft copyMarketing
“These duplicate #10 and #11.”
You can restore them from Trash for 30 days.
Requested
Approved by you · just now
Claude Code via Manoj, agentwants to move 2 tasks to Trash
- #12 Update launch specMarketing
- #14 Draft copyMarketing
“These duplicate #10 and #11.”
You can restore them from Trash for 30 days.
Requested
Declined. Claude Code was told.
Claude Code via Manoj, agentwants to move 2 tasks to Trash
- #12 Update launch specMarketing
- #14 Draft copyMarketing
“These duplicate #10 and #11.”
You can restore them from Trash for 30 days.
Requested
Expired. Claude Code can ask again.
Claude Code via Manoj, agentwants to move 2 tasks to Trash
- #12 Update launch specMarketing
- #14 Draft copyMarketing
and 3 more
“These duplicate #10 and #11.”
You can restore them from Trash for 30 days.
Requested Expires
Waiting for Manoj's approval
Narrow
Under a 24rem container the buttons stack full width, Approve on top, and the times move under them.
Claude Code via Manoj, agentwants to move 2 tasks to Trash
- #12 Update launch specMarketing
- #14 Draft copyMarketing
You can restore them from Trash for 30 days.
Requested Expires
API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
actor* | Person | — | Who asks — an agent (kind: "agent", via its owner). |
requestedAt* | string | number | Date | — | When it was asked. |
verb* | React.ReactNode | — | What it wants to do, after its name: "wants to move 2 tasks to Trash". |
canDecide | boolean | true | The viewer may decide — the agent's owner. Anyone else sees "Waiting for Manoj's approval". |
expiresAt | string | number | Date | null | — | When the request lapses, shown while it waits: "Expires in 7 days". |
items | readonly ApprovalCardItem[] | [] | The records it touches; list the first few and count the rest in moreCount. |
moreCount | number | 0 | How many more records it touches than items lists: "and 3 more". |
now | number | — | Pin the relative times' clock (docs, tests). |
onApprove | (() => void | Promise<unknown>) | — | Approve the whole request; a promise keeps Approve busy. |
onDecline | (() => void | Promise<unknown>) | — | Decline the whole request; a promise keeps Decline busy. |
reason | React.ReactNode | — | The agent's own reason, quoted. |
reassurance | React.ReactNode | — | What makes the action safe to approve: "You can restore them from Trash for 30 days." |
status | ApprovalCardStatus | "pending" | Where it stands. pending shows Approve / Decline to whoever canDecide; approving keeps
Approve busy; the rest show the outcome in place of the buttons. |
statusLine | React.ReactNode | the status's own line | The outcome line in place of the default for status ("Approved by you · just now"). |
Data attributes and CSS variables on ApprovalCard
| Attribute | Values |
|---|---|
data-slot | "approval-card" | "approval-card-actions" | "approval-card-footer" | "approval-card-header" | "approval-card-item" | "approval-card-items" | "approval-card-meta" | "approval-card-more" | "approval-card-reason" | "approval-card-reassurance" | "approval-card-status" | "approval-card-title" | "approval-card-verb" |
data-status | mirrors a prop or state value |
Accessibility
- The card is a region named by its title: "Claude Code via Manoj, agent wants to move 2 tasks to Trash". Approve and Decline are described by the same title.
- A decision made while the card is on screen is announced politely; the first paint is not.
- Status is never colour alone: each outcome is a sentence with its icon.
| Key | Action |
|---|---|
| Tab | Move through the record links and buttons. |
| Enter / Space | Approve or decline. |
| Contract | States tested |
|---|---|
| Behaviour | pending, approving, approved, declined, expired, cancelled, already, cannot-decide, busy, error |
| Accessibility | labeled-region, described-buttons, announced-outcome, browser-accessibility-test |
| Visual | pending, decided, narrow |