# SDK (JavaScript / TypeScript)

> Complete method reference for @hatched/sdk-js — HatchedClient, resources, error classes.

Source: https://docs.hatched.live/docs/reference/sdk-js

{/* AUTO-GENERATED from packages/sdk-js by apps/docs/scripts/generate-sdk-reference.ts.
   Edit JSDoc in the SDK source; do not edit this file directly. */}

Package: [`@hatched/sdk-js`](https://npmjs.com/package/@hatched/sdk-js)

```bash
pnpm add @hatched/sdk-js
```

## HatchedClient

Official Hatched SDK client for JavaScript/TypeScript.

Server-side (secret key):
```ts
const hatched = new HatchedClient({
  apiKey: process.env.HATCHED_API_KEY!,
});
const egg = await hatched.eggs.create({ userId: 'user_123' });
await hatched.eggs.updateStatus(egg.eggId, 'ready');
const op = await hatched.eggs.hatch(egg.eggId);
```

Browser (publishable key, scoped):
```ts
const hatched = new HatchedClient({
  publishableKey: 'hatch_pk_xxxxxxxx',
});
const buddy = await hatched.buddies.get('bdy_abc');
```

### `HatchedClient.health()`

```ts
health()
```

Health check; returns API status metadata.

### `HatchedClient.getRateLimitInfo()`

```ts
getRateLimitInfo()
```

Latest `X-RateLimit-*` snapshot from the most recent response.

### `HatchedClient.getLastRequestId()`

```ts
getLastRequestId(): string | null
```

Request id of the most recent response (for support correlation).

## EggsResource

### `EggsResource.create()`

```ts
create(params: CreateEggParams, signal?: AbortSignal): Promise<Egg>
```

Creates a new pending egg bound to an external user.
New eggs start in `waiting`; call `updateStatus(eggId, 'ready')`
before `hatch()`. Pass `ensure: true` during a first-run bootstrap to
reuse the user's existing `waiting`/`ready` egg instead of creating one.

@example
```ts
const egg = await hatched.eggs.create({ userId: 'user_42' });
await hatched.eggs.updateStatus(egg.eggId, 'ready');
```

### `EggsResource.get()`

```ts
get(eggId: string, signal?: AbortSignal): Promise<Egg>
```

Fetches the canonical state of a single egg.

### `EggsResource.list()`

```ts
list(params: ListEggsParams = {}): Promise<Egg[]>
```

Lists eggs with optional filters.

### `EggsResource.updateStatus()`

```ts
updateStatus(eggId: string, status: 'ready' | 'cancelled', signal?: AbortSignal): Promise<EggStatusChange>
```

Transitions an egg to `ready` or `cancelled`. The API only permits
`ready` and `cancelled` terminal statuses via this endpoint.

### `EggsResource.hatch()`

```ts
hatch(eggId: string, signal?: AbortSignal): Promise<HatchResult>
```

Kicks off an async hatch operation. Poll the returned operationId via
`operations.wait()` to resolve when the buddy art is ready. The egg
must already be in `ready` status.

## BuddiesResource

### `BuddiesResource.get()`

```ts
get(buddyId: string, signal?: AbortSignal): Promise<Buddy>
```

Fetches a buddy by id.

### `BuddiesResource.list()`

```ts
list(params: BuddyListParams = {}): Promise<BuddyList>
```

Lists buddies with optional filters.

### `BuddiesResource.updateName()`

```ts
updateName(buddyId: string, name: string, signal?: AbortSignal): Promise<Buddy>
```

### `BuddiesResource.archive()`

```ts
archive(buddyId: string, signal?: AbortSignal): Promise<Buddy>
```

### `BuddiesResource.updateSkills()`

```ts
updateSkills(buddyId: string, updates: SkillUpdate[], signal?: AbortSignal)
```

### `BuddiesResource.earn()`

```ts
earn(buddyId: string, params: EarnCoinsParams, idempotencyKey?: string, signal?: AbortSignal)
```

Adds coins to a buddy's ledger for a given reason.
Alias: `BuddiesResource.earnCoins`.

### `BuddiesResource.spend()`

```ts
spend(buddyId: string, params: SpendCoinsParams, idempotencyKey?: string, signal?: AbortSignal)
```

Debits coins from a buddy's ledger. Fails with
`InsufficientBalanceError` if the buddy doesn't have enough.

### `BuddiesResource.awardBadge()`

```ts
awardBadge(buddyId: string, badgeKey: string, reason?: string, signal?: AbortSignal)
```

### `BuddiesResource.getBadges()`

```ts
getBadges(buddyId: string, signal?: AbortSignal)
```

### `BuddiesResource.equip()`

```ts
equip(buddyId: string, params: EquipItemsParams, signal?: AbortSignal): Promise<EquipItemsResult>
```

Equips or unequips items on a buddy.

### `BuddiesResource.rerenderAppearance()`

```ts
rerenderAppearance(buddyId: string, signal?: AbortSignal): Promise<RerenderAppearanceResult>
```

Regenerate the buddy's bare stage base image. Use after a hard generation
failure or when `appearance.status === 'failed'` with `code: 'needs_rerender'`.
Equipped items are removed from the rendered set; re-equip after the
appearance returns to `ready`.

### `BuddiesResource.purchaseItem()`

```ts
purchaseItem(buddyId: string, itemId: string, idempotencyKey?: string, signal?: AbortSignal)
```

### `BuddiesResource.getPurchasedItems()`

```ts
getPurchasedItems(buddyId: string, signal?: AbortSignal)
```

### `BuddiesResource.getEvolution()`

```ts
getEvolution(buddyId: string, signal?: AbortSignal)
```

### `BuddiesResource.evolve()`

```ts
evolve(buddyId: string, signal?: AbortSignal)
```

Starts the async operation that advances a ready buddy to its next
evolution stage. Use after `events.send()` returns
`effects.evolutionReady === true` when auto-evolve is disabled.

### `BuddiesResource.getProgression()`

```ts
getProgression(buddyId: string, signal?: AbortSignal)
```

### `BuddiesResource.tokens()`

```ts
tokens(buddyId: string, signal?: AbortSignal): Promise<TokensSummary>
```

Typed token balances for a buddy, grouped into primary (spendable) and
progression (accumulate-only). Returns null for either slot if the
customer has not configured that kind.

### `BuddiesResource.evolutions()`

```ts
evolutions(buddyId: string, params: { page?: number; limit?: number; signal?: AbortSignal } = {}): Promise<{
    data: BuddyEvolutionRecord[];
    pagination: { page: number; limit: number; total: number };
  }>
```

Paginated stage-transition timeline for a buddy (includes both prod
and demo evolutions).

### `BuddiesResource.getUserSummary()`

```ts
getUserSummary(userId: string, signal?: AbortSignal)
```

## EventsResource

### `EventsResource.send()`

```ts
send(params: SendEventParams, signal?: AbortSignal): Promise<EventEffects>
```

Ingests a domain event. The same `eventId` returning twice yields the
cached effect without re-applying rules.

@example
```ts
const effects = await hatched.events.send({
  eventId: 'lesson_lsn_42_user_123',
  userId: 'user_123',
  type: 'lesson_completed',
  properties: { score: 94 },
});
```

### `EventsResource.sendBatch()`

```ts
sendBatch(events: SendEventParams[], signal?: AbortSignal): Promise<{ results: EventEffects[] }>
```

Sends a batch of events in a single call.

## OperationsResource

### `OperationsResource.get()`

```ts
get(operationId: string, signal?: AbortSignal): Promise<Operation<TResult>>
```

Fetches an operation's current status.

### `OperationsResource.wait()`

```ts
wait(operationId: string, options: WaitOptions = {}): Promise<Operation<TResult>>
```

Polls an operation until it reaches `completed` or `failed`.

@throws `Error` if the operation doesn't finish before `timeoutMs` elapses.
@example
```ts
await hatched.eggs.updateStatus(egg.eggId, 'ready');
const op = await hatched.eggs.hatch(egg.eggId);
const finished = await hatched.operations.wait(op.operationId);
```

### `OperationsResource.waitForCompletion()`

```ts
waitForCompletion(operationId: string, options: { timeout?: number; interval?: number; signal?: AbortSignal } = {}): Promise<Operation<TResult>>
```

@deprecated Use `OperationsResource.wait` instead.

## WidgetSessionsResource

### `WidgetSessionsResource.create()`

```ts
create(params: CreateSessionParams, signal?: AbortSignal): Promise<SessionToken>
```

Mints a short-lived widget session token for browser/interactive
widgets. Never ship a secret API key to the browser — always go
through this endpoint.

### `WidgetSessionsResource.revoke()`

```ts
revoke(sessionId: string, signal?: AbortSignal): Promise<void>
```

## EmbedTokensResource

### `EmbedTokensResource.create()`

```ts
create(params: CreateEmbedTokenParams, signal?: AbortSignal): Promise<EmbedToken>
```

Mints a signed token for a read-only embedded widget.

## WebhooksResource

### `WebhooksResource.list()`

```ts
list(signal?: AbortSignal): Promise<WebhookEndpoint[]>
```

Lists webhook endpoints registered for the current customer.

### `WebhooksResource.create()`

```ts
create(params: CreateWebhookParams, signal?: AbortSignal): Promise<WebhookEndpoint>
```

Registers a new webhook endpoint.

### `WebhooksResource.delete()`

```ts
delete(endpointId: string, signal?: AbortSignal): Promise<void>
```

Deletes a webhook endpoint.

### `WebhooksResource.deliveries()`

```ts
deliveries(params: ListDeliveriesParams): Promise<Page<WebhookDelivery>>
```

Lists recent deliveries for a given endpoint.

### `WebhooksResource.replay()`

```ts
replay(endpointId: string, deliveryId: string, signal?: AbortSignal): Promise<unknown>
```

Replays a specific delivery attempt.

### `WebhooksResource.redeliver()`

```ts
redeliver(endpointId: string, deliveryId: string, signal?: AbortSignal): Promise<unknown>
```

Re-enqueues a stored webhook delivery.

### `WebhooksResource.verifySignature()`

```ts
static verifySignature(rawBody: string | Buffer, signatureHeader: string, secret: string, options: VerifySignatureOptions = {}): boolean
```

Verifies the `Hatched-Signature` header for a webhook payload.

The signature format is `t=<unix_ts>,v1=<hmac_sha256_hex>`.
Pass the **raw request body bytes** (not the parsed JSON) — any
reformatting will invalidate the signature.

@example
```ts
const valid = WebhooksResource.verifySignature(rawBody, header, process.env.HATCHED_WEBHOOK_SECRET!);
if (!valid) return new Response('invalid signature', { status: 400 });
```

## HatchedError

Base class for every error raised by the Hatched SDK. Every subclass
carries the HTTP status, stable error code, optional details payload, and
the request id the API echoed back for support correlation.

_No public methods._

## AuthError

Shared base for 401/403 responses.

_No public methods._

## UnauthorizedError

_No public methods._

## ForbiddenError

_No public methods._

## PublishableKeyScopeError

Raised when a request uses a publishable key for an operation the
publishable key is not scoped for (e.g. mutation endpoints).

_No public methods._

## NotFoundError

_No public methods._

## ValidationError

_No public methods._

## RateLimitError

_No public methods._

## InsufficientBalanceError

_No public methods._

## TooManyItemsError

Raised when an equip request asks the buddy to wear more items than the
image compositing pipeline can reliably render. The current cap is four —
the SDK surfaces `max` and `attempted` on `details` so callers can show a
precise error to the end-user.

_No public methods._

## CategoryConflictError

Raised when an equip request tries to put two items in the same category
slot (e.g. two head items). Only the `accessory` category allows stacking.

_No public methods._

## ConflictError

_No public methods._

## ConfigVersionMismatchError

Raised when a buddy is pinned to a config version that does not match
the one the caller expected (e.g. after a migration race).

_No public methods._

## NoPublishedConfigError

Raised by `POST /eggs` (and the bootstrap flow) when the customer has not
published a config version yet. `details.publish_url` points at the
dashboard publish page.

_No public methods._

## ActiveEggLimitError

Raised when `POST /eggs` would exceed the per-user active-egg cap.
`details.active` lists the existing eggs (id + status) so you can hatch or
cancel one — or retry the create with `?ensure=true` to reuse one.

_No public methods._

## UpstreamImageError

_No public methods._

## CreditInsufficientError

Raised when an AI / generative request cannot be authorised because the
customer has no available credits across any pool. The `details` object
includes the amount required and what remains in each pool, plus a URL
the caller can redirect to so the end-customer can top up.

_No public methods._

## EventQuotaExceededError

Raised when a POST /events call would push the customer over the monthly
event quota allowed by their plan. `reset_at` is an ISO timestamp for the
first of the next UTC month; callers should back off until then or upgrade.

_No public methods._

## PlanFeatureLockedError

Raised when the customer's plan does not include the requested feature
(e.g. a Free tier customer trying to use the marketplace API). The SDK
surfaces which plan is required so callers can prompt an upgrade.

_No public methods._

## GatesResource

Generic spend-to-unlock primitive. Customers define gates in their
dashboard (gate_key, token_key, cost, metadata). A buddy calls `unlock`
to spend the configured token cost and flip the gate open — idempotent:
repeat calls return `alreadyUnlocked: true` without touching the economy.

### `GatesResource.list()`

```ts
list(signal?: AbortSignal): Promise<{ gates: TokenGate[] }>
```

Lists gates configured on this customer. Secret-key only.

### `GatesResource.unlock()`

```ts
unlock(buddyId: string, gateKey: string, signal?: AbortSignal): Promise<UnlockResult>
```

Buddy spends `gate.cost` of `gate.tokenKey` to unlock `gateKey`.
Fails with `InsufficientBalanceError` if the buddy lacks tokens and
with `ValidationError('progression_not_spendable')` if the gate
references a progression token.

### `GatesResource.unlocks()`

```ts
unlocks(buddyId: string, signal?: AbortSignal): Promise<{ unlocks: BuddyUnlock[] }>
```

List gates a buddy has unlocked.

## PathsResource

Guided journey primitive — a path is an ordered list of steps; each
step holds an ordered list of sub-steps with an optional completion
condition. Sub-step completions advance the buddy through the path
automatically (rule-engine) or manually via `completeSubStep`.

The `HttpClient` auto-converts wire snake_case → camelCase on every
response, so resource methods read camelCase fields directly without
an intermediate DTO mapping layer.

### `PathsResource.list()`

```ts
list(audience?: string, signal?: AbortSignal): Promise<PathDefinition[]>
```

### `PathsResource.get()`

```ts
get(definitionId: string, signal?: AbortSignal): Promise<PathDefinitionWithSteps>
```

### `PathsResource.create()`

```ts
create(params: CreatePathDefinitionParams, signal?: AbortSignal): Promise<PathDefinition>
```

### `PathsResource.update()`

```ts
update(definitionId: string, params: UpdatePathDefinitionParams, signal?: AbortSignal): Promise<PathDefinition>
```

### `PathsResource.delete()`

```ts
delete(definitionId: string, signal?: AbortSignal): Promise<void>
```

### `PathsResource.setActive()`

```ts
setActive(definitionId: string, isActive: boolean, signal?: AbortSignal): Promise<PathDefinition>
```

Atomic single-active activation: deactivates every other path on
 the same (customer, audience) in a single transaction.

### `PathsResource.addStep()`

```ts
addStep(definitionId: string, params: CreatePathStepParams, signal?: AbortSignal): Promise<PathStep>
```

### `PathsResource.updateStep()`

```ts
updateStep(definitionId: string, stepId: string, params: UpdatePathStepParams, signal?: AbortSignal): Promise<PathStep>
```

### `PathsResource.deleteStep()`

```ts
deleteStep(definitionId: string, stepId: string, signal?: AbortSignal): Promise<void>
```

### `PathsResource.reorderSteps()`

```ts
reorderSteps(definitionId: string, ordering: Array<{ id: string; ordinal: number }>, signal?: AbortSignal): Promise<PathStep[]>
```

### `PathsResource.addSubStep()`

```ts
addSubStep(definitionId: string, stepId: string, params: CreatePathSubStepParams, signal?: AbortSignal): Promise<PathSubStep>
```

### `PathsResource.updateSubStep()`

```ts
updateSubStep(definitionId: string, stepId: string, subStepId: string, params: UpdatePathSubStepParams, signal?: AbortSignal): Promise<PathSubStep>
```

### `PathsResource.deleteSubStep()`

```ts
deleteSubStep(definitionId: string, stepId: string, subStepId: string, signal?: AbortSignal): Promise<void>
```

### `PathsResource.reorderSubSteps()`

```ts
reorderSubSteps(definitionId: string, stepId: string, ordering: Array<{ id: string; ordinal: number }>, signal?: AbortSignal): Promise<PathSubStep[]>
```

### `PathsResource.getForBuddy()`

```ts
getForBuddy(buddyId: string, pathKey: string, signal?: AbortSignal): Promise<PathRuntimePayload>
```

### `PathsResource.completeSubStep()`

```ts
completeSubStep(buddyId: string, pathKey: string, subStepKey: string, signal?: AbortSignal): Promise<ManualCompleteResult>
```

Manually mark a sub-step complete. Idempotent on (buddy, sub-step).
Returns cascade flags so callers can paint celebrations without an
extra round-trip.

## Types

### `NoPublishedConfigDetails`

```ts
export interface NoPublishedConfigDetails {
  customerId?: string;
  customer_id?: string;
  publishUrl?: string;
  publish_url?: string;
  docsUrl?: string;
  docs_url?: string;
}
```

### `ActiveEggLimitEgg`

```ts
export interface ActiveEggLimitEgg {
  eggId: string;
  status: string;
  createdAt: string;
}
```

### `ActiveEggLimitDetails`

```ts
export interface ActiveEggLimitDetails {
  max?: number;
  active?: ActiveEggLimitWireEgg[];
}
```

### `CreditInsufficientDetails`

```ts
export interface CreditInsufficientDetails {
  required?: number;
  available?: number;
  welcome?: number;
  paid?: number;
  promo?: number;
  upgrade_url?: string;
  top_up_url?: string;
}
```

### `EventQuotaExceededDetails`

```ts
export interface EventQuotaExceededDetails {
  used?: number;
  limit?: number;
  reset_at?: string;
  upgrade_url?: string;
}
```

### `PlanFeatureLockedDetails`

```ts
export interface PlanFeatureLockedDetails {
  feature?: string;
  required_plan?: string;
  current_plan?: string;
  upgrade_url?: string;
}
```

### `EggStatus`

```ts
export type EggStatus = 'waiting' | 'ready' | 'hatching' | 'hatched' | 'cancelled';
```

### `CreateEggParams`

```ts
export interface CreateEggParams {
  /** The external user id that owns the egg. */
  userId: string;
  /** Free-form metadata attached to the egg. */
  metadata?: Record<string, unknown>;
  /**
   * When true, return the user's most recent `waiting`/`ready` egg if one
   * already exists instead of creating a new one (idempotent first-run
   * bootstrap; avoids hitting the per-user active-egg cap on retries).
   */
  ensure?: boolean;
}
```

### `Egg`

```ts
export interface Egg {
  eggId: string;
  userId: string;
  status: EggStatus;
  visualVariant: number;
  configVersionId: string;
  /** The buddy hatched from this egg. Non-null once `status === 'hatched'`. */
  buddyId: string | null;
  metadata: Record<string, unknown>;
  createdAt: string;
}
```

### `EggStatusChange`

```ts
export interface EggStatusChange {
  eggId: string;
  status: EggStatus;
  previousStatus: EggStatus;
}
```

### `HatchResult`

```ts
export interface HatchResult {
  operationId: string;
  status: string;
}
```

### `ListEggsParams`

```ts
export interface ListEggsParams {
  userId?: string;
  status?: EggStatus;
  page?: number;
  limit?: number;
  signal?: AbortSignal;
}
```

### `Buddy`

```ts
export interface Buddy {
  id: string;
  customerId: string;
  userId: string;
  audience: string;
  name: string;
  configVersionId: string;
  evolutionStage: number;
  coins: number;
  status: 'active' | 'archived';
  skills: Record<string, number>;
  tokens: Record<string, number>;
  progression?: BuddyProgression;
  imageUrl: string | null;
  baseImageUrl: string | null;
  thumbUrl: string | null;
  equippedItems: BuddyEquippedItem[];
  appearance?: BuddyAppearance;
  createdAt: string;
  updatedAt: string;
}
```

### `BuddyProgression`

```ts
export interface BuddyProgression {
  /** Player-facing XP. Currently maps to totalSkillLevel. */
  xp: number;
  totalSkillLevel: number;
  badgeCount: number;
  itemCount: number;
  currentStreak: number;
  longestStreak: number;
  customCounters: Record<string, number>;
}
```

### `BuddyEquippedItem`

```ts
export interface BuddyEquippedItem {
  itemId: string;
  name: string;
  imageUrl: string | null;
}
```

### `BuddyAppearanceStatus`

```ts
export type BuddyAppearanceStatus = 'ready' | 'pending' | 'awaiting_credits' | 'failed';
```

### `BuddyAppearance`

```ts
export interface BuddyAppearance {
  status: BuddyAppearanceStatus;
  operationId: string | null;
  desiredEquippedItemIds: string[];
  renderedEquippedItemIds: string[];
  retryable: boolean;
  message: string | null;
  error: Record<string, unknown> | null;
}
```

### `BuddyListParams`

```ts
export interface BuddyListParams {
  userId?: string;
  status?: string;
  evolutionStage?: number;
  page?: number;
  limit?: number;
  sort?: string;
  order?: 'asc' | 'desc';
  signal?: AbortSignal;
}
```

### `SkillUpdate`

```ts
export interface SkillUpdate {
  key: string;
  action: 'increase' | 'decrease' | 'set';
  amount?: number;
  value?: number;
}
```

### `EarnCoinsParams`

```ts
export interface EarnCoinsParams {
  amount: number;
  reason: string;
  referenceId?: string;
  /**
   * Token key to earn. Defaults to the customer's `primary` token. Passing
   * a progression token grants progress; passing any other configured token
   * key is accepted by the rule engine. Omit to earn the default coin / primary.
   */
  token?: string;
}
```

### `SpendCoinsParams`

```ts
export interface SpendCoinsParams {
  amount: number;
  reason: string;
  itemId?: string;
  /**
   * Token key to spend. Defaults to the customer's `primary` token. Spending
   * a progression token fails with ValidationError('progression_not_spendable').
   */
  token?: string;
}
```

### `TokenBalance`

```ts
export interface TokenBalance {
  /** Canonical `primary` or `progression` identifier. */
  kind: 'primary' | 'progression';
  /** Customer-defined token key (e.g. `gems`, `xp`). */
  key: string;
  /** Human-readable label for display. */
  label: string;
  /** Current balance. */
  balance: number;
  /** Lifetime earned (earn ledger sum). */
  lifetimeEarned: number;
  /** Lifetime spent (spend ledger sum) — always 0 for progression. */
  lifetimeSpent: number;
}
```

### `TokensSummary`

```ts
export interface TokensSummary {
  primary: TokenBalance | null;
  progression: TokenBalance | null;
}
```

### `BuddyEvolutionRecord`

```ts
export interface BuddyEvolutionRecord {
  id: string;
  buddyId: string;
  fromStage: number;
  toStage: number;
  triggeredByEventId: string | null;
  imageUrl: string | null;
  source: 'prod' | 'demo' | 'auto';
  metadata: Record<string, unknown>;
  occurredAt: string;
}
```

### `EquipItemsParams`

```ts
export interface EquipItemsParams {
  equip?: string[];
  unequip?: string[];
}
```

### `EquipItemsResult`

```ts
export interface EquipItemsResult {
  accepted: boolean;
  operationId: string | null;
  status: 'pending' | 'completed' | string;
  appearanceStatus: BuddyAppearanceStatus | string;
  cached: boolean;
}
```

### `RerenderAppearanceResult`

```ts
export interface RerenderAppearanceResult {
  accepted: boolean;
  operationId: string;
  status: 'pending' | string;
  appearanceStatus: BuddyAppearanceStatus | string;
}
```

### `BuddyList`

```ts
export interface BuddyList {
  data: Buddy[];
  meta: { total: number; page: number; limit: number };
}
```

### `SendEventParams`

```ts
export interface SendEventParams {
  /** Stable id used for idempotency. Re-sending the same eventId is a no-op. */
  eventId: string;
  /** External user id the event belongs to. */
  userId: string;
  /** Event type (e.g. `lesson_completed`, `workout_finished`). */
  type: string;
  /**
   * Audience (role) this event belongs to. Required for customers with 2+
   * audiences; omit for single-audience customers and the server applies
   * the implicit default. Lowercase, snake_case, max 32 chars.
   */
  audience?: string;
  /** When the event occurred. Defaults to "now" server-side if omitted. */
  occurredAt?: Date | string;
  /** Arbitrary key-value payload forwarded to the rule engine. */
  properties?: Record<string, unknown>;
}
```

### `EventStreakUpdate`

Per-streak progression entry returned alongside coin/badge effects when a
tracked event advances a streak. The HTTP client deep-converts snake_case
→ camelCase, so SDK consumers see camelCase keys here.

```ts
export interface EventStreakUpdate {
  definitionKey: string;
  label: string;
  icon: string;
  current: number;
  longest: number;
  milestoneHit: number | null;
  hero: boolean;
}
```

### `EventPathSubStepCompletion`

Per-path completion delta produced when a tracked event closes a sub-step.

```ts
export interface EventPathSubStepCompletion {
  pathKey: string;
  stepKey: string;
  subStepKey: string;
  rewardCoins: number;
  rewardBadgeKey: string | null;
}
```

### `EventPathStepCompletion`

```ts
export interface EventPathStepCompletion {
  pathKey: string;
  stepKey: string;
  rewardCoins: number;
  rewardBadgeKey: string | null;
}
```

### `EventPathCompletion`

```ts
export interface EventPathCompletion {
  pathKey: string;
}
```

### `EventPathUpdate`

```ts
export interface EventPathUpdate {
  pathKey: string;
  subStepCompleted?: EventPathSubStepCompletion;
  stepCompleted?: EventPathStepCompletion;
  pathCompleted?: EventPathCompletion;
}
```

### `EventEffects`

```ts
export interface EventEffects {
  coins?: number;
  badgesAwarded?: string[];
  badgesReady?: string[];
  tokens?: string[];
  /**
   * True when the buddy has met the next evolution condition. If the
   * customer's config does not auto-evolve, call `buddies.evolve(buddyId)`
   * server-side and wait on the returned operation.
   */
  evolutionReady?: boolean;
  streakMilestones?: number[];
  /** Per-streak deltas (current/longest, milestone hits) for active streaks. */
  streakUpdates?: EventStreakUpdate[];
  /**
   * Path widget reconciliation deltas. Each entry covers one sub-step
   * completion plus optional step / path roll-up flags so the host page
   * can paint celebrations without an extra round-trip.
   */
  pathUpdates?: EventPathUpdate[];
}
```

### `OperationStatus`

```ts
export type OperationStatus = 'pending' | 'processing' | 'completed' | 'failed';
```

### `Operation`

```ts
export interface Operation<TResult = unknown> {
  operationId: string;
  /** Alias for {@link Operation.operationId} for callers that prefer `id`. */
  id: string;
  type: string;
  status: OperationStatus;
  result?: TResult;
  error?: string;
  createdAt: string;
  updatedAt: string;
}
```

### `WaitOptions`

```ts
export interface WaitOptions {
  /** Maximum total time to wait, in milliseconds. Default 30_000. */
  timeoutMs?: number;
  /** Poll interval, in milliseconds. Default 2000. */
  intervalMs?: number;
  /** External abort signal. */
  signal?: AbortSignal;
}
```

### `CreateSessionParams`

```ts
export interface CreateSessionParams {
  buddyId: string;
  userId: string;
  scopes: string[];
  ttlSeconds?: number;
}
```

### `SessionToken`

```ts
export interface SessionToken {
  token: string;
  sessionId: string;
  expiresAt: string;
  scopes: string[];
}
```

### `CreateEmbedTokenParams`

```ts
export interface CreateEmbedTokenParams {
  buddyId: string;
  userId: string;
  ttlSeconds?: number;
}
```

### `EmbedToken`

```ts
export interface EmbedToken {
  token: string;
  expiresAt: string;
  mode: 'read-only';
}
```

### `WebhookEndpoint`

```ts
export interface WebhookEndpoint {
  id: string;
  url: string;
  events: string[];
  active: boolean;
  status: 'active' | 'paused';
  secret?: string;
  maskedSecret?: string;
  createdAt: string;
  updatedAt: string;
}
```

### `CreateWebhookParams`

```ts
export interface CreateWebhookParams {
  url: string;
  events: string[];
  description?: string;
}
```

### `WebhookDelivery`

```ts
export interface WebhookDelivery {
  id: string;
  endpointId?: string;
  event: string;
  eventType: string;
  status: 'pending' | 'success' | 'succeeded' | 'failed';
  responseCode?: number | null;
  responseStatus?: number | null;
  attempt: number;
  attempts: number;
  durationMs?: number | null;
  errorMessage?: string | null;
  createdAt: string;
  timestamp: string;
  lastAttemptAt?: string;
}
```

### `ListDeliveriesParams`

```ts
export interface ListDeliveriesParams {
  endpointId: string;
  status?: 'pending' | 'success' | 'failed';
  cursor?: string;
  limit?: number;
  signal?: AbortSignal;
}
```

### `Page`

```ts
export interface Page<T> {
  data: T[];
  nextCursor: string | null;
}
```

### `VerifySignatureOptions`

```ts
export interface VerifySignatureOptions {
  /** Maximum clock-skew in seconds. Defaults to 5 minutes. */
  toleranceSeconds?: number;
  /** Clock used for timestamp validation — useful in tests. */
  now?: () => number;
}
```

### `TokenGate`

```ts
export interface TokenGate {
  id: string;
  gateKey: string;
  tokenKey: string;
  cost: number;
  label: string | null;
  description: string | null;
  metadata: Record<string, unknown>;
  isActive: boolean;
  createdAt: string;
  updatedAt: string;
}
```

### `UnlockResult`

```ts
export interface UnlockResult {
  gateKey: string;
  unlocked: true;
  alreadyUnlocked: boolean;
  unlockedAt: string;
  balanceAfter: number | null;
  metadata: Record<string, unknown>;
}
```

### `BuddyUnlock`

```ts
export interface BuddyUnlock {
  gateKey: string;
  unlockedAt: string;
  metadata: Record<string, unknown>;
}
```

### `PathDisplayMode`

```ts
export type PathDisplayMode = 'straight' | 'zigzag' | 'stepper';
```

### `PathIcon`

```ts
export type PathIcon = 'path' | 'flame' | 'heart' | 'bolt' | 'star' | 'leaf';
```

### `PathConditionType`

```ts
export type PathConditionType =
  | 'event_count'
  | 'milestone'
  | 'streak'
  | 'skill_level'
  | 'collection'
  | 'evolution'
  | 'coin'
  | 'badge_earned'
  | 'gate_unlocked'
  | 'custom';
```

### `PathCondition`

```ts
export interface PathCondition {
  type: PathConditionType;
  config: Record<string, unknown>;
}
```

### `PathDefinition`

```ts
export interface PathDefinition {
  id: string;
  customerId: string;
  audience: string;
  key: string;
  label: string;
  description: string | null;
  icon: PathIcon;
  accentColor: string | null;
  displayMode: PathDisplayMode;
  isActive: boolean;
  createdAt: string;
  updatedAt: string;
}
```

### `PathStep`

```ts
export interface PathStep {
  id: string;
  pathDefinitionId: string;
  key: string;
  label: string;
  description: string | null;
  icon: string | null;
  ordinal: number;
  unlockCondition: Record<string, unknown> | null;
  completionCondition: PathCondition | null;
  rewardCoins: number;
  rewardBadgeKey: string | null;
  isActive: boolean;
  createdAt: string;
  updatedAt: string;
}
```

### `PathSubStep`

```ts
export interface PathSubStep {
  id: string;
  pathStepId: string;
  key: string;
  label: string;
  description: string | null;
  ordinal: number;
  completionCondition: PathCondition | null;
  allowManualComplete: boolean;
  allowSkipAhead: boolean;
  rewardCoins: number;
  rewardBadgeKey: string | null;
  contentUrl: string | null;
  ctaLabel: string | null;
  isActive: boolean;
  createdAt: string;
  updatedAt: string;
}
```

### `PathSubStepStatus`

```ts
export type PathSubStepStatus = 'locked' | 'available' | 'completed';
```

### `PathSubStepRuntime`

```ts
export interface PathSubStepRuntime {
  id: string;
  key: string;
  label: string;
  description: string | null;
  ordinal: number;
  rewardCoins: number;
  rewardBadgeKey: string | null;
  contentUrl: string | null;
  ctaLabel: string | null;
  allowManualComplete: boolean;
  allowSkipAhead: boolean;
  isActive: boolean;
  status: PathSubStepStatus;
  completedAt: string | null;
}
```

### `PathStepRuntime`

```ts
export interface PathStepRuntime {
  id: string;
  key: string;
  label: string;
  description: string | null;
  icon: string | null;
  ordinal: number;
  rewardCoins: number;
  rewardBadgeKey: string | null;
  isActive: boolean;
  unlocked: boolean;
  completed: boolean;
  subSteps: PathSubStepRuntime[];
}
```

### `PathRuntimePayload`

```ts
export interface PathRuntimePayload {
  definition: {
    key: string;
    label: string;
    description: string | null;
    icon: PathIcon;
    accentColor: string | null;
    displayMode: PathDisplayMode;
  };
  steps: PathStepRuntime[];
  currentStepKey: string | null;
  completed: boolean;
  completedAt: string | null;
}
```

### `CreatePathDefinitionParams`

```ts
export interface CreatePathDefinitionParams {
  audience?: string;
  key: string;
  label: string;
  description?: string;
  icon?: PathIcon;
  accentColor?: string;
  displayMode?: PathDisplayMode;
  isActive?: boolean;
}
```

### `UpdatePathDefinitionParams`

```ts
export type UpdatePathDefinitionParams = Partial<CreatePathDefinitionParams>;
```

### `CreatePathStepParams`

```ts
export interface CreatePathStepParams {
  key: string;
  label: string;
  description?: string;
  icon?: string;
  ordinal: number;
  unlockCondition?: Record<string, unknown>;
  completionCondition?: PathCondition;
  rewardCoins?: number;
  rewardBadgeKey?: string;
  isActive?: boolean;
}
```

### `UpdatePathStepParams`

```ts
export type UpdatePathStepParams = Partial<CreatePathStepParams>;
```

### `CreatePathSubStepParams`

```ts
export interface CreatePathSubStepParams {
  key: string;
  label: string;
  description?: string;
  ordinal: number;
  completionCondition?: PathCondition;
  allowManualComplete?: boolean;
  allowSkipAhead?: boolean;
  rewardCoins?: number;
  rewardBadgeKey?: string;
  contentUrl?: string;
  ctaLabel?: string;
  isActive?: boolean;
}
```

### `UpdatePathSubStepParams`

```ts
export type UpdatePathSubStepParams = Partial<CreatePathSubStepParams>;
```

### `ManualCompleteResult`

```ts
export interface ManualCompleteResult {
  alreadyCompleted: boolean;
  subStepKey: string;
  stepKey: string;
  stepCompleted: boolean;
  pathCompleted: boolean;
  rewardCoins: number;
  rewardBadgeKey: string | null;
}
```

## Related

- [Error codes](/docs/reference/error-codes)
- [HTTP API](/docs/reference/http-api)
- [Getting started](/docs/guides/getting-started)
