Skip to content

Take proposal requests on your own website.

A board member reading your services page can ask you for a proposal without leaving it. Install steps and screens for whoever looks after your company’s website.

What you paste
Two lines of HTMLA div and a script, generated by your dashboard.
Platform tabs
7Plus a plain web address that needs no code at all.
Addresses per key
10A live site, a staging site, a few landing pages.
Charge per request
NonePart of every account, with no cap on requests.

What it is.

A small form, hosted by VendoRFP, that appears inside a page on your own website.

A visitor fills in their name, how to reach them, and what their community needs. You get the request in your dashboard under Settings, Website plugin, and you reply to them directly with a proposal. It is part of every account, there is no charge per request, and there is no limit on how many pages you put it on.

  • Room for a key

    Fewer than 10 live keys of your own on your company. That is the only ceiling on getting a new one. Revoke a key you are not using and the room reopens.

  • An admin login

    Your company’s admin login for VendoRFP. A technician login can read the requests that arrive but cannot decide which websites carry the form.

  • A place to paste HTML

    The ability to add a block of HTML to a page on your site. Every platform below has one, and the instructions say where it is.

Get your key and register your addresses.

Three steps on one page of your dashboard.

  1. Open the plugin page in your dashboard

    Sign in and go to Settings, then Website plugin.

  2. Type the web address your site lives at

    Type it exactly as it appears in the browser bar, for example acme-lawns.com. To a browser, acme-lawns.com and www.acme-lawns.com are two different addresses, so adding one offers you the other. Add both if your site answers on both. You can register up to 10 addresses on one key, which is room for a live site, a staging site and a couple of landing pages.

  3. Press the button and copy the key

    You get a key that starts with vrfp_embed_. It is meant to be public and goes into your page’s HTML where anyone can read it. What stops somebody else using it is the list of addresses you just registered. A browser refuses to show your form on any site that is not on it.

Install it on your platform.

The snippet is the same everywhere. What changes is where you paste it, and that is what each tab covers.

The snippet
<div data-vendorfp-proposal-form data-key="vrfp_embed_your_key_here"><a href="https://hoacrew.com/embed/w/vrfp_embed_your_key_here">Request a proposal</a></div>
<script src="https://hoacrew.com/embed/v1.js" async></script>

One Custom HTML block on the page you want the form on. About five minutes, and you do not need a developer. This covers the block editor, the Classic editor, Elementor and Divi.

  1. Open the page for editing

    In the WordPress admin, go to Pages, find the page you want the form on, and press Edit. Your Contact or Services page is the usual choice.

    The WordPress Pages list. Under the Contact row, the Edit link is ringed.
    Edit sits under the page title, and only appears when the pointer is on that row.
  2. Add a Custom HTML block

    Click where you want the form to sit, press the black + button, type Custom HTML into the search box, and choose it. You get an empty box with the words Write HTML in it.

    The block editor with the inserter open, Custom HTML typed into the search box, and the black plus button at the top left ringed.
    The black + is the top left corner of the editor, above the page.
  3. Paste the two lines

    Paste the snippet straight into that box. Do not put it inside a Paragraph block or a Code block. A Code block displays code instead of running it, so the page would show the snippet as text.

    The snippet pasted into a Custom HTML block on the page, the block ringed.
    Pasted, the block shows the code as text. That is the Custom HTML block working.
  4. Publish, then open the live page

    Press Update (or Publish on a new page), then press View Page. The editor shows the raw code. The live page shows the form.

    The editor with the Update button ringed at the top right, and a saved notice offering View Page.
    Update is the top right. View Page arrives in the notice along the bottom once it saves.

The Classic editor

If your editor is one long box with a toolbar above it, not a stack of blocks, you are on the Classic editor. Above the top right corner of the box there are two tabs, Visual and Text. Switch to Text first, then paste. Pasting into Visual turns the code into something WordPress displays, not runs, and switching tabs afterwards does not undo that.

Elementor, Divi and other builders

Page builders replace the WordPress editor with their own, and every one has a block for raw code. The names differ and the job is the same. In Elementor, drag in the widget called HTML and paste the snippet into it. In Divi, add a Code module and paste the snippet into the content box. In Beaver Builder, WPBakery and the rest, look for a module named HTML, Raw HTML or Code, and choose HTML if a builder offers both HTML and Text. Builders usually preview their own canvas without running scripts, so the form will not appear inside the builder. Save, then open the live page.

On something else? The plain HTML tab has a complete worked example, and every platform that lets you paste a block of HTML behaves the same way.

