npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@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.

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/react
import { 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 good return_url for 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: 44px touch targets (Apple HIG, WCAG AAA)
  • All inputs: font-size: 16px minimum - prevents iOS Safari auto-zoom in WebViews
  • :focus-visible rings on all interactive elements - keyboard navigation
  • prefers-reduced-motion respected - all transitions disabled for motion-sensitive users
  • forced-colors media 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-live on 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/settings proxies AgentAdmit GET /api/v1/consent/settings?app_user_id=<user> and returns its JSON (settings, effective, app_defaults).
  • PUT {apiBase}/consent/settings with { caller_class, granted } proxies AgentAdmit PUT /api/v1/consent/settings with app_user_id injected and updated_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 AgentAdmit GET /api/v1/consent/relationship/settings, adding the session-derived subject_user_id.
  • PUT {apiBase}/consent/relationship/settings receives { grantee_user_id, relationship_type, caller_class, granted, scope_group? }, adds the session-derived subject_user_id and updated_via: "user_page", then proxies AgentAdmit PUT /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 AlertConfig payloads that include kill_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 authToken prop. Sent as an Authorization header.
  • 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 (and useAlerts) calls the /alerts/config and /alerts endpoints, including POST requests that mutate app-level alert configuration. The AlertConfig payload includes kill_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 in docs/admin-proxy-contract.md. Embed AlertsPanel only 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.