Entities

Read portal data with getEntity, searchEntities, and the underlying portal client.

Once you've connected, you can read epilot entities through the portal's authentication. The SDK wraps @epilot/customer-portal-client and configures auth from the current session for you, so you never handle tokens directly.

All of these require a resolved session; they throw SparkNotInitializedError otherwise.

getEntity(params)

Fetch a single entity for the portal user. Resolves to an EntityItem or undefined if not found.

import { getEntity } from '@epilot/spark-sdk'

const contract = await getEntity({
  slug: 'contract',
  entity_id: '5da0a718-c822-403d-9f5d-20d4584e0528',
  hydrate: true,
  fields: ['_id', '_title', 'customer', 'products'],
})

console.log(contract?._title)

Pass fields to fetch only what you render; it keeps payloads small and reads fast. From a HEMS's use-asset-query.ts, wrapped in TanStack Query:

import { getEntity, type EntityItem } from '@epilot/spark-sdk'
import { useQuery } from '@tanstack/react-query'

const ASSET_FIELDS = ['_id', '_title', 'title', 'asset_number']

export function useAssetQuery(assetId: string | undefined) {
  const { isMockedMode } = useBlockContext()

  return useQuery<EntityItem | null>({
    queryKey: assetKeys.detail(assetId ?? ''),
    queryFn: async () => {
      if (!assetId) return null
      const entity = await getEntity({
        slug: 'asset',
        entity_id: assetId,
        fields: ASSET_FIELDS,
      })
      return entity ?? null
    },
    enabled: Boolean(assetId) && !isMockedMode,
    staleTime: 60_000,
  })
}

searchEntities(params)

Search entities for the portal user. Resolves to an EntityResponseWithHits (results, hits, pagination).

import { searchEntities } from '@epilot/spark-sdk'

const { results, hits } = await searchEntities({
  slug: 'contract',
  q: 'active',
  size: 10,
  sort: '_created_at:desc',
})

console.log(`Found ${hits} contracts`)
results?.forEach((c) => console.log(c._title))

You can pass structured filters instead of (or with) a q string. This is how a HEMS batch-loads assets by id in use-assets-by-ids-query.ts, then back-fills the per-entity cache so detail views are instant:

import { searchEntities, type EntityItem } from '@epilot/spark-sdk'

const response = await searchEntities({
  slug: 'asset',
  size: ids.length,
  fields: ASSET_FIELDS,
  filters: [{ terms: { _id: ids } }],
})

const byId = new Map<string, EntityItem>()
for (const asset of response.results ?? []) {
  if (asset._id) {
    byId.set(asset._id, asset)
    queryClient.setQueryData(assetKeys.detail(asset._id), asset) // warm the cache
  }
}

getPortalClient()

Both helpers above are thin wrappers over the customer portal client. When you need an operation the SDK doesn't wrap, reach for the configured client directly; auth and base URL are set from the current session on every call.

import { initialize, getPortalClient } from '@epilot/spark-sdk'

await initialize()

const client = await getPortalClient()
const files = await client.getAllFiles({ origin: 'END_CUSTOMER_PORTAL' })

See the @epilot/customer-portal-client docs for the full operation surface.

Types

These come from @epilot/customer-portal-client and are re-exported by the SDK so you can import them from one place:

TypeUsed for
EntityItemA single entity (_id, _title, your attributes…).
EntityGetParamsParams for getEntity.
EntitySearchParamsParams for searchEntities.
EntityResponseRaw entity response.
EntityResponseWithHitsSearch response: results, hits, pagination.
EntitySlugKnown entity slugs.
import type { EntityItem, EntitySearchParams } from '@epilot/spark-sdk'

Entities vs. use cases. Use the entity API to read epilot's own data model. To call an external system wired up through Integration Hub (devices, energy data, vendor APIs…), use executeUseCase instead.

On this page