-
-
- Warning: this can permit your agent to spend real money. Use caution.
-
-
- AgentCard is currently in beta. Card issuance may be limited or waitlisted.
-
-
-
-
-
1. Create an AgentCard account
-
Run these commands:
-
- npm install -g agent-cards{'\n'}agent-cards signup
-
-
+// Capabilities the agent gains once connected, mirrored from AgentCard's MCP
+// tool set (create_card, list_cards, check_balance, …).
+const AGENTCARD_FEATURES: Array<{ included: boolean; label: string }> = [
+ { included: true, label: 'Create and manage virtual debit cards for your agent' },
+ { included: true, label: 'Check balances and review transactions' },
+ { included: true, label: 'Per-task spend limits enforced by Agentcard' },
+];
-
-
2. Add a payment method
-
- Run agent-cards payment-method to link
- a card via Stripe. This funds any virtual cards your agent creates.
-
-
+/**
+ * Settings card for connecting AgentCard via OAuth. Connect/disconnect reuse
+ * the /api/integrations/agentcard/{connect,disconnect} routes; disconnect is a
+ * native same-origin form POST so the route's Origin check passes and the 303
+ * redirect lands back on settings with a success/error param.
+ *
+ * This replaces the old "paste your AgentCard API key" flow: the user clicks
+ * Connect, authenticates with their own AgentCard account (magic-link +
+ * consent), and Kilo stores a per-user OAuth token — Kilo never sees a
+ * long-lived API key.
+ */
+function AgentCardCard({
+ connected,
+ oauthStatus,
+ accountEmail,
+ organizationId,
+}: {
+ connected: boolean;
+ oauthStatus: 'active' | 'action_required' | 'disconnected';
+ accountEmail: string | null;
+ organizationId: string | null;
+}) {
+ const [open, setOpen] = useState(false);
+ const [confirmDisconnect, setConfirmDisconnect] = useState(false);
+ const [isDisconnecting, setIsDisconnecting] = useState(false);
+ const disconnectFormRef = useRef
(null);
-
-
3. Copy your API key
-
- Open ~/.agent-cards/config.json and
- copy the jwt value into the field above.
-
-
+ const settingsPath = organizationId
+ ? `/organizations/${organizationId}/claw/settings`
+ : '/claw/settings';
+ const connectParams = new URLSearchParams({ returnTo: settingsPath });
+ if (organizationId) {
+ connectParams.set('organizationId', organizationId);
+ }
+ const connectUrl = `/api/integrations/agentcard/connect?${connectParams.toString()}`;
+ const disconnectAction = organizationId
+ ? `/api/integrations/agentcard/disconnect?organizationId=${encodeURIComponent(organizationId)}`
+ : '/api/integrations/agentcard/disconnect';
-
-
4. Upgrade your instance
-
- This feature requires the most recent version of OpenClaw. After saving your
- credentials, use Upgrade (not Redeploy) to install the latest image
- and activate AgentCard. Your agent will then have access to tools like{' '}
- create_card,{' '}
- list_cards, and{' '}
- check_balance.
-
-
+ const needsReconnect = oauthStatus === 'action_required';
+ const isHealthyConnected = connected && !needsReconnect;
-
- Learn more at{' '}
-
+
+
+
+
+
+
+
+
+
+
+
+ Warning: this can permit your agent to spend real money. Use caution.
+
+
+ {isHealthyConnected ? (
+ <>
+
+ {accountEmail ? `Connected as ${accountEmail}` : 'Connected'} · your agent can
+ create and spend virtual cards.
+
+
+ >
+ ) : (
+ <>
+ {needsReconnect && (
+
+ Your AgentCard connection needs to be re-authorized. Reconnect to resume
+ access.
+
+ )}
+
+ {AGENTCARD_FEATURES.map(feature => (
+ -
+
+ {feature.label}
+
+ ))}
+
+
+ >
+ )}
+
+
-
-
+
+
+ {/* Native form POST so the disconnect route's same-origin Origin check
+ passes; the 303 redirect navigates back to settings. */}
+
+
+ }
+ isPending={isDisconnecting}
+ pendingLabel="Disconnecting..."
+ onConfirm={() => {
+ const form = disconnectFormRef.current;
+ if (!form) {
+ toast.error('Could not disconnect Agentcard. Please try again.');
+ return;
+ }
+ setIsDisconnecting(true);
+ form.submit();
+ }}
+ />
+ >
);
}
@@ -2604,28 +2685,17 @@ export function SettingsTab({
)}
{/* ── Payments ── */}
- {toolEntries.some(e => e.id === 'agentcard') && (
-
-
Payments
-
- {toolEntries
- .filter(e => e.id === 'agentcard')
- .map(entry => (
- }
- />
- ))}
-
+
{/* ── Password Managers ── */}
{toolEntries.some(e => e.id === 'onepassword') && (
diff --git a/apps/web/src/app/(app)/claw/components/icons/AgentCardIcon.tsx b/apps/web/src/app/(app)/claw/components/icons/AgentCardIcon.tsx
index 4c85eca02c..43cf8d4cbe 100644
--- a/apps/web/src/app/(app)/claw/components/icons/AgentCardIcon.tsx
+++ b/apps/web/src/app/(app)/claw/components/icons/AgentCardIcon.tsx
@@ -1,12 +1,22 @@
+// The real Agentcard brand mark, taken from agentcard.sh
+// (public/landing/home/agentcard-logo-new.svg): a filled card with a chip
+// grid. The website asset uses a white gradient (for dark backgrounds); here
+// we fill with currentColor so the mark inherits the surrounding text color
+// and works in both light and dark themes. viewBox preserves the logo's
+// natural 39:28 aspect ratio.
export function AgentCardIcon({ className }: { className?: string }) {
return (
-