Endpoints for form submissions — both public submission and admin retrieval.
POST /formforge/v1/forms/{id}/submit
Public endpoint for form submissions. In practice, submissions go through the AJAX handler (wp_ajax_formforge_submit / wp_ajax_nopriv_formforge_submit) which handles nonce verification, anti-spam checks, file uploads, and all integrations.
const formEl = document.querySelector( '.formforge-form' );
const formData = new FormData( formEl );
formData.append( 'action', 'formforge_submit' );
fetch( formforgeFront.ajaxUrl, {
method: 'POST',
body: formData
} )
.then( r => r.json() )
.then( data => {
if ( data.success ) {
console.log( 'Success:', data.data.message );
if ( data.data.redirect ) {
window.location.href = data.data.redirect;
}
} else {
console.error( 'Error:', data.data.message );
// data.data.errors may contain per-field validation errors
}
} );{
"success": true,
"data": {
"message": "Thank you! We will get back to you within 24 hours.",
"redirect": "",
"actions": [],
"submit_action": "message",
"submission_id": 42
}
}When the form’s After submit setting is message, the bundled frontend renders a single completion state, hides the completed form controls, and resets non-payment forms after the saved submission succeeds. Payment forms lock the completed controls instead of clearing the captured payment context.
When the setting is redirect, redirect contains a root-relative or same-site URL and the bundled frontend navigates immediately instead of rendering a success-message delay. When the setting is custom_js, Form Forge does not return raw JavaScript; it returns a safe structured action list such as:
{
"actions": [
{
"type": "add_class",
"selector": "#modal",
"value": "is-active"
}
]
}The frontend runner only supports add_class, remove_class, and toggle_class on ID or class selectors. The server rejects arbitrary script execution, external requests, cookie/storage access, assignments, eval, and markup injection.
{
"success": false,
"data": {
"message": "Please correct the errors below.",
"errors": {
"field_2": "Please enter a valid email address."
}
}
}// Simulate a form submission from a custom endpoint or cron job
$handler = FORMFORGE_Form_Handler::instance();
$result = $handler->process_submission( 42, [
'field_1' => 'Jane Smith',
'field_2' => '[email protected]',
'field_3' => 'support',
'field_4' => 'I need help with billing.',
] );GET /formforge/v1/submissions/{form_id}
Get submissions for a form. Requires manage_options. Returns up to 100 most recent submissions ordered by date descending.
curl -s -u "admin:XXXX XXXX XXXX XXXX"
https://example.com/wp-json/formforge/v1/submissions/1[
{
"id": 42,
"form_id": 1,
"data": {
"field_1": {
"label": "Name",
"value": "John Doe",
"type": "text"
},
"field_2": {
"label": "Email",
"value": "[email protected]",
"type": "email"
}
},
"ip_address": "203.0.113.50",
"user_agent": "Mozilla/5.0 ...",
"user_id": 0,
"status": "new",
"created_at": "2025-01-20 14:35:22"
}
]Admin AJAX: PDF and Delete Actions
The Submissions screen uses admin AJAX for per-submission PDF download and deletion:
| Action | Method | Capability | Nonce | Response |
|---|---|---|---|---|
formforge_download_pdf | GET | manage_options | formforge_pdf via nonce | application/pdf attachment named submission-{id}.pdf |
formforge_delete_submission | POST | manage_options | formforge_delete_submission_{id} via _wpnonce | JSON success with { "deleted": id } |
Delete also removes any private files attached to that submission from uploads/formforge-private/. Public form submission continues to use formforge_submit; file upload fields are skipped on Free and rejected server-side if a tampered request posts them after a downgrade.
—