Skip to main content

Forms Block

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

ServiceURL
Formshttps://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 Staging
  • pk_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:

ParameterTypeDescription
includestringRelated resources: submissions,analytics

List Forms

Retrieve a paginated list of forms.

GET /api/v1/forms

Query Parameters:

ParameterTypeDescription
page[number]integerPage number (default: 1)
page[size]integerItems per page (default: 25, max: 100)
filter[status]stringFilter by status: active, draft, archived
filter[slug]stringFilter by slug (exact match)
sortstringSort 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

TypeDescriptionOptions
textSingle-line text inputplaceholder, minLength, maxLength, pattern
textareaMulti-line text inputplaceholder, minLength, maxLength, rows
numberNumeric inputmin, max, step, placeholder
emailEmail input with validationplaceholder
phonePhone number inputplaceholder, format
urlURL input with validationplaceholder
passwordPassword inputminLength, requireUppercase, requireNumber

Selection Fields

TypeDescriptionOptions
dropdownSingle-select dropdownoptions, placeholder
multiselectMulti-select dropdownoptions, maxSelections
radioRadio button groupoptions, layout
checkboxCheckbox groupoptions, minSelections, maxSelections
booleanSingle yes/no checkboxlabel

Date & Time Fields

TypeDescriptionOptions
dateDate pickerminDate, maxDate, format
timeTime pickerminTime, maxTime, format
datetimeDate and time pickerminDateTime, maxDateTime

Advanced Fields

TypeDescriptionOptions
fileFile uploadaccept, maxSize, maxFiles
signatureSignature padwidth, height
ratingStar ratingmax, allowHalf
sliderRange slidermin, max, step
addressAddress autocompletecountries, types
richtextRich text editortoolbar, maxLength
hiddenHidden fieldvalue

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:

ParameterTypeDescription
page[number]integerPage number (default: 1)
page[size]integerItems per page (default: 25, max: 100)
filter[status]stringFilter by status: completed, partial, spam
filter[created_after]datetimeFilter by date range start
filter[created_before]datetimeFilter by date range end
sortstringSort: created_at, -created_at

Export Submissions

Export submissions as CSV or JSON.

GET /api/v1/forms/:form_id/submissions/export

Query Parameters:

ParameterTypeDescription
formatstringExport format: csv, json (default: csv)
filter[created_after]datetimeFilter by date range
filter[created_before]datetimeFilter by date range

Delete Submission

Delete a submission.

DELETE /api/v1/submissions/:id

Validation

Built-in Validators

ValidatorDescriptionOptions
requiredField must have a value-
emailValid email format-
phoneValid phone formatformat
urlValid URL formatprotocols
minLengthMinimum character countvalue
maxLengthMaximum character countvalue
minMinimum numeric valuevalue
maxMaximum numeric valuevalue
patternRegex pattern matchregex, 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

TypeDescription
equalsField value equals specified value
not_equalsField value does not equal specified value
containsField value contains specified string
greater_thanNumeric value is greater than
less_thanNumeric value is less than
is_emptyField has no value
is_not_emptyField 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

ActionDescription
webhookSend submission data to a URL
emailSend email notification
crmCreate CRM contact (via CRM Block)
slackPost to Slack channel
zapierTrigger 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

EventDescription
form.createdNew form created
form.updatedForm updated
form.deletedForm deleted
submission.createdNew submission received
submission.updatedSubmission updated
submission.deletedSubmission 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

CodeDescription
FORMS001Form not found
FORMS002Submission not found
FORMS003Validation failed
FORMS004File upload failed
FORMS005File size exceeds limit
FORMS006Invalid file type
FORMS007Form is inactive
FORMS008Submission limit reached
FORMS009Duplicate submission detected

Rate Limits

PlanRequests/minuteFormsSubmissions/month
Free60101,000
Starter3005010,000
Pro1,000200100,000
EnterpriseCustomUnlimitedUnlimited