docs

@noonmark/sdk

A small client with no dependencies. Everything it does you could do with fetch — it exists for the types and the error handling.

Installation

terminal
npm i @noonmark/sdk

baseUrl is optional and falls back to http://localhost:3000. Point it at your host in production.

submit

Sends a Submission. Returns the id, or throws.

submit.ts
import { Noonmark } from "@noonmark/sdk";

const noonmark = new Noonmark({
  apiKey: "frm_...",
  baseUrl: "https://noonmark.dev",
});

const { id } = await noonmark.submit({
  email: "ana@acme.com",
  satisfaction: 5,
  plan: "pro",
  features: ["api", "webhooks"],
});

Retries

Pass an idempotencyKey and a retry cannot make a second Submission. It is sent as the Idempotency-Key header; the same key on the same Form resolves with the first id.

retry.ts
const { id } = await noonmark.submit(values, { idempotencyKey: order.id });

Use one key per thing your user did — an order id, a UUID made when the screen opened — and reuse it only to retry that. See HTTP ingest.

If the Form's bot protection has Turnstile on, pass the widget's token as { turnstileToken } too. A token works once, so a retry needs a fresh one.

form

Reads the Form definition so you can build the screen without repeating the Fields in your code. It is what makes the survey kind useful — on a feedback Form you rarely need it.

form.ts
const { name, kind, fields } = await noonmark.form();

// fields:
// [
//   { key: "satisfaction", label: "How satisfied are you?",
//     type: "satisfaction", required: true,
//     minLabel: "Not at all satisfied", maxLabel: "Extremely satisfied" },
//   { key: "plan", label: "Plan", type: "select",
//     required: true, options: ["free", "pro", "business"] },
// ]

Read-only, and it never returns Submissions. It uses the same API key as ingest, with a separate rate-limit counter.

Survey.tsx
const { name, fields } = await noonmark.form();

return (
  <form onSubmit={send}>
    <h1>{name}</h1>
    {fields.map((field) => (
      <Control key={field.key} field={field} />
    ))}
  </form>
);

async function send(values) {
  await noonmark.submit(values);
}

The order of the Fields is the order on the screen. Reordering in the Studio changes what people see without touching your code.

Errors

Both methods throw NoonmarkError with status, body and, on a 429, retryAfter in seconds.

errors.ts
import { Noonmark, NoonmarkError } from "@noonmark/sdk";

try {
  await noonmark.submit(values);
} catch (error) {
  if (error instanceof NoonmarkError) {
    if (error.status === 429) {
      await wait(error.retryAfter ?? 30);
    }
    // error.message carries the key that failed
  }
}

Types

TypeWhat it carries
NoonmarkThe client.
NoonmarkErrorstatus, body, retryAfter.
FormDefinitionid, name, kind, fields.
Fieldkey, label, type, required, options?, min?, max?, minLabel?, maxLabel?, default?, hidden?, locked?, when?.
FieldTypeThe ten types — see Field types.
Conditionkey, is?, min?, max? — see Conditional Fields.
FormKind"feedback" or "survey"

What did you think of this content?

On this page