Skip to content

TypeScript SDK

Install @superartifacts/sdk, publish a page, read back what it collected, share it, and handle errors, retries, paging and uploads.

guide~4 minVerified v0.178.0

@superartifacts/sdk has no runtime dependencies. Node 20.3+. It uses only the standard fetch, so other runtimes that provide it may work; only Node is tested.

It is server side only: the client throws if you construct it in a browser. dangerouslyAllowBrowser: true turns that check off for use behind your own proxy, and ships the key to every visitor.

Add the SDK with your coding agent
Your agent installs it, wires up the key and makes a first call.

Install

bash
npm install @superartifacts/sdk

Create a key in Settings → API keys and put it in SUPERARTIFACTS_API_KEY.

First call

ts
import { SuperArtifacts } from '@superartifacts/sdk'

const sa = new SuperArtifacts()

const me = await sa.account.whoami()
console.log(me)

const published = await sa.artifacts.publish({
  slug: 'hello',
  title: 'Hello',
  prompt: 'A page that says hello.',
  html: '<h1>Hello</h1>',
})
console.log(published)

const { artifacts } = await sa.artifacts.list()
console.log(artifacts)

for await (const row of sa.data.iterArtifactRows({ artifact: 'hello' })) {
  console.log(row)
}

Every method takes one object holding the operation's path, query and body fields together, and an optional second argument for that request alone.

ts
await sa.artifacts.update({ artifact: 'hello', visibility: 'private' }, { timeout: 30_000 })

Responses are untyped JSON until the API describes them: every result is an open object, so destructure and index it without a cast.

Configuration

ts
new SuperArtifacts({
  apiKey: process.env.SUPERARTIFACTS_API_KEY, // the default
  baseUrl: 'https://superartifacts.app',      // the default
  timeout: 60_000,                            // milliseconds per attempt
  maxRetries: 2,
  fetch: globalThis.fetch,
})

Per request: { idempotencyKey, timeout, signal, headers }.

Publish a page

A page is private when it first publishes. Publishing the same slug again adds a new version at the same address.

ts
const page = await sa.artifacts.publish({
  slug: 'launch-notes',
  title: 'Launch notes',
  prompt: 'A page of launch notes.',
  html: '<h1>Launch notes</h1>',
})
console.log(page.url)

Collect data and read it back

Without a policy, a form stores nothing. This one is an answer sheet: one row per person. The form in rsvp.html saves to it, as Collect responses describes.

ts
import { readFile } from 'node:fs/promises'

await sa.artifacts.publish({
  slug: 'rsvp',
  prompt: 'An RSVP form.',
  html: await readFile('rsvp.html', 'utf8'),
  policy: {
    policyVersion: 1,
    writes: 'upsert',
    perUser: 'single',
    read: 'own',
    retentionDays: null,
    recordCap: { perVisitor: 1, perArtifact: 500 },
    fields: [
      { name: 'attending', type: 'boolean', required: false, pii: 'none', description: 'Yes or no.' },
    ],
  },
})

for await (const row of sa.data.iterArtifactRows({ artifact: 'rsvp' })) {
  console.log(row)
}

Share an artifact

ts
await sa.shares.addToArtifact({
  artifact: 'launch-notes',
  emails: ['dana@example.com'],
  role: 'commenter',
})

Each person gets an email. The role is viewer unless you say otherwise, and sharing endpoints allow 20 calls an hour per key.

Errors

Every failure is a SuperArtifactsError with status, code, message and, when the API asked you to wait, retryAfter in seconds. Error codes lists every code. A client-side failure has status 0 and its own code: a missing key is an AuthenticationError with missing_api_key.

ClassWhen
InvalidArgumentsError400
AuthenticationError401, or no key to send
PlanLimitError402
PermissionError403 forbidden
ScopeRequiredError403 scope_required: a read key called a write
NotFoundError404
ConflictError409
PayloadTooLargeError413: over 1 MiB, see Uploads
RateLimitError429
InternalError5xx
ConnectionErrorno answer arrived (status 0)
TimeoutErrorthe attempt ran out of time (status 0)
ts
import { NotFoundError } from '@superartifacts/sdk'

try {
  await sa.artifacts.get({ artifact: 'nope' })
} catch (error) {
  if (error instanceof NotFoundError) console.log(error.code)
  else throw error
}

