Hop Survey
Current Version: 1.0.0- EE7
-
Overview
Invite-only surveys for ExpressionEngine: campaigns, unique recipient links, email delivery, open tracking, reminders (including a second reminder), completion status, and CSV exports. Survey answers are stored in Freeform.
Hop Survey includes a starter Freeform form (
hop_survey_default) when Freeform is installed. We recommend Test Send (Single) on each campaign’s Manage page before your first live send.The survey page is not linked from site navigation — you distribute invite URLs by email (or any channel you choose).
-
System Requirements
- ExpressionEngine 7+
- Solspace Freeform 3.0.0+ (
freeform_nextadd-on; installed and licensed — not Freeform Classic) - PHP 8.1+ (match your EE install)
- Outbound mail: Hop Survey → Email Settings (SMTP host, port, and from address are required before invites, reminders, or test mail will send). Per-environment overrides: Configuration (
hop_survey_mail).
-
Installation
- Unzip the folder to a temporary location on your local hard drive.
- Upload the
hop_surveyfolder to/system/user/addons/on the server. - Navigate to Add-ons in your EE control panel and install the add-on.
Install also seeds the
hop_survey_emailstemplate group (invite,reminder,reminder_2,results) from bundled HTML in the add-on. Existing templates are never overwritten on upgrade.When Freeform Next is installed (or added later before you first open Hop Survey in the CP), Hop Survey creates a starter Freeform form
hop_survey_default— one required yes/no question you can customize or replace. It is seeded once per site; if you delete that form in Freeform, Hop Survey will not recreate it. -
First-time Setup
1. Freeform form
Hop Survey includes a starter form
hop_survey_defaultwhen Freeform is available — one required yes/no question (“Are you satisfied with our work?”). Use it as-is, customize it in Add-ons → Freeform, or build your own form and assign it under campaign Settings.If you installed Hop Survey before Freeform, open Add-ons → Hop Survey once after installing Freeform so the default form can be created.
2. Campaign and recipients
In Add-ons → Hop Survey, create a campaign (New Campaign), assign a Freeform form under Settings (the default
hop_survey_defaultor your own), and add recipients on Recipients (manually or CSV import — see Control Panel guide). Optional email-me-a-copy toggle: Control Panel guide.3. Front-end survey page
Create a survey page (default
/survey) and wrap the form with{exp:hop_survey:gate}. Full setup: Survey page. Custom path: Configuration (hop_survey → survey_path). Tag index: Template tags.Local preview of Freeform forms without invites: Configuration (
hop_survey_test/{exp:hop_survey:test_gate}).4. Email settings
Configure Hop Survey → Email Settings, or
hop_survey_mailin Configuration for environment-specific SMTP. Confirm delivery with Hop Survey → Send Test Email.5. Test the full survey flow (recommended)
Before your first live send, use Campaign → Manage → Test Send (Single). Enter your own email address and send a preview invite. The email is marked
[TEST]and does not update recipient sent dates or status in Recipients.Open the invite link, complete the survey, and confirm templates, SMTP,
{survey_path}, and the assigned Freeform form behave as expected. See Control Panel guide.6. Go live
When you are satisfied with the test flow, send invites from Manage → Send survey.
-
Survey Page
Create a template at
/survey(or yoursurvey_path). Hop Survey builds invite links as{site_url}/{survey_path}?invite=TOKEN— for examplehttps://yoursite.example/survey?invite=…. You do not add the query string to the template.Hop Survey does not create a front-end template group for you. Put
{exp:hop_survey:gate}on the ExpressionEngine template that serves that path. The template group does not have to be namedsurvey.Local form preview without a real invite (developers only): Configuration (
hop_survey_test).Token resolution
The gate resolves the invite token in this order (first valid match wins; invalid values are skipped):
- POST field
hop_survey_invite(hidden input — required for multipage Freeform posts) - GET query
?invite=TOKEN - Request-scoped session cache from earlier in the same request (not durable across page loads)
Multipage continuity depends on the hidden
hop_survey_invitefield and Freeformreturn="{survey_return}"(which re-attaches?invite=), not on long-lived session storage.Default setup
- Create a template that answers
/survey(commonly template groupsurvey, templateindex). - Wrap the survey UI in
{exp:hop_survey:gate}…{/exp:hop_survey:gate}. - Inside the gate, render your Freeform form when
{status}isok. - Set Freeform’s
returnto{survey_return}so multipage posts stay on the configured path with the invite token. - Include a hidden field so partial progress and completion stay tied to the invite:
{exp:hop_survey:gate} {if status == "ok"} {if freeform_form_exists == "yes"} {exp:freeform_next:form form="{freeform_handle}" return="{survey_return}" submission_token="{submission_token}"} <input type="hidden" name="hop_survey_invite" value="{token}"> {!-- render Freeform fields normally --} {/exp:freeform_next:form} {/if} {if:elseif status == "completed"} … thank you … {if:elseif status == "expired"} … expired message … {if:else} … invalid / missing token … {/if} {/exp:hop_survey:gate}For multipage resume, Freeform uses statuses
partially_complete/complete— see Reporting.Gate
statusvaluesUse these exact strings in
{if status == "…"}conditionals:Value Description okValid, active invite. Show the survey form. missingNo valid token in the request (no usable POST, ?invite=, or request cache).invalidToken present but not found (unknown or deleted invite). expiredInvite expires_atis in the past.completedRecipient already submitted the survey. Gate
statusis not the same as the Recipients table Status column. Funnel stages such as “Email opened” appear only in the CP and CSV exports — see Reporting.Gate variables
Variable Values Description {status}ok,missing,invalid,expired,completedInvite validity for template branching. {freeform_form_exists}yes,noWhether the campaign’s assigned Freeform form exists on this site. {show_send_results_option}yes,noWhether this campaign shows the Freeform survey_send_resultscheckbox. Alwaysnoon{exp:hop_survey:test_gate}.{token}string Invite token from POST, URL, or request cache. {email}string Recipient email from the invite row. {name}string Full Name from the invite row. {first_name}string First word of Name (text before the first space). {last_name}string Remainder of Name after the first space (empty for a single word). {campaign_title}string Campaign title. {freeform_handle}string Freeform form handle assigned to the campaign. {submission_token}string Freeform submission token for resume / partial progress. {survey_path}string Configured survey URL path (no leading slash). {survey_return}string Path plus ?invite=for Freeformreturn=.Full tag index (including email placeholders): Template tags.
Scale endpoint labels
Use
{exp:hop_survey:scale_ends}on numeric/select scale questions so the survey page shows the same low/high labels as invite emails. Labels are parsed from Freeform field instructions (for example0 = Not at all | 5 = Very satisfiedor0 = Not great 5 = Really great). If instructions omit labels, defaults are Not at all and Very satisfied.Self-closing (renders the default markup):
{exp:hop_survey:scale_ends instructions="{field:instructions}"}Pair form when you need
{low}and{high}in your own markup:{exp:hop_survey:scale_ends instructions="{field:instructions}"} <span>{low}</span><span>{high}</span> {/exp:hop_survey:scale_ends}Optional email-me-a-copy checkbox
When enabled for the campaign, Hop Survey shows the Freeform checkbox with handle
survey_send_resultson this page automatically — render Freeform fields normally. When disabled, Hop Survey hides that field. How to add the field and use the Settings toggle: Control Panel guide.Checkbox “Other” with custom text
When a checkbox-group question includes an Other option plus free text, model both parts in Freeform — Hop Survey does not merge custom POST fields for you.
- Checkbox group — Include an option with value
other(label can be “Other (please specify)”). - Detail field — Add an optional textarea (or text field) on the same page, e.g. handle
my_question_other. - Survey template — Render the detail field with Freeform tags (
{field:render_input}or{field:render}) so partial progress, resume, exports, and submissions all use Freeform’s normal storage. Use JavaScript only to show or hide the detail block when Other is checked.
Do not use a plain HTML
<textarea name="…">that is absent from the Freeform form layout — Freeform will not persist it.Custom checkbox markup for the group is fine; the detail field must still be a real Freeform field on the layout.
Custom invite URL path
Config syntax and validation rules: Configuration (
hop_survey → survey_path). Place{exp:hop_survey:gate}on the EE template that serves that path:survey_pathTypical EE template survey(default)survey/indexclient-surveyclient-survey/indexfeedback/takefeedback/takeIf Manage shows a survey-path warning before send, the ExpressionEngine template for that path is missing or only on disk — see Control Panel guide and Configuration.
- POST field
-
Control Panel Guide
Task Control Panel path List campaigns Add-ons → Hop Survey Create campaign New Campaign Add recipients Campaign → Recipients Import recipients CSV Campaign → Recipients (columns email,name— see below)Assign Freeform form / expiry / pin / internal notes / email-me-a-copy Campaign → Settings Send / test / export CSV Campaign → Manage (Send survey, Send reminder, Send second reminder, Test Send, export) Edit email templates Hop Survey → Email Templates (invite, reminder, second reminder, and results — see Template tags) SMTP settings Hop Survey → Email Settings (Configuration for hop_survey_mailoverrides)Send test email Hop Survey → Send Test Email Email open webhook Hop Survey → Email Open Webhook (Configuration for signing secret) Test Send before going live
We strongly recommend Manage → Test Send (Single) before Send survey. Enter your own email, send the preview, open the invite link, and complete the survey. Test emails are marked
[TEST]in the subject and do not update recipient sent dates or status in Recipients — use them to verify SMTP, email templates,{survey_path}, and the assigned Freeform form end-to-end.Default starter form
Hop Survey seeds
hop_survey_defaultin Freeform when the add-on is installed (or the first time you open Hop Survey in the CP after Freeform is installed). It includes one yes/no question. Customize or replace it in Add-ons → Freeform, or assign a different form under campaign Settings. Deletinghop_survey_defaultin Freeform will not recreate it.Recipients CSV import
Import accepts two columns:
email,name(lowercase headers). A header row is optional. The Recipients for import export type matches this format — see Reporting.Email-me-a-copy on survey forms
- In Freeform, add a checkbox field with handle
survey_send_results(exact handle) to the campaign’s form. - Until that field exists on the selected form, the campaign Settings → “Email me a copy” on survey forms toggle stays disabled.
- Set the toggle to Yes to show the checkbox on the survey page (Hop Survey shows or hides it automatically). No hides it.
- When a respondent checks the box and submits, Hop Survey can send the results-copy email (
{responses}in the results template).
Reminders
Manage has Send reminder and Send second reminder:
- Send reminder — Recipients who have not opened the invite email or the survey.
- Send second reminder — Recipients who already received the first reminder and still have not opened the email or survey.
Edit HTML under Hop Survey → Email Templates (REMINDER and SECOND REMINDER). Subjects can also be overridden with
reminder_subjectandreminder_2_subject— see Configuration.Pin, notes, and re-opening a completed invite
- Pin campaign (New Campaign or Settings) — Pinned campaigns stay at the top of the Campaigns list and in the add-on side navigation.
- Internal notes (Settings) — Staff-only. Not shown to recipients or included in survey emails.
- Allow submit again (Recipients → edit a completed recipient) — Clears completion so that invite link can be used again.
Survey path readiness
Before a bulk send, review the Manage confirmation checklist. A survey-path warning means no ExpressionEngine template matches
survey_path(or it exists only on disk). Fix the front-end template or correctsurvey_pathin Configuration so invite links do not 404. The check does not HTTP-probe the live URL and does not block send. - In Freeform, add a checkbox field with handle
-
Configuration
All optional
config.phpkeys are documented below. Use the Control Panel for day-to-day settings where noted; these blocks are for per-environment overrides. Other documentation sections link here instead of repeating examples.hop_survey → survey_path
Custom invite URL path (default
survey). Hop Survey appends?invite=to invite links, for example/survey?invite=….$config['hop_survey']['survey_path'] = 'client-survey'; // invite links: /client-survey?invite=… // or a nested template: $config['hop_survey']['survey_path'] = 'feedback/take'; // invite links: /feedback/take?invite=…Rules:
- No leading or trailing slash
- Letters, numbers,
_,-, and/only - Invalid or empty values fall back silently to
survey
Put
{exp:hop_survey:gate}on the EE template that serves the configured path — template examples: Survey page. Manage readiness warnings: Control Panel guide.hop_survey_mail
Configure Hop Survey → Email Settings in the Control Panel first. Override SMTP credentials and mail subjects per environment without storing secrets in the database. Keys match the CP form:
smtp_host,smtp_port,smtp_user,smtp_pass,smtp_crypto,from_email,from_name,subject,reminder_subject,reminder_2_subject,results_subject. Unset or blank values keep the CP-saved setting.smtp_cryptovalues:tls(STARTTLS, typical port 587),ssl(typical port 465), or empty for unencrypted.$config['hop_survey_mail'] = [ 'smtp_host' => 'smtp.example.com', 'smtp_port' => '587', 'smtp_user' => '[email protected]', 'smtp_pass' => '…', 'smtp_crypto' => 'tls', 'from_email' => '[email protected]', 'from_name' => 'Example Surveys', ];hop_survey_test
Enable survey preview URLs on local environments only (
ENVmust belocal). Map test variants to your Freeform form handles, then serve{exp:hop_survey:test_gate}on templates such as/survey_test/single.$config['hop_survey_test'] = [ 'enabled' => true, 'forms' => [ 'single' => 'your_single_page_handle', 'multiple' => 'your_multipage_handle', ], ];{exp:hop_survey:test_gate variant="single"} {if status == "ok"} … Freeform form … {if:else} <h2>{status_heading}</h2> <p>{status_message}</p> {/if} {/exp:hop_survey:test_gate}Tag parameter:
variant="single"orvariant="multiple"(aliases:form=, or URL segment). Keys must matchhop_survey_test.forms.Test tokens use the
hst_prefix plus a configured Freeform handle fromhop_survey_test.formsand do not mark production invites complete.The test gate does not output
{survey_path},{survey_return},{freeform_form_exists}, or{submission_token}. Hardcode Freeformreturn=(and path) in test templates. The optionalsurvey_send_resultscheckbox is not shown on test previews.Test gate
statusvaluesValue Description okTest surveys enabled and form loaded. disabledhop_survey_test.enabledis false orENVis notlocal.invalidMissing or empty variantparameter.missing_formVariant not configured, or configured Freeform handle not found on this site. Test-only variables:
{variant},{is_test}(alwaysyes),{status_heading},{status_message}. Tag index: Template tags.hop_survey_email_open_webhook → webhook_secret
Optional Svix signing secret for SMTP provider
email.openedwebhooks. Register the endpoint URL under Hop Survey → Email Open Webhook in the Control Panel.$config['hop_survey_email_open_webhook'] = [ 'webhook_secret' => 'whsec_…', ];Without a secret, the endpoint returns
503. Complements the{email_open_pixel}GIF in invite and reminder emails — see Template tags.Email open tracking:
{email_open_pixel}loads the open_pixel ACT GIF.{invite_url},{survey_button}, and answer links in{first_question_embed}use the survey_click ACT endpoint so a recipient click still records an open when clients block remote images. -
Reporting
Survey answers live in Freeform. Hop Survey tracks invite delivery and engagement in the CP Recipients table and CSV exports. Use Manage → Full campaign data to export answers joined with invite tracking.
Recipients Status column
Highest-priority label wins. Terminal states override funnel stages.
Status Description Completed Survey submitted ( completed_atset).Expired Invite past expires_at.Invalid Token not found in the database. Missing token Invite row has no token. Partially complete Multipage survey started but not finished (Freeform submission partially_complete).Survey opened Recipient loaded the survey page ( survey_visited_at).Email opened Invite email opened ( opened_atvia pixel or webhook).Second reminder sent Second reminder email sent. Reminder sent First reminder email sent. Email sent Invite email sent ( sent_at).Not emailed On the list but invite not yet sent. These labels are not the same strings as gate
{status}(ok,missing,invalid,expired,completed).Export types (Manage page)
Type Description Full campaign data Invite metadata plus Freeform submission answers (answers live in Freeform; this export joins them to each invite). Recipients & tracking Invite rows with sent, opened, and completed timestamps (no survey answers). Recipients for import Two columns only: email,name(lowercase headers), matching the Recipients CSV import format. Import steps: Control Panel guide.Invite CSV columns
Full / recipients exports include: Email, Name, Campaign, Token, Invite URL, Status, Sent At, Opened At, Reminded At, Survey Visited At, Partial Started At, Completed At, Expires At, Submission ID.
Freeform submission statuses
Hop Survey creates these Freeform status handles on install when missing:
Handle Description partially_completeApplied while a multipage survey is in progress. completeApplied when the respondent submits the final page. Partial answers are saved as Freeform submissions. The invite row stores
submission_idand page index so returning visitors can resume (use{submission_token}on the Survey page). -
Troubleshooting
Common checks
- Invites or reminders not sending — Confirm Hop Survey → Email Settings (SMTP host, port, and from address), then use Send Test Email.
- First campaign send — Use Manage → Test Send (Single) before Send survey to preview the full invite and survey flow without affecting recipient status.
- Second reminder has no recipients — Recipients must have received the first reminder and still not opened the email or survey.
- Survey path warning on send — Create an ExpressionEngine template that matches
survey_path, or correct the path in Configuration. - Gate shows
missing,invalid, orexpired— Confirm the invite URL still includes?invite=, and that the invite has not expired or already completed. See Survey page. - Default form missing — If Hop Survey was installed before Freeform, open Add-ons → Hop Survey once after installing Freeform. Hop Survey creates
hop_survey_defaultonly once; if you deleted it, create or assign another form under campaign Settings. - Answers missing from Hop Survey exports — Answers live in Freeform; use Manage → Full campaign data to join them with invite tracking. See Reporting.
Still stuck? Contact [email protected].
-
Updating
- Download a new version from this website, and extract on your hard drive.
- Upload the new files to your system’s addon directory.
-
Uninstalling
- Navigate to Add-ons → Hop Survey and uninstall the module.
- Remove
system/user/addons/hop_survey/from the server.
Uninstall does not delete the
hop_survey_emailstemplate group or the seeded Freeform formhop_survey_default. Remove those in Design → Templates and Add-ons → Freeform if you no longer need them.