@codevision0501/v-form-sdk
v0.2.2
Published
TypeScript client for the v-form API.
Downloads
607
Readme
@codevision0501/v-form-sdk
TypeScript client for the v-form API. Programmatically create forms, list responses, and manage your workspace from any Node.js or browser environment.
Install
npm install @codevision0501/v-form-sdkQuick start
import { VForm } from "@codevision0501/v-form-sdk";
const vf = new VForm({
apiKey: process.env.VFORM_API_KEY!,
});
const externalUserId = "pdf-vision-user-123";
// Create a form by passing questions
const form = await vf.forms.create({
externalUserId,
metadata: { source: "pdf-vision" },
title: "Customer feedback Q1",
status: "live",
questions: [
{ type: "welcome", title: "Help us improve.", description: "Three quick questions." },
{ type: "short_text", title: "What's your name?", required: true },
{
type: "multiple",
title: "Which plan are you on?",
options: [{ label: "Free" }, { label: "Pro" }, { label: "Team" }],
},
{ type: "nps", title: "How likely to recommend us?" },
{ type: "thank_you", title: "Thanks!" },
],
});
console.log("Form created:", form.publicUrl);
// Or have Claude draft one from a prompt (requires Pro plan)
const draft = await vf.forms.generate({
prompt: "Quick survey for SaaS founders about feature priorities",
tone: "Direct",
length: "6–10 q",
status: "draft",
});
// List forms for one external user
const forms = await vf.forms.list({ externalUserId, status: "live", limit: 10 });
// Read responses for a form scoped to the same external user
const { data, nextCursor } = await vf.forms.responses(form.id, {
externalUserId,
limit: 100,
});
for (const response of data) {
console.log(response.submittedAt, response.answers);
}
// Update / publish
await vf.forms.update(form.id, { status: "paused" });
// Delete
await vf.forms.delete(form.id);API
new VForm(options)
| Option | Type | Description |
| --------- | -------- | ------------------------------------------------------------ |
| apiKey | string | Required. Generate one in your workspace settings. |
| baseUrl | string | Optional. Defaults to https://sdk.v-form.co; override only for self-hosted deployments. |
| fetch | function | Override the global fetch. Required on Node < 18. |
For integrations that create forms for many users with one v-form API key, pass
externalUserId when creating and listing forms. v-form stores it on the form
and lets you filter lists and responses by that same value.
Forms
| Method | Description |
| ----------------------------------- | ---------------------------------------------------- |
| forms.create(input) | Create a form from explicit questions |
| forms.generate(input) | Create a form by AI from a prompt (Pro plan) |
| forms.list(options?) | List forms in your workspace |
| forms.get(id) | Fetch a single form with its questions |
| forms.update(id, patch) | Patch title, description, status, or coverColor |
| forms.delete(id) | Permanently delete a form and its responses |
| forms.responses(id, options?) | Paginated response listing |
Question types
welcome · short_text · long_text · email · multiple · rating · nps · date · file · signature · thank_you
multiple accepts an options array; ids are assigned server-side and returned on read.
Webhooks
Manage webhook endpoints (programmatic)
const wh = await vf.webhooks.create({
url: "https://my-server.example/hooks/vform",
events: ["response.submitted", "form.updated"],
formIds: ["form_abc123"], // optional — scope to specific forms; omit for all
});
console.log(wh.secret); // ⚠️ shown ONCE — store it now
await vf.webhooks.list();
await vf.webhooks.get(wh.id);
await vf.webhooks.delete(wh.id);Verify incoming webhook signatures
The signing secret returned at create time is used to verify each delivery. Use
verifyWebhookSignature in your handler — async, works in Node 18+, Bun, Deno,
edge runtimes, and the browser (Web Crypto under the hood):
// Next.js App Router
import { verifyWebhookSignature } from "@codevision0501/v-form-sdk";
export async function POST(req: Request) {
const rawBody = await req.text(); // raw body, NOT parsed JSON
const sig = req.headers.get("x-vform-signature");
const ok = await verifyWebhookSignature(
rawBody,
sig,
process.env.VFORM_WEBHOOK_SECRET!,
);
if (!ok) return new Response("Unauthorized", { status: 401 });
const event = JSON.parse(rawBody);
if (event.type === "response.submitted") {
// event.data.{formId,responseId,answers,...}
}
return new Response("ok");
}The verifier checks the HMAC-SHA256 and rejects signatures older than 5 minutes
(override with { toleranceSec: 600 }). Failures return false rather than
throwing — never trust an event if it returns false.
Event types
response.submitted · form.created · form.updated · form.deleted
Each event payload:
{
id: "evt_...", // unique per event, use for idempotency
type: "response.submitted",
createdAt: "2026-05-23T...",
workspaceId: "ws_...",
data: { /* event-specific fields */ }
}Error handling
All API errors throw a VFormError with status (HTTP code) and body (parsed JSON if available):
import { VForm, VFormError } from "@codevision0501/v-form-sdk";
try {
await vf.forms.create({ title: "X", questions: [] });
} catch (err) {
if (err instanceof VFormError) {
console.error(err.status, err.message, err.body);
}
}Common statuses:
| Status | Meaning |
| ------ | --------------------------------------------------------- |
| 400 | Invalid input (check body.details for Zod errors) |
| 401 | Missing or invalid API key |
| 402 | Plan limit hit (forms, monthly responses, AI gen, etc.) |
| 404 | Form not found in your workspace |
| 429 | Rate limit — wait per retry-after header |
| 503 | Server misconfigured (e.g. AI requested without API key) |