Register every address your site answers on.

The list of addresses is how a browser decides where your form may appear, and it matches exactly.

Four different addressesExample
https://acme-lawns.com
https://www.acme-lawns.com
http://acme-lawns.com
http://www.acme-lawns.com

A browser treats these as four different places. Most sites answer on two of them and redirect the other two. Whichever ones a visitor can end up on need to be on your list. Adding one on the plugin page offers you its companion for exactly this reason. Take the offer.

The extra addresses each platform answers on
PlatformAddress to register as wellWhy
WordPressBoth spellings of your domainMost WordPress sites answer on the bare name and on www.
SquarespaceThe squarespace.com trial addressA site on a trial answers at an address like oak-hollow-lawns.squarespace.com. Remove it once you are live.
WixNone for the buttonA button opens the form as its own page, so nothing needs registering.
WebflowThe webflow.io addressEvery project also answers at a webflow.io address, and that is where people look first after publishing.
ShopifyThe myshopify.com addressShopify previews use the built-in address. Add it while you set up and remove it after.
Plain HTMLYour local serverlocalhost:3000 or whichever address your test server answers on.
React or Next.jsDevelopment and preview addressesA preview deployment on a different address is a different address.
  • A staging or preview address

    Webflow sites also answer at webflow.io, Shopify stores at myshopify.com, Squarespace trials at squarespace.com. Testing there means registering there.

  • A second domain

    A company that owns both a .com and a .net, or a separate landing page domain, needs each one listed.

  • A page opened from your desktop

    A file opened directly rather than served by a web server has no address for us to check against anything, so the form will not appear. Put the page on a server, even a local one, and register the address it answers on.

If your site has a security policy.

Most websites have no Content-Security-Policy and this section does not apply to them.

If yours has one, because a security plugin added it, your host sets it, or whoever built the site wrote one, it needs to allow two things from us.

The two allowances
script-src  https://hoacrew.com
frame-src   https://hoacrew.com

The first lets the loader script run. The second lets the form’s frame render. Allow the first and forget the second and you get a blank space where the form should be, with a line in the browser’s Console tab naming frame-src. Nothing else is needed. The form loads no fonts, no analytics and no other company’s code, so there is no third allowance hiding behind these two.

Where a security policy comes from on each platform
PlatformWhere it comes fromWhat to do
WordPressAlmost always a security plugin. Wordfence, iThemes Security and Really Simple SSL all offer to add one.Find the plugin’s headers or Content-Security-Policy screen and add the two allowances there. If you cannot find where it is set, turn the plugin’s header feature off for a moment and reload the page. If the form appears, that plugin is the place to add them.
SquarespaceSquarespace gives you no place to write one.Nothing, unless your domain sits behind a rule you added yourself, such as Cloudflare, that adds headers on the way out.
WixWix gives you no place to write one.Nothing. The button route needs no allowances, because nothing of ours loads on your page.
WebflowWebflow gives you no place to set one.Nothing, unless your domain is behind something you added yourself, typically a Cloudflare rule that adds headers as pages leave.
ShopifyUsually an app. Security and headers apps are the common source.Add the two allowances to the app that set the policy. Turning that app off for a moment confirms it is the one responsible.
Plain HTMLAn nginx or Apache header, a line in your framework’s config, or a meta tag in the head.Add the two allowances to whichever it is. The HTML tab has the same policy as a meta tag.
React or Next.jsA headers entry in your config or a line in your middleware.Add the two allowances. The React tab has the next.config.mjs version.

How to tell it worked.

Three checks, and a table of what each platform’s editor shows you in the meantime.

  1. Open the live page in a normal tab

    Use the real site, not the editor and not a builder preview.

  2. Send yourself a test

    The form is there with your company’s name on it. Take more than 4 seconds over it, because a submission completed unusually fast is placed under suspected spam.

  3. Look in your dashboard

    Open Settings, then Website plugin. Your test is in the list.

What the editor shows, and where to look for the real thing
PlatformIn the editorWhere to check
WordPressThe block editor shows the raw code. Page builders usually preview without running scripts.The live page, in a normal browser tab. Use View page source and search for proposal-form if nothing appears.
SquarespaceA grey placeholder where the Code block is.The live site in a normal tab, signed out of Squarespace if you can.
WixThe editor preview does not show the form.Your published site. Press the button.
WebflowA placeholder block. The Designer does not run embedded code.The published page, not the Designer preview.
ShopifyThe customizer preview may or may not render it.The live page on your selling domain.
Plain HTMLNot applicable.The page served over http or https, never opened from disk.
React or Next.jsNot applicable.Your running app, with its address on the list.

