The Best Moving Form Ever — Setup Guide
A complete technical reference for embedding the MoveRight intake form on any moving company website. Covers token generation, all embed options, the full API field reference, attribution tracking, and multi-step form variants.
Overview
MoveRight's embeddable intake form can be added to any moving company website — whether built on WordPress, Wix, Squarespace, Webflow, or custom HTML. When a customer submits the form, a new lead is automatically created in MoveRight with full attribution data, address information, and an optional virtual estimate appointment.
The system has three components: a tracking script (captures Google Ads click IDs, UTM parameters, and referrer data across every page), an embeddable form widget (a single div element you place on your quote/booking page), and a secure token (generated inside MoveRight to authenticate form submissions).
Generate Token
Create a lead source token in MoveRight admin. This encodes your zone, referral source, and customer type.
Add Script
Paste one script tag in your site's <head>. It captures attribution data across all pages.
Embed Form
Add a single <div> element to your quote page with your token URL.
Step 1 — Generate a Lead Source Token
Each form embed uses a unique token that identifies which MoveRight zone (franchise location) should receive the leads, what referral source to tag them with, and what customer type to assign. The token is generated inside the MoveRight admin panel.
- 1
Open the Marketing page
In MoveRight, log in as an admin. Click Business → Marketing in the left sidebar.
- 2
Find Lead Sources
Scroll to the very bottom of the Marketing page to the "Lead Sources" section.
- 3
Click "Add Lead Source"
A panel will open where you configure the token defaults.
- 4
Configure the token
- Origin (optional) — human label for this form, e.g. "Web Form", "Google Ads Landing Page"
- Source (optional) — how the customer heard about you, e.g. "Google Ads", "Organic Search", "Yelp"
- Customer Type (optional) — e.g. "Residential" or "Commercial"
- Name — auto-generated, or enter your own for easy identification
- 5
Copy your POST URL
Click Review → Create. In the right panel click Actions → View Instructions. Copy the POST URL — it looks like:
https://moveright.app/api/intake/token?token=lead_LONGTOKEN
maxUses limit you can set to cap submissions.
Step 2 — Add the Tracking Script
Add this script tag to the <head> of every page on your website — not just the quote page. This is what captures Google click IDs, UTM parameters, referral data, and session information before a customer ever reaches your booking form.
<script
src="https://intake.cdn.moveright.app/app/main.js"
id="moveright-script"
type="text/javascript"
></script> The script is lightweight and non-blocking. It does not render anything on the page — it only reads and stores session data to localStorage for later submission with the form.
What the script captures
Google Click ID (gclid)
Captured on first touch and stored. Automatically sets "Google Ads" as the referral source.
UTM parameters
utm_source, utm_medium, utm_campaign, utm_term, utm_content — captured from the first landing URL.
Referring domain
The site that sent the visitor (e.g. google.com, yelp.com). Mapped to human-readable referral sources.
First page visited
The URL of the landing page — stored as intake.initialWebPage on the job.
PostHog session ID
If PostHog is installed, the session_id and session_replay_url are captured for session replay links in the lead.
Google Ads source
gad_source parameter captured and stored alongside gclid.
Step 3 — Embed the Form
On your quote or booking page, add the following div element. The script tag from Step 2 will automatically detect it and render the full multi-step form inside it.
Recommended: Best Moving Form Ever
<div
id="best-moving-form-ever-v1"
url="https://moveright.app/api/intake/token?token=lead_YOURTOKEN"
redirect="https://yourmovingcompany.com/thank-you"
name="Your Moving Company Name"
privacylink="https://yourmovingcompany.com/privacy"
termslink="https://yourmovingcompany.com/terms"
consentemail="[email protected]"
primarycolor="#6ab33d"
></div> Replace lead_YOURTOKEN with the token you generated in Step 1.
Full production example (used by You Move Me)
<!-- In <head> on every page -->
<script
src="https://intake.cdn.moveright.app/app/main.js"
id="moveright-script"
type="text/javascript"
></script>
<!-- On your quote/booking page only -->
<div
id="best-moving-form-ever-v1"
url="https://moveright.app/api/intake/token?token=lead_YOURTOKEN"
redirect="https://yourmoveme.com/thank-you"
name="You Move Me, Inc."
privacylink="https://yourmoveme.com/privacy"
termslink="https://yourmoveme.com/terms"
consentemail="[email protected]"
></div> Form Variants
MoveRight offers three embeddable form widget IDs. Choose based on your use case:
best-moving-form-ever-v1 Recommended The modern multi-step wizard form. Always renders the full "default" variant with all steps. Best for dedicated quote/booking pages where you want the highest quality lead data.
Steps
- → Move Type (dwelling)
- → Move Size (bedrooms)
- → Origin address
- → Destination address
- → Move description + photo upload
- → Move date
- → Preferred callback time
- → Contact info (name, phone, email, SMS consent)
moving-form-ymm A/B Test Same multi-step form but with automatic A/B testing enabled. 80% of visitors get one of the four multi-step variants; 20% get a simple single-step form. Variant is sticky per visitor via localStorage. Used on youmoveme.com franchise sites.
Steps
- → Auto-routes between: default, contact-info-first, default-slim, contact-info-first-slim variants
moving-form-moveright Legacy Single-page form with all fields visible at once. No multi-step wizard. Useful for simple integrations or when you want a compact form layout.
Steps
- → Origin area code / zip
- → Destination area code / zip
- → Move date
- → Name, phone, email
- → About your move (textarea)
- → Contact method preference
Multi-step variant step configurations
The best-moving-form-ever-v1 form supports four step-order variants. Only default is used when you embed via id="best-moving-form-ever-v1". The A/B variants are used by moving-form-ymm.
| Variant | Step order | Notes |
|---|---|---|
| default | move_type → move_size → origin → destination → move_description → move_date → contact_time → contact_info | Contact info collected last. All steps. |
| contact-info-first | contact_info_collect → move_type → move_size → origin → destination → move_description → move_date → contact_time | Lead captured on step 1 before move details. |
| default-slim | move_type → move_size → origin → destination → contact_info | Removes description, date, and callback time steps. |
| contact-info-first-slim | contact_info_collect → move_type → move_size → origin → destination | Both contact-first and slim combined. |
All Div Attributes
These HTML attributes are read from the <div> element at runtime. They configure the form's behavior, branding, and consent text. All are optional except url.
best-moving-form-ever-v1 & moving-form-ymm
| Attribute | Required | Description |
|---|---|---|
| url | Yes | The full POST endpoint URL including your token query parameter. Format: https://moveright.app/api/intake/token?token=lead_YOURTOKEN |
| redirect | Yes | URL to redirect the customer to after successful form submission, e.g. https://yoursite.com/thank-you |
| name | No | Your legal company name, used in the SMS consent text. E.g. "ABC Movers LLC". |
| privacylink | No | URL of your privacy policy page, linked from the consent text. |
| termslink | No | URL of your terms of service page, linked from the consent text. |
| consentemail | No | Email address shown in the consent disclosure, e.g. [email protected]. |
| submittext | No | Custom text for the submit button. Defaults to "Get My Official Estimate". |
| primarycolor | No | Hex color code for accent/branding elements, e.g. #a3c617. Defaults to brand green. |
| secondarycolor | No | Secondary brand color for UI elements. |
| defaultlanguage | No | Language code for the form UI. Supports "en" (English) and "fr" (French). Defaults to "en". |
| enablecamera | No | "true" to show the in-app camera button for photo capture on mobile. Defaults to false. |
moving-form-moveright (legacy)
| Attribute | Required | Description |
|---|---|---|
| url | Yes | POST endpoint URL with token. |
| redirect | No | URL to redirect to after submission. |
| consenttext | No | Full HTML consent disclosure shown below the form. |
| submittext | No | Submit button text. Defaults to "Get Estimate". |
| contactmethodenabled | No | "true" to show the "Preferred contact method" field. |
| contactmethodlabel | No | Custom label for the contact method field. |
| contactmethodplaceholder | No | Placeholder text for the contact method field. |
| aboutmoveplaceholder | No | Custom placeholder text for the "About your move" textarea. |
API Field Reference
If you're submitting leads programmatically (not using the embeddable widget), POST a JSON body to your token URL. All fields are optional unless noted.
Minimal example (cURL)
curl -X POST \
"https://moveright.app/api/intake/token?token=lead_YOURTOKEN" \
-H "Content-Type: application/json" \
-d '{
"givenName": "Jane",
"familyName": "Smith",
"email": "[email protected]",
"phone": "555-867-5309",
"moveDate": "2026-09-15",
"areaCode": "90210",
"destinationAreaCode": "94103",
"customerNotes": "Have a piano and two dogs",
"waitForResponse": true
}' Customer fields
| Field | Type | Description |
|---|---|---|
| name | string | Full name — parsed into givenName/familyName automatically. Supports "First Last", "Last, First", etc. |
| givenName | string | Customer first name. Used if name is not provided. |
| familyName | string | Customer last name. Used if name is not provided. |
| phone | string | Customer phone number. Normalized and used for deduplication against existing customers. Example: 555-867-5309 |
| string | Customer email. Validated and used for deduplication. Example: [email protected] | |
| smsOptIn | "true"|"false" | Whether the customer consented to SMS notifications. Stored on the customer record. |
| customerNotes | string | Free-text notes from the customer about their move. Shown in the new-lead notification. |
| contactmethod | string | Preferred contact channel: "Phone", "Email", "Text", or "ASAP". "ASAP" triggers an immediate callback request flag. |
Move details fields
| Field | Type | Description |
|---|---|---|
| moveDate / timeline | string (date) | Preferred move date. Parsed by dayjs; year defaults to current year if omitted. Stored as YYYY-MM-DD. Example: 2026-09-15 |
| areaCode | string | Origin zip/postal code or full address. Used to resolve the serving franchise zone. Example: 90210 |
| destinationAreaCode | string | Destination zip/postal code or full address. Used for distance calculation. Example: 94103 |
| originAddress | string | Full origin street address. Preferred over areaCode when provided. Example: 27 E Court St, Doylestown, PA 18901 |
| destAddress | string | Full destination street address. Preferred over destinationAreaCode when provided. |
| homeType | string | Origin dwelling type: "House", "Apartment", "Condo", "Townhouse", etc. Stored as startLocation.dwellingType. |
| homeSize | string (int) | Number of bedrooms as a string. E.g. "3". Stored as startLocation.bedrooms. |
| stairs | string (int) | Number of flights of stairs at origin. Stored as startLocation.stairs. |
| parkingInformation | string | Parking notes for the origin location. E.g. "Street parking only, 30-min limit". |
| callBackTime | string | Preferred callback time as free text. E.g. "Monday morning". Shown in the lead notification. |
| virtualEstimateTime | ISO 8601 | Requested start time for a virtual estimate appointment. If valid, a calendar event is created and immediately booked. Example: 2026-09-20T14:00:00-05:00 |
| virtualEstimateAssetId | UUID | Optional — ID of the Estimator to assign to the virtual estimate calendar event. |
Attribution & metadata fields
| Field | Type | Description |
|---|---|---|
| contactOrigin | string | How this lead originated. E.g. "Web Form", "OBE", "Email", "Call Rail". Falls back to tokenMetadata.origin. Stored as jobOrigin. |
| referralSource | string | How the customer heard about the company. E.g. "Google Ads", "Yelp". Falls back to tokenMetadata.source. |
| customerType | string | "Residential" or "Commercial". Falls back to tokenMetadata.customerType. Stored as jobType. |
| gclid | string | Google Click ID. Triggers "Google Ads" as the referral source. Example: EAIaIQobChMI... |
| initialWebPage | URL | URL of the landing page where the session started. Stored as intake.initialWebPage. |
| submittedFrom | URL | URL of the page the form was submitted from. Stored as intake.submittedFrom. |
| referrer | string | HTTP referrer / referring domain. Stored as intake.referrer. |
| sourcedata | JSON string | Full SourceData object (see Attribution section). Includes UTM, gclid, PostHog session, pageview count, etc. |
| legal_agreement | string | Text or identifier of the legal/terms agreement the customer acknowledged. Stored in job metadata as legalAgreement. |
Response control fields
| Field | Type | Description |
|---|---|---|
| waitForResponse / respondWithData | boolean | When true, the endpoint returns a JSON body with job.id, job.code, userId, zone, etc. instead of redirecting. Use this when you need the job ID for follow-up API calls. |
| generateIntakeToken | boolean | When true (and respondWithData is true), the response includes an intake token (intk_ prefix) instead of a leadsource token. Intake tokens identify a specific job+customer for post-submission flows. |
| URL_REDIRECT | URL | Fallback redirect URL if no redirectURL is configured on the zone. All job details are appended as query parameters (fn, ln, phone, email, origin, destination, desiredDate, job, code, user). |
Attribution & Source Data
The tracking script automatically captures session data and attaches it to the form submission as a JSON-serialized sourcedata field. You don't need to do anything — the script handles this automatically. Below is the full SourceData object shape for reference when building custom integrations.
// sourcedata field — JSON-serialized and included automatically by the widget
{
"session_id": "ph_session_abc123", // PostHog session ID
"session_replay_url": "https://...", // Session replay link (PostHog)
"start_timestamp": 1748000000, // Unix seconds — session start
"end_timestamp": 1748000240, // Unix seconds — session end
"entry_current_url": "https://...", // First page visited (landing page)
"entry_pathname": "/moving-quote",
"exit_current_url": "https://...", // Page where form was submitted
"exit_pathname": "/get-a-quote",
"entry_utm_source": "google", // UTM source
"entry_utm_campaign": "spring-move-2026", // UTM campaign
"entry_utm_medium": "cpc",
"entry_utm_term": "local movers near me",
"entry_utm_content": "hero_banner",
"entry_referring_domain": "google.com", // Referring domain → "Google Search"
"entry_gclid": "EAIaIQobChMI...", // Google Click ID → "Google Ads"
"entry_gad_source": "1",
"pageview_count": 4, // Pages visited before submitting
"channel_type": "search", // "social" → "Organic Social"
"session_duration": 187, // Session length in seconds
"submittedFrom": "https://..." // Page the form was on
} How referral source is determined
| Signal | Resolved as |
|---|---|
| entry_gclid present | Google Ads |
| entry_referring_domain = "google.com" | Google Search |
| entry_referring_domain = "yelp.com" | Yelp |
| channel_type = "social" | Organic Social |
| entry_utm_source present | Value of utm_source |
| referralSource body field | Manual override (any value) |
| tokenMetadata.source | Default from token (fallback) |
Response Shape
By default, the API responds with a 302 redirect to your redirect URL. To receive JSON instead, include "waitForResponse": true in the POST body.
JSON response (waitForResponse: true)
{
"job": {
"id": "7af1bc10-e356-11ef-8bf8-7dd6e69e92c7", // Job UUID (for API calls)
"code": "TLED-GVQ-XZN" // Human-readable job code
},
"userId": "354dfbd0-8d1b-11ed-9001-89ed3e4c1f5d", // Customer user UUID
"domainZone": "ymm", // Top-level brand zone
"zone": "231e6b81-8018-11ed-82aa-a382b51465c0", // Franchise zone (UUID)
"redirectURL": "https://yoursite.com/thank-you?job=7af1...&code=TLED...",
"token": "lead_xxx:secret", // New token (if generateIntakeToken)
"respondWithData": true,
"jobInput": { ... }, // Echo of parsed input (for debugging)
"error": undefined // Present only on failure
} Redirect query parameters appended to your redirect URL
| Parameter | Value |
|---|---|
| zone | Resolved franchise zone UUID |
| fn | Customer first name |
| ln | Customer last name |
| phone | Customer phone |
| Customer email | |
| origin | Origin address (truncated to 100 chars) |
| destination | Destination address (truncated to 100 chars) |
| desiredDate | Preferred move date |
| job | Created job UUID |
| code | Created job short code (e.g. TLED-GVQ-XZN) |
| user | Created user UUID |
Error cases
All error cases return HTTP 200 with an error field in the JSON.
- No token: error = "No token provided" (no ?token query param)
- Invalid token: error = "Invalid Token" (expired, wrong type, maxUses exceeded)
- Zone not found: empty IntakeResponse — no job or user created
- Lead creation failure: error = error message, no job/user fields
Advanced Usage
Check service area coverage before showing the form
Before rendering the form, you can check whether a given zip code is serviced by your franchise:
GET /api/intake/token/:areaCode?zone=ZONE_UUID
// Response:
{
"serviced": true,
"zoneId": "231e6b81-...",
"franchiseId": "ymm-kc",
"zip": "64108",
"selfSurveyEnabled": false
} Virtual estimate scheduling
To let customers pick a virtual estimate time slot before submitting, fetch available windows first:
POST /api/intake/estimator-availability
{
"token": "lead_YOURTOKEN",
"origin": "90210", // optional — narrows to correct zone
"destination": "94103" // optional
}
// Returns: available Estimator time windows for the next 48 hours Then include virtualEstimateTime in the form submission body with the selected ISO 8601 datetime. A calendar event is automatically created and booked in MoveRight.
Session config (for custom form builds)
If you're building a fully custom form (not using the embeddable widget), you can fetch the zone's configuration:
POST /api/intake/validate
{
"token": "lead_YOURTOKEN",
"origin": "90210", // optional
"destination": "94103" // optional
}
// Returns: { isValid: true, googleMapsKey, businessName, largeMoveSize,
// largeMoveEstimateMethod, dockAddress, dockCoordinates,
// formattedBusinessHours, ... } Large move routing
Jobs where the origin has more bedrooms than intake.largeMoveSize (default: 3) are flagged as large moves. The intake.largeMoveEstimateMethod setting controls whether they get routed to a virtualEstimate or onSiteEstimate calendar event type.
Long-distance zone routing
If the move distance exceeds intake.distanceLimit (kilometers), the job is automatically reassigned to the intake.interstateZone rather than the local franchise zone. Contact MoveRight to configure these values for your account.
Zip-Only Entry Form
On your homepage or other pages, you can build a simple two-field form (origin zip + destination zip) that redirects to your full booking page with those values pre-filled. The full form picks them up and skips the location steps.
<!-- Simple ZIP entry form — on homepage or any page -->
<form onsubmit="handleZipForm(event)">
<input type="text" id="origin-zip" placeholder="Moving from (ZIP)" />
<input type="text" id="dest-zip" placeholder="Moving to (ZIP)" />
<button type="submit">Get a Quote</button>
</form>
<script>
function handleZipForm(e) {
e.preventDefault();
const origin = document.getElementById('origin-zip').value;
const dest = document.getElementById('dest-zip').value;
// Redirect to your booking page with pre-filled values
window.location.href = '/get-a-quote?origin=' + encodeURIComponent(origin)
+ '&destination=' + encodeURIComponent(dest);
}
</script>
<!-- On your /get-a-quote page, the MoveRight form reads
?origin and ?destination from the URL automatically --> The embeddable form automatically reads ?origin and ?destination query parameters from the URL and pre-populates the origin and destination fields, skipping those steps for the customer.
FAQ
Can I embed the form on multiple pages on my website?
Yes, but you should only have the form div on one page (your booking/quote page). The tracking script should be on every page. Having multiple form divs on different pages is supported but unusual — each will render independently.
Can I use a different token for different campaigns?
Yes — and this is the recommended approach. Create one token per marketing channel or campaign. Name them descriptively (e.g. "Google Ads Form", "Yelp Landing Page") and each will tag the resulting leads with the appropriate source and origin values.
What happens when a returning customer submits the form?
MoveRight deduplicates by email and normalized phone number within your zone. If a matching customer is found, the new lead is associated with their existing customer record. A new job is still created — no data is lost.
Does the form work on mobile?
Yes. The form is fully responsive and mobile-optimized. The multi-step wizard format is specifically designed for mobile UX. Use enablecamera="true" to allow customers to upload photos directly from their phone camera.
Can I style the form to match my brand?
Yes. Use the primarycolor attribute to set your brand color. Additional CSS customization is possible — contact MoveRight support for guidance on CSS custom properties available in the form's Shadow DOM.
How do I test that the form is working?
Submit a test lead and check MoveRight admin — the new job should appear immediately at the Lead stage with all the form data. You can also add waitForResponse: true and log the JSON response in your browser console to inspect the created job ID.
What is the "Best Moving Form Ever"?
It's the codename for the latest multi-step intake form variant developed by MoveRight based on conversion optimization work across 30+ moving company websites. The multi-step wizard format (one question per step) consistently outperforms single-page forms for both completion rate and lead quality.
Does the form support French?
Yes. Add defaultlanguage="fr" to the div element to render the form in French. The form uses Lingui internationalization, and all labels, consent text, and error messages are translated.
Ready to add the form to your site?
Start a free trial, then generate your token in the MoveRight admin panel.
Setup takes under 10 minutes. No developer needed for standard embeds.