Resources
Developer Guide

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.

~12 min read Full API reference LLM-ready format

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).

1

Generate Token

Create a lead source token in MoveRight admin. This encodes your zone, referral source, and customer type.

2

Add Script

Paste one script tag in your site's <head>. It captures attribution data across all pages.

3

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. 1

    Open the Marketing page

    In MoveRight, log in as an admin. Click Business → Marketing in the left sidebar.

  2. 2

    Find Lead Sources

    Scroll to the very bottom of the Marketing page to the "Lead Sources" section.

  3. 3

    Click "Add Lead Source"

    A panel will open where you configure the token defaults.

  4. 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. 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

Security note: Treat the token like a password. Anyone with the token URL can submit leads to your MoveRight account. The token has an optional 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
Best Moving Form Ever

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
YMM Form (A/B Router)

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
Simple Form (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
email 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
email 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.