Forms Block

Dynamic form builder API with 20+ field types, validation, and workflows
The Forms Block provides a complete form building solution. Create unlimited forms with any combination of field types, validation rules, conditional logic, and submission workflows - all through a RESTful API.
Key Features
- Dynamic Form Builder - Create forms programmatically with JSON configuration
- 20+ Field Types - Text, email, phone, date, file upload, signature, rating, and more
- Validation Engine - Built-in validators with custom rule support
- Conditional Logic - Show/hide fields based on user responses
- Submission Workflows - Trigger webhooks, emails, and CRM integrations on submit
- Multi-Page Forms - Build wizard-style forms with step navigation
- Form Analytics - Track submissions, completion rates, and field drop-off
- Real-time Webhooks - Instant notifications for form events
API Endpoint
| Service | URL |
|---|---|
| Forms | https://forms.api.us.23blocks.com |
Environment Routing: One URL, one environment. All shared-platform API keys are live keys:
pk_live_*/sk_live_*→ your production tenant. Need a separate staging environment? It is available as a paid add-on on deployments in your own cloud account — see 23blocks in a Box.
Quick Start
Installation
npm install @23blocks/sdk
Basic Usage
import { create23BlocksClient } from '@23blocks/sdk';
const client = create23BlocksClient({
urls: { forms: 'https://forms.api.us.23blocks.com' },
apiKey: 'your-api-key',
});
// Create a contact form
const form = await client.forms.create({
name: 'Contact Form',
fields: [
{
type: 'text',
name: 'full_name',
label: 'Full Name',
required: true
},
{
type: 'email',
name: 'email',
label: 'Email Address',
required: true,
validators: ['email']
},
{
type: 'textarea',
name: 'message',
label: 'Your Message',
placeholder: 'How can we help?',
maxLength: 1000
}
]
});
// Submit form data
const submission = await client.forms.submit(form.id, {
full_name: 'Jane Smith',
email: 'jane@example.com',
message: 'I would like to learn more about your API.'
});
Authentication
Required Headers
All API requests require authentication:
Authorization: Bearer [JWT_TOKEN]
X-API-Key: [COMPANY_API_KEY]
Content-Type: application/json
API Reference
Forms
Create Form
Create a new form with field definitions.
POST /api/v1/forms
Request Body:
{
"data": {
"type": "forms",
"attributes": {
"name": "Contact Form",
"description": "Get in touch with our team",
"slug": "contact",
"status": "active",
"settings": {
"submit_button_text": "Send Message",
"success_message": "Thank you! We'll be in touch soon.",
"redirect_url": null,
"collect_ip": true,
"enable_captcha": true
},
"fields": [
{
"type": "text",
"name": "full_name",
"label": "Full Name",
"required": true,
"placeholder": "John Smith"
},
{
"type": "email",
"name": "email",
"label": "Email Address",
"required": true,
"validators": ["email"]
}
],
"actions": [
{
"type": "webhook",
"url": "https://api.example.com/leads",
"method": "POST"
},
{
"type": "email",
"to": "team@company.com",
"subject": "New Contact Form Submission"
}
]
}
}
}
Response:
{
"data": {
"id": "form_xyz789",
"type": "forms",
"attributes": {
"name": "Contact Form",
"description": "Get in touch with our team",
"slug": "contact",
"status": "active",
"fields": [...],
"actions": [...],
"settings": {...},
"submission_count": 0,
"created_at": "2025-01-15T10:30:00Z",
"updated_at": "2025-01-15T10:30:00Z"
}
}
}
Get Form
Retrieve a form by ID.
GET /api/v1/forms/:id
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
include | string | Related resources: submissions,analytics |
List Forms
Retrieve a paginated list of forms.
GET /api/v1/forms
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
page[number] | integer | Page number (default: 1) |
page[size] | integer | Items per page (default: 25, max: 100) |
filter[status] | string | Filter by status: active, draft, archived |
filter[slug] | string | Filter by slug (exact match) |
sort | string | Sort field: created_at, -created_at, name |
Update Form
Update an existing form.
PATCH /api/v1/forms/:id
Delete Form
Delete a form (soft delete by default).
DELETE /api/v1/forms/:id
Duplicate Form
Create a copy of an existing form.
POST /api/v1/forms/:id/duplicate
Field Types
Forms Block supports 20+ field types:
Basic Fields
| Type | Description | Options |
|---|---|---|
text | Single-line text input | placeholder, minLength, maxLength, pattern |
textarea | Multi-line text input | placeholder, minLength, maxLength, rows |
number | Numeric input | min, max, step, placeholder |
email | Email input with validation | placeholder |
phone | Phone number input | placeholder, format |
url | URL input with validation | placeholder |
password | Password input | minLength, requireUppercase, requireNumber |
Selection Fields
| Type | Description | Options |
|---|---|---|
dropdown | Single-select dropdown | options, placeholder |
multiselect | Multi-select dropdown | options, maxSelections |
radio | Radio button group | options, layout |
checkbox | Checkbox group | options, minSelections, maxSelections |
boolean | Single yes/no checkbox | label |
Date & Time Fields
| Type | Description | Options |
|---|---|---|
date | Date picker | minDate, maxDate, format |
time | Time picker | minTime, maxTime, format |
datetime | Date and time picker | minDateTime, maxDateTime |
Advanced Fields
| Type | Description | Options |
|---|---|---|
file | File upload | accept, maxSize, maxFiles |
signature | Signature pad | width, height |
rating | Star rating | max, allowHalf |
slider | Range slider | min, max, step |
address | Address autocomplete | countries, types |
richtext | Rich text editor | toolbar, maxLength |
hidden | Hidden field | value |
Field Configuration Example
{
"type": "file",
"name": "resume",
"label": "Upload Resume",
"required": true,
"options": {
"accept": [".pdf", ".doc", ".docx"],
"maxSize": 5242880,
"maxFiles": 1
},
"helpText": "PDF or Word document, max 5MB"
}
Submissions
Submit Form
Submit data to a form.
POST /api/v1/forms/:form_id/submissions
Request Body:
{
"data": {
"type": "submissions",
"attributes": {
"values": {
"full_name": "Jane Smith",
"email": "jane@example.com",
"message": "Hello!"
},
"metadata": {
"source": "website",
"utm_source": "google"
}
}
}
}
Response:
{
"data": {
"id": "sub_abc123",
"type": "submissions",
"attributes": {
"form_id": "form_xyz789",
"values": {
"full_name": "Jane Smith",
"email": "jane@example.com",
"message": "Hello!"
},
"metadata": {
"source": "website",
"utm_source": "google",
"ip_address": "192.168.1.1",
"user_agent": "Mozilla/5.0..."
},
"status": "completed",
"created_at": "2025-01-15T14:30:00Z"
}
}
}
Get Submission
Retrieve a submission by ID.
GET /api/v1/submissions/:id
List Submissions
Get submissions for a form.
GET /api/v1/forms/:form_id/submissions
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
page[number] | integer | Page number (default: 1) |
page[size] | integer | Items per page (default: 25, max: 100) |
filter[status] | string | Filter by status: completed, partial, spam |
filter[created_after] | datetime | Filter by date range start |
filter[created_before] | datetime | Filter by date range end |
sort | string | Sort: created_at, -created_at |
Export Submissions
Export submissions as CSV or JSON.
GET /api/v1/forms/:form_id/submissions/export
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
format | string | Export format: csv, json (default: csv) |
filter[created_after] | datetime | Filter by date range |
filter[created_before] | datetime | Filter by date range |
Delete Submission
Delete a submission.
DELETE /api/v1/submissions/:id
Validation
Built-in Validators
| Validator | Description | Options |
|---|---|---|
required | Field must have a value | - |
email | Valid email format | - |
phone | Valid phone format | format |
url | Valid URL format | protocols |
minLength | Minimum character count | value |
maxLength | Maximum character count | value |
min | Minimum numeric value | value |
max | Maximum numeric value | value |
pattern | Regex pattern match | regex, message |
Custom Validation Rules
{
"type": "text",
"name": "company_email",
"label": "Work Email",
"validators": [
"required",
"email",
{
"type": "pattern",
"regex": "^[a-zA-Z0-9._%+-]+@company\\.com$",
"message": "Please use your company email address"
}
]
}
Server-Side Validation
POST /api/v1/forms/:form_id/validate
Request Body:
{
"values": {
"email": "invalid-email",
"full_name": ""
}
}
Response:
{
"valid": false,
"errors": {
"email": ["Please enter a valid email address"],
"full_name": ["This field is required"]
}
}
Conditional Logic
Show or hide fields based on user responses.
Condition Types
| Type | Description |
|---|---|
equals | Field value equals specified value |
not_equals | Field value does not equal specified value |
contains | Field value contains specified string |
greater_than | Numeric value is greater than |
less_than | Numeric value is less than |
is_empty | Field has no value |
is_not_empty | Field has a value |
Configuration Example
{
"fields": [
{
"type": "radio",
"name": "contact_method",
"label": "Preferred Contact Method",
"options": [
{ "value": "email", "label": "Email" },
{ "value": "phone", "label": "Phone" }
]
},
{
"type": "phone",
"name": "phone_number",
"label": "Phone Number",
"conditions": {
"show_when": {
"field": "contact_method",
"operator": "equals",
"value": "phone"
}
}
}
]
}
Multiple Conditions
{
"conditions": {
"show_when": {
"logic": "and",
"rules": [
{ "field": "country", "operator": "equals", "value": "US" },
{ "field": "age", "operator": "greater_than", "value": 18 }
]
}
}
}
Multi-Page Forms
Create wizard-style forms with multiple pages.
Configuration
{
"name": "Application Form",
"settings": {
"multi_page": true,
"show_progress": true,
"allow_back_navigation": true
},
"pages": [
{
"title": "Personal Information",
"fields": [
{ "type": "text", "name": "first_name", "label": "First Name" },
{ "type": "text", "name": "last_name", "label": "Last Name" }
]
},
{
"title": "Contact Details",
"fields": [
{ "type": "email", "name": "email", "label": "Email" },
{ "type": "phone", "name": "phone", "label": "Phone" }
]
},
{
"title": "Review",
"fields": [],
"settings": {
"show_summary": true
}
}
]
}
Save Progress
POST /api/v1/forms/:form_id/progress
Request Body:
{
"session_id": "sess_abc123",
"current_page": 2,
"values": {
"first_name": "Jane",
"last_name": "Smith"
}
}
Actions & Workflows
Available Actions
| Action | Description |
|---|---|
webhook | Send submission data to a URL |
email | Send email notification |
crm | Create CRM contact (via CRM Block) |
slack | Post to Slack channel |
zapier | Trigger Zapier webhook |
Webhook Action
{
"type": "webhook",
"url": "https://api.example.com/leads",
"method": "POST",
"headers": {
"Authorization": "Bearer {{secrets.api_key}}"
},
"payload_template": {
"lead_name": "{{values.full_name}}",
"lead_email": "{{values.email}}"
}
}
Email Action
{
"type": "email",
"to": "{{values.email}}",
"cc": ["team@company.com"],
"subject": "Thank you for your submission",
"template": "submission_confirmation",
"variables": {
"name": "{{values.full_name}}"
}
}
CRM Integration
{
"type": "crm",
"action": "create_contact",
"mapping": {
"first_name": "{{values.first_name}}",
"last_name": "{{values.last_name}}",
"email": "{{values.email}}",
"tags": ["website-lead"]
}
}
Analytics
Get Form Analytics
GET /api/v1/forms/:form_id/analytics
Response:
{
"data": {
"form_id": "form_xyz789",
"period": "last_30_days",
"metrics": {
"views": 5420,
"starts": 2150,
"completions": 1823,
"completion_rate": 84.79,
"average_time_seconds": 145,
"submissions_by_day": [
{ "date": "2025-01-15", "count": 62 },
{ "date": "2025-01-14", "count": 58 }
]
},
"field_analytics": [
{
"field": "email",
"drop_off_rate": 2.3,
"average_time_seconds": 12
},
{
"field": "message",
"drop_off_rate": 8.5,
"average_time_seconds": 45
}
]
}
}
Webhooks
Webhook Events
| Event | Description |
|---|---|
form.created | New form created |
form.updated | Form updated |
form.deleted | Form deleted |
submission.created | New submission received |
submission.updated | Submission updated |
submission.deleted | Submission deleted |
Create Webhook
POST /api/v1/webhooks
Request Body:
{
"data": {
"type": "webhooks",
"attributes": {
"url": "https://api.example.com/forms-webhook",
"events": ["submission.created"],
"form_id": "form_xyz789"
}
}
}
Webhook Payload
{
"event": "submission.created",
"timestamp": "2025-01-15T14:30:00Z",
"data": {
"submission_id": "sub_abc123",
"form_id": "form_xyz789",
"form_name": "Contact Form",
"values": {
"full_name": "Jane Smith",
"email": "jane@example.com"
}
}
}
SDK Examples
TypeScript
import { create23BlocksClient } from '@23blocks/sdk';
const client = create23BlocksClient({
urls: { forms: 'https://forms.api.us.23blocks.com' },
apiKey: process.env.BLOCKS_API_KEY!,
});
// Create a survey form with conditional logic
async function createSurveyForm() {
const form = await client.forms.create({
name: 'Customer Satisfaction Survey',
fields: [
{
type: 'rating',
name: 'satisfaction',
label: 'How satisfied are you?',
required: true,
options: { max: 5 }
},
{
type: 'textarea',
name: 'feedback',
label: 'What could we improve?',
conditions: {
show_when: {
field: 'satisfaction',
operator: 'less_than',
value: 4
}
}
},
{
type: 'textarea',
name: 'testimonial',
label: 'Would you like to share a testimonial?',
conditions: {
show_when: {
field: 'satisfaction',
operator: 'greater_than',
value: 3
}
}
}
],
actions: [
{
type: 'webhook',
url: 'https://api.example.com/surveys'
}
]
});
return form;
}
// Handle form submission with validation
async function submitForm(formId: string, data: Record<string, any>) {
// Validate first
const validation = await client.forms.validate(formId, data);
if (!validation.valid) {
throw new Error(`Validation failed: ${JSON.stringify(validation.errors)}`);
}
// Submit
const submission = await client.forms.submit(formId, {
values: data,
metadata: {
source: 'mobile-app',
version: '2.1.0'
}
});
return submission;
}
// Export submissions
async function exportSubmissions(formId: string, startDate: Date, endDate: Date) {
const csv = await client.forms.exportSubmissions(formId, {
format: 'csv',
filter: {
created_after: startDate.toISOString(),
created_before: endDate.toISOString()
}
});
return csv;
}
Error Codes
| Code | Description |
|---|---|
FORMS001 | Form not found |
FORMS002 | Submission not found |
FORMS003 | Validation failed |
FORMS004 | File upload failed |
FORMS005 | File size exceeds limit |
FORMS006 | Invalid file type |
FORMS007 | Form is inactive |
FORMS008 | Submission limit reached |
FORMS009 | Duplicate submission detected |
Rate Limits
| Plan | Requests/minute | Forms | Submissions/month |
|---|---|---|---|
| Free | 60 | 10 | 1,000 |
| Starter | 300 | 50 | 10,000 |
| Pro | 1,000 | 200 | 100,000 |
| Enterprise | Custom | Unlimited | Unlimited |
Related Resources
- Marketing Page - Feature overview and pricing
- Auth Block - Secure form submissions with authentication
- CRM Block - Create contacts from form submissions
- Files Block - Handle file uploads
Use This Block with AI Agents
This block ships an official Claude Code plugin from the public 23blocks marketplace (19 plugins, 205 skills). Your agent gets full working knowledge of this API — endpoints, identity registration, and conventions.
# one-time: add the 23blocks marketplace
claude plugin marketplace add https://github.com/23blocks-OS/ai-agents
# install this block's plugin
claude plugin install forms-block
Configure BLOCKS_API_URL, BLOCKS_API_KEY, and BLOCKS_AUTH_TOKEN. Register identity first: each block is autonomous — register the user with this block's identity endpoint before private calls (only user_unique_id is required; email/phone are optional notification-routing fields).
Full reference: AI Agents & Claude Code Plugins · github.com/23blocks-OS/ai-agents