# SDK spec

URL: /code/sdk/spec · Id: `page:code/sdk/spec`

> What the startup-builder package exports, and what each call resolves to.

The `startup-builder` package exports `$`, a typed client with one method per operation on every [Noun](/business/nouns/noun).

### Install
```bash
npm install startup-builder
```

### Import
`$` reads its key from `STARTUP_BUILDER_API_KEY`; the key selects the namespace.

```ts
import { $ } from 'startup-builder'

const offer = await $.Offer.publish({ offer: 'offer_1' })
```

### Modes
| Call | Mode | Resolves to |
| --- | --- | --- |
| `await $.Offer.publish({ offer: 'offer_1' })` | The Verb default | The result |
| `await $.try.Offer.publish({ offer: 'offer_1' })` | `try` | The result; nothing is stored |
| `await $.send.Offer.publish({ offer: 'offer_1' })` | `send` | The pending Action |
| `await $.do.Offer.publish({ offer: 'offer_1' })` | `do` | The result, after the Action finishes |

### Results and actions
`await` resolves to the result, or throws the Action when it ends `failed`, `refused`, or `canceled`. `.settled` resolves to the Action in any final status.

```ts
const action = await $.Offer.publish({ offer: 'offer_1' }).settled

if (action.status === 'done') console.log(action.result)
else console.log(action.error?.type) // https://startupbuilder.dev/errors/<slug>
```

### Ids
A record's `$id` is its URL, and every input that names a record takes it, so a returned record passes straight into the next call. `get`, `update` and `delete` name the record `id`; every other Verb names it by its Noun. The [API spec](/code/api/spec#ids) has the rule.

```ts
import { $ } from 'startup-builder'

const offer = await $.Offer.create({ price: { amount: 29, currency: 'USD' }, interval: 'month' })
await $.Offer.publish({ offer: offer.$id })
await $.Offer.update({ id: offer.$id, terms: 'Cancel any time from the billing portal.' })
```

### Lists
A list call resolves to one page, an array with the `after` cursor of the next. `.all()` walks every page, fetching each as the loop reaches it.

```ts
import { $ } from 'startup-builder'

const page = await $.Invoice.list({ where: 'paidAt = null', limit: 50 })
const next = await $.Invoice.list({ where: 'paidAt = null', after: page.after })

for await (const invoice of $.Invoice.list({ where: 'paidAt = null' }).all()) console.log(invoice.$id)
```

### Call options
Every method takes an options object after its input. `$.with(options)` returns a client scoped by `namespace`, `key`, `env` or `for`, whose calls default to the same options.

| Option | Default | Does |
| --- | --- | --- |
| `mode` | The Verb default | `try`, `send` or `do` |
| `idempotencyKey` | Generated per call | Makes a repeat return the first Action |
| `timeout` | 30 seconds | Stops waiting for the response; the work goes on |
| `signal` | None. | An `AbortSignal` that stops waiting when it fires |
| `maxRetries` | `3` | How often to retry what time fixes |
| `headers` | None. | Extra headers, such as `traceparent` |

### Events
Subscribe by Noun and past tense, or by the full Event name. Each handler gets the Event record: `$id`, `$type`, `name`, `at`, `seq` and `data`.

```ts
import { $ } from 'startup-builder'

$.on.Offer.published((event) => console.log(event.seq, event.data))

$.on('Offer.published', (event) => console.log(event.name, event.at))
```

### Webhooks
`verifyWebhook` checks a delivery's `StartupBuilder-Signature` header against the raw body and the Watch's signing secret, and returns the Event, the same record `$.on` hands over. It throws when no signature matches, or when the newest is older than `tolerance` seconds (300 by default).

```ts
import { verifyWebhook } from 'startup-builder'

export async function POST(request: Request) {
  const event = verifyWebhook(await request.text(), request.headers.get('StartupBuilder-Signature') ?? '', process.env.WATCH_SECRET ?? '')
  if (event.name === 'Order.delivered') console.log(event.data.order.$id)
  return new Response(null, { status: 204 })
}
```

### Types
[startup-builder.d.ts](https://startupbuilder.dev/startup-builder.d.ts) holds the types of `$` for every public operation: each record as a top-level type (`Offer`), the client and its runtime types in `SB` (`SB.Action`, `SB.Call`), and the project-file helpers (`Startup`, `fn`, `workflow`, `cases`, `env`, `evaluator`, `mapping`). The subpaths have their own: [startup-builder/react.d.ts](https://startupbuilder.dev/startup-builder/react.d.ts), [startup-builder/test.d.ts](https://startupbuilder.dev/startup-builder/test.d.ts), [startup-builder/ui.d.ts](https://startupbuilder.dev/startup-builder/ui.d.ts), [startup-builder/globals.d.ts](https://startupbuilder.dev/startup-builder/globals.d.ts). Each caller also gets its own from `https://api.sb/{namespace}/$.d.ts`: only the Verbs its Role may run.