Embedded web components
Add a catalog and connection manager to any web application, without React.
Complete the backend ticket setup first. @arc0/connect
is prepared locally and not yet published. Import it from the workspace or a local
package artifact. It has no React dependency.
<arc0-connect></arc0-connect>
<arc0-manage></arc0-manage>import { Arc0Client } from '@arc0/connect';
import '@arc0/connect/elements';
const client = new Arc0Client({
apiUrl: 'https://api.arc0.ai',
appUrl: 'https://app.arc0.ai',
getTicket: async ({ browserChallenge, signal }) => {
const response = await fetch('/api/arc0-ticket', {
method: 'POST', signal,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ browserChallenge }),
// Add your existing application CSRF protection.
});
if (!response.ok) throw new Error('Sign in again');
return response.json();
},
});
document.querySelector('arc0-connect').client = client;
document.querySelector('arc0-manage').client = client;Share one client between both components. Dispose it on sign-out or identity change,
remove the old components and create a fresh client for the new identity. Alternatively,
assign .options to an individual element so it owns its client and disposes it when
removed. <arc0-connections> is an alias for <arc0-manage>.
The catalog supports search, category filtering and each available authentication
method. The manager displays only the session user's personal connections, with
reconnect and an explicit confirmation before disconnect. Revocation first removes
runtime access and may remain revoking while provider cleanup finishes. Pagination
is available through “Load more connections”.
Script tag
Copy dist/arc0-connect.umd.js from your local package build into your own static
assets. The script registers the elements and exposes the same client constructor:
<script src="/vendor/arc0-connect.umd.js"></script>
<script>
const client = new Arc0Connect.Arc0Client({
apiUrl: 'https://api.arc0.ai',
appUrl: 'https://app.arc0.ai',
getTicket: window.getArc0Ticket,
});
document.querySelector('arc0-connect').client = client;
document.querySelector('arc0-manage').client = client;
</script>window.getArc0Ticket implements the same authenticated callback shown above. No
public CDN is assumed before publication. ESM, CommonJS and AMD are also supported.
Branding and browser policy
Both elements render in a shadow root. The authenticated branding API supplies the name, logo, support link, light/dark palettes and radius. The server determines whether “Powered by Arc0” is displayed under your plan. There is no browser override for that entitlement. A missing branding endpoint during a staggered rollout falls back to Arc0 defaults with attribution visible; a connection-only manager uses the same defaults if branding requires catalog scope. Other failures display an error.
Permit the API origin in CSP connect-src, your package source in script-src, and
your branding logo origin in img-src. On browsers supporting constructable
CSSStyleSheet.replaceSync() and shadow-root adoptedStyleSheets, the elements
use an adopted stylesheet: no inline stylesheet permission is required, including
with style-src 'self' or style-src 'none'. Theme tokens use CSSOM property
assignments rather than style-attribute text.
Older browsers fall back to an inline <style> in the shadow root. That fallback
must be authorized by style-src-elem (or style-src when style-src-elem
is absent): either a CSP hash matching the exact stylesheet text in that package
release, or an inline-style allowance such as 'unsafe-inline'. A nonce-only
policy does not authorize the fallback:
the package does not accept or propagate a nonce. Hosts requiring strict style
CSP should use a browser supporting adopted stylesheets or build their own UI
with the headless client. Never broaden script permissions for this fallback.
Embedded popup URLs include the non-secret ?embedded=1 policy marker. The hosted
worker serves these starts, and their API completion pages, with
Cross-Origin-Opener-Policy: same-origin. This severs the merchant opener before
provider navigation, preventing provider pages from navigating the merchant tab.
The client polls the authenticated result for the exact flow and authorization
attempt. A merchant may use same-origin-allow-popups, but that policy alone does
not protect against a provider navigating its opener. Result delivery does not
require a retained opener. The separate hosted catalog keeps its own
popup messaging policy; this marker grants no authority or subject selection.
Events and headless use
arc0:result bubbles a public {status, connectionId?} result. arc0:error contains
a fixed {code}. Neither includes tickets or tokens. A blocked popup reports
popup_blocked; invoke client.connect() directly from the click handler. Cancel connection stops waiting and cleans up the message listener. If a provider
severs the opener, exact-flow polling continues until completion or the bounded flow expiry.
An older API without flow receipts, an invalid receipt, or expiry yields
{status: "unknown"} and “Result unknown. Refresh connections.” in the elements.
The headless client exposes listApps(), listConnections({cursor,limit}),
connect({app,authMethod,reconnectConnectionId}), revoke(id), getSession(),
getBranding(), subscribe() and dispose(). Follow nextCursor when listing
connections. Explicitly confirm revocation in your own UI. The client never infers flow success from a connection-list change. Refresh
connections explicitly after an unknown result; a connection made in another tab
must not be attributed to this flow.
Use cancelConnect() (or “Cancel connection” in the component) to stop waiting
when you close a popup. A provider can make its popup appear closed through COOP;
the client keeps polling until completion or expiry rather than treating that as failure.
Browser requirements: native Web Crypto (in a secure context), custom elements,
Shadow DOM, AbortController, AbortSignal.any/timeout, URLSearchParams and CSS
color-mix. The client reports unsupported_browser when required crypto or abort
APIs are missing. This slice verifies Chromium; it does not claim Safari/Firefox
browser acceptance. Server expiry remains authoritative; elapsed-time deadlines
avoid relying on the device wall clock. Popup polling may obtain a fresh ticket
and retry a failed read once; writes are never automatically replayed.