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: Use the same URL for all environments. Your API key determines the environment:
pk_test_*/sk_test_*→ Routes to Stagingpk_live_*/sk_live_*→ Routes to Production
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