UIAP SDK API
UIAP SDK API v0.1
Section titled “UIAP SDK API v0.1”| Field | Value |
|---|---|
| Status | Draft |
| Version | 0.1 |
| Date | 2026-03-27 |
| Dependencies | [UIAP-CORE], [UIAP-WEB] |
| Editors | Patrick |
1. Purpose
Section titled “1. Purpose”UIAP SDK API v0.1 defines the integration and runtime API for Web applications that:
- publish a
PageGraph, - register Actions,
- evaluate Policy locally,
- emit Signals,
- operate Frame Bridges,
- optionally render presentation features such as Overlay/Ghost Cursor.
The SDK is the first-party layer within the app. This is exactly where control belongs, because CSP can restrict external scripts and the Same-Origin Policy limits direct DOM access across origins. For cross-origin communication, postMessage() is the appropriate bridge mechanism. ([MDN Web Docs][3])
Normative Terms
Section titled “Normative Terms”The key words MUST, MUST NOT, SHOULD, MAY in this document are to be interpreted as described in RFC 2119 and BCP 14, when and only when they appear in ALL CAPS.
2. Packaging and Installation Modes
Section titled “2. Packaging and Installation Modes”2.1 Recommended Packages
Section titled “2.1 Recommended Packages”package "@uiap/web-sdk"package "@uiap/web-sdk/overlay"package "@uiap/web-sdk/frame-bridge"2.2 Installation Modes
Section titled “2.2 Installation Modes”-
ESM / npm / first-party bundle Recommended for production apps.
-
CDN script Only if
script-srcpermits it. -
Frame bridge For cooperating child/parent contexts via
postMessage(). -
External driver adapter Optional for out-of-process automation via WebDriver or WebDriver BiDi. ([MDN Web Docs][3])
3. Top-Level API
Section titled “3. Top-Level API”interface UIAPConfig { app: { id: string; version: string; locale?: string; };
transport: UIAPTransport;
observe?: ObserveConfig; policy?: SDKPolicyConfig | false; overlay?: OverlayConfig | false; frames?: FrameConfig | false; annotations?: AnnotationConfig; debug?: boolean | "info" | "warn" | "error";}interface UIAPClient { start(): Promise<void>; stop(): Promise<void>; destroy(): Promise<void>;
getSnapshot(input?: SnapshotOptions): Promise<PageGraph>; publishSnapshot(input?: SnapshotOptions): Promise<PageGraph>; emitSignal(signal: WebSignal): void;
registerAction(descriptor: ActionDescriptor, handler: ActionHandler): Unsubscribe; unregisterAction(actionId: ActionId): void;
registerElementAdapter(adapter: UIAPElementAdapter): Unsubscribe; registerPolicyEvaluator(evaluator: SDKPolicyEvaluator): Unsubscribe;
bindElement(node: Element, binding: ElementBinding): Unsubscribe; bindScope(node: Element, binding: ScopeBinding): Unsubscribe;
setRouteProvider(provider: RouteProvider): void; createFrameBridge(options: FrameBridgeOptions): FrameBridge;
on<T = unknown>(event: SDKEventName, listener: (payload: T) => void): Unsubscribe;
overlay?: OverlayController;}type Unsubscribe = () => void;4. Transport API
Section titled “4. Transport API”interface UIAPTransport { send(message: UIAPEnvelope): Promise<void> | void; close?(): Promise<void> | void; onMessage(listener: (message: UIAPEnvelope) => void): Unsubscribe; onError?(listener: (error: unknown) => void): Unsubscribe;}- The SDK MUST remain transport-agnostic.
- A Transport MUST deliver messages in UIAP Core envelopes.
- The SDK MAY adapt multiple transports as long as exactly one per active session is write-primary.
5. State Collection API
Section titled “5. State Collection API”The SDK SHOULD derive roles, names, descriptions, and states from Web semantics rather than mere pixel magic. Browsers produce an Accessibility Tree with name, role, description, and state; aria-label and aria-labelledby are central sources for Accessible Names. For runtime changes, MutationObserver is the native tool. ([MDN Web Docs][4])
interface ObserveConfig { root?: Document | ShadowRoot; includeHidden?: boolean; // default false includeNonInteractive?: boolean; // default false throttleMs?: number; // default 100 maxNodes?: number; // default implementation-defined signals?: WebSignalKind[]; // default supported set}interface SnapshotOptions { scopes?: ScopeId[]; documents?: DocumentId[]; includeHidden?: boolean; includeNonInteractive?: boolean;}- The SDK MUST be able to produce at least one snapshot.
- The SDK SHOULD derive deltas from DOM/state changes.
- The SDK MUST be able to update bounding boxes, focus, and visible interaction states.
- The SDK SHOULD detect route, dialog, toast, and validation signals or accept them from the app.
6. Annotation API
Section titled “6. Annotation API”interface ElementBinding { id: StableId; scopeId?: ScopeId; meaning?: string; name?: string; defaultAction?: ActionId; risk?: RiskLevel; sensitive?: boolean; success?: SuccessSignal[]; metadata?: Record<string, unknown>;}interface ScopeBinding { id: ScopeId; kind: ScopeKind; parentScopeId?: ScopeId; name?: string; metadata?: Record<string, unknown>;}bindElement()andbindScope()SHOULD mirror the correspondingdata-uiap-*metadata or register them internally.- Explicit bindings MUST take precedence over heuristics.
- An SDK MAY maintain bindings both via DOM attributes and via an in-memory registry.
Recommended HTML Attributes
Section titled “Recommended HTML Attributes”data-uiap-id="video.submit"data-uiap-scope="video.create.form"data-uiap-meaning="use_case"data-uiap-action="video.create"data-uiap-risk="confirm"data-uiap-sensitive="true"data-uiap-ignore="true"7. Action Registry API
Section titled “7. Action Registry API”interface ActionHandlerContext { actionHandle: string; action: ActionDescriptor; target?: ResolvedTarget; args: Record<string, unknown>; snapshot: PageGraph;
policy?: PolicyDecision;
emitSignal(signal: WebSignal): void; requestConfirmation(input: ActionConfirmationRequestPayload): Promise<"granted" | "denied">; waitForUser(note: string): Promise<void>;
overlay?: OverlayController;}type ActionHandlerResult = | { status: "succeeded"; returnValue?: Record<string, unknown>; sideEffectState?: "none" | "applied" | "unknown"; verification?: VerificationOutcome; } | { status: "failed"; error: RuntimeErrorDescriptor; sideEffectState?: "none" | "applied" | "unknown"; };type ActionHandler = (ctx: ActionHandlerContext) => Promise<ActionHandlerResult>;registerAction()MUST support domain actions and primitive actions.- For registered domain actions,
appActionSHOULD be preferred. - Handlers MUST set
sideEffectStatecorrectly. - Non-idempotent handlers SHOULD NOT perform silent retries.
8. Policy Hook API
Section titled “8. Policy Hook API”interface SDKPolicyConfig { mode?: "local-only" | "delegated" | "hybrid"; // default hybrid document?: PolicyDocument;}type SDKPolicyEvaluator = (context: PolicyContext) => Promise<PolicyDecision> | PolicyDecision;local-only: Decision made exclusively within the SDKdelegated: Decision delegated to an external policy counterparthybrid: Local preflight rules plus optional delegation
Enforcement Rules
Section titled “Enforcement Rules”- The SDK MUST have at least one policy decision before every Action.
- A local
denydecision MUST be final. confirmMUST be routed into the Runtime Confirmation flow.handoffMUST triggerwaitForUser()or an equivalent UX.
The SDK SHOULD consider browser state when requireUserActivation is set. navigator.userActivation makes this state queryable; this is precisely why this check belongs locally in the SDK and not in some late backend oracle. ([MDN Web Docs][2])
9. Element Adapter API
Section titled “9. Element Adapter API”For complex widgets, design system components, and those creative frontend excesses that look like controls visually but are semantically half art installations, the SDK needs adapters.
interface UIAPElementAdapter { id: string;
match(node: Element): boolean;
describe( node: Element, ctx: { route?: RouteContext; scopeId?: ScopeId; } ): Partial<UIElement> | null;
execute?( node: Element, request: ActionRequestPayload, ctx: ActionHandlerContext ): Promise<ActionHandlerResult | null>;}- Adapters SHOULD run before generic heuristics.
- Adapters MAY publish additional semantics and actions.
- Adapters SHOULD be used for custom components and open Shadow Roots.
Open and closed Shadow Roots must be treated differently: open roots can be traversed by script, closed ones cannot. For closed Shadow DOM you therefore need either host-side bindings or explicit adapters on the host, rather than hoping that JavaScript will suddenly learn to read minds. ([MDN Web Docs][5])
10. Route Provider API
Section titled “10. Route Provider API”interface RouteProvider { getRoute(): RouteContext; subscribe?(listener: (route: RouteContext) => void): Unsubscribe;}- The SDK SHOULD prefer an app-specific
routeId. - If no provider is set, the SDK MAY use URL-based fallbacks.
11. Signal API
Section titled “11. Signal API”interface SignalEmitter { emitSignal(signal: WebSignal): void;}Recommended Signals
Section titled “Recommended Signals”route.changedtoast.shownstatus.changedvalidation.changeddialog.openeddialog.closedsubmission.startedsubmission.finished
App-internal DOM Hooks
Section titled “App-internal DOM Hooks”The SDK SHOULD additionally offer DOM-side integration points via CustomEvent, because CustomEvent.detail can cleanly carry app data along. ([MDN Web Docs][6])
Recommended events:
uiap:readyuiap:snapshotuiap:signaluiap:action-requestuiap:action-resultuiap:policy-decision
12. Frame Bridge API
Section titled “12. Frame Bridge API”Cross-origin DOM remains restricted by the Same-Origin Policy. When parent and child need to cooperate, the bridge must therefore rely on messaging rather than direct DOM access. window.postMessage() is the native mechanism for this. ([MDN Web Docs][7])
interface FrameConfig { enabled?: boolean; channel?: string; // default "uiap"}interface FrameBridgeOptions { mode: "parent" | "child"; targetWindow: Window; targetOrigin: string; channel?: string;}interface FrameBridge { connect(): void; disconnect(): void; send(message: UIAPEnvelope): void;}targetOriginMUST be set explicitly.- A Bridge MUST validate the incoming
originandchannel. - Cross-origin frames without a bridge MUST be treated as
opaque. - Bridged frames SHOULD publish their own
documentIdand optionally abridgeSessionId.
13. Overlay API
Section titled “13. Overlay API”interface OverlayConfig { ghostCursor?: boolean; highlightStyle?: "outline" | "spotlight" | "pulse"; narration?: boolean; zIndex?: number;}interface OverlayController { showCursor(target: ResolvedTarget, options?: { pace?: "instant" | "humanized" }): void; highlight(target: ResolvedTarget, options?: { style?: "outline" | "spotlight" | "pulse" }): void; announce(text: string): void; clear(): void;}- Overlay is presentation, not truth.
- Overlay MUST NOT fake success when
action.resultis missing. ghostCursorSHOULD use a separate visual cursor.
14. External Drivers
Section titled “14. External Drivers”interface ExternalDriverAdapter { execute( request: ActionRequestPayload, target: ResolvedTarget | undefined ): Promise<ActionHandlerResult>;}For out-of-process automation, the SDK MAY use a driver adapter built on WebDriver or WebDriver BiDi. The important thing is that the UIAP lifecycle remains the same: same policy, same verification, same error classes. ([W3C][8])
15. SDK Events
Section titled “15. SDK Events”type SDKEventName = | "ready" | "snapshot" | "signal" | "action:accepted" | "action:progress" | "action:result" | "policy:decision" | "error";16. Minimal Conformance Scope
Section titled “16. Minimal Conformance Scope”A conforming @uiap/web-sdk MUST:
- Offer
createUIAP()or an equivalent initialization, - Be able to publish snapshots,
- Support at least
registerAction,registerPolicyEvaluator,bindElement,bindScope,emitSignal, - Enforce local policy decisions,
- Correctly handle open/closed shadow boundaries,
- Correctly mark opaque cross-origin contexts as inaccessible,
- Emit Action and Policy lifecycle events.
A conforming SDK SHOULD:
- Publish deltas,
- Use
MutationObserver, - Offer
CustomEventhooks, - Support
postMessageframe bridges, - Optionally offer an overlay.
17. Reference Initialization
Section titled “17. Reference Initialization”import { createUIAP } from "@uiap/web-sdk";
const uiap = createUIAP({ app: { id: "videoland", version: "1.4.2", locale: "de-CH" },
transport: myTransport,
observe: { throttleMs: 120, includeHidden: false, includeNonInteractive: false },
policy: { mode: "hybrid", document: { modelVersion: "0.1", extension: "uiap.policy", defaults: { onSafeRisk: "allow", onConfirmRisk: "confirm", onBlockedRisk: "handoff", onUnknownAction: "deny", onSensitiveRead: "confirm", onSecretRead: "deny" }, rules: [] } },
overlay: { ghostCursor: true, highlightStyle: "spotlight", narration: true }});
uiap.bindScope(document.querySelector("[data-form='video-create']")!, { id: "video.create.form", kind: "form", name: "Video erstellen"});
uiap.bindElement(document.querySelector("#title")!, { id: "video.title", scopeId: "video.create.form", meaning: "title"});
uiap.bindElement(document.querySelector("#submit")!, { id: "video.submit", scopeId: "video.create.form", defaultAction: "video.create", risk: "confirm", success: [ { kind: "route.changed", pattern: "/videos/:id" }, { kind: "toast.contains", text: "erstellt" } ]});
uiap.registerAction( { id: "video.create", kind: "domain", targetKinds: ["scope"], executionModes: ["appAction", "semanticUi"], risk: { level: "confirm", tags: ["external_effect"] } }, async (ctx) => { const granted = await ctx.requestConfirmation({ actionHandle: ctx.actionHandle, actionId: "video.create", risk: { level: "confirm", tags: ["external_effect"] }, preview: { summary: "Video erstellen" } });
if (granted !== "granted") { return { status: "failed", sideEffectState: "none", error: { code: "confirmation_denied", message: "Action was not confirmed" } }; }
// domain app action await app.actions.createVideo();
ctx.emitSignal({ signalId: "sig_1", kind: "toast.shown", level: "success", text: "Video erstellt" });
return { status: "succeeded", sideEffectState: "applied" }; });
uiap.registerPolicyEvaluator((context) => { if (context.dataClasses?.includes("credential")) { return { decision: "handoff", reasonCodes: ["credential_data", "human_actor_required"] }; }
if (context.risk?.level === "confirm") { return { decision: "confirm", reasonCodes: ["risk_confirm"] }; }
return { decision: "allow", reasonCodes: ["policy_default"] };});
await uiap.start();Normative References
Section titled “Normative References”- [UIAP-CORE] UIAP Core v0.1
- [UIAP-WEB] UIAP Web Profile v0.1
- [RFC2119] Key words for use in RFCs to Indicate Requirement Levels, BCP 14
Security Considerations
Section titled “Security Considerations”- The SDK MUST run in the same origin context as the host application; external script injection MUST NOT be enabled.
- Transport credentials MUST NOT be hardcoded in client-side code.
targetOriginfor Frame Bridges MUST be set explicitly, never"*".- Policy evaluators MUST be executed locally before Actions are handed to the transport.
- Overlay elements MUST NOT obscure real UI elements of the host application or intercept clicks.
Changelog
Section titled “Changelog”| Version | Date | Changes |
|---|---|---|
| 0.1 | 2026-03-27 | Initial draft |