Table of Contents
This set of explanations provides the mental model behind Recurring Form Submissions for Gravity Forms. It covers how a series progresses, how time and catch‑up work, what happens on deletes/deactivations, how data persists, and which practical limitations to expect.
Series Lifecycle (Conceptual) #
- Start: A form submission occurs on a form with an active Recurring Submissions feed. The feed evaluates conditional logic on the parent submission only. If conditions pass, a series is created for that entry.
- Cadence: The series defines a cadence (every N days/weeks/months/years) plus an end condition (resubmission count or end date).
- Run: On eligible days, the plugin generates a new child entry (a normal GF submission), runs all enabled feeds/notifications for that form, and logs the outcome.
- Track: Parent/child entries maintain bi‑directional links and notes. The series accumulates progress until the end condition is met or the series is canceled.
- Complete: When the final resubmission succeeds, the series is marked complete.
Scheduling, Time Storage & Catch‑Up #
- Time model: All times are stored in UTC and displayed in the site’s configured time zone.
- Resubmission worker: A background worker runs on a frequent heartbeat (every 15 minutes by default), looks for series with occurrences due now or earlier, and processes them in batches. It claims work with a database lock, so a burst of many series due at the same time drains safely without dropping any.
- Failed / overdue check: A separate self‑heal scan runs less often (twice a day by default) as a backstop, re‑attempting failed or overdue occurrences.
- Expected vs. actual run time: An occurrence stores its target time (the original submission’s time of day, advanced by the cadence), but it is only executed on the next worker heartbeat at or after that target. With the default 15‑minute heartbeat, a run lands within ~15 minutes of its target — provided cron is firing reliably (see below).
- Cron must actually fire: The worker relies on WordPress cron. WordPress’s built‑in (“pseudo”) cron only runs when the site receives traffic, so on low‑traffic sites scheduled runs can be delayed or skipped. For dependable timing, configure a real server cron to hit
wp-cron.phpon a schedule (and, on managed hosts such as Kinsta, note the platform cron interval — e.g. ~15 minutes — which sets the practical floor for how often the worker can run). - Missed runs: If the site was down or cron didn’t fire, missed occurrences are picked up on the next run.
- Manual run semantics: Manually processing the next submission does not change the long‑term cadence; the following occurrence remains scheduled relative to its original plan.
Developer Filters (Cron Cadence) #
The cadence of both background jobs is filterable. Values are in seconds; a one‑minute floor is enforced. A change takes effect after the affected event’s next run, when WordPress reschedules it.
rfsfgf_worker_interval(int, default15 * MINUTE_IN_SECONDS) — how often the resubmission worker runs.rfsfgf_failed_check_interval(int, default12 * HOUR_IN_SECONDS) — how often the failed / overdue check runs.rfsfgf_failed_check_first_run(int Unix timestamp, defaulttime()) — the first‑run anchor for the failed / overdue check, used to pin it to a preferred time of day.
// Run the worker every 5 minutes (requires a server cron firing at
// least that often to take full effect).
add_filter( 'rfsfgf_worker_interval', fn() => 5 * MINUTE_IN_SECONDS );
// Run the failed/overdue check every 6 hours, first at 02:00 site time.
add_filter( 'rfsfgf_failed_check_interval', fn() => 6 * HOUR_IN_SECONDS );
add_filter( 'rfsfgf_failed_check_first_run', fn() => strtotime( 'tomorrow 02:00' ) );
Deletion, Deactivation & Uninstall #
- Delete vs. Trash:
- If the source entry or form is deleted permanently (not just trashed), the series stops.
- If either is trashed, nothing changes; the series continues.
- Feed deactivation: If the feed is deactivated after a series has started, that series continues and must be manually canceled if you want it to stop.
- Feed deletion: Deleting the feed cancels any active series associated with that feed.
- Plugin deactivation: While deactivated, no submissions are processed. Upon reactivation, overdue occurrences are picked up on the first run (data remains intact in the database).
- Plugin uninstall: Removes plugin data and tables; the system is cleaned as if the plugin was never installed (aside from the entries it created!).
Data Model & Persistence #
- Storage: Series and state are stored in custom database tables managed by the plugin.
- Parent/Child linkage: Parent entries record series metadata; each child is linked back to its parent. Links are visible via entry notes and used internally for auditing and progress computation.
- Form duplication: Duplicating a form duplicates feeds but not existing series. New series begin only on new submissions that meet the feed’s conditions.
Custom Entry Meta Fields #
The plugin exposes the following entry meta fields for search, filtering, and merge tags.
Parent Entries #
rfsfgf_is_parent_submissionLabel: Is Parent Submission Values:'true'or'false'Filterable:is,isnottotal_resubmissionsLabel: Total Resubmissions Type: Numeric Filterable:is,isnot,>,<,contains
Child / Auto‑submitted Entries #
is_auto_submissionLabel: Is Auto Submission Values:'true'or'false'Filterable:is,isnotresubmission_progressLabel: Resubmissions Progress Format: e.g.,3 of 10or5 until Dec 31, 2025Filterable:is,isnot,contains
Both Parent and Child #
rfsfgf_feed_labelLabel: Feed Label Behavior: Entry‑specific; supports merge tags Filterable:is,isnot,contains
Availability:
- Searchable/filterable in the Gravity Forms entry list
- Usable via custom merge tags in notifications/confirmations
- Accessible via
GFAPIand GF entry‑meta functions or our GravityOps Search plugin - Optional columns in the entry list view
Interaction With Gravity Forms Features (Concepts) #
- Conditional Logic: Evaluated on the parent submission only to determine whether a series starts.
- Calculation Fields: Recomputed on each submission.
- Unique ID & GPPA: Unique ID fields produce a new unique value per resubmission; GPPA re‑hydrates in dynamic mode; Snapshot mode copies stored values, but Unique ID still remains unique for resubmissions.
- Notifications & Feeds: Each resubmission is a normal GF submission, so all active feeds/notifications execute accordingly.
Limitations & Edge Cases #
- No pause state: Series can be canceled, not paused.
- Manual next run: Manually processing a run does not shift the schedule anchor; the next occurrence follows the original cadence.
- No backfills: The system never creates retroactive runs for dates in the past.
- Feed edits: When a series starts, it takes a snapshot of the feed’s configuration (cadence, limits, etc.) and stores it in the series record. Subsequent edits to the feed settings do not retroactively affect active series. Only new series created after the edit will use the new settings.
- Snapshot vs. Dynamic Inputs:
- If Snapshot Entry Inputs Now is enabled, the plugin stores a fixed copy of all entry values at the moment the series starts. Every future resubmission will use these exact values.
- If Snapshot Entry Inputs Now is disabled, the plugin reads the current values from the parent entry in the database at the moment of each resubmission. This allows you to update the parent entry to change values for future runs in that series.
- Parent deletion impact: Deleting the parent stops the series and leaves child entries intact for audit history.
- Validation failures: A failed resubmission does not create an entry; failure is logged. The behavior of the series then depends on its limit type:
- Number-based limits: A failure does not increment the
total_resubmissionscount. The series will reschedule the attempt for the next interval and continue until the required number of successful (or skipped by conditional logic) resubmissions is reached. This effectively “retries” the failed slot at the next scheduled time. - Date-based limits: A failure does not increment the count, but the end date remains fixed. The series will be deleted once the end date is reached, regardless of whether any scheduled runs failed.
- Number-based limits: A failure does not increment the
Design Rationale (Why It Works This Way) #
- Reliability first: A frequent, lock‑claimed worker plus a less‑frequent self‑heal scan ensures bursts drain safely and that missed runs (downtime, cron hiccups) are caught up on the next run.
- Auditability: Parent/child linkage, entry notes, and explicit failure states guarantee traceability across the series lifecycle.
- Operational safety: Deactivation preserves data and catches up later; uninstall removes all traces for clean hand‑offs.