Skip to content
View raw ↗

SDK Reference

foundation-sdk is the typed client your frontend uses to talk to its Foundation backend — auth, data, files, integrations, account, and config, from one instance you set up once.

The canonical, agent-oriented version of this reference is served at /agents/foundation-sdk-reference.md and bundled into llms-full.txt. This page is the same surface, rendered for reading.

Install

bash
npm install foundation-sdk
npm install aws-amplify            # Cognito apps
# or: npm install @auth0/auth0-spa-js   # Auth0 apps

Initialize

Await once, share everywhere. See Set up the SDK for dev vs production details.

ts
import { createFoundation } from 'foundation-sdk'
import { cognitoAuth } from 'foundation-sdk/cognito' // or auth0Auth from 'foundation-sdk/auth0'

const foundation = await createFoundation({
  configUrl: import.meta.env.VITE_FOUNDATION_CONFIG_URL,
  tenantId: import.meta.env.VITE_FOUNDATION_TENANT_ID,
  appId: import.meta.env.VITE_FOUNDATION_APP_ID,
  baseUrl: import.meta.env.VITE_FOUNDATION_API_BASE_URL, // dev-only
  auth: cognitoAuth,
})

In production, Foundation writes /foundation-env.json and the SDK reads it — so createFoundation({ auth }) works with no identity values.

Public init request

The SDK loads public bootstrap configuration by calling configUrl exactly as provided, usually https://.../api/v1/public/init. It separately loads authenticated backend configuration from the API base URL after auth. For tooling or non-JS clients that call the public endpoint directly, send these headers:

http
X-Foundation-Mvp-Tenant-Id: <tenant-id>
X-Foundation-Mvp-Application-Id: <application-id>

The values are the same tenantId and appId used during SDK initialization. Use these header names exactly; alternate tenant or application header names are not accepted.

A missing or blank required header returns 400 with a message naming the header(s) — Missing required header(s): X-Foundation-Mvp-Tenant-Id. An unknown or malformed application id returns 400 Unknown application.

Auth

ts
foundation.auth.user
foundation.auth.isAuthenticated
foundation.auth.getToken()
foundation.auth.login(options?)
foundation.auth.logout(options?)
foundation.auth.handleCallback(url?)
foundation.auth.signIn(email, password)
foundation.auth.signUp(email, password, metadata?)
foundation.auth.confirmSignUp(email, code)
foundation.auth.resendSignUpCode(email)
foundation.auth.forgotPassword(email)
foundation.auth.resetPassword(email, code, newPassword)
foundation.auth.onChange(callback)

Sign-in and sign-up return result objects — branch on result.isSignedIn / result.nextStep?.signInStep. Call handleCallback() on hosted-login callback routes, and refresh auth state after login, logout, callback, and registration. See Authentication.

signUp profile fields

signUp(email, password, metadata?) accepts an optional metadata object. Just pass the fields — the SDK routes each to the right place; you never construct Cognito attributes or client metadata yourself.

ts
await foundation.auth.signUp(email, password, {
  given_name: 'Ada',      // Cognito attribute
  family_name: 'Lovelace',// Cognito attribute
  name: 'Ada Lovelace',   // profile field -> saved on the account
  nickname: 'Ada',
  picture: 'https://…',
  locale: 'en-GB',
  timezone: 'Europe/London'
})
  • Profile fieldsname, nickname, picture, locale, timezone — are stored on the Foundation account user record, not on the Cognito user. (The SDK forwards them via Cognito ClientMetadata; the backend persists them during registration.)
  • Cognito attributesgiven_name, family_name, and any other pool-writable attribute — are written to Cognito.
  • Passing a profile field such as name as a raw Cognito attribute would fail with A client attempted to write unauthorized attribute; the SDK avoids this by routing it for you. Values are strings; null/undefined are dropped.

Database

ts
foundation.db.list<T>(entity, options?)
foundation.db.get<T>(entity, id)
foundation.db.create<T>(entity, data)
foundation.db.update<T>(entity, id, updates)
foundation.db.save<T>(entity, data)
foundation.db.delete(entity, id)

list options: { filters?, limit?, cursor?, orderBy?, orderDir? }. Paginate with cursor / nextCursor for lists that can grow. See Entities.

Files

ts
foundation.files.upload({ name, contentType, file, sha256 })
foundation.files.initiate({ name, contentType, contentLength, sha256, metadata? })
foundation.files.get(fileId)
foundation.files.delete(fileId)
foundation.files.list({ limit?, cursor? }?)

Compute sha256 before upload. Use initiate only for manual control of the signed upload. See Uploads & Files.

Integrations

ts
foundation.integration.all()
foundation.integration.list()
foundation.integration.connections()
foundation.integration.status(source)
foundation.integration.connect(source)
foundation.integration.disconnect(source, configurationId)

Use all() for catalog plus connection state. On connect(source), if the result has a url, open a popup or redirect, then refresh status. Only disconnect after explicit confirmation. See Integrations.

Account

ts
foundation.account.get<TUser, TAccount>()
foundation.account.update(data)
foundation.account.usage()
foundation.account.resendVerification()

usage() returns consumption vs configured limits — drives plan gating. See Billing.

Config

ts
foundation.config.app
foundation.config.features
foundation.config.plans
foundation.config.theme
foundation.config.connectors
foundation.config.resources
foundation.config.auth
foundation.config.raw

Read backend config to hide unavailable UI and apply theme- or plan-aware behavior instead of hard-coding. See Branding.

For apps acting as OAuth authorization servers:

ts
const client = await foundation.oauth.getClient(clientId)
const { code } = await foundation.oauth.authorizeConsent({
  client_id, redirect_uri, scope, state, code_challenge, code_challenge_method,
})

Only call authorizeConsent after explicit user approval, then redirect back with code and state.

Logging

ts
foundation.log.info(message, context?)
foundation.log.warn(message, context?)
foundation.log.error(message, context?)
foundation.log.event(eventName, data?)

Fire-and-forget — never block the UI on logging.

Web components

ts
import 'foundation-sdk/components'
;(window as any).__foundation = foundation
html
<foundation-connect integration-id="github-oauth"></foundation-connect>

Events: success, error, close. In Vue, configure isCustomElement for foundation-* tags. See Components & connector.

Critical rules

  • Always await createFoundation(...); initialize once and share it. The returned instance is fully initialized, so no separate foundation.ready wait is needed.
  • Use exactly one auth provider entry point.
  • baseUrl is dev-only — production must not depend on it.
  • Never put private secrets in frontend code.
  • Set window.__foundation only when using foundation-sdk/components.