Retries and idempotency

  • A 429 is retried after its Retry-After. Past 60 seconds it is not waited out: the RateLimitError comes straight back with retryAfter set.
  • 502, 503, 504, timeouts and network errors are retried with backoff on GET, DELETE and any POST carrying an Idempotency-Key.
  • Every POST carries one: a fresh UUID per call, reused across that call's retries, so a retried publish happens once. Pass { idempotencyKey } to make a call safe to repeat across restarts, or { idempotencyKey: false } to send none, which turns these retries off.
  • No other 4xx is retried, and neither is a PATCH or PUT.

Pagination

Operations that page have an iterator that fetches pages as you read.

ts
for await (const row of sa.data.iterRows({ dataset: 'signups' })) console.log(row)
for await (const row of sa.data.iterArtifactRows({ artifact: 'hello' })) console.log(row)
for await (const entry of sa.entries.iter({ collection: 'posts' })) console.log(entry)
for await (const mention of sa.mentions.iter()) console.log(mention)

sa.comments.list returns one page.

Uploads

A request body is capped at 1 MiB. Bigger files are staged, then published by id.

ts
import { readFile } from 'node:fs/promises'

const { uploadId } = await sa.uploads.upload([
  { path: 'images/hero.png', data: await readFile('hero.png') },
])

await sa.artifacts.publish({
  slug: 'hello',
  prompt: 'A page with a hero image.',
  html: '<img src="images/hero.png">',
  uploads: uploadId,
})

One file may be 5 MiB and one upload 10 MiB. A staged set expires after 30 minutes.

Every method

MethodDoes
sa.account.whoamiWho this key publishes as
sa.account.helpHow the operations fit together
sa.artifacts.listList your artifacts, newest first
sa.artifacts.publishPublish an artifact, or a new version of one
sa.artifacts.getOne artifact: its versions, published version and declaration
sa.artifacts.updateChange visibility, gallery label, live phase or submission email
sa.artifacts.deleteDelete an artifact with every version, permanently
sa.artifacts.readFileRead one file of a version as text (default: the published index.html)
sa.artifacts.openA ten-minute viewer URL for an artifact, private included
sa.artifacts.serveVersionServe an existing version again
sa.live.startTurn an artifact's live room on
sa.live.stopTurn an artifact's live room off, disconnecting every visitor
sa.uploads.stageStage big files for a publish; PUT each to its URL
sa.uploads.uploadStage files and send their bytes
sa.data.listArtifactRowsRows visitors submitted to one artifact, newest first
sa.data.iterArtifactRowsEvery row of one artifact, page by page
sa.data.writeArtifactRowsWrite rows into an artifact you may edit
sa.data.listRowsRows submitted to a dataset or a gallery label
sa.data.iterRowsEvery row of a dataset or label, page by page
sa.data.queryAggregate what was collected: numbers, never rows
sa.schema.getThe data catalog: datasets and content collections, or a comparison
sa.schema.defineCollectionCreate or replace a content collection
sa.entries.listOne page of a content collection's entries
sa.entries.iterEvery entry, page by page
sa.entries.createCreate one entry, or a batch
sa.secrets.useCall an approved API with a stored secret you cannot read (workspace keys)
sa.templates.listFind, rank or list templates
sa.templates.saveCreate or revise a template draft
sa.templates.getOne template in full, with its install prompt
sa.templates.getHistoryA template's changelog
sa.templates.getFeedbackComments on a template's artifacts, as themes
sa.templates.actInstall, uninstall, rate, report, roll back or request verification
sa.templates.releaseRelease one version of your template
sa.projects.listYour projects
sa.projects.createCreate a project, optionally seeded with artifacts
sa.projects.getOne project and its items
sa.projects.updateRename, set visibility, reorder, move, fold groups or set a rule
sa.projects.deleteDelete a project; its artifacts stay
sa.projects.addArtifactsAdd artifacts to a project
sa.projects.removeArtifactRemove one artifact (or item) from a project
sa.shares.addToArtifactGive named people access to an artifact
sa.shares.addToProjectGive named people access to a project
sa.shares.addToWorkspaceInvite editors to a workspace (returns accept links)
sa.comments.listComment threads on an artifact, newest first
sa.comments.respondReply to and/or resolve threads, one outcome each
sa.comments.getScreenshotOne message's screenshot, base64 in `image`
sa.mentions.listWhere people @mentioned you; listing marks them seen
sa.mentions.iterEvery mention, page by page
sa.signatures.listAn artifact's signature requests, newest first
sa.signatures.requestAsk named people to sign one version of an artifact
sa.signatures.getOne signature request and its signers
sa.signatures.remindEmail a signer their link again
sa.signatures.withdrawClose an open signature request