# SDK quickstart

> Install @hatched/sdk-js, authenticate, and make your first calls from Node or TypeScript.

Source: https://docs.hatched.live/docs/guides/sdk-quickstart

`@hatched/sdk-js` is the official TypeScript SDK for the Hatched API. It
ships as dual ESM + CJS, runs on Node 18+, Cloudflare Workers, Vercel
Edge, Deno, and Bun. Full package on
[npmjs.com/package/@hatched/sdk-js](https://npmjs.com/package/@hatched/sdk-js).

## Install

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

## Initialise a client

```ts
import { HatchedClient } from '@hatched/sdk-js';

const hatched = new HatchedClient({
  apiKey: process.env.HATCHED_API_KEY!,
  // optional overrides:
  baseUrl: 'https://api.staging.hatched.live/api/v1',
  timeoutMs: 15_000,
  maxRetries: 3,
  fetch: globalThis.fetch,
});
```

`hatch_test_*` secret keys default to the staging API if you omit `baseUrl`;
`hatch_live_*` keys default to production.

The SDK parses the [canonical error envelope](/docs/reference/error-codes)
and throws typed `HatchedError` subclasses with `requestId`, `code`, and
`statusCode` fields.

> Secret keys (`hatch_live_*`, `hatch_test_*`) are **server-only**. The
> SDK throws if instantiated in a DOM environment. See
> [Auth model](/docs/concepts/auth-model) for browser options.

## Core resources

```ts
// Eggs & buddies
const egg = await hatched.eggs.create({ userId });
await hatched.eggs.updateStatus(egg.eggId, 'ready');
await hatched.eggs.hatch(egg.eggId);
await hatched.buddies.list({ userId });

// Economy
await hatched.buddies.earn(buddyId, { amount: 50, reason: 'lesson_reward' });
await hatched.buddies.spend(buddyId, { amount: 20, reason: 'item_purchase' });
const equip = await hatched.buddies.equip(buddyId, { equip: [itemId] });
if (equip.operationId) await hatched.operations.wait(equip.operationId);
const buddy = await hatched.buddies.get(buddyId);
console.log(buddy.appearance?.status);
// Recovery only: use when buddy.appearance?.error?.code === 'needs_rerender'
// await hatched.buddies.rerenderAppearance(buddyId);

// Events
await hatched.events.send({ eventId, userId, type, properties });

// Operations (async image jobs)
const op = await hatched.eggs.hatch(egg.eggId);
const finished = await hatched.operations.wait(op.operationId);

// Widget sessions
await hatched.widgetSessions.create({ buddyId, userId, scopes, ttlSeconds });
await hatched.widgetSessions.revoke(sessionId);
```

## Error handling

```ts
import { HatchedError, RateLimitError, ValidationError } from '@hatched/sdk-js';

try {
  await hatched.events.send({ ... });
} catch (err) {
  if (err instanceof RateLimitError) {
    await sleep(err.retryAfter * 1000);
  } else if (err instanceof ValidationError) {
    console.error('bad payload:', err.details);
  } else if (err instanceof HatchedError) {
    console.error(err.code, err.requestId, err.message);
  } else {
    throw err;
  }
}
```

The SDK exposes a typed class per known error code. See
[Error codes](/docs/reference/error-codes) for the full catalogue.

## Retries and idempotency

- `events.send` is idempotent on `eventId` — passing the same id twice
  returns the cached effect without re-applying rules.
- The client retries `GET`s and `idempotent: true` `POST`s on network
  failures, `408`, `429` (with `Retry-After` honoured), and 5xx responses.
  Exponential backoff + jitter.
- 4xx responses (other than 408/429) surface immediately.

## Cancellation

Every resource method accepts an optional `AbortSignal` as its last
argument. The SDK combines it with the built-in timeout via
`AbortSignal.any`, so either source can cancel the request.

```ts
const controller = new AbortController();
setTimeout(() => controller.abort(), 500);
await hatched.buddies.get(buddyId, controller.signal);
```

## Rate limits + request ids

```ts
await hatched.events.send({ ... });

console.log(hatched.getRateLimitInfo());
// { limit: 1000, remaining: 986, reset: 1735689600, retryAfter: undefined }

console.log(hatched.getLastRequestId());
// 'req_abc_123'
```

Include the request id in any support ticket and we can look up the full
trace.
