HatchedDocs
Guides

Getting started

Ten minutes from zero to a buddy in your product — create an egg, send your first event, embed a widget.

This guide walks through the full integration path. If you only have ten minutes, this is the one to read.

Wiring this into a real app? Read First user bootstrap alongside it — same flow, with the parts you can't skip spelled out: publish your config first, reuse an existing buddy instead of creating a new egg on every load, persist buddy_id, the snake_case raw API, and hatch latency. Skipping those is the #1 cause of broken first-run integrations.

1. Sign up and grab an API key

  1. Create an account at the Hatched dashboard.
  2. Pick a dashboard preset — language-learning, fitness, productivity, or custom — it seeds your customer config with sensible defaults.
  3. Publish your config. Picking a preset in step 2 publishes your first config version automatically, so eggs.create works straight away. (If you built a config from scratch, open the rules editor and hit Publish — eggs.create returns 409 no_published_config until one is published. Later edits also sit on a draft until you publish them.) New buddies pin to the snapshot you publish.
  4. Go to Developers → API keys and create a secret key (prefix hatch_live_ in production, hatch_test_ for sandbox).

Secret keys are server-only. Never ship one to a browser bundle.

2. Install the SDK

pnpm add @hatched/sdk-js
# or
npm install @hatched/sdk-js
import { HatchedClient } from '@hatched/sdk-js';

const hatched = new HatchedClient({
  apiKey: process.env.HATCHED_API_KEY!,
});

The SDK throws on construction if it detects a browser runtime. For browser integrations, mint a widget session token server-side (step 5) or use a publishable key.

3. Create an egg and hatch it

A buddy is born from an egg. Do this once per user — before creating an egg, check whether the user already has a buddy (hatched.buddies.list({ userId })) or whether you've stored one. Creating an egg on every page load fills up the per-user egg limit; the bootstrap guide has the full reuse pattern. ensure: true makes the create call reuse this user's existing waiting/ready egg if there is one.

const egg = await hatched.eggs.create({ userId: 'user_42', ensure: true });

if (egg.status === 'waiting') {
  await hatched.eggs.updateStatus(egg.eggId, 'ready');
}
const hatchOp = await hatched.eggs.hatch(egg.eggId);
const finished = await hatched.operations.wait(hatchOp.operationId);

const buddyId = finished.result.buddyId;
console.log('Buddy ready:', buddyId);
// Persist buddyId against your app user — you need it for the widget session below
// and on every future page load. (See "Persist buddy_id" in the bootstrap guide.)
// Once the egg is hatched, GET /eggs/:id also returns its buddy_id.

Image generation runs asynchronously; operations.wait polls the hatch operation until the buddy's art is ready (typically 5–45 seconds). Show a loading state in your UI rather than blocking on it.

4. Send your first event

await hatched.events.send({
  eventId: 'lesson_lsn_1_user_42',
  userId: 'user_42',
  type: 'lesson_completed',
  properties: { lessonId: 'lesson_1', durationMs: 5 * 60 * 1000 },
});

The rule engine evaluates the event against the buddy's pinned config and applies coin, skill, badge, streak, and evolution effects in a single transaction. eventId provides idempotency — re-sending the same id returns the cached effect.

When an event satisfies the next evolution condition, the SDK response includes effects.evolutionReady === true. If your config does not enable auto-evolve, start the stage transition from your backend:

const effects = await hatched.events.send({
  eventId: 'lesson_lsn_2_user_42',
  userId: 'user_42',
  type: 'lesson_completed',
});

if (effects.evolutionReady) {
  const evolveOp = await hatched.buddies.evolve('bdy_abc');
  await hatched.operations.wait(evolveOp.operationId);
}

5. Embed the buddy widget

On any page your user visits, mint a widget session token on your server, using the buddyId you stored in step 3:

const session = await hatched.widgetSessions.create({
  buddyId, // from the hatch result / your stored value — NOT the userId
  userId: 'user_42',
  scopes: ['read', 'events:track', 'marketplace:browse'],
  ttlSeconds: 60 * 15,
});

This is the interactive token (data-session-token). For a purely read-only display mount, use embedTokens.create(...) instead (data-embed-token, no scopes) — see Auth model.

Pass the token to the client and render the widget:

<script
  src="https://cdn.hatched.live/widget.js"
  data-session-token="{{session.token}}"
  defer
></script>

<div data-hatched-mount="buddy"></div>

That's it. The widget mounts in a Shadow DOM, pulls buddy state, and reflects new events in real time.

Next steps