Skip to main content

Forms API

Authenticated Endpoints

MethodEndpointDescription
GET/formsList forms (query: status, search, page, limit)
GET/forms/:idGet form by ID
POST/formsCreate form
PUT/forms/:idUpdate form
DELETE/forms/:idSoft delete form
POST/forms/:id/duplicateDuplicate form as draft
GET/forms/:id/submissionsList submissions (query: page, limit, status, dateFrom, dateTo)
GET/forms/:id/analyticsSubmission analytics (stats, trends, field breakdowns)
POST/forms/:id/submissions/:submissionId/retry-webhookRetry failed webhook action

Public Endpoints (No Auth)

MethodEndpointDescription
GET/forms/public/:tenantSlug/:tokenGet public form for rendering
POST/forms/public/:tenantSlug/:token/submitSubmit form data

Submission Request

The submission body is a flat JSON object keyed by each field's name (the name shown in the form builder's field settings). Custom fields use the cf_ prefix.

{
"firstName": "Jane",
"lastName": "Doe",
"email": "jane@example.com",
"phone": "+971501234567",
"cf_lead_score": 80,
"__recaptchaToken": "<v3 token>"
}
  • __recaptchaToken is only required when the form's settings.requireCaptcha is enabled.
  • Phone numbers are auto-normalised to E.164 server-side.
  • The response includes the created entity IDs per chained submit action.

Sample Requests

cURL

curl -X POST 'https://api.example.com/api/forms/public/acme/abc123token/submit' \
-H 'Content-Type: application/json' \
-d '{"firstName":"Jane","email":"jane@example.com"}'

JavaScript (fetch)

const response = await fetch(
'https://api.example.com/api/forms/public/acme/abc123token/submit',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ firstName: 'Jane', email: 'jane@example.com' }),
},
);
const result = await response.json();

PHP (cURL)

$ch = curl_init('https://api.example.com/api/forms/public/acme/abc123token/submit');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
'firstName' => 'Jane',
'email' => 'jane@example.com',
]));
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
$response = curl_exec($ch);
curl_close($ch);

Python (requests)

import requests

response = requests.post(
'https://api.example.com/api/forms/public/acme/abc123token/submit',
json={'firstName': 'Jane', 'email': 'jane@example.com'},
)
print(response.json())

Flutter (Dart, package:http)

import 'dart:convert';
import 'package:http/http.dart' as http;

final response = await http.post(
Uri.parse('https://api.example.com/api/forms/public/acme/abc123token/submit'),
headers: {'Content-Type': 'application/json'},
body: jsonEncode({'firstName': 'Jane', 'email': 'jane@example.com'}),
);
tip

The form builder's Embed button (top bar of the builder) opens a "Share & Integrate" modal that generates these snippets pre-filled with the form's actual URL, token, and field keys — just copy and paste.

Form Object

{
"id": "uuid",
"name": "Contact Us",
"description": "Main contact form",
"status": "active",
"type": "standard",
"token": "abc123",
"tenantSlug": "acme",
"fields": [
{
"id": "field-1",
"type": "text",
"label": "Full Name",
"name": "full_name",
"required": true,
"width": "full"
}
],

"settings": {
"successMessage": "Thank you!",
"redirectUrl": null,
"allowMultiple": false,
"requireCaptcha": true,
"notifyEmails": ["admin@acme.com"]
},
"submitActions": [
{
"type": "create_lead",
"enabled": true,
"fieldMapping": {
"first_name": "full_name",
"email": "email_field"
}
}
],
"branding": {
"logoUrl": "https://...",
"primaryColor": "#7C3AED",
"backgroundColor": "#FFFFFF"
},
"submissionCount": 42,
"createdAt": "2026-01-15T...",
"updatedAt": "2026-03-19T..."
}

Field Types

TypeInputHas Options
textSingle-line textNo
emailEmail with validationNo
phonePhone numberNo
numberNumeric inputNo
dateDate pickerNo
textareaMulti-line textNo
selectDropdownYes
radioRadio buttonsYes
checkboxCheckboxesYes
fileFile uploadNo
headingDisplay onlyNo
paragraphDisplay onlyNo
dividerDisplay onlyNo

Field Width

The width property on each field controls how much of the row it occupies. Adjacent non-full fields auto-pack into the same row on the rendered form (12-column grid, mobile-responsive).

WidthFraction
full1/1 (default)
two-thirds2/3
half1/2
third1/3
quarter1/4

heading, paragraph, and divider fields always span the full row regardless of the width value.

Submit Action Types

TypeDescriptionConfig
create_leadCreate CRM leadfieldMapping
create_contactCreate CRM contactfieldMapping
create_accountCreate CRM accountfieldMapping
webhookPOST to external URLwebhookUrl
send_emailEmail to submitteremailFieldName, subject, body