When a user submits a form, the data rarely just sits there. It needs to go somewhere, trigger something, or transform into something else. Most form platforms handle this through rigid integrations or custom server-side code you write and maintain. Form.io treats this differently. Every behavior that happens when a form is submitted, updated, or deleted is handled through Form Actions: configurable middleware components that attach to forms and execute in sequence when submission events occur.
This includes behaviors you might take for granted. Saving data to the database? That is an action. Sending an email notification? An action. Authenticating a user? Also an action. Even the fundamental act of persisting a submission is not hardcoded into the platform. It is a Save Submission action that you can modify, reorder, or remove entirely.
Actions Are Middleware, Not Hooks
The mental model matters here. Form Actions are not post-submission hooks that fire after the important work is done. They are the submission pipeline itself. When data arrives at the Form.io server, it passes through each action attached to that form in the order they are stacked. Each action can inspect, modify, or redirect the submission payload before passing it along.
This architecture means you can build complex business workflows without writing server-side code. An employment application form might have four actions stacked: Save Submission to persist the applicant data, Email to notify HR, another Email to send a confirmation to the applicant, and a Webhook to push the data to your CRM. Each action executes sequentially, and each can be configured with its own conditional logic to determine whether it should fire.
The Handler setting controls when the action runs relative to the database write. Actions set to “Before” execute before MongoDB persists the submission. Actions set to “After” execute once the data is saved. Setting both causes the action to run twice. This distinction matters when you need to validate external conditions before allowing a save, or when you need the submission ID (which only exists after persistence) for subsequent operations.
The Method setting determines which CRUD operations trigger the action. You can configure an action to fire on Create (new submission), Read (viewing a submission), Update (editing), Delete, or Index (searching). Most actions target Create, but the flexibility exists for scenarios like sending notifications when submissions are deleted or triggering webhooks when records are viewed.
Core Actions and What They Actually Do
Form.io includes a set of built-in actions that cover common data flow patterns. Understanding what each one does, and does not do, helps you design submission pipelines that match your application requirements.
Save Submission is the action that persists data to MongoDB. Every new form includes this action by default. Without it, submissions would flow through the pipeline and disappear. The action can save data to the form itself or map fields to a different Resource. This mapping capability lets forms feed centralized data stores. A Registration form might save its email and password fields to a User Resource while saving the full registration details to itself.
Login authenticates users against a Resource containing user accounts. It compares the submitted credentials against existing submissions in the target Resource, and if a match is found, generates a JWT token that authenticates the user session. The action includes configurable lockout settings: maximum login attempts, time windows, and lockout durations. For a Login form to work, you typically remove the Save Submission action (since you do not want to create new records for login attempts) and add only the Login action.
Email sends email notifications when submission events occur. The action requires you to configure your own email transport in Project Settings, as Form.io does not provide a default mail service. The message body supports Nunjucks templating, which means you can interpolate submission data directly into the email content using {{ data.fieldName }} syntax. You can attach uploaded files or generate a PDF of the submission to include as an attachment.
Webhook makes HTTP requests to external endpoints with the submission payload. This is how you integrate Form.io with systems that do not have native actions. The webhook can use any REST verb, include custom headers, transform the payload using JavaScript, and optionally wait for a response before continuing to the next action. The formio-webhook-receiver repository provides an example receiver application for testing webhook integrations locally.
Role Assignment adds or removes roles from user submissions when events occur. The most common use case is granting roles during registration: when a new user submits the registration form, the action assigns them a default role that determines their permissions throughout the application.
Conditional Execution and Action Stacking
Actions do not have to fire on every submission. Each action includes a Conditional setting that uses the same logic system as field conditions. You can write Simple conditions that check field values, or Advanced conditions using JavaScript that evaluate more complex criteria.
Consider an expense report form where submissions over $1,000 require manager approval. You might stack two Email actions: one that always sends a confirmation to the submitter, and another that only fires when data.amount > 1000 to notify the manager. The conditional logic keeps the pipeline simple while handling branching business rules.
Action order matters because actions execute sequentially. If a Webhook action needs to capture an external system’s response and store it with the submission, that webhook must run before the Save Submission action completes. If the webhook runs after, the external ID would require a separate update to persist. The Form.io interface lets you drag actions to reorder them, and the “Wait for webhook response” option ensures the pipeline pauses until the external system responds.
Webhooks as the Universal Connector
The Webhook action deserves deeper examination because it serves as the escape hatch for any integration Form.io does not natively support. When a form submission needs to reach an external database, trigger a cloud function, update a CRM, or synchronize with a relational database, webhooks are typically the mechanism.
The default webhook payload includes the request object, the submission object, and any URL parameters. The Transform Payload setting lets you reshape this data using JavaScript before it ships. If your target system expects a specific JSON structure, you transform the payload to match rather than asking the external system to adapt to Form.io’s schema.
Response handling provides two useful capabilities. First, the “Wait for webhook response” option causes the submission pipeline to pause until the external system responds. Any errors from the webhook stop the submission process and display to the user. Second, the External ID settings let you capture identifiers from the external system’s response and store them with the submission. If your webhook creates a record in Salesforce and returns an ID, you can persist that ID alongside the Form.io submission for future reference.
The retry mechanism handles transient failures. You can configure retry type (constant delay, linear backoff, exponential backoff, or jitter), maximum attempts, and initial delay. This prevents single network hiccups from breaking integrations with external systems.
Authentication Actions Beyond Username and Password
Form.io supports multiple authentication mechanisms, each implemented as a distinct action type.
OAuth authenticates users against external identity providers like GitHub, Facebook, or Office 365. The action opens a popup window for the provider’s login flow, then captures the authenticated identity. You can configure OAuth to log in existing users, register new users, or link an OAuth account to an already-authenticated session. The configuration requires registering your application with the OAuth provider and configuring the integration in Project Settings.
LDAP Login authenticates against enterprise directory services. The action connects username and password fields to an LDAP directory configured in Project Settings. You can map LDAP groups to Form.io roles, so users authenticated through LDAP automatically receive appropriate permissions based on their directory membership.
Two-Factor Authentication adds a second verification step using one-time codes from authenticator apps. The action validates codes submitted through a designated form field against the user’s configured 2FA secret.
Each authentication action follows the same middleware pattern. They inspect submission data, perform verification against an external authority, and either allow the pipeline to continue (with an authenticated JWT token in the response) or reject the submission with an error.
Actions for External Integrations
Beyond webhooks, Form.io provides specialized actions for specific platforms.
Google Sheets maps form fields to columns in a Google Sheets spreadsheet. Each submission appends a new row to the configured sheet. The action is useful for creating simple, automatically-updated reports without building a separate data pipeline.
Group Assignment enables field-based access control by assigning users to groups defined as Resources. This powers scenarios where permissions depend on organizational structure rather than static roles. A user assigned to a “Teachers” group might have different access than one assigned to “Students,” even if both hold the same base role.
eSign integrates with electronic signature providers to capture legally-binding signatures on form submissions. The action coordinates between Form.io and the signature provider, handling the redirect flow and tracking signature status.
What Actions Cannot Do
Actions operate within the request-response cycle of a form submission. They are not background jobs, scheduled tasks, or long-running processes. If an action takes too long to execute, the submission request may time out.
Actions cannot modify the form schema or other forms. They operate on submission data within a single form’s context. Cross-form workflows require either the Save Submission action mapping to Resources, webhooks triggering external orchestration, or application-level code coordinating multiple form interactions.
Actions do not provide built-in queuing or guaranteed delivery for external integrations. If a webhook fails and retries are exhausted, the submission proceeds (or fails) without that action’s completion. For mission-critical integrations, consider having your webhook endpoint provide its own queuing and retry mechanisms.
Custom actions require server-side development. The built-in actions cover common patterns, but if you need behavior that none provide, you would extend the Form.io server codebase or implement the logic behind a webhook endpoint.
Action Logs for Debugging
The Security Module includes Action Logs that record every action execution with timestamps, status, and contextual details. When a submission does not trigger the expected behavior, logs reveal whether the action fired, what data it received, and whether it succeeded or failed. Logs automatically delete after 30 days.
Action Logs must be enabled per-form through the Actions tab settings. Once enabled, clicking any log entry shows the full execution context, including the submission data the action processed and any error messages returned.
Designing Submission Pipelines
When building a form, think of the Actions tab as defining the submission pipeline. Start with the minimal set of actions: typically just Save Submission for data collection forms, or Login (without Save Submission) for authentication forms. Add actions as requirements demand them.
Keep action order aligned with data dependencies. If you need submission IDs for external systems, those webhooks should run After the Save Submission. If you need to validate against external data before saving, those checks should run Before with “Wait for response” enabled.
Use conditional logic to handle branching without duplicating forms. A single approval form can route to different managers based on submission data, rather than creating separate forms for each approval path.
Test webhooks locally before deploying to production. The combination of ngrok for tunneling and the webhook receiver example lets you verify payload structure and response handling without exposing development endpoints publicly.
Related Resources
- Actions Documentation covers configuration details for each action type
- Authentication and Authorization explains how Login and OAuth actions integrate with the broader auth system
- Form Validation & Conditional Logic explains the conditional system used by action conditionals
- Relational Databases documents webhook-based SQL synchronization patterns
- formio-webhook-receiver provides example code for building webhook endpoints
