﻿# TypeScript SDK

The Simmit TypeScript SDK is the official typed client for the Simmit API. It submits SimulationCraft (SimC) profiles, polls job status, reads results, and verifies webhooks, with generated types for every request and response. It targets Node 20+ and ships zero runtime dependencies.

```sh
npm install @simmit/sdk
```

Source and issues live at [github.com/voidly-labs/simmit-sdk-typescript](https://github.com/voidly-labs/simmit-sdk-typescript). The package is published as [`@simmit/sdk`](https://www.npmjs.com/package/@simmit/sdk).

## Authenticate

The client reads your secret key from the `SIMMIT_SECRET_KEY` environment variable by default. [Create a client](/clients), then create a new key to get one. The secret key spends credits, so keep it server-side and never ship it to a browser.

```ts
import Simmit from '@simmit/sdk'

const client = new Simmit({
  secretKey: process.env.SIMMIT_SECRET_KEY
  // ^^ This is the default and can be omitted
})
```

## Submit and wait

`jobs.createAndWait` submits a job and resolves once the sim reaches a terminal state. Reach for it from scripts, bots, and queue workers that can hold a promise open until the sim finishes.

```ts
const job = await client.jobs.createAndWait({
  build: { channel: 'latest' },
  profile: { text: profileText }
})

const result = await client.jobs.getResult(job.id)
```

`onCreated` fires with the job ID before polling starts; `onPoll` fires after each status poll, so you can surface progress:

```ts
await client.jobs.createAndWait(params, {
  onCreated: res => console.log('queued', res.id),
  onPoll: status => console.log(status.status, status.progress?.percent)
})
```

## Submit, poll, and read separately

A web app can't hold a promise open across requests. Drive the lifecycle yourself instead: `jobs.create` returns immediately with a job ID to persist, then poll `jobs.getStatus` on your own cadence and read the result once the job is terminal.

```ts
const { id } = await client.jobs.create({
  build: { channel: 'nightly' },
  profile: { text: profileText }
})

const status = await client.jobs.getStatus(id)
if (status.status === 'completed') {
  const result = await client.jobs.getResult(id)
}
```

`jobs.getStatus` never throws for a job still in flight, so it is the supported way to drive a custom poll loop. For production web flows, subscribe to a `job.terminal` webhook rather than polling. See [Webhooks](/docs/api-advanced/webhooks).

## Typed errors

Every API error is a subclass of `SimmitError` with a narrowed `code` and `meta`. Branch on the class:

```ts
import { InvalidProfileError, InsufficientCreditsError } from '@simmit/sdk'

try {
  await client.jobs.create(params)
} catch (err) {
  if (err instanceof InvalidProfileError) {
    console.error(err.meta.blocked) // the rejected profile lines
  } else if (err instanceof InsufficientCreditsError) {
    console.error(err.meta?.maxAffordableRuntimeSeconds)
  } else {
    throw err
  }
}
```

`jobs.createAndWait` also throws `JobFailedError`, `JobCancelledError`, and `JobTimedOutError` for the non-success terminal states, plus `JobWaitTimeoutError` if the wait deadline passes first. On a wait timeout the job keeps running and billing, so call `client.jobs.cancel(id)` to stop the spend. The underlying status codes are listed in [API Errors](/docs/api/errors).

## Verify webhooks

`unwrapWebhook` verifies a webhook signature and returns the parsed event. It is standalone, so it needs no client and no secret key, only your webhook signing secret. That keeps it safe to run inside a receiver.

```ts
import { unwrapWebhook } from '@simmit/sdk'

const event = await unwrapWebhook(
  rawBody, // the raw request body, exactly as received
  signatureHeader, // the X-Simmit-Signature header value
  process.env.SIMMIT_WEBHOOK_SECRET
)

if (event.payload.status === 'completed') {
  // handle the terminal job
}
```

See [Webhooks](/docs/api-advanced/webhooks) for delivery, retries, and the signature scheme.

## Use it in a web app

The SDK is server-side only. A web flow submits a job on one request, persists the returned id, and reads the result on a later request.

`new Simmit()` throws when no secret key is set. Frameworks that evaluate route modules at build time, like Next.js, run that code with no environment, so construct the client lazily and read the key at first use:

```ts
// lib/simmit.ts
import 'server-only'
import Simmit from '@simmit/sdk'

let client: Simmit | undefined

export function simmit(): Simmit {
  return (client ??= new Simmit())
}
```

The [Next.js web app example](https://github.com/voidly-labs/simc-nextjs-web) wires this end to end: paste a SimC profile, follow a live status page, and render the headline DPS.

---

_HTML version: https://docs.staging.simmit.gg/docs/sdks/typescript · Full docs index: https://docs.staging.simmit.gg/llms.txt_
