@onaio/akuko-embed-sdk
v0.1.1
Published
Framework-agnostic embed SDK for Akuko dashboards
Readme
@onaio/akuko-embed-sdk
Framework-agnostic SDK for embedding Akuko dashboards and
storytelling content into any web page. It mounts a sandboxed <iframe> and
talks to it over a versioned postMessage bridge — themes, filters, visibility,
member re-sync, and lifecycle events all flow through one small, typed API.
- Zero runtime dependencies. Ships as ESM + UMD with TypeScript types.
- Framework-agnostic. Use it from vanilla JS, or via the React wrapper
@onaio/akuko-embed-react. - Auth-aware. Public dashboards embed with no login; private/federated dashboards use redirect-based Keycloak brokering (Path D), so they keep working even when third-party cookies are blocked.
Install
pnpm add @onaio/akuko-embed-sdk
# or: npm install @onaio/akuko-embed-sdk
# or: yarn add @onaio/akuko-embed-sdkQuick start (vanilla)
<div id="mount"></div>
<script type="module">
import { AkukoDashboard } from "@onaio/akuko-embed-sdk";
const dashboard = new AkukoDashboard({
container: "#mount",
dashboardId: "your-dashboard-id",
baseUrl: "https://akuko.io",
});
dashboard.on("akuko:ready", () => console.log("dashboard ready"));
// …later: dashboard.destroy();
</script>container accepts a CSS selector string or an HTMLElement. The constructor
throws if the container can't be found.
Entry modes
Pass exactly one of these mutually-exclusive entry props (the constructor
throws must pass exactly one of … otherwise):
| Prop | Loads | Use when |
|------|-------|----------|
| dashboardId | a single published dashboard (post) | you have a specific dashboard/post id |
| spaceUuid | a whole Akuko space | you want the full space surface |
| onadataOrgId | resolves an OnaData org → its linked space, brokered | embedding from an OnaData-authenticated host |
| onadataProjectId | resolves an OnaData project → its linked folder, brokered | per-project analytics in an OnaData host |
| onadataFormId | resolves a single OnaData form → its generated dashboard, brokered | embedding one form's auto-generated dashboard |
| linkProjectsOrgId | the standalone "link projects" picker surface | rendering the admin link-projects modal |
The onadata* and linkProjects* modes run Akuko's brokered OIDC login
inside the iframe (same-origin), call /v1/embed/resolve, then redirect
themselves to the resolved space/onboarding and emit
akuko:resolved back over the bridge. The host never makes a
cross-origin API call.
When the host knows the owning org (and, for a form, its project) but the current
user hasn't completed a personal OnaData grant, pass resolveOnadataOrgId (and
resolveOnadataProjectId for a form) alongside the entry prop. These are
companions, not entry props — they let akuko resolve role-appropriate content
for every authenticated org user, not just the admin who provisioned analytics.
Options
All options are passed to the AkukoDashboard constructor (AkukoSDKOptions):
| Option | Type | Default | Notes |
|--------|------|---------|-------|
| container | string \| HTMLElement | — | Required. Mount point. |
| dashboardId | string | — | Entry mode (see above). |
| spaceUuid | string | — | Entry mode. |
| onadataOrgId | string | — | Entry mode. |
| onadataProjectId | string | — | Entry mode. |
| onadataFormId | string | — | Entry mode — a single OnaData form's generated dashboard. |
| linkProjectsOrgId | string | — | Entry mode (org slug). |
| resolveOnadataOrgId | string | — | Grant-independent companion (not an entry prop): owning org slug, so akuko can derive the embed's state without the caller's personal OnaData grant. Pairs with onadataProjectId / onadataFormId. Does not count toward "exactly one entry prop". |
| resolveOnadataProjectId | string | — | Companion: the form's project id. Pairs with onadataFormId. |
| baseUrl | string | https://akuko.io | Origin serving the embed /embed/* routes. |
| apiBaseUrl | string | — | Accepted for back/forward-compat but no longer used — resolve/resync run inside the iframe. Safe to keep passing. |
| idpHint | string | — | Keycloak kc_idp_hint alias for silent federation (see SSO). |
| initialPostId | string | — | Post/dashboard to open on mount, overriding the configured default. For host deep-linking/sharing (forwarded as ?post=). |
| theme | AkukoTheme | — | Initial theme (see below). |
| visibility | AkukoVisibility | — | Initial chrome visibility. |
| filters | Record<string, unknown> | — | Initial filter values. |
| hostIsAdmin | boolean | — | Hint that the current user can administer the embedded org/project; gates the in-iframe onboarding consent for an unlinked org. UX-only — the backend re-verifies ownership at provisioning. |
| hostUser | { email: string } | — | The host's current signed-in OnaData user; lets the embed detect a host user-switch and tear down a stale session. Untrusted — only forces a re-auth on mismatch. |
| allowedOrigin | string | origin of baseUrl | Origin the bridge accepts messages from. |
| sandbox | string | allow-scripts allow-same-origin allow-popups allow-forms allow-downloads | iframe sandbox attribute. The iframe is also granted fullscreen; clipboard-write via the allow attribute (so the embed can enter OS fullscreen and the in-embed Share can copy the post URL). |
| layout | "content" \| "fill" | content | content auto-resizes to the embed height; fill makes the embed fill a host-sized container (host owns height). |
| minHeight | number \| string | — | content layout only: a minimum height floor (px number or CSS length) so a short/empty dashboard still fills down to the host footer. Ignored in fill. |
| iframeLoadTimeoutMs | number | 30000 | Watchdog before akuko:error{code:'IFRAME_LOAD_FAILED'}. 0 disables; <5000 not recommended. |
| logoutTimeoutMs | number | 2000 | How long logout() waits for akuko:logoutComplete before resolving anyway. |
AkukoTheme
primary, background, foreground, card, border, radius,
fontFamily, darkMode (boolean), and chart1–chart5. All optional.
AkukoVisibility
Booleans to show/hide chrome: header, breadcrumbs, footer, undoRedo,
chartActions, filterBar.
Methods
const d = new AkukoDashboard({ /* … */ });| Method | Description |
|--------|-------------|
| setTheme(theme) | Apply an AkukoTheme. Cached and re-applied on every akuko:ready. |
| setVisibility(v) | Show/hide chrome. Cached and replayed on ready. |
| setViewport(viewport) | Report the iframe's currently-visible vertical band ({ top, height }) so in-iframe floating UI can center itself under content layout. The React wrapper does this automatically. Cached and replayed. |
| setFilters(filters) | Push filter values into the dashboard. Cached and replayed. |
| clearFilters(keys?) | Clear specific filter keys, or all filters when called with no args. |
| refresh() | Ask the dashboard to reload its data. |
| getState() | Request current state; response arrives via akuko:stateResponse. |
| resyncMembers(opts) | Re-sync OnaData members into the linked space/folder. opts.scope is "org" or "project". Progress via akuko:provisioningStatus. |
| linkProjects() | Navigate the iframe to the org's project-picker. Throws unless constructed with onadataOrgId. |
| logout() | Tear down the in-iframe Akuko session (RP-initiated end-session). Resolves on akuko:logoutComplete or after logoutTimeoutMs. Never rejects, never hangs. |
| destroy() | Remove the iframe and tear down the bridge. |
State pushed via setTheme / setVisibility / setFilters / setViewport
(and the hostUser identity) is replayed automatically on each akuko:ready, so
it survives the iframe's full-document replacements during brokered OIDC + SPA
navigation.
Events
Subscribe with on(type, handler) and unsubscribe with off(type, handler):
const onResize = (p) => console.log("height", p.height);
d.on("akuko:resize", onResize);
d.off("akuko:resize", onResize);The AkukoEventType union:
| Event | Fires when |
|-------|------------|
| akuko:ready | a surface inside the iframe has mounted and is ready. |
| akuko:resolved | brokered resolve completed; payload is ResolveResponse (linked / unlinked / provisioning / reauth_required). |
| akuko:resize | content height changed ({ height }); ignored in fill layout. |
| akuko:filterChange | the user changed a filter inside the dashboard. |
| akuko:cellClick | a chart/table cell was clicked. |
| akuko:dashboardChange | the active dashboard changed. |
| akuko:provisioningStatus | JIT member-sync progress. |
| akuko:stateResponse | response to getState(). |
| akuko:linkProjects / akuko:linkProjectsDone | link-projects surface opened / finished. |
| akuko:navigate | the embed asks the host to navigate to a project's analytics page (payload { scope, onadataProjectId, ownerUsername? }). |
| akuko:share | the embed asks the host to copy a shareable post URL (payload { postId, scope, url? }); the embed also copies it itself, so handling is optional. |
| akuko:logout / akuko:logoutComplete | logout requested / acknowledged. |
| akuko:error | an error occurred (e.g. { code: 'IFRAME_LOAD_FAILED' }). |
| akuko:close | the embed asked the host to close it (e.g. a modal surface). |
The bridge only accepts messages from allowedOrigin (default: the origin of
baseUrl) and scopes them to this iframe, so multiple embeds on one page
don't cross-talk.
SSO (federated login)
For private or federated dashboards, Akuko authenticates users via Keycloak
identity brokering (Path D) — a redirect-based OIDC flow, so it works
cross-domain and survives third-party-cookie blocking. Your host passes the
brokered IdP alias as idpHint; first-time users go straight from your app →
Akuko iframe → your IdP → back → dashboard, with no broker-selection UI.
See ./docs/sso-path-d-brokering.md for the
full setup, and ./examples/keycloak-path-d-setup.sh
for a sanitized kcadm.sh reference.
Host recipes
Concrete WordPress and Directus integration recipes live in
./docs/host-recipes.md.
Testing with canary builds
Branch builds are published under the canary dist-tag so you can test an
unreleased change in a consumer before it ships:
pnpm add @onaio/akuko-embed-sdk@canaryBrowser support
Modern evergreen browsers. Note that browsers with third-party-cookie blocking (Safari ITP, Firefox ETP) restrict cross-site cookies — but Akuko's federated login (Path D) is redirect-based, not cookie-shared, so embedded private dashboards keep working under those restrictions.
License
Apache-2.0
