Paste it into the body
You need your key first.
This is the code. Replace hoac_embed_your_key_here with the key from your console. That is the only edit it needs.
The snippet
<div data-hoacrew-proposal-form data-key="hoac_embed_your_key_here"><a href="https://hoacrew.com/embed/w/hoac_embed_your_key_here">Request a proposal</a></div> <script src="https://hoacrew.com/embed/v1.js" async></script>
Two lines: the place the form goes, and the script that puts it there. The link inside the first line is a plain one to your hosted form, and it is what a visitor gets if the script never arrives — a browser with scripting off, a corporate proxy, a blocker. The loader clears it the moment it runs, so nobody sees both. That wording is yours: change Request a proposal to whatever you would rather it said and nothing else is affected.
The <div> is where the form appears; the <script> puts it there. Both belong inside <body>, and the div has to come first in the document. Beyond that the placement is yours — the form fills the width of whatever contains it and asks for the height it needs.
For a form in two places, repeat the <div> and put the same key on both. Your company has one install key and both forms use it; the two mount independently, and each can carry different options. The <script> stays on the page once however many divs there are.
Register both spellings of your address.
acme-lawns.com and www.acme-lawns.com are two different places. If your site answers on both, list both on the plugin page. The same goes for http:// against https://. This is the reason behind almost every form that never appears.A complete worked example
Save this as a file, put your key in it, put it on your web server, and it works. It is a whole page rather than a fragment so there is nothing left to infer.
contact.html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Contact — Acme Lawns</title>
</head>
<body>
<h1>Ask us for a proposal</h1>
<p>Tell us about your community and we will put a proposal together.</p>
<div data-hoacrew-proposal-form data-key="hoac_embed_your_key_here"><a href="https://hoacrew.com/embed/w/hoac_embed_your_key_here">Request a proposal</a></div>
<script src="https://hoacrew.com/embed/v1.js" async></script>
</body>
</html>The options all go on the same <div> as attributes. Set a heading, a service to preselect, a light or dark treatment, and a color for the send button:
With every option set
<div data-hoacrew-proposal-form data-key="hoac_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/hoac_embed_your_key_here?service=Landscaping&heading=Get%20a%20proposal%20for%20your%20community&accent=%231f7a4d">Get a proposal for your community</a></div> <script src="https://hoacrew.com/embed/v1.js" async></script>
Anything that is not a hex color is ignored rather than placed into the page, and the text on the button is set to whichever of near-black or white reads better on the color you chose, so an unlucky choice does not produce a button nobody can read.
Loading order, and pages built after load
The script is marked async and does nothing until the document is ready, so it never holds up your page. When it runs it looks for every element carrying data-hoacrew-proposal-form and puts a frame inside each one.
If part of your page appears later — a tab, a modal, anything drawn by your own code after the document loaded — the script has already finished looking. Call this once the new element is in the document and it picks it up:
After you add a mount point yourself
if (window.HOAcrewEmbed) window.HOAcrewEmbed.mount();
It is safe to call as often as you like: a mount point that already has a form is skipped.
What the script touches on your page.
window.HOAcrewEmbed. The form itself lives inside the frame, on our side of a boundary the browser enforces, so nothing on your page can read what a visitor types into it — and nothing in it can read your page.How to tell it worked
- Open the page over
http://orhttps://from a web server, not as afile://path off your desktop. A page opened from disk has no web address for us to check against your list, and the form will not show. - Send a test, taking more than a few seconds over it: a submission completed unusually fast is filed as suspected spam.
- Open Settings → Website plugin in your HOAcrew console. Your test is in the list.
Testing on your own machine works: register the address your local server answers on, such as localhost:3000, and the form appears there like anywhere else.
If your server sets a security policy
Most websites have no Content-Security-Policy and this section does not apply to them. If yours has one — 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 developer console 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.
On a site you run yourself the policy is wherever you put it — an nginx or Apache header, a line in your framework’s config, or a <meta> tag in the head. Whichever it is, the two source lists need our address on them.
As a meta tag, if that is where yours lives
<meta http-equiv="Content-Security-Policy"
content="script-src 'self' https://hoacrew.com; frame-src https://hoacrew.com">If you use nonces or hashes for scripts, our loader is an ordinary external script and needs neither — a host allowance is enough. The snippet carries no inline JavaScript and no inline style on purpose, so you never need unsafe-inline for it.
A link, if you would rather not embed it
Every key also has a plain web address that opens the same form as a full page. Put it behind any button, in an email signature, on a Google Business profile, or on a page you cannot add code to.
Your hosted form
https://hoacrew.com/embed/w/hoac_embed_your_key_here
It is the same form, sending to the same place, and requests from it arrive in your console exactly like the ones from the embedded version.
The same form, somewhere else
WordPress
Block editor, Classic editor, Elementor and Divi.
Squarespace
Every plan has code blocks; Core and above can run one.
Wix
Wix runs pasted code on its own address, so this one is different.
Webflow
Custom code needs a paid plan. Register the webflow.io address too.
Shopify
Theme Customizer, no code editor needed.
React or Next.js
A script tag written in JSX never runs — use the component instead.