@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
npm i @noonmark/sdkbaseUrl 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.
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.
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.
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.
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.
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
| Type | What it carries |
|---|---|
Noonmark | The client. |
NoonmarkError | status, body, retryAfter. |
FormDefinition | id, name, kind, fields. |
Field | key, label, type, required, options?, min?, max?, minLabel?, maxLabel?, default?, hidden?, locked?, when?. |
FieldType | The ten types — see Field types. |
Condition | key, is?, min?, max? — see Conditional Fields. |
FormKind | "feedback" or "survey" |
What did you think of this content?
Members
Who is in a Workspace and what they may do. Two roles, owner and member; owners invite by email, remove, change roles and can delete the Workspace.
Field types
A Field has a key, a label, a type and required. Scale, satisfaction and choice carry a little more; a rule can show a Field only when an earlier one matches.