Link to the hosted form instead.

Every key also has a plain web address that opens the same form as a full page.

Put it behind a button, in an email signature, on a Google Business profile, or on a page you cannot add code to. It needs no plan, no code block and no addresses registered, because the list of allowed addresses governs where the form may be shown inside another page, and a link opens it as its own.

Your hosted form
https://hoacrew.com/embed/w/vrfp_embed_your_key_here

Requests from it arrive in your dashboard exactly like the ones from the embedded version. It is also what a visitor is shown automatically if the embedded form cannot load for any reason, so nobody is ever left looking at a blank space.

Where the hosted link goes on each platform
PlatformHow to add it
WordPressAn ordinary link or button in any block. It needs no plugin, no code block and no particular level of login.
SquarespaceA Button block with an External link, set to open in a new window. Works on every plan.
WixA button with a Web address link. This is the route we recommend on Wix.
WebflowA Link Block or Button with its link type set to URL.
ShopifyA link in the rich-text editor of any page, or a button in a section that offers one.
Plain HTMLAn anchor tag with the hosted address as its href.
React or Next.jsAn anchor or a Link component with the hosted address.

Options.

Everything is set with an attribute on the div. All of them are optional, and the form works with none of them.

The five attributes
AttributeWhat it does
data-keyRequired. Your key from the dashboard. Everything else follows from it.
data-headingThe line above the form. Defaults to Request a proposal.
data-servicePreselects the service. Put the plugin on your pool page with this set to Pool cleaning and the visitor has one less thing to fill in. Use any wording you like. It is shown as you type it.
data-themelight or dark. Leave it off and the form matches the page it sits on.
data-accentA hex color for the send button, so it matches your site. Anything that is not a hex color is ignored rather than put into the page.
The text on the send button is set to whichever of near-black or white reads better on the color you chose, so a dark green or a pale yellow both end up with a button somebody can read.
With every option setExample
<div data-vendorfp-proposal-form data-key="vrfp_embed_your_key_here" data-service="Landscaping" data-heading="Get a proposal for your community" data-theme="light" data-accent="#1f7a4d"><a href="https://hoacrew.com/embed/w/vrfp_embed_your_key_here?service=Landscaping&amp;heading=Get%20a%20proposal%20for%20your%20community&amp;accent=%231f7a4d">Get a proposal for your community</a></div>
<script src="https://hoacrew.com/embed/v1.js" async></script>

Track it in your own analytics.

When a request is sent, the element fires an event you can listen for.

It is a bubbling vendorfp:proposal-request-sent event. It carries no visitor data at all, not a name and not an email, just the fact that a request went through, so adding a conversion tag never turns your tag manager into a holder of somebody else’s contact details.

Anywhere on the page
document.addEventListener('vendorfp:proposal-request-sent', function () {
  // your own conversion tracking here
  if (window.gtag) window.gtag('event', 'generate_lead');
});

In React or Next.js the same listener goes in an effect. The React tab has it written out with the cleanup.

What a visitor sees.

  • On arrival

    A form with their name, email, phone, community name, property address, what they need, and a message box. Only the name and one way to reach them are required.

  • While sending

    The button reads Sending and cannot be pressed twice.

  • On success

    The form is replaced by a short confirmation naming your company. Nothing redirects and nothing pops up.

  • If they miss a field

    One sentence saying which one, above the form, with everything they typed still in the boxes.

  • If the page has been open a long time

    After 30 minutes the form asks them to reload before sending. Their text stays in the boxes while they do.

  • If their connection drops

    One sentence, and the button comes back so they can try again.

Security and privacy.

The short version, and then the page to forward to whoever asks for the long one.

  • Your key is public, and that is fine

    It ships in your page’s HTML, and it can do exactly one thing: create a proposal request for your company. It reads nothing back.

  • Only your addresses can display it

    Every time the form loads, we tell the browser which addresses may show it, and the browser enforces that. A site that is not on your list never receives the form. Its visitors get a button to your hosted form instead, and anything they send from it still arrives addressed to you.

  • The form is not in your page

    It sits in a frame on our side of a boundary the browser enforces, so the other scripts on your page cannot read what a visitor types. Analytics, chat and a plugin from 2019 alike. Our frame cannot read your page either.

  • Automated submissions are kept

    Two checks run, and neither needs a puzzle from your visitors. Anything that trips one is recorded under Filtered as spam, where one press moves it back. A real customer is worth more than a clean inbox.

  • What is recorded

    What they typed, which of your addresses they were on, and the time. Their IP address is not stored. A one-way fingerprint of it is stored instead. No cookies and no tracking.

  • No email is sent

    Not to you and not to the visitor. The request appears in your dashboard, which cannot bounce and needs nobody’s mail server to be working.

