TypeScript SDK for FormFor -- structured input infrastructure for AI agents. Collect human input, approvals, and structured data from within your AI workflows.
npm install formforimport { FormFor } from 'formfor'
const ff = new FormFor('ff_live_...')
// Yes/no approval -- blocks until the human responds
const { approved } = await ff.ask('Deploy to production?', {
to: 'ops@company.com',
})
// Collect structured data
const response = await ff.collect({
title: 'Customer Onboarding',
to: 'customer@example.com',
fields: [
{ id: 'name', type: 'text', label: 'Full Name', required: true },
{ id: 'plan', type: 'select', label: 'Plan', options: ['starter', 'pro', 'enterprise'] },
],
})
console.log(response.data) // { name: "Jane Smith", plan: "pro" }Creates a confirmation form, delivers it, and waits for the response.
const result = await ff.ask('Approve $5,000 expense?', {
to: 'manager@company.com',
context: 'Q1 marketing budget',
expires: '24h',
})
result.approved // true | false
result.notes // optional reviewer notes
result.form_id // form ID for reference| Option | Type | Description |
|---|---|---|
to |
string |
Recipient email (required) |
context |
string |
Background info for the recipient |
expires |
string |
Duration: "1h", "24h", "7d" |
webhook_url |
string |
Async webhook notification |
metadata |
Record<string, any> |
Custom metadata |
Creates a multi-field form and waits for the response.
const response = await ff.collect({
title: 'Bug Report',
to: 'eng@company.com',
fields: [
{ id: 'severity', type: 'select', label: 'Severity', options: ['P0', 'P1', 'P2'] },
{ id: 'description', type: 'textarea', label: 'Description', required: true },
{ id: 'screenshot', type: 'file', label: 'Screenshot' },
],
expires: '7d',
})Creates a form and returns immediately.
const form = await ff.createForm({
title: 'Feedback',
fields: [{ id: 'rating', type: 'rating', label: 'How would you rate us?' }],
to: 'user@example.com',
webhook_url: 'https://your-app.com/webhooks/formfor',
})
form.id // "form_abc123"
form.url // "https://forms.formfor.ai/form_abc123"
form.status // "pending"const response = await ff.getResponse('form_abc123')
// null if not yet completedconst { forms, total } = await ff.listForms({ status: 'pending', limit: 10 })await ff.cancelForm('form_abc123')await ff.remindForm('form_abc123')Waits for a response via WebSocket with automatic polling fallback.
const form = await ff.createForm({ /* ... */ })
const response = await ff.waitForResponse(form.id, '2h')| Type | Description |
|---|---|
text |
Single-line text |
textarea |
Multi-line text |
number |
Numeric input |
email |
Email address |
url |
URL |
phone |
Phone number |
select |
Single selection |
multi_select |
Multiple selections |
confirm |
Yes/no |
date |
Date picker |
datetime |
Date and time |
file |
File upload |
rating |
Star rating |
signature |
Signature capture |
fields: [
{ id: 'role', type: 'select', label: 'Role', options: ['engineer', 'manager', 'other'] },
{ id: 'team_size', type: 'number', label: 'Team Size', when: { field: 'role', equals: 'manager' } },
{ id: 'role_other', type: 'text', label: 'Specify', when: { field: 'role', equals: 'other' } },
]{ id: 'age', type: 'number', label: 'Age', validate: { min: 18, max: 120 } }
{ id: 'doc', type: 'file', label: 'Document', validate: { maxSize: '10MB', accept: '.pdf,.docx' } }import { FormFor, FormForError } from 'formfor'
try {
await ff.ask('Approve?', { to: 'user@example.com', expires: '1h' })
} catch (err) {
if (err instanceof FormForError) {
err.code // "timeout", "expired", "api_error", etc.
err.message // Human-readable
err.status // HTTP status (if applicable)
}
}const ff = new FormFor('ff_live_...', {
baseUrl: 'https://api.formfor.ai', // default
})All types exported:
import type {
FormField, FieldType, Form, FormResponse, CreateFormParams,
AskOptions, AskResult, CollectOptions, Branding,
ListFormsOptions, ListFormsResult, FormForOptions,
} from 'formfor'MIT