Architecture · Forms, review and workflow runtime
Event-Driven Forms and Workflow Backend for WordPress on AWS
Keep form authoring and placement in WordPress while durable drafts, submissions, discussions, review state, notifications and downstream actions run behind an explicit API and event boundary.
WordPress / static frontend
↓ Flow runtime
Frontend API
↓
Submission + draft state
├→ uploads
├→ discussion / review state
└→ workflow events
↓
email / webhooks / actions
Execution boundary
A form becomes a workflow when state must survive the page request
Long forms, approvals and review processes need durable state. Flow separates the browser experience from backend persistence and downstream execution, so the public page can remain static while the process continues independently.
Form / review UI in WordPress
↓ browser
/frontend/* API
├→ save / load / finalize draft
├→ submit record
├→ upload contract
└→ discussion / rating inputs
↓ durable state + events
/admin/* → protected management
↓
workflow dispatch → email / webhook / process action
Security boundary Public form routes and privileged admin routes have different risk profiles. Anonymous submission controls, authenticated review access and administrative configuration should not share one broad authorization surface.
What the Flow stack creates
The Flow backend separates API, compute, state, payload, event, security and operational concerns. This inventory shows which AWS resources serve each architectural job.
| Layer | AWS resources | Architectural job |
|---|---|---|
| API layer | Regional API Gateway REST API, optional custom domain, optional Route53 records | Expose separate frontend and admin route families without tying form execution to WordPress hosting. |
| Compute layer | Forms API Lambda, Workflow Dispatcher Lambda, Email Sender Lambda, Webhook Dispatcher Lambda, deployment-time Custom Resource Lambda | Keep synchronous form handling, async workflow processing, email delivery and outbound webhooks as separate operational units. |
| State layer | DynamoDB tables for submissions, submission events, templates, workflow definitions, form definitions, webhook endpoints and process maps | Store form definitions, records and workflow state in purpose-specific tables with TTL/retention instead of treating the WordPress database as the integration log. |
| Payload layer | S3 payload bucket and templates bucket, optional existing buckets | Move large file transfer and reusable email/template assets into object storage. |
| Event layer | EventBridge rules and events such as submission created/updated/status/action and AI agent completion/failure | Turn form work into observable events that can trigger workflows without blocking the visitor. |
| Security layer | Cognito authorizer for admin routes, optional IAM/NONE modes, WAF, reCAPTCHA, IP allow/block lists, SSM/KMS for secrets | Apply different protections to public form submissions and privileged management APIs. |
| Operations layer | CloudWatch log groups, SQS dead-letter queue, configurable log retention, optional GuardDuty malware protection | Give the runtime its own logs, retry/failure surface and payload scanning options. |
Frontend API versus Admin API
The two route families serve different callers and should not inherit the same trust assumptions. This table makes the runtime split visible before form behavior or permissions are configured.
| Route family | Examples | Typical caller | Security posture |
|---|---|---|---|
| Frontend submission | /frontend/forms/{formId}/submit | A rendered Flow form on a public or protected page | Can run with no user auth, but should use reCAPTCHA/WAF/rate limits for anonymous traffic. |
| Frontend drafts | /frontend/forms/{formId}/drafts, /drafts/load, /drafts/delete, /drafts/{submissionId}/submit | A visitor saving, resuming or finalizing a long form | Draft credentials and final validation are separated so draft saves do not trigger final workflows. |
| Frontend upload preparation | /frontend/forms/{formId}/upload-url | A form component preparing a large attachment | Returns a presigned S3 upload contract; the payload does not need to pass through WordPress. |
| Admin forms/submissions | /admin/forms, /admin/forms/{formId}/submissions | WP Admin or management UI | Should be protected with Cognito/IAM and optionally IP allowlisting. |
| Admin templates/workflows/webhooks | /admin/templates, /admin/workflows, /admin/webhook-endpoints | Administrators configuring business behavior | These routes change runtime behavior and should never be treated like public frontend endpoints. |
Data model: why several tables are useful
The backend separates definitions, current records, event history and integration configuration because they have different lifecycles. This table explains why a single generic form row is not enough for durable workflows.
| Table family | What it represents | Why it is separate |
|---|---|---|
| Form definitions | The structure and versioning of forms used by the frontend | A form can change while old submissions must remain understandable. |
| Submissions | Current submission state, draft/final status and core field data | This is the operational record that admin views and workflow steps query. |
| Submission events | Append-style history: created, updated, status changed, action invoked | Audit and retry behavior should not overwrite the current submission row. |
| Templates | Reusable email/template metadata | Email content changes should be managed independently from submission records. |
| Workflow definitions | Rules, actions and routing behavior | Workflow logic has its own lifecycle and should be versioned/managed explicitly. |
| Webhook endpoints | Outbound integration targets and signing settings | External systems are operational dependencies, not just form fields. |
| Process maps | Runtime mapping between processes, submissions and actions | Complex workflows need correlation state beyond a single form record. |
Security and abuse controls
Public form routes and privileged management routes should not share one protection model. This table shows where bot controls, WAF, identity and secret storage belong in the Flow runtime.
| Control | Where it applies | Design reason |
|---|---|---|
| reCAPTCHA | Public frontend form endpoints | Reduce bot submissions before they become stored records, emails, webhooks or model/workflow costs. |
| WAF rate limits | Frontend and admin path prefixes | Throttle abuse differently for visitor-facing and admin-facing routes. |
| Admin Cognito authorizer | /admin/* routes | Keep form definitions, submissions, templates, workflows and webhook configuration behind a real identity boundary. |
| SSM/KMS secrets | reCAPTCHA secrets and webhook signing secrets | Keep shared secrets out of WordPress settings and template source. |
| GuardDuty malware protection | Payload bucket when enabled | Add a scanning option for uploaded payloads before downstream processing relies on them. |
Deployment parameters that change the architecture
These parameters are not cosmetic form settings. They change authentication, abuse controls, storage ownership, secrets, domains and operational behavior, so they should be reviewed as architecture decisions.
| Parameter area | Examples | Architectural effect |
|---|---|---|
| Auth modes | FrontendApiAuthMode, AdminApiAuthMode, AdminCognitoUserPoolId, scopes | Controls whether frontend and admin surfaces are public, IAM-protected or Cognito-protected. |
| Abuse protection | EnableRecaptcha, reCAPTCHA mode/site key/threshold, EnableWAF, allowed/blocked IP lists | Determines how much anonymous traffic can reach the backend and which paths are rate-limited or allowlisted. |
| Storage ownership | TemplatesBucketName, PayloadBucketName, prefixes | Lets teams use created buckets or attach existing storage conventions. |
| Secrets | EnableKmsForSecrets, webhook signing secret, reCAPTCHA secret | Controls whether secrets are stored with a dedicated KMS key and SSM parameters. |
| Domain/DNS | ApiCustomDomainName, certificate ARN, Route53 settings | Moves the API from an execute-api URL to a branded domain when the DNS/certificate path is ready. |
| Operations | Data retention, log retention, Lambda memory/timeout/log level | Controls cost, observability and runtime headroom without changing WordPress pages. |
Implementation path
Model the process before wiring the actions
The same backend can support simple forms and more structured processes if draft, submission, review and action state remain explicit.
- Define the record and its lifecycle — Decide what is a draft, what becomes a submitted record, which review states exist and which fields or attachments must persist between sessions.
- Separate visitor and reviewer operations — Keep public or authenticated frontend actions under a narrow runtime surface and protect administrative form, submission and workflow management separately.
- Emit events after durable state changes — Trigger email, webhooks or other process actions only after the relevant record or status change is accepted, so downstream failures do not erase the original submission.
- Test retries and handoffs — Verify draft resume, final submit, review decisions, discussion or rating updates, failed notifications and external webhook errors as separate failure paths.
When an event-driven Flow backend is worth the separation
Good fit
Forms that are really business processes
- Users need to save and resume a long form across sessions.
- Submissions enter review, approval, discussion, rating or status workflows after the initial form step.
- The public frontend may be static, while state and downstream actions must remain live and independently operable.
Keep it simpler
A conventional form path may be enough when
- The form is short and the process ends with one submission and a simple notification.
- The site is fully dynamic and an existing form plugin already covers the workflow without operational friction.
- There is no need for durable drafts, backend review state, event history or external workflow actions.
Problem guides
Buyer problems backed by this architecture
How do I replace email and spreadsheet approvals with a WordPress workflow?
Start with Replace Email and Spreadsheet Approvals with a WordPress Workflow. It frames the operational pain; this architecture explains where records, review state and workflow actions live.
How do users save a long WordPress form and continue later?
See Let Users Save a Long WordPress Form and Continue Later. Durable draft state belongs behind the browser runtime rather than inside one PHP page request.
How do I combine forms, discussion and ratings in one review workflow?
See Build a WordPress Review Workflow with Forms, Discussion and Ratings for the review use case, and Add Discussions, Replies and Ratings to a Static WordPress Frontend when the collaboration experience must remain live after static publishing.
Can this still work on statically published WordPress?
Yes. The page can be served from static hosting while Flow calls the configured backend from the browser. See Make WordPress Static Without Losing Dynamic Features for the wider static-plus-runtime model.
Start with the workflow problem
Move the process out of inboxes before adding more automation
Choose the buyer problem first—approval, save-and-resume or structured review—then use this architecture to define persistence, authorization and event boundaries.
