Spark SDK
Build custom portal blocks that run inside the epilot customer portal.
@epilot/spark-sdk is the toolkit for building portal blocks: custom
applications that render inside an <iframe> embedded in the epilot customer
portal. The SDK handles everything the portal boundary makes hard: authenticating
against the portal, reading entities, executing Integration Hub use cases,
staying in sync with the portal's theme and language, and resizing the iframe to
fit your content.
You write a normal React (or framework-agnostic) app. The SDK gives it a secure, typed line back to the portal it lives in.
Mental model
A portal block never talks to epilot APIs directly. It runs in a sandboxed iframe on a different origin, so every interaction goes through a postMessage bridge to the parent portal window, which holds the user's auth and proxies requests on your behalf.
┌─────────────────────────────────────────────────────────┐
│ epilot Customer Portal (parent window, holds auth) │
│ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ <iframe> Your portal block │ │
│ │ │ │
│ │ @epilot/spark-sdk │ │
│ │ • initialize() → session (token, …) │ │
│ │ • getEntity() → entity reads │ │
│ │ • executeUseCase() → Integration Hub │ │
│ │ • onLocaleUpdate() → language sync │ │
│ │ • updateContentHeight → iframe resize │ │
│ └──────────────────────────────────────────────────┘ │
│ ▲ postMessage bridge ▼ │
└─────────────────────────────────────────────────────────┘The lifecycle is always the same:
- Connect: call
initialize(). The portal hands back aSparkSessioncontaining an auth token, the API base URL, the active theme and language, and your block's installation config. - Read & act: use
getEntity/searchEntitiesto read portal data andexecuteUseCaseto call Integration Hub. - Stay in sync: the SDK keeps theme and language aligned with the portal automatically, and you report your content height so the portal can size the iframe.
The examples in these docs follow a HEMS built with Spark, a complete portal block. Almost every code sample is drawn from that pattern, so you can see each piece in full context.
Installation
npm install @epilot/spark-sdk
# or
pnpm add @epilot/spark-sdkQuick start
import { initialize, getEntity } from '@epilot/spark-sdk'
// 1. Connect to the parent portal (do this once, at startup)
const session = await initialize()
console.log('Connected to portal', session.portalId)
// 2. Read data through the portal's auth
const contract = await getEntity({
slug: 'contract',
entity_id: '5da0a718-c822-403d-9f5d-20d4584e0528',
})
console.log(contract?._title)In a React app you'll wrap this in a provider so the whole tree can wait for the connection. See Getting started for the full pattern used by a HEMS built with Spark.
Explore the SDK
Getting started
Bootstrap a block: session provider, content sizing, error states.
Session & bridge
initialize, getSession, the SparkSession object, and block config.
Entities
Read portal data with getEntity, searchEntities, and getPortalClient.
Use cases
Call Integration Hub through the portal proxy with executeUseCase.
Theming
Inherit the portal's CSS variables, light/dark mode, and viewport.
Localization
Keep your block's language in sync with the portal.
Custom events
Send and receive your own messages across the bridge.
Errors
SparkError, timeouts, and the not-initialized guard.
API reference
Every export at a glance.
Why use the SDK
- Secure by construction: auth never leaves the parent portal; the bridge can be locked to trusted origins.
- Typed end to end: entity and use-case shapes are fully typed, with
convenience re-exports from
@epilot/customer-portal-client. - Framework-agnostic core: the bridge is plain Promises and callbacks; the React patterns in these docs are a convention, not a requirement.
- Tree-shakeable, ESM & CJS: import only what you use.