@agentadmit/react
v2.2.0
Published
AgentAdmit React companion components: Connect button for the hosted consent page, connections list, consent settings, relationship consent, alerts, and admin panel. The consent step runs on the AgentAdmit hosted consent page.
Maintainers
Readme
AgentAdmit React SDK
Companion React components for apps that integrate AgentAdmit: the pages around the consent step, on your own site.
Where the consent step runs: on the AgentAdmit hosted consent page, opened on your app's behalf. Your backend creates a consent session (POST /api/v1/apps/{app_id}/consent-sessions), your frontend sends the signed-in user to the returned session_url, and the hosted page owns scope selection, duration, intent in the user's own words, existing-grant review, the presence ceremony, and the one-time token. Your app never sees the token and ships no consent UI or WebAuthn code. Full walkthrough: App Owner Guide, Step 4.
Get started: Sign up at agentadmit.com → Get your test keys → Install the backend SDK → Add the consent-session call and a Connect button → Optionally add these components. Test keys are available immediately after signup. Live keys become available when you subscribe an app.
What this package is for
| Component | Use it for |
|-----------|-----------|
| ConnectionsList | The user's active and pending agent connections, with purpose, intent, and revoke |
| ConsentSettingsPanel | The user's caller-identity consent switches (people, in-app AI, external agents) |
| RelationshipConsentPanel | Per-relationship consent switches for multi-party data (subject ↔ grantee) |
| PromptTemplates | After the user returns from the hosted page: templates that fit the granted scopes, with a token placeholder the user fills in |
| AlertsPanel, AgentAdmitAdminPanel | Admin surfaces: alerts and thresholds, connections, usage, activity |
| ConnectAgentButton / useConsentSession | Start the hosted consent page from your Agent Access page (your backend creates the session) |
Quick Start
npm install @agentadmit/reactimport { ConnectAgentButton, ConnectionsList, useAgentAdmit } from '@agentadmit/react';
// Import the default stylesheet (recommended)
import '@agentadmit/react/styles';
function AgentAccessPage() {
const { connections, loading, revokeConnection } = useAgentAdmit({
apiBase: '/agentadmit', // your backend proxy (see Backend Proxy Contract)
authToken: userSessionToken, // your app's user session token
});
return (
<div className="agent-admit-panel">
{/* Your backend creates the consent session with your aa_ API key and returns { session_url }.
The button sends the signed-in user to the hosted consent page; the return_url brings them back. */}
<ConnectAgentButton
createUrl="/api/agentadmit/consent-session"
requestHeaders={{ Authorization: `Bearer ${userSessionToken}` }}
/>
<ConnectionsList connections={connections} loading={loading} onRevoke={revokeConnection} />
</div>
);
}ConnectAgentButton (or the useConsentSession hook behind it) POSTs to your endpoint, expects { session_url } in the response, refuses anything that is not https:, and navigates there. Pass body for fields your backend forwards (a template id, a declared purpose), onSessionUrl to open the page your own way, and label / startingLabel / className for copy and styling.
useAgentAdmit lists and revokes the signed-in user's connections through your backend proxy (GET {apiBase}/connections, DELETE {apiBase}/connections/{id}). The proxy injects the user's app_user_id and calls AgentAdmit with your aa_ API key.
Where to Put It
Add an "Agent Access" page or tab in your app with the Connect button and the connections list. Common placements:
- Sidebar navigation item (recommended)
- Tab within Settings or Account page
- Dedicated route like
/settings/agent-access, which is also a goodreturn_urlfor the consent session
Styling & Customization
Default Styles (Recommended)
The SDK ships a production-ready default stylesheet. Import it once at your app's entry point:
// In your main entry file (e.g. main.tsx, _app.tsx, layout.tsx)
import '@agentadmit/react/styles';
// or equivalently:
import '@agentadmit/react/dist/styles/agent-admit-panel.css';The stylesheet is fully scoped to .agent-admit-panel - it won't affect any other part of your app.
CSS Custom Properties (Tokens)
Customize the look by overriding --aap-* tokens. Put this anywhere after the import:
/* globals.css or a <style> tag */
.agent-admit-panel {
/* Brand color */
--aap-color-primary: #7c3aed;
--aap-color-primary-hover: #6d28d9;
--aap-color-primary-text: #ffffff;
/* Shape */
--aap-radius: 10px;
--aap-radius-md: 14px;
--aap-radius-lg: 18px;
/* Typography */
--aap-font-family: 'Inter', sans-serif;
}Available tokens:
| Token | Default | Description |
|-------|---------|-------------|
| --aap-color-primary | #2563eb | Primary action color |
| --aap-color-danger | #dc2626 | Destructive actions, errors |
| --aap-color-bg | #ffffff | Panel background |
| --aap-color-surface | #f9fafb | Card / section surfaces |
| --aap-color-text | #111827 | Primary text |
| --aap-color-text-secondary | #4b5563 | Secondary/description text |
| --aap-color-border | #e5e7eb | Dividers and card borders |
| --aap-color-focus | #2563eb | Focus ring color |
| --aap-font-family | inherit | Inherits from host app by default |
| --aap-font-size-base | 16px | Input font size (min 16px - iOS zoom prevention) |
| --aap-radius | 6px | Default border radius |
| --aap-touch-target-min | 44px | Minimum touch target (Apple HIG, WCAG AAA) |
Dark Mode
Dark mode is automatic via prefers-color-scheme: dark. To force it:
<ConnectionsList theme="dark" /> // Forces dark (adds .aa-dark)
<ConnectionsList theme="light" /> // Forces light (adds .aa-light)
<ConnectionsList theme="system" /> // Follows OS preference (default)Custom CSS Classes
Every component accepts className. All internal elements use aa-* classes you can override:
| Class | Element |
|-------|--------|
| agent-admit-panel | Root container (token scope + CSS reset) |
| aa-panel | Panel layout (padding, border, shadow) |
| aa-btn-primary | Primary action buttons |
| aa-btn-secondary | Secondary / cancel buttons |
| aa-pill | Scope permission pills |
| aa-duration-option | Duration picker buttons |
| aa-token-display | Token display area |
| aa-template-card | Prompt template cards |
| aa-connection-card | Connection list items |
| aa-input, aa-field-input | Text inputs |
| aa-select | Select dropdowns |
| aa-tab | Tab bar buttons |
Responsive Behavior
The panel uses CSS container queries (@container), not viewport media queries. This means it responds to its own rendered width - not the browser window. The layout adapts correctly whether the panel is in a:
- Full-page route
- Modal dialog
- Sidebar or drawer
- Native mobile WebView
No configuration needed - just drop it in and it works at any width.
Accessibility
The default stylesheet is built to meet WCAG 2.2 AA and Apple HIG standards out of the box:
- All interactive elements:
min-height: 44pxtouch targets (Apple HIG, WCAG AAA) - All inputs:
font-size: 16pxminimum - prevents iOS Safari auto-zoom in WebViews :focus-visiblerings on all interactive elements - keyboard navigationprefers-reduced-motionrespected - all transitions disabled for motion-sensitive usersforced-colorsmedia query - Windows High Contrast Mode supported- Color contrast: ≥4.5:1 for body text, ≥3:1 for UI components (WCAG AA)
- ARIA attributes:
role,aria-expanded,aria-controls,aria-pressed,aria-checked,aria-liveon all components
Full compliance guide: agentadmit.com/docs/compliance
PromptTemplates (post-consent)
After the user returns from the hosted consent page, show the templates that fit the scopes they granted. The user pastes the token into the template themselves; your app never sees it.
import { PromptTemplates } from '@agentadmit/react';
<PromptTemplates
templates={yourTemplates} // { id, title, requiredScopes, template, editableFields?, role?, isHero? }
editableFields={yourFields} // { fieldKey: { label, placeholder, default } }
exampleCategories={yourExamples} // quick one-line prompts, filtered by scope
selectedScopes={connection.scopes} // the granted scopes, from the session outcome or ConnectionsList
userRole={user.role}
/>Omit token: the component then copies the template alone and the user adds the token themselves (the hosted page shows it to them once). Template and field shapes are documented in the App Owner Guide (Step 4, "Template data structures").
Declared purpose, user intent, and existing-grant review
These live on the hosted consent page. Your consent session's purpose (the app's reason, up to 300 chars) is shown before approval and recorded on the grant; the page offers the user an optional "Your Intent" field (their own words, recorded alongside it); and when the user already holds active grants for your app, the page blocks with a review step (revoke or knowingly keep each one) before a new grant can be created, enforced server-side. <ConnectionsList> shows purpose under the agent label and user_intent beside it, labeled "Your intent".
ConsentSettingsPanel (Caller-Identity Consent)
Independent per-user consent toggles for the three caller classes: people the user shares with, your in-app AI, and external AI agents. No toggle implies another; any combination is allowed. State lives in AgentAdmit's hosted Consent Ledger.
import { ConsentSettingsPanel } from '@agentadmit/react';
<ConsentSettingsPanel
apiBase="/agentadmit"
authToken={userSessionToken}
onConsentChange={(cls, granted) => console.log(cls, granted)}
/>Backend proxy contract (your server injects the user's app_user_id and calls AgentAdmit with your aa_ API key, which never ships to the browser):
GET {apiBase}/consent/settingsproxies AgentAdmitGET /api/v1/consent/settings?app_user_id=<user>and returns its JSON (settings,effective,app_defaults).PUT {apiBase}/consent/settingswith{ caller_class, granted }proxies AgentAdmitPUT /api/v1/consent/settingswithapp_user_idinjected andupdated_via: "user_page".
Props: showHumanSession (default false; most apps govern human sharing in their own UI), heading / description (override the panel copy — say WHOSE data these switches govern; this panel controls the signed-in user's OWN data and agents), copy (override label/description per class), presence (see below), theme, className, onConsentChange. The useConsentSettings hook is exported for custom layouts.
Hosted ceremony for consent changes (recommended)
The documented path for changing a user's own switches is AgentAdmit's hosted consent-change page. Your backend mints a session with POST /api/v1/consent/sessions ({ app_user_id, changes: [{ caller_class, granted }], return_url }) and your PUT {apiBase}/consent/settings proxy answers 200 { "ceremony_required": true, "ceremony_url": "https://agentadmit.com/consent-change/scsess_…" }. The panel then sends the user there; they confirm the exact change with Face ID, Touch ID, or a security key; AgentAdmit applies the switch itself with independently verifiable evidence and returns the user to return_url, where the panel refetches true state. Your app ships no WebAuthn code and never writes the switch.
<ConsentSettingsPanel
apiBase="/agentadmit"
authToken={userSessionToken}
// optional: open the hosted page your own way (default: full-page navigation)
onHostedCeremony={(url) => openSheet(url)}
/>Presence step-up on consent changes
A computer-use agent operating the user's logged-in session could otherwise flip these switches. Pass presence to require a WebAuthn ceremony (Touch ID, Windows Hello, passkey) before a change is accepted: when your proxy answers a consent PUT with 403 { "error": "presence_attestation_required" }, the panel runs the ceremony against your endpoints and retries the PUT once with the resulting single-use handle attached.
<ConsentSettingsPanel
apiBase="/agentadmit"
authToken={userSessionToken}
heading="Your AI & Agents"
description="Choose what your own AI agents and this app's AI may do with your data."
presence={{
optionsUrl: '/agentadmit/presence/options',
verifyUrl: '/agentadmit/presence/verify',
requestHeaders: { Authorization: `Bearer ${userSessionToken}` },
// attestationField: 'presence_attestation_id' (default) — use
// 'presence_session_id' for the AgentAdmit hosted-session contract.
}}
/>Your proxy must return the ceremony handle from the verify endpoint (presence_attestation_id for an app-native WebAuthn backend, or presence_session_id for the hosted contract) and consume it, single-use, before applying the change — the server side is the security boundary, not the browser. For full control, pass resolvePresence(ctx) to useConsentSettings instead of presence and return the exact body fields to merge into the retried PUT. For consent changes that need independently verifiable evidence, use the hosted ceremony sessions described under "Ceremony-confirmed changes" instead of an app-run ceremony.
RelationshipConsentPanel (Multi-Party Caller-Identity Consent)
Use RelationshipConsentPanel when the signed-in data owner controls how a specific third party may reach their data. A client can independently allow their trainer to view the client's data directly, use the app's in-app AI to review it, or use the trainer's external AI agents to review it. The same component fits doctor/patient, accountant/client, tutor/student, and other app-defined relationships.
import { RelationshipConsentPanel } from '@agentadmit/react';
<RelationshipConsentPanel
apiBase="/agentadmit"
authToken={clientSessionToken}
granteeUserId={trainer.id}
relationshipType="trainer"
granteeLabel="your trainer"
presence={{
optionsUrl: '/agentadmit/presence/options',
verifyUrl: '/agentadmit/presence/verify',
requestHeaders: { Authorization: `Bearer ${clientSessionToken}` },
}}
onConsentChange={(callerClass, granted) => {
console.log(callerClass, granted);
}}
/>All three rows are always independent. The defaults deny every relationship class until the data owner grants that specific subject/grantee pair.
Relationship proxy contract
Your backend is the security boundary. It MUST authenticate the signed-in data owner, derive subject_user_id from that session, verify that the requested grantee relationship exists in your product, and only then call AgentAdmit with your server-side aa_ API key. Never accept subject_user_id from the browser.
GET {apiBase}/consent/relationship/settings?grantee_user_id=<grantee>&relationship_type=<type>proxies AgentAdmitGET /api/v1/consent/relationship/settings, adding the session-derivedsubject_user_id.PUT {apiBase}/consent/relationship/settingsreceives{ grantee_user_id, relationship_type, caller_class, granted, scope_group? }, adds the session-derivedsubject_user_idandupdated_via: "user_page", then proxies AgentAdmitPUT /api/v1/consent/relationship/settings.
Ceremony-confirmed changes (strongest evidence)
RelationshipConsentPanel writes switches through your backend proxy, which carries the app-record evidence tier. For decisions that need independently verifiable proof, use a hosted ceremony session instead: your backend calls POST /api/v1/consent/relationship/sessions and opens the returned session_url for the data owner, who confirms the exact change with a passkey on AgentAdmit's hosted page. AgentAdmit witnesses the ceremony and records verifiable consent evidence — the owner's passkey signs a cryptographic commitment to the change set and labels shown. See the App Owner Guide's "Ceremony-confirmed relationship changes" section. The panel and ceremony sessions compose: render current state with the panel, route the consequential changes through a ceremony.
Props: granteeUserId, relationshipType, and granteeLabel are required. granteeLabel is user-facing copy (for example, "your trainer" or "Dr. Rivera"); it is never used as an authorization identifier. scopeGroup, heading, description, copy, presence, theme, className, and onConsentChange are optional. The useRelationshipConsentSettings hook is exported for custom layouts and supports a custom resolvePresence callback.
Admin Panel Component
The React SDK includes <AgentAdmitAdminPanel> for app owners and MCP server operators to embed in their admin dashboard:
import { AgentAdmitAdminPanel } from '@agentadmit/react';
<AgentAdmitAdminPanel
apiBase="/agentadmit"
authToken={adminJwt}
appId="app_yourappid"
/>Four tabs: Connections (all users, search/filter, revoke), Usage (calls vs tier, overage tracking), Alerts (embedded AlertsPanel with thresholds + kill switch), Activity (full audit trail with expandable details).
Declared purpose: when a connection carries a purpose field, the Connections tab shows it under the agent label, and the search box matches purpose text. Declared purpose: the user-facing reason recorded on the grant at the consent moment. Review-time record only, never an enforcement input.
User-declared intent: when a connection carries a user_intent field (the user's own words, distinct from the app's declared purpose), the Connections tab shows it in the expanded card as "User intent", Activity rows show it beside the purpose, and both search boxes match intent text. Like the purpose, it is a review-time record, never an enforcement input.
Consent evidence (opt-in, v1.10.0): pass evidence to add a "Consent evidence" expander to each connection card - the admin/audit view of the verifiable-consent-evidence surface (dispute resolution, "was this connection really authorized by a ceremony?"). Lazily fetched per card from GET {apiBase}/admin/connections/{connection_id}/evidence (contract below); nothing is requested until an admin asks. Off by default, so existing backends without the endpoint see no change. Tier labels keep the claim ceilings: a hosted-witnessed VCE record renders as independently verifiable; the app's own ceremony record and app-attested facts render as the app's attestation, never as independently verifiable; connections without evidence say so honestly, and fetch failures render an honest unavailable state rather than a fabricated tier. Evidence is a review-time record, never an enforcement input.
App owners see everything and can respond to abuse without leaving their app. Auto-refreshes every 30 seconds by default.
Add theme="light" (or "system") if your admin dashboard is not dark - the default is "dark".
Backend Proxy Contract (Admin Panel & Alerts)
<AgentAdmitAdminPanel> (via useAdminData) and <AlertsPanel> (via useAlerts) call your backend at apiBase, which proxies to AgentAdmit using your API key. Your backend must expose the endpoints below and return these exact JSON shapes - the hooks read these field names literally (e.g. usage, events, occurred_at). All requests carry Authorization: Bearer <authToken>; your backend must restrict every one of these endpoints to admin users.
Error convention (all endpoints): any non-2xx response with a JSON body containing error_description shows that message in the panel's error banner.
GET {apiBase}/admin/connections?app_id=...
{
"connections": [
{
"connection_id": "conn_abc123", // required
"status": "active", // "active" | "revoked" | "expired"
"scopes": ["read:orders"], // string[]
"user_id": "u_123",
"user_label": "[email protected]", // display name; falls back to user_id
"agent_id": "agent_9", // optional
"agent_label": "Claude", // display name; falls back to agent_id
"purpose": "Reconcile June invoices", // optional — declared purpose, shown under the agent label
"user_intent": "Make sure nothing is overdue", // optional — user-declared intent, shown in the expanded card
"role": "user", // optional
"created_at": "2026-06-12T19:00:00Z", // ISO 8601
"last_used": "2026-06-12T19:26:00Z", // optional
"expires_at": "2026-06-13T19:00:00Z" // optional
}
],
"total": 14
}GET {apiBase}/admin/usage?app_id=...
The hook reads response.usage - if that key is missing the Usage tab shows "No usage data available."
{
"usage": {
"app_id": "app_yourappid",
"tier": {
"name": "standard",
"call_limit": 10000, // number | null (null renders as unlimited / ∞)
"calls_used": 1234,
"calls_remaining": 8766, // number | null
"period_start": "2026-06-01T00:00:00Z", // optional
"period_end": "2026-07-01T00:00:00Z", // optional
"overage_calls": 0, // optional
"overage_enabled": false // optional
},
"active_connections": 2,
"total_connections": 14,
"breakdown": [ // optional - per-agent/scope/endpoint bars
{ "label": "Claude", "calls": 900 }
]
}
}GET {apiBase}/admin/activity?app_id=...&limit=50&offset=0
The hook reads response.events and response.total. Omit optional fields rather than sending null/empty strings.
{
"events": [
{
"occurred_at": "2026-06-12T19:26:17Z", // required, ISO 8601
"event_id": "evt_1", // optional (falls back to list index)
"connection_id": "conn_abc123",
"user_id": "u_123",
"user_label": "[email protected]",
"purpose": "Weekly workout summaries for my coach", // optional: declared purpose on the grant
"user_intent": "Keep my coach in the loop", // optional: user-declared intent on the grant
"agent_id": "agent_9",
"agent_label": "Claude",
"scope": "read:orders", // scope that was used
"action": "GET", // HTTP method or action name
"endpoint": "/api/orders", // resource path accessed
"status_code": 200,
"details": { "note": "..." } // optional, shown as expandable JSON
}
],
"total": 10
}DELETE {apiBase}/admin/connections/{connection_id}
Revokes any user's connection (proxy to the hosted /api/v1/revoke - that call is what actually kills the agent's tokens). Return any 2xx on success; the panel optimistically removes the row and then re-fetches the list.
GET {apiBase}/admin/connections/{connection_id}/evidence (only when evidence is enabled)
Admin variant of the user-facing consent-evidence route: your backend looks the connection up WITHOUT a user-ownership filter (admin guard instead), proxies the hosted GET /api/v1/connections/{connection_id}/evidence, and merges in your app's own ceremony record when you keep one:
{
"connection_id": "conn_abc123",
"display_tier": "app_record", // "hosted_vce" | "app_record" | "presence_fact" | "none"
"hosted": { // the hosted evidence endpoint's answer (or your degraded stub)
"evidence_available": true,
"tier": "presence_fact",
"reason": "app_attested_ceremony",
"claim": "…",
"ceremony": { "verified_at": "…", "uv": true, "method": "app:my_webauthn", "provenance": "app_attested" },
"commitment": { "hash": "…", "preimage_version": 1 }, // hosted_vce only
"ledger": { "tamper_evident": true, "granted_event_present": true }
},
"app_record": { // your own ceremony record, when you keep one
"present": true, "uv": true, "verified_at": "…",
"claim": "Verified by <your app>'s passkey ceremony at grant time (…; not independently verifiable)."
}
}ADMIN-ONLY, like the rest of these endpoints. Keep your claim strings inside the ceilings: never present an app record or app-attested fact as independently verifiable.
Alerts endpoints (useAlerts / the Alerts tab)
ADMIN-ONLY. All three alerts endpoints (both GETs and the POST) must be restricted to admin users by your backend proxy. The POST endpoint accepts
AlertConfigpayloads that includekill_switch_enabled, which controls the app-wide kill switch for all agent connections. Do not route end-user tokens to these endpoints.Platform-enforced since Aug 2026: AgentAdmit itself now rejects any weakening change made with API-key credentials (
403 weakening_requires_human) — disabling an alert, raising thresholds, or disabling the kill switch requires a human in the AgentAdmit dashboard. Because your backend proxy authenticates to AgentAdmit with your API key, callers reaching these endpoints through your proxy can only tighten protections; an agent (or a compromised caller) cannot defang the kill switch even if your proxy's admin gating fails. Keep the admin restriction anyway — defense in depth, and alert history is still sensitive.
| Method | Path | Returns |
|---|---|---|
| GET | {apiBase}/alerts/config?app_id=...[&connection_id=...] | { "app_id", "app_level": { "<alert_type>": AlertConfig }, "connection_overrides": {}, "alert_types": string[] } |
| GET | {apiBase}/alerts?app_id=...&limit=50&offset=0[&alert_type=...] | { "events": AlertEvent[], "total", "limit", "offset" } |
| POST | {apiBase}/alerts | body { "app_id", "alert_type", ...AlertConfig } → 2xx on success |
// AlertConfig (all fields optional)
{ "enabled": true, "threshold_value": 100, "threshold_window_minutes": 5,
"threshold_rate_per_minute": 20, "stale_days": 30,
"kill_switch_enabled": false, "kill_switch_threshold_value": 500,
"kill_switch_threshold_window_minutes": 5 }
// AlertEvent
{ "id": "evt_1", "app_id": "app_yourappid", "connection_id": "conn_abc123",
"alert_type": "volume_spike", "triggered_at": "2026-06-12T19:00:00Z",
"details": { "message": "..." } }These shapes match what the AgentAdmit hosted service returns from /api/v1/alerts*, so the alerts endpoints can be thin pass-through proxies; the /admin/* endpoints are assembled by your backend (connections + audit log + usage data), typically from the backend SDK's storage plus your own user table for user_label.
Important
Architecture: AgentAdmit uses mandatory hosted introspection. All token validation goes through api.agentadmit.com on the backend. The consent step runs on the hosted consent page. This React SDK handles companion frontend UI only. Token validation is handled by the backend SDK (Python/Node/Java/PHP/Ruby/Go).
In-app AI scopes. If your app has built-in AI features (analysis, plan generation, photo recognition), do not expose those as agent scopes. The user's AI agent can read the raw data and do the analysis itself. Exposing in-app AI endpoints to agents creates double cost for both you and your users. Define your scopes around raw data access, not in-app AI triggers.
Rate Limiting
The AgentAdmit API enforces rate limits and may return HTTP 429. Because this is a frontend React SDK, the hook surfaces rate limit information as state rather than auto-retrying (server-side retry is handled automatically by the backend SDKs).
Detecting rate limits
const {
revokeConnection,
isRateLimited, // true when last request was 429
rateLimitInfo, // { retryAfter, limit, remaining, reset }
clearRateLimit,
} = useAgentAdmit({ apiBase, authToken });
// In your UI
if (isRateLimited && rateLimitInfo?.retryAfter) {
return <p>Too many requests. Please try again in {Math.ceil(rateLimitInfo.retryAfter)} seconds.</p>;
}RateLimitInfo type
interface RateLimitInfo {
retryAfter: number | null; // Retry-After header (seconds), or null
limit: number | null; // X-RateLimit-Limit
remaining: number | null; // X-RateLimit-Remaining
reset: number | null; // X-RateLimit-Reset (Unix timestamp)
}Hook return values
| Property | Type | Description |
|----------|------|-------------|
| isRateLimited | boolean | true if last request returned 429 |
| rateLimitInfo | RateLimitInfo \| null | Rate limit details, or null |
| clearRateLimit | () => void | Manually clear rate limit state |
Rate limit state auto-clears on the next successful request.
Note: Automatic server-side retries with backoff are handled by the backend SDK (Python, Node.js, Go, etc.). The React hook intentionally surfaces the rate limit as UI state so your app can display feedback to the user.
Documentation
Full integration guide: https://agentadmit.com/docs/app-owner-guide Hosted consent page + template data structures: Step 4 of the guide
Data Collection & Privacy
The AgentAdmit React SDK is designed for maximum privacy compliance.
What the SDK transmits
- Auth token - Your user's JWT, provided by your app via the
authTokenprop. Sent as anAuthorizationheader. - Scope selections - The permissions the user selects in the UI. Sent to your API endpoint.
- Duration preference - The connection duration the user selects. Sent to your API endpoint.
What the SDK does NOT collect
- No device identifiers (IDFA, GAID, or device fingerprinting)
- No location, contacts, photos, or media
- No analytics, telemetry, or crash reporting
- No advertising identifiers or tracking
- No cookies or persistent local storage
- No Apple Required Reason APIs
Where data goes
ALL data is sent to the apiBase URL you configure - your own backend server. The SDK does not send data to AgentAdmit's servers or any third party. The SDK has zero hardcoded external domains.
Apple App Store
This package includes a PrivacyInfo.xcprivacy privacy manifest for React Native / iOS distribution. When filling out Apple's Privacy Nutrition Labels, the AgentAdmit SDK's data collection is minimal - see our compliance guide for copy-paste answers.
Google Play
When filling out the Google Play Data Safety form, the AgentAdmit SDK does not independently collect or share user data with third parties. All data processing occurs between the user's device and your own server. See our compliance guide for copy-paste Data Safety form answers.
License
All rights reserved. Patent pending.
AlertsPanel Component
ADMIN-ONLY SURFACE. Do not expose AlertsPanel to end users.
AlertsPanel(anduseAlerts) calls the/alerts/configand/alertsendpoints, including POST requests that mutate app-level alert configuration. TheAlertConfigpayload includeskill_switch_enabled, which controls the app-wide kill switch for all agent connections. Exposing these endpoints or this component to end users lets any end user disable your app's kill switch. Your backend proxy MUST restrict every alerts endpoint to admin users only -- this requirement is stated indocs/admin-proxy-contract.md. EmbedAlertsPanelonly in your admin dashboard, behind your existing admin authentication.
Drop-in component for alert history and threshold configuration. Embed this in your admin dashboard behind admin authentication:
import { AlertsPanel } from '@agentadmit/react';
// authToken MUST be an admin credential -- your backend proxy enforces admin-only access
// to all alerts endpoints (GET and POST), including kill_switch_enabled.
// Do NOT pass a regular user session token here.
<AlertsPanel apiBase="/agentadmit" authToken={adminSession.token} appId="app_abc123" />useAlerts Hook
import { useAlerts } from '@agentadmit/react';
// authToken MUST be an admin credential -- configureAlert POSTs app-level alert config
// including kill_switch_enabled. Use this hook only in admin contexts.
const { alertEvents, configureAlert, fetchAlertEvents } = useAlerts({
apiBase: '/agentadmit', authToken: adminSession.token, appId: 'app_abc123',
});
await configureAlert('volume_spike', { enabled: true, threshold_value: 100, threshold_window_minutes: 5 });Notifying Your Users
AgentAdmit detects anomalies, fires alerts, and (with kill switch) auto-revokes connections. How you notify your own users is up to you. AgentAdmit provides the data -- you deliver it through your own system (in-app notifications, email, push, etc.).
- Poll alerts -- Use the SDK methods above from your backend to check for new events, then notify users through your existing system.
- Webhook delivery (coming soon) -- Configure a webhook URL in your AgentAdmit dashboard. When an alert fires, AgentAdmit POSTs the payload to your server.
- React SDK -- Embed the
<AlertsPanel>component in your admin dashboard so admins can monitor alert history and adjust thresholds.
End-user connection activity
ConnectionActivity is an optional, read-only companion to ConnectionsList.
It expands on demand and shows recorded permission checks, not proof that a
business action completed. Import the stylesheet once.
import { ConnectionActivity } from '@agentadmit/react';
import '@agentadmit/react/styles';
<ConnectionActivity
apiBase="/api/agentadmit"
authToken={signedInUserToken}
connectionId={connection.connection_id}
theme="dark"
/>Your backend must implement GET /connections/{connection_id}/activity under
apiBase. Authenticate the app user, check connection ownership, derive
app_user_id from that session, and use your server-only app key to read
GET /api/v1/audit/export with both app_user_id and connection_id,
environment=live, format=json, from=now minus 30 days, and limit (1-50;
component requests 20). The optional cursor continues the export's oldest-first
chain order. Do not let browser query parameters override ownership or environment.
Rate-limit reads, return Cache-Control: private, no-store, check every returned
row's app/user/connection/environment, and fail closed on an upstream mismatch.
Return only this display contract (never a raw export):
{
"connection_id": "conn_example",
"window_days": 30,
"events": [{
"id": "row_example",
"timestamp": "2026-09-20T12:00:00Z",
"scope": "read:orders",
"label": "Read orders",
"decision": "allowed"
}],
"next_cursor": null
}Map hosted status success to allowed, scope_denied/consent_denied to
denied, confirmation_required to the same value, bound_exceeded to
limit_reached, and consent/confirmation-policy outages to unavailable.
Unrecognized statuses are unknown, never assumed successful. Use your static
permission catalog for labels; avoid rendering arbitrary endpoint paths, query
strings, metadata or error messages. Omit tokens/JTIs, IDs of other users,
request bodies, purpose/intent text, hashes, chain_input and raw evidence.
The component resets data on account/connection changes and aborts old requests. Loading failures are distinct from empty history. Retention can shorten the 30-day window, and some invalid/revoked-token attempts are rejected before an audit row is written. This display is not a complete attempt ledger or an independently verifiable evidence bundle. It is a React DOM component; native apps implement the same user-owned proxy contract with their own native UI.
