SDK (JavaScript / TypeScript)
Complete method reference for @hatched/sdk-js — HatchedClient, resources, error classes.
Package: @hatched/sdk-js
pnpm add @hatched/sdk-jsHatchedClient
Official Hatched SDK client for JavaScript/TypeScript.
Server-side (secret key):
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):
const hatched = new HatchedClient({
publishableKey: 'hatch_pk_xxxxxxxx',
});
const buddy = await hatched.buddies.get('bdy_abc');HatchedClient.health()
health()Health check; returns API status metadata.
HatchedClient.getRateLimitInfo()
getRateLimitInfo()Latest X-RateLimit-* snapshot from the most recent response.
HatchedClient.getLastRequestId()
getLastRequestId(): string | nullRequest id of the most recent response (for support correlation).
EggsResource
EggsResource.create()
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
const egg = await hatched.eggs.create({ userId: 'user_42' });
await hatched.eggs.updateStatus(egg.eggId, 'ready');EggsResource.get()
get(eggId: string, signal?: AbortSignal): Promise<Egg>Fetches the canonical state of a single egg.
EggsResource.list()
list(params: ListEggsParams = {}): Promise<Egg[]>Lists eggs with optional filters.
EggsResource.updateStatus()
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()
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()
get(buddyId: string, signal?: AbortSignal): Promise<Buddy>Fetches a buddy by id.
BuddiesResource.list()
list(params: BuddyListParams = {}): Promise<BuddyList>Lists buddies with optional filters.
BuddiesResource.updateName()
updateName(buddyId: string, name: string, signal?: AbortSignal): Promise<Buddy>BuddiesResource.archive()
archive(buddyId: string, signal?: AbortSignal): Promise<Buddy>BuddiesResource.updateSkills()
updateSkills(buddyId: string, updates: SkillUpdate[], signal?: AbortSignal)BuddiesResource.earn()
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()
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()
awardBadge(buddyId: string, badgeKey: string, reason?: string, signal?: AbortSignal)BuddiesResource.getBadges()
getBadges(buddyId: string, signal?: AbortSignal)BuddiesResource.equip()
equip(buddyId: string, params: EquipItemsParams, signal?: AbortSignal): Promise<EquipItemsResult>Equips or unequips items on a buddy.
BuddiesResource.rerenderAppearance()
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()
purchaseItem(buddyId: string, itemId: string, idempotencyKey?: string, signal?: AbortSignal)BuddiesResource.getPurchasedItems()
getPurchasedItems(buddyId: string, signal?: AbortSignal)BuddiesResource.getEvolution()
getEvolution(buddyId: string, signal?: AbortSignal)BuddiesResource.evolve()
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()
getProgression(buddyId: string, signal?: AbortSignal)BuddiesResource.tokens()
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()
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()
getUserSummary(userId: string, signal?: AbortSignal)EventsResource
EventsResource.send()
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
const effects = await hatched.events.send({
eventId: 'lesson_lsn_42_user_123',
userId: 'user_123',
type: 'lesson_completed',
properties: { score: 94 },
});EventsResource.sendBatch()
sendBatch(events: SendEventParams[], signal?: AbortSignal): Promise<{ results: EventEffects[] }>Sends a batch of events in a single call.
OperationsResource
OperationsResource.get()
get(operationId: string, signal?: AbortSignal): Promise<Operation<TResult>>Fetches an operation's current status.
OperationsResource.wait()
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
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()
waitForCompletion(operationId: string, options: { timeout?: number; interval?: number; signal?: AbortSignal } = {}): Promise<Operation<TResult>>@deprecated Use OperationsResource.wait instead.
WidgetSessionsResource
WidgetSessionsResource.create()
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()
revoke(sessionId: string, signal?: AbortSignal): Promise<void>EmbedTokensResource
EmbedTokensResource.create()
create(params: CreateEmbedTokenParams, signal?: AbortSignal): Promise<EmbedToken>Mints a signed token for a read-only embedded widget.
WebhooksResource
WebhooksResource.list()
list(signal?: AbortSignal): Promise<WebhookEndpoint[]>Lists webhook endpoints registered for the current customer.
WebhooksResource.create()
create(params: CreateWebhookParams, signal?: AbortSignal): Promise<WebhookEndpoint>Registers a new webhook endpoint.
WebhooksResource.delete()
delete(endpointId: string, signal?: AbortSignal): Promise<void>Deletes a webhook endpoint.
WebhooksResource.deliveries()
deliveries(params: ListDeliveriesParams): Promise<Page<WebhookDelivery>>Lists recent deliveries for a given endpoint.
WebhooksResource.replay()
replay(endpointId: string, deliveryId: string, signal?: AbortSignal): Promise<unknown>Replays a specific delivery attempt.
WebhooksResource.redeliver()
redeliver(endpointId: string, deliveryId: string, signal?: AbortSignal): Promise<unknown>Re-enqueues a stored webhook delivery.
WebhooksResource.verifySignature()
static verifySignature(rawBody: string | Buffer, signatureHeader: string, secret: string, options: VerifySignatureOptions = {}): booleanVerifies 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
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()
list(signal?: AbortSignal): Promise<{ gates: TokenGate[] }>Lists gates configured on this customer. Secret-key only.
GatesResource.unlock()
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()
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()
list(audience?: string, signal?: AbortSignal): Promise<PathDefinition[]>PathsResource.get()
get(definitionId: string, signal?: AbortSignal): Promise<PathDefinitionWithSteps>PathsResource.create()
create(params: CreatePathDefinitionParams, signal?: AbortSignal): Promise<PathDefinition>PathsResource.update()
update(definitionId: string, params: UpdatePathDefinitionParams, signal?: AbortSignal): Promise<PathDefinition>PathsResource.delete()
delete(definitionId: string, signal?: AbortSignal): Promise<void>PathsResource.setActive()
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()
addStep(definitionId: string, params: CreatePathStepParams, signal?: AbortSignal): Promise<PathStep>PathsResource.updateStep()
updateStep(definitionId: string, stepId: string, params: UpdatePathStepParams, signal?: AbortSignal): Promise<PathStep>PathsResource.deleteStep()
deleteStep(definitionId: string, stepId: string, signal?: AbortSignal): Promise<void>PathsResource.reorderSteps()
reorderSteps(definitionId: string, ordering: Array<{ id: string; ordinal: number }>, signal?: AbortSignal): Promise<PathStep[]>PathsResource.addSubStep()
addSubStep(definitionId: string, stepId: string, params: CreatePathSubStepParams, signal?: AbortSignal): Promise<PathSubStep>PathsResource.updateSubStep()
updateSubStep(definitionId: string, stepId: string, subStepId: string, params: UpdatePathSubStepParams, signal?: AbortSignal): Promise<PathSubStep>PathsResource.deleteSubStep()
deleteSubStep(definitionId: string, stepId: string, subStepId: string, signal?: AbortSignal): Promise<void>PathsResource.reorderSubSteps()
reorderSubSteps(definitionId: string, stepId: string, ordering: Array<{ id: string; ordinal: number }>, signal?: AbortSignal): Promise<PathSubStep[]>PathsResource.getForBuddy()
getForBuddy(buddyId: string, pathKey: string, signal?: AbortSignal): Promise<PathRuntimePayload>PathsResource.completeSubStep()
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
export interface NoPublishedConfigDetails {
customerId?: string;
customer_id?: string;
publishUrl?: string;
publish_url?: string;
docsUrl?: string;
docs_url?: string;
}ActiveEggLimitEgg
export interface ActiveEggLimitEgg {
eggId: string;
status: string;
createdAt: string;
}ActiveEggLimitDetails
export interface ActiveEggLimitDetails {
max?: number;
active?: ActiveEggLimitWireEgg[];
}CreditInsufficientDetails
export interface CreditInsufficientDetails {
required?: number;
available?: number;
welcome?: number;
paid?: number;
promo?: number;
upgrade_url?: string;
top_up_url?: string;
}EventQuotaExceededDetails
export interface EventQuotaExceededDetails {
used?: number;
limit?: number;
reset_at?: string;
upgrade_url?: string;
}PlanFeatureLockedDetails
export interface PlanFeatureLockedDetails {
feature?: string;
required_plan?: string;
current_plan?: string;
upgrade_url?: string;
}EggStatus
export type EggStatus = 'waiting' | 'ready' | 'hatching' | 'hatched' | 'cancelled';CreateEggParams
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
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
export interface EggStatusChange {
eggId: string;
status: EggStatus;
previousStatus: EggStatus;
}HatchResult
export interface HatchResult {
operationId: string;
status: string;
}ListEggsParams
export interface ListEggsParams {
userId?: string;
status?: EggStatus;
page?: number;
limit?: number;
signal?: AbortSignal;
}Buddy
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
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
export interface BuddyEquippedItem {
itemId: string;
name: string;
imageUrl: string | null;
}BuddyAppearanceStatus
export type BuddyAppearanceStatus = 'ready' | 'pending' | 'awaiting_credits' | 'failed';BuddyAppearance
export interface BuddyAppearance {
status: BuddyAppearanceStatus;
operationId: string | null;
desiredEquippedItemIds: string[];
renderedEquippedItemIds: string[];
retryable: boolean;
message: string | null;
error: Record<string, unknown> | null;
}BuddyListParams
export interface BuddyListParams {
userId?: string;
status?: string;
evolutionStage?: number;
page?: number;
limit?: number;
sort?: string;
order?: 'asc' | 'desc';
signal?: AbortSignal;
}SkillUpdate
export interface SkillUpdate {
key: string;
action: 'increase' | 'decrease' | 'set';
amount?: number;
value?: number;
}EarnCoinsParams
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
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
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
export interface TokensSummary {
primary: TokenBalance | null;
progression: TokenBalance | null;
}BuddyEvolutionRecord
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
export interface EquipItemsParams {
equip?: string[];
unequip?: string[];
}EquipItemsResult
export interface EquipItemsResult {
accepted: boolean;
operationId: string | null;
status: 'pending' | 'completed' | string;
appearanceStatus: BuddyAppearanceStatus | string;
cached: boolean;
}RerenderAppearanceResult
export interface RerenderAppearanceResult {
accepted: boolean;
operationId: string;
status: 'pending' | string;
appearanceStatus: BuddyAppearanceStatus | string;
}BuddyList
export interface BuddyList {
data: Buddy[];
meta: { total: number; page: number; limit: number };
}SendEventParams
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.
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.
export interface EventPathSubStepCompletion {
pathKey: string;
stepKey: string;
subStepKey: string;
rewardCoins: number;
rewardBadgeKey: string | null;
}EventPathStepCompletion
export interface EventPathStepCompletion {
pathKey: string;
stepKey: string;
rewardCoins: number;
rewardBadgeKey: string | null;
}EventPathCompletion
export interface EventPathCompletion {
pathKey: string;
}EventPathUpdate
export interface EventPathUpdate {
pathKey: string;
subStepCompleted?: EventPathSubStepCompletion;
stepCompleted?: EventPathStepCompletion;
pathCompleted?: EventPathCompletion;
}EventEffects
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
export type OperationStatus = 'pending' | 'processing' | 'completed' | 'failed';Operation
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
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
export interface CreateSessionParams {
buddyId: string;
userId: string;
scopes: string[];
ttlSeconds?: number;
}SessionToken
export interface SessionToken {
token: string;
sessionId: string;
expiresAt: string;
scopes: string[];
}CreateEmbedTokenParams
export interface CreateEmbedTokenParams {
buddyId: string;
userId: string;
ttlSeconds?: number;
}EmbedToken
export interface EmbedToken {
token: string;
expiresAt: string;
mode: 'read-only';
}WebhookEndpoint
export interface WebhookEndpoint {
id: string;
url: string;
events: string[];
active: boolean;
status: 'active' | 'paused';
secret?: string;
maskedSecret?: string;
createdAt: string;
updatedAt: string;
}CreateWebhookParams
export interface CreateWebhookParams {
url: string;
events: string[];
description?: string;
}WebhookDelivery
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
export interface ListDeliveriesParams {
endpointId: string;
status?: 'pending' | 'success' | 'failed';
cursor?: string;
limit?: number;
signal?: AbortSignal;
}Page
export interface Page<T> {
data: T[];
nextCursor: string | null;
}VerifySignatureOptions
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
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
export interface UnlockResult {
gateKey: string;
unlocked: true;
alreadyUnlocked: boolean;
unlockedAt: string;
balanceAfter: number | null;
metadata: Record<string, unknown>;
}BuddyUnlock
export interface BuddyUnlock {
gateKey: string;
unlockedAt: string;
metadata: Record<string, unknown>;
}PathDisplayMode
export type PathDisplayMode = 'straight' | 'zigzag' | 'stepper';PathIcon
export type PathIcon = 'path' | 'flame' | 'heart' | 'bolt' | 'star' | 'leaf';PathConditionType
export type PathConditionType =
| 'event_count'
| 'milestone'
| 'streak'
| 'skill_level'
| 'collection'
| 'evolution'
| 'coin'
| 'badge_earned'
| 'gate_unlocked'
| 'custom';PathCondition
export interface PathCondition {
type: PathConditionType;
config: Record<string, unknown>;
}PathDefinition
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
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
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
export type PathSubStepStatus = 'locked' | 'available' | 'completed';PathSubStepRuntime
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
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
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
export interface CreatePathDefinitionParams {
audience?: string;
key: string;
label: string;
description?: string;
icon?: PathIcon;
accentColor?: string;
displayMode?: PathDisplayMode;
isActive?: boolean;
}UpdatePathDefinitionParams
export type UpdatePathDefinitionParams = Partial<CreatePathDefinitionParams>;CreatePathStepParams
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
export type UpdatePathStepParams = Partial<CreatePathStepParams>;CreatePathSubStepParams
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
export type UpdatePathSubStepParams = Partial<CreatePathSubStepParams>;ManualCompleteResult
export interface ManualCompleteResult {
alreadyCompleted: boolean;
subStepKey: string;
stepKey: string;
stepCompleted: boolean;
pathCompleted: boolean;
rewardCoins: number;
rewardBadgeKey: string | null;
}