If it is not showing up.

One cause accounts for most of these and it produces no error message anywhere. Two minutes will tell you whether it is yours.

The two-minute check

  1. Copy the address out of your browser bar

    Open the page with the missing form and copy the whole address from the bar at the top of the browser, exactly as it appears. That includes https and includes www if it is there.

  2. Open the plugin page in your dashboard

    Go to Settings, then Website plugin, and look at the list of addresses on your key.

  3. Compare them character by character

    The address you copied has to be on that list, spelled the same way. Not a version of it, and not the domain it belongs to. If it is missing, add it and reload the page. The fix takes effect immediately and you do not touch your website again.

The other causes

If the address was already on the list, spelled exactly as you copied it, and nothing is in that panel, work down this list.

  • You see a Request a proposal button instead of the form

    That is the fallback, and it is deliberate. The frame did not load, so rather than leave a hole in your page the loader puts a working button there instead. Nobody is stranded while you fix it. The cause is nearly always the address.

  • Nothing appears at all, not even a button

    The button lives in the snippet itself as plain HTML, so if even that is missing the snippet is not on the page. Look at the page’s source and search it for proposal-form. If it is not there, your website builder removed the code when you saved. Some builders and some WordPress logins strip script tags. Ask whoever administers the site, or use the hosted link instead.

  • The key has been turned off

    A revoked key stops working everywhere on the next request, by design. Check the plugin page. If there is no live key, issue one and repaste the snippet, because a new key is a new string.

  • The key in the page is not the key on the plugin page

    A snippet pasted from an older note, or a key truncated by a builder’s text box, both look fine and both fail. Copy it again from the dashboard rather than from anywhere else.

  • Your site has a security policy

    If something on your site sets a Content-Security-Policy, it has to allow our address in both its script and its frame lists. The security policy section above has the two allowances, and the browser’s Console tab names which one is missing.

  • A builder that runs code inside its own frame

    Wix is the one this affects. Its embed box puts your code on a Wix address rather than yours, so the address being checked is not the one you registered. The Wix tab has the working route.

  • The form is there but cut off at the bottom

    The form measures itself and asks for the height it needs, which works everywhere except inside a builder that locks its embed to a fixed height. Drag the embed box taller.

  • An ad blocker or a privacy extension

    Try the page in a private window with extensions off. If it appears there, the extension is blocking the frame for you and not for your visitors. That is worth knowing before you change anything else.

Reading the browser’s Console tab

Right-click the page, choose Inspect, and open the Console tab. Three messages there are worth recognizing, and each names its own fix.

Three Console messages worth recognizing
The messageWhat it meansThe fix
A data-vendorfp-proposal-form element has no data-key attributeThe div is on the page but the key is missing from it.Repaste the snippet from your dashboard. The key is part of it.
A message about frame-ancestorsThe browser refused to display our form on this address.That is the address list. Run the two-minute check above.
A message about script-src or frame-src naming your own siteYour site’s own security policy is blocking us.Add the two allowances from the security policy section.
If the Console tab is empty and the page source contains the snippet, the frame is loading and the problem is height rather than display. Scroll, or look at whether the container around it has a fixed size.

A test that sent but did not arrive

The form said it went through and your dashboard shows nothing. Look under Filtered as spam on the plugin page first.

A submission completed in under 4 seconds lands under Filtered as spam on the plugin page, and a test typed by somebody who already knows what every box says can complete it faster than that. That is the check doing its job, not a fault. One press moves it back, and nothing was ever discarded. A request that trips a check is still written, still yours, and still answerable.

Changing it or turning it off.

Adding or removing an address takes effect on the next page load. You do not touch your website again. Changing the heading, the color or the preselected service means copying the snippet again, because those live in the snippet.

Turning the key off stops the form appearing anywhere, immediately. Every request it collected stays in your dashboard. Those are your customers, and burning a key never takes them with it. Issuing a new key is the only capped action: 10 live keys of its own is as many as a company holds at once, and revoking one opens a slot for another.

Your key and your addresses live on one page.

Issue the key, list the addresses your form may appear on, and copy the snippet. The requests it collects stay in your dashboard whatever you later do to the key.