Skip to content

Getting started

This walks you from nothing to a running Hailer App making its first authenticated call.

Install

In an existing web project:

bash
npm install @hailer/app-sdk

The SDK ships as an ES module and is framework-agnostic — it works with Vite, React, Svelte, Vue, or plain TypeScript. It has no runtime UI of its own; it's a client you call.

The fastest start is the official scaffolder, which sets up a Vite project with the SDK already wired up:

bash
npm create @hailer/app@latest
cd my-app
npm install
npm run dev

See @hailer/create-app for templates and non-interactive flags.

Register a Local Development app

Your dev server runs at something like http://localhost:3000, but a Hailer App only has an API to talk to when it's loaded inside Hailer. To develop against your running dev server:

  1. In Hailer, create an app of type Local Development.
  2. Point its URL at your dev server (e.g. http://localhost:3000).
  3. Open the app in Hailer.

Hailer now loads your dev server in an iframe and completes the handshake with the SDK. Edits reload automatically. When you're ready to ship, see Publishing.

Instantiate the client

Create one HailerApi instance after the window exists — in onMount (Svelte), useEffect (React), or top-level in a browser entry file. Pass callbacks for the lifecycle events you care about:

ts
import { HailerApi } from '@hailer/app-sdk';

const hailer = new HailerApi({
  // Host is ready — safe to make calls
  connected: async () => {
    const me = await hailer.user.current();
    console.log(`Signed in as ${me.firstname} ${me.lastname}`);
  },
  // Page opened outside Hailer — only hailer.public.* works
  outside: () => {
    console.log('Running standalone');
  },
  // Init + whenever settings change (theme, languages)
  settings: settings => applyTheme(settings.theme),
  // Init + whenever the app config changes
  config: config => console.log('config', config),
  // Per subscribed signal (see the Signals guide)
  signals: signal => console.log('signal', signal.name, signal.data),
  error: err => console.error(err),
});

Wait for connected before calling

The client isn't ready the instant you construct it — it first performs a handshake with the host frame. Make your first calls inside the connected callback (or after it has fired). Calling earlier sends into a frame that hasn't answered the handshake yet.

One instance only

Construct HailerApi once and reuse it. A second instance logs App Id is already set...! Are you using hot-reload? and does not register again.

Make your first call

Every module method returns a Promise. On the host side each call runs with the current user's permissions and throws a HailerError on failure.

ts
// List workflows visible to the current user
const workflows = await hailer.workflow.list();

// List activities in a workflow's phase
const activities = await hailer.activity.list(workflows[0]._id, phaseId);

// Create an activity
const [created] = await hailer.activity.create(workflows[0]._id, [
  { name: 'New ticket', fields: { [fieldId]: 'High' } },
]);

App scope

Whether your app is private or workspace-scoped changes what user.get, workflow.list, workspace.list, team.list, and permission.map return, and which methods are allowed. See Private vs workspace apps.

Next steps

Hailer Developer Documentation