Forms API
Complete API reference for creating forms, handling submissions, public endpoints, and conversational form chat.
Overview
The Forms API allows you to create, manage, and collect data through forms. Forms support 20 field types, public submission endpoints, file uploads, data table integration, embed codes, analytics, and an AI-powered conversational chat mode.
Endpoint Summary
Form CRUD
| Method | Endpoint | Description |
|---|---|---|
GET | /api/v1/forms | List all forms |
POST | /api/v1/forms | Create a form |
GET | /api/v1/forms/:id | Get a form |
GET | /api/v1/forms/slug/:slug | Get form by slug |
PUT | /api/v1/forms/:id | Update a form |
DELETE | /api/v1/forms/:id | Delete a form |
POST | /api/v1/forms/:id/publish | Publish form |
POST | /api/v1/forms/:id/unpublish | Unpublish form |
POST | /api/v1/forms/:id/archive | Archive form |
POST | /api/v1/forms/:id/duplicate | Duplicate form |
Submissions
| Method | Endpoint | Description |
|---|---|---|
POST | /api/v1/forms/:id/submit | Submit form data |
GET | /api/v1/forms/:id/submissions | List submissions |
GET | /api/v1/forms/:id/submissions/:submissionId | Get submission |
DELETE | /api/v1/forms/:id/submissions/:submissionId | Delete submission |
GET | /api/v1/forms/:id/analytics | Form analytics |
Dynamic Fields & Files
| Method | Endpoint | Description |
|---|---|---|
GET | /api/v1/forms/:id/fields/:fieldId/options | Get field options from data source |
GET | /api/v1/forms/:id/files/:fileId | Download uploaded file |
Public Endpoints (No Auth Required)
| Method | Endpoint | Description |
|---|---|---|
GET | /api/v1/public/forms/:slug | Get public form by slug |
POST | /api/v1/public/forms/:slug/submit | Public form submission |
POST | /api/v1/public/forms/:slug/upload | Public file upload |
GET | /api/v1/public/forms/:slug/embed | Get embed code |
GET | /api/v1/public/forms/:slug/fields/:fieldId/options | Public field options |
Form Chat (Conversational Forms)
| Method | Endpoint | Description |
|---|---|---|
GET | /api/v1/public/forms/:slug/conversations/:conversationId | Get conversation |
GET | /api/v1/public/forms/:slug/conversations/:conversationId/messages | Get messages |
POST | /api/v1/public/forms/:slug/conversations/:conversationId/messages | Send message |
POST | /api/v1/public/forms/:slug/conversations/:conversationId/messages/stream | Stream message |
List Forms
GET /api/v1/forms
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
page | integer | 1 | Page number |
per_page | integer | 20 | Items per page (max 100) |
status | string | - | Filter: draft, published, archived |
search | string | - | Search by name |
sort | string | -created_at | Sort field (- prefix for descending) |
Response
json{ "data": [ { "id": "form_abc123", "name": "Contact Form", "slug": "contact-form", "description": "General inquiries form", "status": "published", "field_count": 5, "submission_count": 342, "created_at": "2025-11-01T10:00:00Z", "updated_at": "2025-12-07T15:30:00Z" } ], "pagination": { "page": 1, "per_page": 20, "total": 8, "total_pages": 1 } }
Example
bashcurl "/api/v1/forms?status=published" \ -H "Authorization: Bearer your_api_key"
Create Form
POST /api/v1/forms
Request Body
json{ "name": "Contact Form", "description": "General inquiries and feedback", "fields": [ { "id": "name", "type": "text", "label": "Full Name", "placeholder": "Enter your name", "required": true, "validation": { "min_length": 2, "max_length": 100 } }, { "id": "email", "type": "email", "label": "Email Address", "placeholder": "[email protected]", "required": true }, { "id": "subject", "type": "select", "label": "Subject", "required": true, "options": [ { "value": "general", "label": "General Inquiry" }, { "value": "support", "label": "Technical Support" }, { "value": "sales", "label": "Sales Question" } ] }, { "id": "rating", "type": "rating", "label": "How would you rate our service?", "required": false, "validation": { "max": 5 } }, { "id": "message", "type": "textarea", "label": "Message", "placeholder": "How can we help?", "required": true, "validation": { "min_length": 10, "max_length": 2000 } }, { "id": "signature", "type": "signature", "label": "Your Signature", "required": false } ], "settings": { "submit_button_text": "Send Message", "success_message": "Thank you! We'll get back to you soon.", "redirect_url": null, "notifications": { "email": "[email protected]" } }, "data_table_config": { "table_id": "dt_contacts", "auto_save": true, "field_mapping": { "name": "full_name", "email": "email_address", "subject": "inquiry_type", "message": "body" } }, "workflow_id": "wf_xyz789", "branding": { "logo_url": "https://company.com/logo.png", "primary_color": "#4F46E5", "background_color": "#F9FAFB" } }
Form Fields
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Form name (max 100 chars) |
description | string | No | Form description |
fields | array | Yes | Form field definitions |
settings | object | No | Form settings |
workflow_id | string | No | Workflow to trigger on submission |
data_table_config | object | No | Auto-save submissions to a data table |
branding | object | No | Custom branding |
Field Types
| Type | Description | Validation Options |
|---|---|---|
text | Single-line text | min_length, max_length, pattern |
textarea | Multi-line text | min_length, max_length |
email | Email address | Auto-validated |
phone | Phone number | pattern |
number | Numeric input | min, max, step |
select | Dropdown select | options |
multi_select | Multiple select | options, min_selections, max_selections |
checkbox | Single checkbox | - |
checkbox_group | Multiple checkboxes | options |
radio | Radio buttons | options |
date | Date picker | min_date, max_date |
datetime | Date and time | min_date, max_date |
file | File upload | allowed_types, max_size_mb |
url | URL input | Auto-validated |
hidden | Hidden field | - |
section | Section divider | - |
paragraph | Static text block | - |
signature | Signature pad | - |
rating | Star/numeric rating | max |
slider | Range slider | min, max, step |
Response
json{ "data": { "id": "form_abc123", "name": "Contact Form", "slug": "contact-form", "description": "General inquiries and feedback", "status": "draft", "fields": [...], "settings": {...}, "created_at": "2025-12-07T10:00:00Z", "updated_at": "2025-12-07T10:00:00Z" } }
Get Form
GET /api/v1/forms/:id
Response
json{ "data": { "id": "form_abc123", "name": "Contact Form", "slug": "contact-form", "description": "General inquiries and feedback", "status": "published", "fields": [ { "id": "name", "type": "text", "label": "Full Name", "required": true } ], "settings": { "submit_button_text": "Send Message", "success_message": "Thank you!" }, "workflow_id": "wf_xyz789", "data_table_config": { "table_id": "dt_contacts", "auto_save": true, "field_mapping": { "name": "full_name", "email": "email_address" } }, "branding": {...}, "stats": { "submissions": 342, "views": 1520, "conversion_rate": 22.5 }, "created_at": "2025-11-01T10:00:00Z", "updated_at": "2025-12-07T15:30:00Z", "published_at": "2025-11-02T09:00:00Z" } }
Get Form by Slug
GET /api/v1/forms/slug/:slug
Same response format as Get Form. Useful when you have the human-readable slug instead of the UUID.
Update Form
PUT /api/v1/forms/:id
Only include fields you want to update. Returns the updated form object.
json{ "name": "Updated Contact Form", "fields": [...], "settings": { "success_message": "Thanks for reaching out!" } }
Note: Updating fields on a published form creates a new version. Existing submissions retain their original field structure.
Delete Form
DELETE /api/v1/forms/:id
Returns 204 No Content. This also deletes all submissions. Use archive instead for data retention.
Lifecycle Endpoints
Publish
POST /api/v1/forms/:id/publish
Changes status from draft to published, enabling submissions via public and authenticated endpoints.
json{ "data": { "id": "form_abc123", "status": "published", "slug": "contact-form", "published_at": "2025-12-07T10:00:00Z" } }
Unpublish
POST /api/v1/forms/:id/unpublish
Returns the form to draft status, disabling public submissions.
Archive
POST /api/v1/forms/:id/archive
Archives the form, preventing new submissions while retaining all existing data and submission history.
Duplicate
POST /api/v1/forms/:id/duplicate
Creates a copy of the form with all fields and settings. The duplicate is created in draft status with a new ID and slug.
json{ "data": { "id": "form_def456", "name": "Contact Form (Copy)", "slug": "contact-form-copy", "status": "draft", "field_count": 5, "created_at": "2025-12-07T11:00:00Z" } }
Submit Form
POST /api/v1/forms/:id/submit
Submit data to a form via the authenticated API.
Request Body
json{ "data": { "name": "John Doe", "email": "[email protected]", "subject": "general", "message": "I have a question about your services.", "rating": 4 }, "metadata": { "source": "api", "utm_source": "google" } }
Parameters
| Field | Type | Required | Description |
|---|---|---|---|
data | object | Yes | Form field values (keys match field IDs) |
metadata | object | No | Additional context metadata |
Response
json{ "data": { "submission_id": "sub_xyz789", "form_id": "form_abc123", "status": "received", "workflow_execution_id": "exec_abc123", "created_at": "2025-12-07T10:30:00Z" }, "message": "Thank you! We'll get back to you soon." }
Validation Errors
json{ "error": { "code": "validation_error", "message": "Form validation failed", "details": { "errors": [ { "field": "email", "message": "Invalid email address" }, { "field": "message", "message": "Must be at least 10 characters" } ] } } }
List Submissions
GET /api/v1/forms/:id/submissions
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
page | integer | 1 | Page number |
per_page | integer | 20 | Items per page |
status | string | - | Filter: received, processed, archived |
after | string | - | Submissions after ISO date |
before | string | - | Submissions before ISO date |
search | string | - | Search in field values |
Response
json{ "data": [ { "id": "sub_xyz789", "form_id": "form_abc123", "status": "processed", "data": { "name": "John Doe", "email": "[email protected]", "subject": "general", "message": "I have a question..." }, "metadata": { "ip_address": "192.168.1.1", "user_agent": "Mozilla/5.0...", "source": "website" }, "workflow_execution_id": "exec_abc123", "created_at": "2025-12-07T10:30:00Z" } ], "pagination": { "page": 1, "per_page": 20, "total": 342, "total_pages": 18 } }
Get Submission
GET /api/v1/forms/:id/submissions/:submissionId
Response
json{ "data": { "id": "sub_xyz789", "form_id": "form_abc123", "form_name": "Contact Form", "status": "processed", "data": { "name": "John Doe", "email": "[email protected]", "subject": "general", "message": "I have a question about your services.", "rating": 4 }, "metadata": { "ip_address": "192.168.1.1", "user_agent": "Mozilla/5.0...", "source": "website", "page_url": "https://company.com/contact", "referrer": "https://google.com" }, "files": [ { "field_id": "attachment", "file_id": "file_001", "filename": "document.pdf", "size_bytes": 245000, "mime_type": "application/pdf" } ], "workflow_execution": { "id": "exec_abc123", "status": "completed", "output": { "ticket_id": "TKT-12345" } }, "created_at": "2025-12-07T10:30:00Z", "processed_at": "2025-12-07T10:30:05Z" } }
Delete Submission
DELETE /api/v1/forms/:id/submissions/:submissionId
Returns 204 No Content.
Form Analytics
GET /api/v1/forms/:id/analytics
Returns aggregated analytics for the form.
json{ "data": { "total_submissions": 342, "total_views": 1520, "conversion_rate": 22.5, "avg_completion_time_seconds": 45, "submissions_by_day": [ { "date": "2025-12-01", "count": 18 }, { "date": "2025-12-02", "count": 24 } ], "top_sources": [ { "source": "website", "count": 210 }, { "source": "email", "count": 85 }, { "source": "api", "count": 47 } ], "field_completion_rates": { "name": 100, "email": 100, "phone": 68.4, "message": 95.2 }, "drop_off_fields": [ { "field_id": "phone", "drop_off_rate": 12.3 } ] } }
Dynamic Field Options
GET /api/v1/forms/:id/fields/:fieldId/options
Returns options for a select/multi-select field that is backed by a dynamic data source (e.g., a data table column).
json{ "data": [ { "value": "dept_eng", "label": "Engineering" }, { "value": "dept_sales", "label": "Sales" }, { "value": "dept_mkt", "label": "Marketing" } ] }
Download File
GET /api/v1/forms/:id/files/:fileId
Returns the uploaded file as a binary download with the appropriate Content-Type header.
Public Endpoints
These endpoints require no authentication and are used for rendering and submitting public-facing forms.
Get Public Form
GET /api/v1/public/forms/:slug
Returns the form definition for rendering. Includes fields, branding, and settings but excludes internal metadata.
json{ "data": { "name": "Contact Form", "slug": "contact-form", "description": "General inquiries and feedback", "fields": [...], "branding": { "logo_url": "https://company.com/logo.png", "primary_color": "#4F46E5", "background_color": "#F9FAFB" }, "settings": { "submit_button_text": "Send Message", "success_message": "Thank you!" } } }
Public Submission
POST /api/v1/public/forms/:slug/submit
json{ "data": { "name": "Jane Smith", "email": "[email protected]", "message": "Hello, I'd like to learn more." } }
Response:
json{ "data": { "submission_id": "sub_abc789", "status": "received" }, "message": "Thank you! We'll get back to you soon." }
Public File Upload
POST /api/v1/public/forms/:slug/upload
Upload a file as part of a public form submission. Use multipart/form-data.
| Field | Type | Required | Description |
|---|---|---|---|
file | file | Yes | The file to upload |
field_id | string | Yes | The form field ID this file belongs to |
Response:
json{ "data": { "file_id": "file_001", "filename": "resume.pdf", "size_bytes": 245000, "mime_type": "application/pdf" } }
Include the returned file_id in the submission data object for the corresponding field.
Get Embed Code
GET /api/v1/public/forms/:slug/embed
json{ "data": { "script_embed": "<script src=\"https://forms.arcanflows.io/embed.js\" data-form-slug=\"contact-form\"></script>", "iframe_embed": "<iframe src=\"https://forms.arcanflows.io/f/contact-form?embed=true\" width=\"100%\" height=\"600\" frameborder=\"0\"></iframe>" } }
Public Field Options
GET /api/v1/public/forms/:slug/fields/:fieldId/options
Same as the authenticated field options endpoint, but accessible without authentication for public form rendering.
Form Chat (Conversational Forms)
Conversational forms allow users to complete a form through a chat-like interface. An AI agent guides the user through each field conversationally.
Get Conversation
GET /api/v1/public/forms/:slug/conversations/:conversationId
json{ "data": { "id": "conv_abc123", "form_slug": "contact-form", "status": "active", "fields_completed": 2, "fields_total": 5, "collected_data": { "name": "John Doe", "email": "[email protected]" }, "created_at": "2025-12-07T10:30:00Z" } }
Get Messages
GET /api/v1/public/forms/:slug/conversations/:conversationId/messages
json{ "data": [ { "id": "msg_001", "role": "assistant", "content": "Hi! I'd love to help you get in touch. What's your name?", "field_id": "name", "created_at": "2025-12-07T10:30:00Z" }, { "id": "msg_002", "role": "user", "content": "John Doe", "created_at": "2025-12-07T10:30:15Z" }, { "id": "msg_003", "role": "assistant", "content": "Nice to meet you, John! What's your email address?", "field_id": "email", "created_at": "2025-12-07T10:30:16Z" } ] }
Send Message
POST /api/v1/public/forms/:slug/conversations/:conversationId/messages
json{ "content": "[email protected]" }
Response:
json{ "data": { "user_message": { "id": "msg_004", "role": "user", "content": "[email protected]" }, "assistant_message": { "id": "msg_005", "role": "assistant", "content": "Great! What topic is your inquiry about? You can choose: General Inquiry, Technical Support, or Sales Question.", "field_id": "subject" }, "fields_completed": 2, "fields_total": 5, "form_completed": false } }
When all fields are collected, the response indicates form completion:
json{ "data": { "user_message": { "id": "msg_010", "role": "user", "content": "Thanks!" }, "assistant_message": { "id": "msg_011", "role": "assistant", "content": "Thank you, John! Your message has been submitted. We'll get back to you soon." }, "fields_completed": 5, "fields_total": 5, "form_completed": true, "submission_id": "sub_xyz789" } }
Stream Message
POST /api/v1/public/forms/:slug/conversations/:conversationId/messages/stream
Same request body as Send Message, but returns a Server-Sent Events (SSE) stream for the assistant response.
Data Table Integration
Forms can automatically save submissions to data tables. Configure this via the data_table_config field when creating or updating a form.
Configuration
json{ "data_table_config": { "table_id": "dt_contacts", "auto_save": true, "field_mapping": { "name": "full_name", "email": "email_address", "subject": "inquiry_type", "message": "body", "rating": "satisfaction_score" } } }
| Field | Type | Description |
|---|---|---|
table_id | string | Target data table ID |
auto_save | boolean | Automatically insert a row on each submission |
field_mapping | object | Maps form field IDs to data table column names |
When auto_save is enabled, every new submission creates a row in the specified data table using the field mapping.
Field Configuration Examples
Select with Dynamic Data Source
json{ "id": "department", "type": "select", "label": "Department", "required": true, "data_source": { "type": "data_table", "table_id": "dt_departments", "value_column": "id", "label_column": "name" } }
The options are loaded at render time via the /fields/:fieldId/options endpoint.
File Upload
json{ "id": "resume", "type": "file", "label": "Upload Resume", "required": true, "validation": { "allowed_types": ["pdf", "doc", "docx"], "max_size_mb": 10 }, "help_text": "PDF, DOC, or DOCX. Max 10MB." }
Slider
json{ "id": "budget", "type": "slider", "label": "Monthly Budget", "required": true, "validation": { "min": 100, "max": 10000, "step": 100 }, "help_text": "Drag to select your budget range" }
Signature
json{ "id": "consent_signature", "type": "signature", "label": "Sign to Confirm", "required": true, "help_text": "Draw your signature above" }
Conditional Field
json{ "id": "other_description", "type": "textarea", "label": "Please describe", "required": true, "conditional": { "field": "subject", "operator": "equals", "value": "other" } }
Error Handling
All error responses follow a consistent format:
json{ "error": { "code": "validation_error", "message": "Form validation failed", "details": { "errors": [ { "field": "email", "message": "Invalid email address" }, { "field": "message", "message": "Must be at least 10 characters" } ] } } }
| HTTP Status | Error Code | Description |
|---|---|---|
| 400 | validation_error | Submission data failed validation |
| 404 | form_not_found | Form does not exist or is not published |
| 409 | form_not_published | Cannot submit to a draft or archived form |
| 413 | file_too_large | Uploaded file exceeds size limit |
| 415 | unsupported_file_type | File type not allowed |
| 429 | rate_limit_exceeded | Too many submissions |