Skip to content

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.

Install

Terminal
npm install startup-builder

Import

$ reads its key from STARTUP_BUILDER_API_KEY; the key selects the namespace.

TypeScript
import { $ } from 'startup-builder'

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

Modes

CallModeResolves to
await $.Offer.publish({ offer: 'offer_1' })The Verb defaultThe result
await $.try.Offer.publish({ offer: 'offer_1' })tryThe result; nothing is stored
await $.send.Offer.publish({ offer: 'offer_1' })sendThe pending Action
await $.do.Offer.publish({ offer: 'offer_1' })doThe 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.

TypeScript
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 has the rule.

TypeScript
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.

TypeScript
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.

OptionDefaultDoes
modeThe Verb defaulttry, send or do
idempotencyKeyGenerated per callMakes a repeat return the first Action
timeout30 secondsStops waiting for the response; the work goes on
signalNone.An AbortSignal that stops waiting when it fires
maxRetries3How often to retry what time fixes
headersNone.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.

TypeScript
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).

TypeScript
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 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, startup-builder/test.d.ts, startup-builder/ui.d.ts, 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.