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
npm install startup-builderImport
$ reads its key from STARTUP_BUILDER_API_KEY; the key selects the namespace.
import { $ } from 'startup-builder'
const offer = await $.Offer.publish({ offer: 'offer_1' })Modes
| Call | Mode | Resolves to |
|---|---|---|
await $.Offer.publish({ offer: | The Verb default | The result |
await $.try.Offer.publish({ offer: | try | The result; nothing is stored |
await $.send.Offer.publish({ offer: | send | The pending Action |
await $.do.Offer.publish({ offer: | 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.
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.
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.
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.
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).
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:: only the Verbs its Role may run.