Forms API
Manage forms and process submissions programmatically.
Try it — API Explorer
Authentication
All endpoints except form submission require your API key:
Authorization: Bearer YOUR_API_KEY
The submission endpoint (POST /api/v1/forms/{id}/submissions) is public -- no API key needed.
Forms
List forms
GET /api/v1/forms
cURL:
curl -H "Authorization: Bearer YOUR_API_KEY" \
https://your-site.clearcms.app/api/v1/forms
Response:
{
"data": [
{
"id": "form_abc123",
"slug": "contact",
"name": "Contact Form",
"description": "Main contact form",
"status": "published",
"fields": [...],
"settings": {...},
"createdAt": "2026-03-01T10:00:00Z",
"updatedAt": "2026-03-15T09:30:00Z"
}
],
"total": 3
}
Get a form
GET /api/v1/forms/{id}
Returns a single form with all fields and settings.
Create a form
POST /api/v1/forms
Request body:
{
"name": "Feedback Form",
"slug": "feedback",
"description": "Collect visitor feedback",
"fields": [
{
"type": "email",
"name": "email",
"label": "Your email",
"required": true,
"width": "full"
},
{
"type": "select",
"name": "rating",
"label": "How would you rate us?",
"required": true,
"width": "full",
"options": [
{ "label": "Excellent", "value": "5" },
{ "label": "Good", "value": "4" },
{ "label": "Average", "value": "3" },
{ "label": "Poor", "value": "2" },
{ "label": "Terrible", "value": "1" }
]
},
{
"type": "textarea",
"name": "comments",
"label": "Comments",
"placeholder": "Tell us more...",
"required": false,
"width": "full"
}
],
"settings": {
"submitButtonText": "Send Feedback",
"successMessage": "Thanks for your feedback!"
}
}
You can also create a form from a template by passing templateId instead of fields.
Update a form
PUT /api/v1/forms/{id}
Pass any combination of name, description, fields, settings, or status.
Delete a form
DELETE /api/v1/forms/{id}
Deletes the form and all its submissions. Cannot be undone.
Field types
Each field in the fields array supports these properties:
| Property | Type | Required | Description |
|---|---|---|---|
type | string | Yes | One of the 18 field types (see below) |
name | string | Yes | Submission key (no spaces) |
label | string | Yes | Display label |
placeholder | string | No | Input placeholder text |
helpText | string | No | Help text below the field |
required | boolean | Yes | Whether the field must be filled in |
width | string | No | full or half. Default: full |
options | array | No | For select, radio, and checkbox group fields |
validation | object | No | { minLength, maxLength, min, max, pattern, patternMessage } |
acceptedTypes | array | No | For file fields: ["image/*", ".pdf"] |
maxFileSize | number | No | Max file size in bytes |
Available types: text, email, phone, url, number, textarea, select, multiselect, radio, checkbox, checkboxGroup, date, time, datetime, file, hidden, heading, paragraph
Submissions
Submit a form (public)
POST /api/v1/forms/{id}/submissions
cURL:
curl -X POST \
-H "Content-Type: application/json" \
-d '{"data":{"email":"test@example.com","message":"Hello"}}' \
https://your-site.clearcms.app/api/v1/forms/{id}/submissions
No API key required. The form must have published status.
Request body:
{
"data": {
"email": "visitor@example.com",
"rating": "5",
"comments": "Love it!"
}
}
For file upload fields, include file references:
{
"data": {
"name": "Jane Smith",
"resume": null
},
"files": [
{
"fieldName": "resume",
"mediaId": "media_xyz789",
"filename": "resume.pdf"
}
]
}
Response:
{
"success": true,
"message": "Thank you for your submission!",
"redirect": "https://example.com/thank-you"
}
message and redirect come from the form's settings. redirect is only present if configured.
If the honeypot catches a bot, the endpoint returns a success response without saving the submission.
List submissions
GET /api/v1/forms/{id}/submissions
Query parameters:
| Parameter | Type | Description |
|---|---|---|
status | string | Filter by status: new, read, replied, archived, spam |
limit | integer | Results per page. Default: 50. Max: 200 |
offset | integer | Pagination offset |
Response:
{
"data": [
{
"id": "sub_def456",
"formId": "form_abc123",
"data": {
"email": "visitor@example.com",
"rating": "5",
"comments": "Love it!"
},
"submittedAt": "2026-03-26T14:20:00Z",
"submitterEmail": "visitor@example.com",
"status": "new"
}
],
"total": 42
}
Update submission status
PUT /api/v1/forms/{id}/submissions
Request body:
{
"submissionId": "sub_def456",
"status": "read"
}
Status values: new, read, replied, archived, spam
Form settings reference
The settings object supports:
{
"submitButtonText": "Submit",
"submitButtonStyle": "primary",
"successMessage": "Thank you for your submission!",
"successRedirect": "https://example.com/thank-you",
"honeypotEnabled": true,
"captchaType": "none",
"notifyEmails": ["admin@example.com"],
"sendConfirmation": false,
"confirmationSubject": "We received your message",
"confirmationMessage": "Thanks for reaching out. We'll reply within 24 hours.",
"addToNewsletter": false,
"newsletterListId": "list_abc",
"webhookUrl": "https://hooks.example.com/form-submit",
"requireAuth": false,
"allowMultiple": true
}
All settings are optional. Omitted fields use defaults.
Webhook payloads
When a form has a webhookUrl, ClearCMS sends a POST request to that URL on each submission:
{
"event": "form.submission",
"formId": "form_abc123",
"formSlug": "contact",
"submission": {
"id": "sub_def456",
"data": {
"email": "visitor@example.com",
"message": "Hello!"
},
"submittedAt": "2026-03-26T14:20:00Z",
"submitterEmail": "visitor@example.com"
}
}
Webhooks fire asynchronously after the submission is saved. ClearCMS expects a 200 response. If the endpoint returns a non-200 status or times out, the submission is still stored -- webhook delivery is best-effort.
Error responses
| Status | Meaning |
|---|---|
400 | Invalid request body or duplicate slug |
401 | Missing or invalid API key |
403 | Insufficient permissions |
404 | Form not found |
422 | Form is not published (submissions only) |
500 | Server error |