PageSourceSearch

https://haulhq.app/_next/static/chunks/app/docs/layout-9cd1f63fa1f…?dpl=dpl_Gv5SLcY1y8hz5BWvef69Laop8pMc

js haulhq.app collected 2026-10-01 10:39:46 UTC 207,134 bytes, 1 lines download raw bytes

1(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[3998],{56726:function(e,t,o){Promise.resolve().then(o.bind(o,35045)),Promise.resolve().then(o.bind(o,63941)),Promise.resolve().then(o.t.bind(o,74760,23)),Promise.resolve().then(o.t.bind(o,68443,23))},35045:function(e,t,o){"use strict";o.d(t,{DocsSidebar:function(){return _}});var a=o(76029),s=o(20524),n=o(66068),i=o(59795),r=o(36008),h=o(41974);let d=(0,o(15915).Z)("menu",[["path",{d:"M4 5h16",key:"1tepv9"}],["path",{d:"M4 12h16",key:"1lakjw"}],["path",{d:"M4 19h16",key:"1djgab"}]]);var l=o(30642),u=o(23514),c=o(45130),p=o(97597),y=o(92091);let m=["booked","scheduled","dispatched","in_progress","completed","invoiced","paid","missed","cancelled"],g={booked:"Booked",scheduled:"Scheduled",dispatched:"Dispatched",in_progress:"In progress",completed:"Completed",invoiced:"Invoiced",paid:"Paid",missed:"Missed",cancelled:"Cancelled"},w={booked:"A real job with no date on the calendar yet. A booked job with no date is **on hold**.",scheduled:"It has a date and a time window, so it is on the schedule and counts against your capacity.",dispatched:"The crew is on the way. This is the move that texts the customer.",in_progress:"The crew is on site and the clock has started.",completed:"The work is done. Nothing has been collected yet.",invoiced:"An invoice exists for the work and is waiting to be paid.",paid:"The money is recorded against the job.",missed:"Its day and your grace period both passed with the job still open.",cancelled:"Called off. It keeps its reason and stays in the record."},f={actionable:"Yes, it is still work",terminal:"No, it is over",missed:"Yes, in the Missed list"},b=m.map(e=>[g[e],w[e],f[c.jG[e]]]),k={actual_start:"start time",actual_end:"finish time",completed_at:"completion time",scheduled_date:"date",scheduled_time_start:"time window",scheduled_time_end:"time window",preferred_time_window:"time window",job_costs:"hold marker"},v=[];function x(e){let t=[];for(let o of Object.keys(e)){if("status"===o)continue;let a=k[o];if(!a){v.includes(o)||v.push(o);continue}let s=e[o],n="job_costs"===o?(0,p.Tm)(s)?"sets":"clears":null===s?"clears":"stamps",i="".concat(n," the ").concat(a);t.includes(i)||t.push(i)}if(!t.length)return"Nothing but the status";let o=t.join(", ");return o.charAt(0).toUpperCase()+o.slice(1)}function T(e){return e.fireEnRoute?"Yes, the on-the-way text":"No"}let j=[...function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:p.HR,t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:m,o=[];for(let a of e){let e=new Map;for(let o of t){if(o===a)continue;let t=(0,p.n9)(o,a);if(!t)continue;let s=x(t.updates),n=T(t),i="".concat(s,"|").concat(n),r=e.get(i);r?r.from.push(g[o]):e.set(i,{move:"Move to ".concat(g[a]),from:[g[o]],records:s,texts:n})}o.push(...e.values())}return o}(),...function(){let e=(0,p.fO)(p.Sy[0],{}),t=(0,p.pI)(p.Fj[0]),o=(0,p.Hi)(p.tj[0]),a=[];return e&&a.push({move:"Put On Hold",from:p.Sy.map(e=>g[e]),records:x(e.updates),texts:T(e)}),t&&a.push({move:"Close After Trip",from:p.Fj.map(e=>g[e]),records:x(t.updates),texts:T(t)}),o&&a.push({move:"Close as settled",from:p.tj.map(e=>g[e]),records:x(o.updates),texts:T(o)}),a}()].map(e=>[e.move,e.from.join(", "),e.records,e.texts]),I=function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:u._U;return e.map(e=>[u.eG[e].label,u.eG[e].code,u.eG[e].description,(0,y.$A)({service_type:"dumpster_rental",trip_type:e})?"Yes":"No"])}();var S=o(93672),H=o(29392),A=o(43664);let P="yourname",W={form:"haulhq_form",book:"haulhq_booking",rental:"haulhq_booking",products:"haulhq_products",product:"haulhq_product",reviews:"haulhq_reviews",gallery:"haulhq_gallery","zip-cta":"haulhq_zip_cta","service-area":"haulhq_service_area"},q={slug:"website/html-embed",title:"Three ways to put HaulHQ on your website",description:"Your HaulHQ page, the WordPress plugin, or a copyable HTML embed: which one fits your site, what each gives you, and the honest limits of the embed.",layer:2,workflow:"both",blocks:[{type:"callout",tone:"key",title:"You have three options, and the first one is already live",text:"Every HaulHQ account has its own public page at `yourname.haulhq.app`. It takes quotes and bookings, shows your services and reviews, and runs the chatbot, with nothing to install. If you have no website, or your website is built on something that does not accept raw HTML, **that page is your answer** and you can stop reading here. The other two options are for putting HaulHQ inside a site you already run."}
1,{type:"video",title:"Choosing between your HaulHQ page, the plugin and an embed"},{type:"h2",text:"Option 1: use your HaulHQ page"},{type:"p",text:"Link to it from anywhere: your Google Business Profile, a button on your existing site, a text message, the side of the truck. It is finished, it is yours, and it cannot be mis-pasted. See [Your HaulHQ page](/docs/website/haulhq-page). If you want your own domain to reach it, see [Point your domain at your HaulHQ page](/docs/website/point-your-domain)."},{type:"h2",text:"Option 2: use the WordPress plugin"},{type:"p",text:"The richest option. The free plugin renders your forms, booking, product list, reviews, gallery and service area directly into your WordPress pages, with search-engine markup and the floating chat launcher in the corner. If your site is WordPress, start at [Install the WordPress plugin](/docs/start-here/install-the-plugin)."},{type:"h2",text:"Option 3: paste an HTML embed"},{type:"p",text:'For a site that is not WordPress but lets you paste raw HTML: a hand-built site, or a builder with a "custom HTML" or "embed code" block. You paste one tag and the quote form (or the booking flow, or the chatbot) appears inside your page. **There is no setting to turn on and nothing to connect.** The tag is addressed to your account by your HaulHQ address, so it carries no key and is safe in public page source.'},{type:"p",text:"The tags for your account, with your address already filled in, are under **Settings > Website**, on the **HTML Embed** tab. Copy from there rather than editing the examples below."},{type:"table",head:["What","Address","What it does","Who sees it"],rows:A.gs.map(e=>[e.label,"`".concat((0,A.TL)(e.name),"`"),e.description,0===e.verticals.length?"Everyone":e.verticals.map(e=>"junk_removal"===e?"Junk removal":"Dumpster rental").join(", ")])},{type:"code",caption:"The quote form. Replace ".concat(P," with your HaulHQ address, or copy the pre-filled version from Settings."),code:(0,H.gK)("form",P)},{type:"p",text:"That is the whole embed. The form scrolls inside its box if it grows taller than the height you gave it, so nothing is ever cut off. To have the box grow to fit instead, the page needs a listener. **On WordPress, the HaulHQ Bridge plugin is that listener from version ".concat(H.R5," on**; older versions ignore these tags, so update the plugin or paste the script. **On any other site, paste this once anywhere on the same page:**")},{type:"code",caption:"Lets every HaulHQ embed on the page size itself. Needed unless the page is WordPress with HaulHQ Bridge ".concat(H.R5," or newer. Without it, the embed scrolls inside its box, which also works. Harmless alongside the plugin."),code:(0,H.IM)(P)},{type:"callout",tone:"info",title:"Does your site accept raw HTML?",text:"The tag needs a field that takes HTML as written. Most builders have one, usually called an HTML, Embed or Custom Code block. **If yours escapes the tag into visible text, or strips it, use option 1 or 2 instead.** We support the tag and your HaulHQ page; we do not support any particular site builder, so we cannot tell you where its HTML block lives or why it might refuse a frame."},{type:"h2",text:"Renting containers takes two pages"},{type:"p",text:"Every other embed is one tag on one page. Container rental is different, because a customer who picks a container has to go somewhere to book it, and a tag cannot open a window over your page the way the WordPress plugin can. So you make two pages and tell HaulHQ where the second one is."},{type:"steps",items:["Make a page on your site for booking. Paste the **Container rental** tag on it and nothing else. This is where customers finish.","Put its address in **Settings > Website > HTML Embed**, in the Booking Page URL box at the top. A full address starting with `https://`, or a path on your own site starting with a slash.","Paste the **Products & equipment** tag wherever you list your containers. That is the page customers land on first.","Paste the script from this page once on **both** pages, unless your site is WordPress with the HaulHQ plugin, which does it for you."]},{type:"p",text:"Book Now on a container then opens your booking page with that container already chosen. The customer never leaves your site, and your booking page is a real address you can link to, bookmark and count visits to."}
1,{type:"callout",tone:"info",title:"If you skip the Booking Page URL",text:"Book Now still works. The booking flow opens inside the same box the grid is in, so the customer can finish, but there is no page of your own to link to or measure and the box changes under them. **Setting the address is the better version of this, and it takes one line.**"},{type:"p",text:"One more page you can make, and it is optional: a page about a single container, using the **Single product** tag. Put its address in that product's Detail Page URL in your catalog, and the grid will link the container's name to it while Book Now still goes to your booking page."},{type:"h2",text:"Each embed, its tag and its parameters"},{type:"p",text:"Every tag below is addressed to your account by your HaulHQ address; copy the pre-filled ones from **Settings > Website > HTML Embed**. Parameters go on the end of the `src`, as `?name=value`, and are named after the matching WordPress shortcode attribute so the two read alike. A parameter the embed does not know is ignored. **A value in CAPITALS is a placeholder you must replace; the embed shows nothing until you do.**"},...A.gs.flatMap(e=>{var t,o;return[{type:"h3",text:e.label},{type:"p",text:e.description},{type:"code",caption:"".concat(e.label,". Replace ").concat(P," with your HaulHQ address."),code:(0,H.gK)(e.name,P)},...(null!==(t=e.requiredParams)&&void 0!==t?t:[]).map(e=>({type:"p",text:"**Required:** replace `".concat(e.placeholder,"` with ").concat(e.help)})),...e.params.length>0?[{type:"p",text:"Parameters: ".concat(e.params.map(e=>"`".concat(e,"`")).join(", "),". Same meaning as on the `[").concat(null!==(o=W[e.name])&&void 0!==o?o:e.name,"]` shortcode.")}]:[]]}),{type:"h2",text:"The honest limits of an embed"},{type:"list",items:["**No search-engine value.** An embed is a frame; what is inside it belongs to HaulHQ's page, not yours. Your page carries its own headings, text and markup, and that is where your SEO lives. If you want HaulHQ content that search engines read as part of your site, that is the WordPress plugin.","**No floating chat launcher.** On WordPress the plugin puts a chat bubble in the corner of every page. The chatbot embed is a panel that sits where you paste it. The conversation is identical; only the packaging differs.","**Only what is in the table above.** Everything listed there is embeddable; anything else the plugin renders on WordPress (the services list, the full gallery page) is not. All of it is finished on your HaulHQ page, which is the shortest path if it matters to you and you are not on WordPress."]},{type:"h2",text:"Test it before you tell anyone"},{type:"steps",items:["Open the published page, not the builder preview. Previews often refuse to load third-party frames that are fine when live.","Do it on a phone. Most of your customers will.","Complete a real submission end to end and check the lead arrives under **Leads** in HaulHQ.","Scroll to the bottom of the form and confirm you can reach the last button, with or without the optional script."]},{type:"mistakes",items:[{wrong:'"I pasted the tag and my page shows the code as text."',right:"The field you pasted into escapes HTML rather than rendering it. Find your builder's HTML, Embed or Custom Code block; if it has none, or strips the tag, link to your HaulHQ page instead. That is option 1 and it works everywhere."},{wrong:'"The form is cut off at the bottom."',right:"The frame is shorter than the form and nothing on the page is listening for its height. The form still scrolls inside its box, so nothing is lost. To have it grow to fit: on WordPress, update HaulHQ Bridge to ".concat(H.R5," or newer; anywhere else, paste the script from the HTML Embed tab once on the same page. Or give the tag a taller height.")},{wrong:'"I embedded the form so my site would rank for junk removal."',right:"An embed contributes nothing to your page's search ranking; its contents belong to HaulHQ's page. Write your own headings and text on the page around the form. Search-engine markup for your products and reviews is a WordPress plugin feature."},{wrong:'"The product grid on my WordPress site has search-engine markup; the embedded one does not. Is the embed broken?"',right:"No, and the difference is by design. On WordPress the plugin renders the grid into your page itself, with Product and ItemList markup search engines read as yours. An embedded grid is a frame: it is a booking surface, not an SEO asset, and its markup belongs to HaulHQ's page. Write your own headings and text around it, and if the markup matters to you, that is the WordPress plugin."},{wrong:'"The frame is blank on my site but works when I open the address directly."',right:"Your site is sending a security header (a Content-Security-Policy `frame-sr
1c` or `frame-ancestors` rule) that blocks frames from other addresses. Ask whoever set up the site to allow `*.haulhq.app`, or use option 1."}]}]},C=[];function E(e){return u.rW[e]||(C.includes(e)||C.push(e),e)}let Q=function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:u.zR;return e.map(e=>{let t=u.ZO[e];return[u.cG[e],t?E(t):"No payment recorded",t?"Yes":"No"]})}(),N=function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:u.oF;return e.map(e=>[E(e),e])}(),O=[{label:"Start here",kind:"orientation",pages:[{slug:"start-here/two-workflows",title:"How HaulHQ works: the two workflows",description:"HaulHQ runs two different businesses. Junk removal is quoted. Dumpster rental is booked. Almost every setup mistake comes from mixing them up.",layer:1,blocks:[{type:"p",text:"Before anything else, one idea. HaulHQ supports two kinds of work, and they run in **opposite directions**. Almost everything that confuses people during setup comes from applying one workflow to the other."},{type:"video",title:"The two workflows, side by side"},{type:"compare",junk:["A customer describes a pile of stuff.","You give a price -- instantly from load size, or as a written estimate.","**You close it, and that is the winning move.** Junk jobs are won on fast contact. A ready customer can also self-book.","The job is scheduled and hauled."],dumpster:["A customer picks a container size and dates.","**They check out themselves.** Price and availability are known up front.","You deliver the container.","You pick it up when the rental ends."]},{type:"callout",tone:"key",title:"The one sentence to remember",text:"Junk removal is **quoted**. Dumpster rental is **booked**. Dumpsters are never added to a quote form, and junk is never sold through the booking widget."},{type:"h2",text:"Why this matters more than it sounds"},{type:"p",text:'The two workflows use different settings screens, different shortcodes, and different pages on your website. If you run both and you start in **Settings > Quote Form** while building your dumpster pages, you are in the junk removal workflow and nothing you do there will affect them. It is the only screen in the product with the word "form" in it, which is why people find it first. (A dumpster-only company does not see that screen at all.)'},{type:"h2",text:"Which one are you?"},{type:"p",text:"You may be both. HaulHQ calls these **verticals**, and you picked yours during onboarding. Everything in the product -- menus, settings, shortcodes -- is filtered by that choice."},{type:"table",head:["If you...","You are","Start with"],rows:[["Haul away junk with a truck and trailer","Junk removal","[The quote workflow](/docs/junk-removal/quote-workflow)"],["Drop off containers and collect them later","Dumpster rental","[The booking workflow](/docs/dumpster-rental/booking-workflow)"],["Both","Both","Read both. They do not overlap as much as you would expect."]]},{type:"h2",text:"The things that are shared"},{type:"p",text:"Some parts of HaulHQ do not care which workflow you are in: your service area, your business details, your hours, your photo gallery, disposal sites, deposits and discounts, and your website. In this documentation those live **below** the two workflow sections in the sidebar -- under Your website, Business settings and Money -- not inside either one."},{type:"mistakes",items:[{wrong:'"I will add my dumpster sizes to the quote form."',right:"The quote form is junk removal only. Dumpster sizes live under **Equipment**, and are sold through the booking widget. See [Equipment & units](/docs/dumpster-rental/catalog-equipment)."},{wrong:'"Settings > Quote Form controls the form on my website."',right:"It controls the **junk removal** quote form (`[haulhq_form]`). It has no effect on the booking widget (`[haulhq_booking]`), which is what a dumpster site uses."},{wrong:'"I need the shortcode called booking, because customers book with me."',right:"Only if you rent containers. For junk removal, the shortcode you want is `[haulhq_form]` -- it takes a booking as well as an estimate request. See [Your quote form](/docs/junk-removal/quote-form) for which switch decides which, and [Which shortcodes, and where](/docs/start-here/which-shortcodes)."}]}]},{slug:"start-here/get-your-website-live",title:"Get your website live",description:"The ordered path from a fresh HaulHQ account to a working website that takes bookings, end to end.",layer:1,blocks:[{type:"p",text:"This is the whole path, in order. Each step links to the detail. If you are handing this to a web developer, send them [Working with a web developer](/docs/start-here/working-with-a-developer) first."},{type:"video",title:"From signup to a live site"},{type:"h2",text:"In HaulHQ"}
1,{type:"steps",items:["Finish onboarding. It ends when your public site is live, which is the point at which the rest of this works.","Set your **service area**. The wizard offers to fill it in from your own zip code and how far you drive -- use that rather than typing, and untick anywhere you will not go. Add cities *and* zips: cities are what your public pages are gated on, so zips alone will leave you dark, and county names count as neither. See [Service area](/docs/business/service-area).","Add what you sell. Dumpster rental: add each container size on the **Equipment** screen, with its number of units, which creates the bookable units -- see [Equipment & units](/docs/dumpster-rental/catalog-equipment). Junk removal: add your services on the same screen and put a price range on your load sizes under **Settings > Pricing > Load Tiers** -- see [Load tiers & capacity](/docs/junk-removal/load-tiers).","Set your **hours and scheduling**, then set **booking capacity**. These are two different screens and this trips up nearly everyone -- see [Scheduling & business hours](/docs/business/scheduling-and-hours).","Connect **Stripe** if you want to take payment online."]},{type:"h2",text:"Stop here and decide: do you need WordPress at all?"},{type:"p",text:"The steps above are enough. **When you finish them your HaulHQ page at `yourname.haulhq.app` is live and taking bookings**, and for a good number of operators that is the whole website. Everything below this line is the WordPress route, and it is worth thirty seconds deciding whether you want it before you start."},{type:"callout",tone:"key",title:"Read this before installing anything",text:"[Which website should I use?](/docs/website/which-website) lays out the three options honestly -- what the free page does, what it cannot do, and when WordPress genuinely earns its keep. **Start on the free page and move up when you have a reason** is the right answer for most people."},{type:"h2",text:"On your own WordPress site"},{type:"callout",tone:"info",title:"Not on WordPress?",text:"These steps are the WordPress route. If you are on something else, or have no website at all, you can stop after the HaulHQ section above -- [your HaulHQ page](/docs/website/haulhq-page) already is a website, it takes bookings on its own, and you can link to it from anywhere. If you run a site that accepts raw HTML, you can also paste the quote form into it; see [Three ways to put HaulHQ on your website](/docs/website/html-embed)."},{type:"steps",items:["Install the WordPress plugin and connect it. See [Install the WordPress plugin](/docs/start-here/install-the-plugin).","Work out which shortcodes you need and which page each goes on. See [Which shortcodes, and where](/docs/start-here/which-shortcodes).","Paste them in and publish.","Test a real booking or quote from your phone, on the live site, not a preview. If you start a dumpster checkout and stop before paying, the booking shows as **Awaiting payment** and releases itself after two hours -- that is the abandoned-checkout rule working, not a fault."]},{type:"h2",text:"Then, once jobs are running"},{type:"p",text:"Your crew taking photos on jobs is what fills the gallery on your website, once you **feature** the pairs you want shown. Nothing has to be uploaded separately, but you do have to know it works that way -- see [Photos & your gallery](/docs/business/photos-and-gallery)."},{type:"callout",tone:"warn",title:"The most common reason a site looks broken",text:"An empty **Equipment** screen or a service area with no **cities**. Both produce pages that render but show nothing, which reads as a plugin problem and is not one."}]},{slug:"start-here/install-the-plugin",title:"Install the WordPress plugin",description:"Install the HaulHQ Bridge plugin, connect it to your account with an API key, and confirm the connection.",layer:1,blocks:[{type:"p",text:"The HaulHQ Bridge plugin is a thin connector. It does not store your data -- it pulls your catalog, reviews, service area and photos from HaulHQ and renders them on your WordPress site through shortcodes."}
1,{type:"callout",tone:"info",title:"Only if you want a WordPress site",text:"This page assumes you have decided. If you have not, **your HaulHQ page is already live and taking bookings without any of this** -- see [Which website should I use?](/docs/website/which-website) first. The plugin is free either way; what WordPress costs you is hosting, a domain and the upkeep. If your site is not WordPress, the plugin does not apply; see [Three ways to put HaulHQ on your website](/docs/website/html-embed) for the other two."},{type:"video",title:"Installing and connecting the plugin"},{type:"h2",text:"Install it"},{type:"steps",items:["In WordPress, go to **Plugins > Add New** and search for **HaulHQ Bridge**.","Install and activate it. **Settings > HaulHQ** appears in the WordPress admin menu, under Settings.","Alternatively, download it from [wordpress.org/plugins/haulhq-bridge](https://wordpress.org/plugins/haulhq-bridge/) and upload the zip."]},{type:"h2",text:"Connect it to your account"},{type:"steps",items:["In HaulHQ, go to **Settings > Website** and open the **WordPress** tab. Your API key is there, along with a copy button.","In WordPress, go to **Settings > HaulHQ**, on the **Connection** tab, and paste the key.","Click **Test Connection**. It should say *Connection successful! API key is valid.* If it does not, the key is wrong or was truncated on copy."]},{type:"callout",tone:"info",title:"Treat the API key like a password",text:"It identifies your account to the plugin. If you are handing setup to a contractor, give them the key rather than your HaulHQ login -- see [Working with a web developer](/docs/start-here/working-with-a-developer)."},{type:"h2",text:"The plugin settings screen"},{type:"p",text:"There are four tabs. What each does is covered in [Plugin settings](/docs/wordpress/plugin-settings): **Connection** (your API key, and the chat widget switch), **Shortcodes** (a reference with copy buttons and a live preview), **Styling** (button colors, or your theme's own styles), and **Cache** (how long content is held before refetching). Every shortcode is also a **block** in the WordPress editor -- see [Plugin settings](/docs/wordpress/plugin-settings)."},{type:"p",text:"Already have a contact form on the site? Once the plugin is connected, submissions from Gravity Forms, WPForms and Contact Form 7 arrive in HaulHQ as leads with nothing else to set up. The field labels that work are on [Form integration](/docs/wordpress/form-integration)."},{type:"mistakes",items:[{wrong:'"I changed something in HaulHQ and the website did not update."',right:"The plugin caches content. In WordPress, go to **Settings > HaulHQ**, the **Cache** tab, and press **Clear Cache Now** -- or wait for it to expire. See [Plugin settings](/docs/wordpress/plugin-settings)."},{wrong:'"Test Connection fails but the key looks right."',right:"Copy it again with the copy button rather than selecting it by hand. A truncated key is the usual cause, and it fails identically to a wrong one."}]}]},{slug:"start-here/which-shortcodes",title:"Which shortcodes, and where",description:"A decision map from what you are trying to achieve to the shortcode that does it and the page it belongs on.",layer:1,blocks:[{type:"p",text:"There are ten shortcodes, and every one of them is also a **block** in the WordPress editor (search the inserter for HaulHQ). You do not need ten. Start from **what you are trying to do**, not from the list -- reading the list is how people end up with the wrong one."},{type:"video",title:"Choosing shortcodes for your site"},{type:"h2",text:"If you rent dumpsters"},{type:"p",text:"Four shortcodes and three pages will give you a working site."},{type:"table",head:["You want to...","Use","On this page"],rows:[["Show the containers you rent","[`[haulhq_products]`](/docs/wordpress/haulhq-products)",'Your "our dumpsters" page'],["Give each size its own page","[`[haulhq_product]`](/docs/wordpress/haulhq-product)","One page per size -- this is what ranks in search"],["Let customers book and pay","[`[haulhq_booking]`](/docs/wordpress/haulhq-booking)","Your book-online page"],["Show where you deliver","[`[haulhq_service_area]`](/docs/wordpress/haulhq-service-area)","Footer, or a service-area page"]]},{type:"h2",text:"If you do junk removal"},{type:"table",head:["You want to...","Use","On this page"],rows:[["Take quote requests and bookings","[`[haulhq_form]`](/docs/wordpress/haulhq-form)","Your quote / booking page"],["Start the flow from anywhere","[`[haulhq_zip_cta]`](/docs/wordpress/haulhq-zip-cta)","Home page hero, header, sidebar -- use it repeatedly"],["List what you haul","[`[haulhq_services]`](/docs/wordpress/haulhq-services)","Your services page"]]}
1,{type:"callout",tone:"key",title:"The single most common wrong turn",text:'Junk removal does **not** use `[haulhq_booking]`. `[haulhq_form]` already handles booking a pickup as well as requesting an estimate. Reaching for the shortcode named "booking" because you want customers to book is the mistake that has cost the most setup time so far.'},{type:"h2",text:"Either way"},{type:"table",head:["You want to...","Use","On this page"],rows:[["Show your reviews","[`[haulhq_reviews]`](/docs/wordpress/haulhq-reviews)","Home page, near a call to action"],["Show a few before/after photos","[`[haulhq_gallery]`](/docs/wordpress/haulhq-gallery)","A section on an existing page"],["Give past work its own page","[`[haulhq_gallery_page]`](/docs/wordpress/haulhq-gallery-page)","A dedicated gallery page"]]},{type:"mistakes",items:[{wrong:'"Where is the chatbot shortcode?"',right:"There is not one, and there never has been. The chatbot is a **global switch** in the plugin, on the **Connection** tab (*Enable chatbot widget on frontend pages*): turn it on and it appears on every page of your site -- except any you list in HaulHQ under **Settings > Chatbot**, *Hide the chat widget on these pages*. Nothing needs to be pasted anywhere."},{wrong:'"I pasted the shortcode and the page is blank."',right:"Almost always an empty Equipment screen or a service area with no cities, not a broken shortcode. Fill those in and the same shortcode starts rendering. If you are logged in to WordPress, most shortcodes now show you a notice naming the cause; visitors see nothing."}]}]},{slug:"start-here/working-with-a-developer",title:"Working with a web developer",description:"What to send a developer building your site, and why you should invite them as a user instead of sharing your owner login.",layer:1,blocks:[{type:"p",text:"Plenty of operators hire someone to build the website. This page is what to send them, and one thing to do before you do."},{type:"video",title:"Inviting a developer instead of sharing your login"},{type:"callout",tone:"warn",title:"Do not share your owner login",text:"Your owner account can see customer records, payment settings, and your Stripe connection. For website work a developer needs **only your API key** -- copy it from the WordPress tab of Settings > Website and send it; they do not need a login at all. If they also need to work inside HaulHQ, **invite them as a user** from Settings > Team, with **Invite Member**, as a Manager or Dispatcher. Those roles still see customer records, but cannot read the API key, touch billing or export your data, and you can remove them when the work is done without changing your own password."},{type:"h2",text:"Send them this"},{type:"steps",items:["[How HaulHQ works: the two workflows](/docs/start-here/two-workflows). **Send this first.** A developer who does not know which workflow you are in will build the wrong thing competently.","Whether you are junk removal, dumpster rental, or both.","[Install the WordPress plugin](/docs/start-here/install-the-plugin), plus your API key from the **WordPress** tab of **Settings > Website**.","[Which shortcodes, and where](/docs/start-here/which-shortcodes)."]},{type:"h2",text:"What you have to do yourself"},{type:"p",text:"A developer cannot finish without these, and they are yours to fill in: your **Equipment** screen (sizes and services), your **prices**, your **service area** cities, and your **hours and booking capacity**. The shortcodes render whatever is in your account -- if the account is empty, a correctly built page still shows nothing."},{type:"h2",text:"What to check before you pay"},{type:"list",items:["Submit a real quote or booking from your phone on the live site. It should appear in HaulHQ within seconds.","Check the confirmation email arrives with your business as the sender name. It is sent from a HaulHQ address with your email as the reply-to, so replies reach you.","Open every page on a phone. Most of your customers are on one.","Confirm your service area and reviews render, not just the booking page."]},{type:"mistakes",items:[{wrong:'"My developer says the plugin is broken -- nothing shows up."',right:"Check your Equipment screen and your service-area cities first. An empty account renders empty pages, and that is by far the most common cause."},{wrong:'"They built the quote form but I rent dumpsters."',right:"A workflow mismatch, not a mistake in the build. Send them [the two workflows](/docs/start-here/two-workflows) and the dumpster shortcodes."}]}]}]},{label:"Junk removal",kind:"reference",pages:[{slug:"junk-removal/quote-workflow",title:"The quote workflow",description:"How a junk removal job moves through HaulHQ: the customer describes the work, you price it, and a person closes it -- or they book themselves, if you have priced your load sizes.",layer:2,workflow:"junk",blocks:[{type:"p",text:"Junk removal is **quoted**, not booked, because the price depends on a pile nobody has seen yet. The flow is built around getting to a number fast and then getting a person in front of the customer."},{type:"video",title:"A junk job from lead to complete"},{type:"h2",text:"The path"},{type:"steps",items:["A customer checks their zip on your site and describes the job.","They pick a **load size** and see an instant price range, or ask for a written estimate.","HaulHQ captures a **lead** with their photos and details.","You send an [estimate](/docs/junk-removal/estimates), or the price they already saw stands.","**You close it** -- a call or a text, fast. This is where junk jobs are won. Customers who are ready can also book the pickup themselves from the same form.","The job is scheduled, hauled, photographed and completed."]}
1,{type:"callout",tone:"key",title:"The human close is the strength, not the fallback",text:'It is easy to read "a person closes it" as the thing dumpster rental has outgrown. It is the opposite. **Answering a lead fast is how junk jobs are won**, and it is what HaulHQ is built to make possible -- the lead lands with photos, a price and contact details already attached. Self-booking is a convenience for customers who are already decided; it does not replace the call.'},{type:"h2",text:"Both closing paths, and which one to set up"},{type:"p",text:"The two paths are not alternatives you pick between. **Both are live on the same form at the same time.** What you control is how much of the price a visitor sees before they hand over their details -- see [Your quote form](/docs/junk-removal/quote-form)."},{type:"table",head:["Path","What the customer does","What you get"],rows:[["**You close it** (most jobs)","Describes the job, sees a price range, leaves their details","A lead with photos, a price and a phone number. You call or text."],["**They self-book**","Picks a load size, then picks a date and time window","A lead **and** a scheduled job, created together. The lead is recorded as converted, so nothing is lost."]]},{type:"callout",tone:"warn",title:"Do not chase self-bookings at the cost of leads",text:"The number to watch is **leads**, not online bookings. HaulHQ measured this on its own tenants: the gated multi-step form produced 13 leads where the simpler one produced 45. Self-booking suits an operator whose load sizes are priced tightly enough to quote sight-unseen. If that is not you, showing a price range and getting the call is the stronger setup."},{type:"h2",text:"What to set up"},{type:"list",items:["**[Load tiers](/docs/junk-removal/load-tiers)** -- the sizes customers pick from, and the price range for each.","**Price book** -- per-item prices the chatbot quotes from. See [Range pricing](/docs/junk-removal/range-pricing) for how the numbers are used. [Written estimates](/docs/junk-removal/estimates) are built line by line.","**[Your quote form](/docs/junk-removal/quote-form)** -- the fields, the appearance, and whether the price is shown before contact details.","**Book online and save** -- an optional fixed amount off a load size for customers who book online, set under **Settings > Money > Discounts**. The price reveal shows the list range struck through and the discounted range. Codes a customer types are a dumpster thing -- see [Discount codes](/docs/money/discount-codes).","**[Deposits](/docs/money/deposits)** -- whether a booking is offered a deposit, on which sizes, and where the customer is asked.","A quote page on your website carrying [`[haulhq_form]`](/docs/wordpress/haulhq-form), plus [`[haulhq_zip_cta]`](/docs/wordpress/haulhq-zip-cta) wherever you want the flow to start."]},{type:"h2",text:"When a job does not go to plan"},{type:"p",text:"Two things happen to real jobs that the normal path does not cover: the customer goes quiet before the date, and you do the work and forget to move the job along until the evening. Both have a proper action in the job's **More** menu, on the desktop job page and on your phone."},{type:"table",head:["Action","When","What happens"],rows:[["**Put On Hold**","A booked, scheduled or missed job whose date is no longer real -- the customer is deciding, or the day came and went.","The job keeps everything but its date. It leaves the calendar, capacity and the reminder texts, and shows as **On hold** on the jobs list (there is an On hold filter) with how long it has waited. No message goes to the customer. When they are ready, **Set a date** puts it back on the calendar and sends the normal rescheduled text."],["**Close After Trip**","A job you did on the day and did not step through on the phone -- it is still booked or scheduled and the date is today or earlier.","It goes straight to **completed** with a **Finished at** time you set (defaults to now), and the **\"we're on our way\" text is not sent**, because the customer has already seen you. Everything that normally follows completion still happens: the review request, the invoice when you take payment, and Collect Payment opens for you at once. The job's start time is left blank rather than 
1invented."]]},{type:"callout",tone:"info",title:"Neither applies to a dumpster rental",text:"A rental's dates belong to its booking, and a rental completes at the dump after the pickup. Holding one trip would leave the booking wrong, so both actions are junk-removal only. To move a rental, extend or reschedule it from its work order."},{type:"h2",text:"The internal note"},{type:"p",text:"Every lead and every job has an **internal note**: one free-text field, **Add note** when it is empty, **Edit** when it is not. It is for the things you would write on the back of the ticket -- *gate code is 4411*, *call before 8 or he will not answer*, *watch the dog*. Owners, dispatchers and the assigned driver can read and change it; the driver sees it on their job screen just above the access notes."},{type:"callout",tone:"key",title:"The customer never sees it",text:"The internal note is not on the estimate, the confirmation, the invoice, the receipt or any text or email. **Notes to Customer** on an estimate is the field that reaches them. Keep the two straight: one is for them, one is for you."},{type:"p",text:"When a lead becomes a job, whichever way it happens -- you book it, the customer approves an estimate, the chatbot books it, or they book from your page -- the note is **copied to the job** so it is there on the day. Changing it on the job does not change it on the lead."},{type:"h2",text:"Where the lead came from"},{type:"p",text:"Every lead records which ad or page produced it, with nothing to set up. A Google Ads click carries its click id (the `gclid`); a tagged link carries its UTM values; and the page the visitor came from is kept too. On the lead -- and on the job it becomes -- the **Source** line reads in plain words: *Google Ads*, *Facebook*, *Direct*, with the keyword or campaign when there is one."},{type:"list",items:["Nothing to configure. It works whenever Google Ads auto-tagging is on, which is its default.","The new-lead email carries an **Ad Source** line when there is one.","For the whole picture, export **Leads** from **Settings > Platform > Data**: one column per value -- click id, source, medium, campaign, keyword -- plus the settled job value and when it converted, so you can see which ads paid."]},{type:"h2",text:"Texting the customer"},{type:"p",text:"The lead, the job and the customer record each carry a **Messages** thread. Texts you send from it go out on your **business number**, not anyone's personal phone, and the customer's replies land back in the same thread. Your drivers get the same thread, in the truck: it is on the job in the driver app, so a driver's \"10 minutes out\" comes from your number too, and the customer's reply is pushed to the driver and crew assigned to that job and nobody else."},{type:"mistakes",items:[{wrong:'"Junk removal cannot take a booking, so I need the booking shortcode."',right:"It can. `[haulhq_form]` takes a booking as well as a lead -- see [Your quote form](/docs/junk-removal/quote-form). `[haulhq_booking]` is for **containers**, not junk, and adding it will not help. See [the two workflows](/docs/start-here/two-workflows)."},{wrong:'"A human close means HaulHQ is behind on automation."',right:"It means junk jobs are won on contact, and the product is built to get you there fast. The form exists to produce a lead you can call, which is why contact details are captured early rather than at the end of a checkout."},{wrong:'"My load tiers show Call for pricing and I did not ask for that."',right:'A tier with a blank price range renders as "Call for pricing". Fill in the minimum and maximum -- see [Range pricing](/docs/junk-removal/range-pricing).'}]}]},{slug:"junk-removal/quote-form",title:"Your quote form",description:"Settings > Quote Form is not cosmetic. Together with two settings on other screens it decides whether a visitor becomes a lead you call or a booking on your calendar.",layer:2,workflow:"junk",blocks:[{type:"p",text:"Most people find **Settings > Quote Form** while looking for somewhere to change a heading, and never come back. That is a shame, because the switches on this screen -- plus two that live elsewhere -- are **the control panel for which path a customer takes**. Nothing else in the product decides that."},{type:"video",title:"Configuring the quote form"}
1,{type:"callout",tone:"key",title:"This screen is junk removal only",text:"Settings > Quote Form controls [`[haulhq_form]`](/docs/wordpress/haulhq-form) and the quote section of your HaulHQ page. It has **no effect** on the container booking widget, and dumpster sizes are never added here. If you rent containers, the screen you want is [Equipment & units](/docs/dumpster-rental/catalog-equipment)."},{type:"h2",text:"The four levers, and where each one actually lives"},{type:"p",text:"Two of the four are **not on this screen**, which is the single most common reason someone concludes a setting does not exist. Check this table before hunting."},{type:"table",head:["Lever","Where it lives","What it decides"],rows:[["**Show prices on the form**","Settings > Quote Form","Whether a visitor sees the price range before or after giving contact details"],["**Show instant booking-time picker**","Settings > **Scheduling**",'Whether the customer is offered up to three concrete tap-to-book times after the price, instead of a generic "Book Now"'],["**Same-day cutoff time**","Settings > **Scheduling**",'Whether "today" is offered at all, and until what time of day'],["**Which form to embed**","The shortcode, not a setting",'`form="standard"` swaps the multi-step zip flow for a single-screen lead form'],["**Show the quote form on my landing page**","Settings > Quote Form","Whether the quote section renders on your HaulHQ page at all. A WordPress embed is unaffected either way."]]},{type:"h2",text:"Show prices on the form"},{type:"p",text:"This is the one that changes the shape of the funnel. **Leave it on unless you have a specific reason.** An instant price is HaulHQ's differentiator: competitors gate the number because they cannot compute one, and a visitor who sees a price has a reason to keep going."},{type:"list",items:["**On (the default).** Load-size cards show a price range. The visitor sees a number, then chooses to book or to leave their details. With a **book online and save** promotion on that size (Settings > Money > Discounts), the reveal shows the list range struck through and the discounted range beneath it.",'**Off.** Cards show "Get your price" instead of a range, contact details are captured first, and the estimate is revealed after. You collect a contact from people who would have bounced on the number -- and you lose the people who would have kept going because of it.']},{type:"callout",tone:"warn",title:"Turning the price off is a deliberate exception, not a tactic",text:"It is worth trying only if you can measure the result. HaulHQ's own measurement went the other way: the gated flow produced **13 leads where the ungated one produced 45**. Shown is the default for a reason."},{type:"h2",text:"Instant booking times, and the same-day cutoff"},{type:"p",text:'Both live under **Settings > Scheduling**, in the "Instant Booking Times" card. The picker turns the moment after the price from a generic "Book Now" into up to three specific slots the customer can tap. It is junk jobs only -- containers use their own calendar.'},{type:"callout",tone:"info",title:"The cutoff is read in your business timezone",text:"Same-day is offered until the cutoff time each day, and **blank means never offer same-day**. The time is evaluated in the timezone set under [Settings > General](/docs/business/business-info-and-branding), not the visitor's. If you have not set a timezone, set one -- an Eastern operator with a 2pm cutoff and no timezone was taking same-day bookings until 5pm local."},{type:"h2",text:"Standard form vs the multi-step flow"},{type:"p",text:'The default is the **multi-step flow**: check zip, pick a load size, see a price, then book or leave details. Adding `form="standard"` to the shortcode embeds a **single-screen lead form** instead -- every field at once, one submit.'},{type:"code",code:'[haulhq_form form="standard"]',caption:"The single-screen form. Fewer steps, no zip gate, no price cards."},{type:"callout",tone:"key",title:"Say this plainly: the multi-step form underperformed on leads",text:'Over the same period on the same site, the gated multi-step form produced **13 leads against the simpler form\'s 45**. The multi-step flow is still the default because it is what delivers an instant price, which is the differentiator. But if leads are the number you are judged on and yours are thin, `form="standard"` is the first thing to try.'},{type:"h2",text:"Fields, appearance and the success message"},{type:"list",items:["**Fields** -- turn any field on or off, mark it required, rename its label and placeholder, and add your own. Name, phone, email and zip ship on and required, which is what makes the result a lead you can act on.","**Heading, subheading and button text** -- the words around the form.","**Primary color** -- the button and accent color, so the form matches your site.","**Success message** -- what the customer reads after submitting. Say what happens next and how fast, because this is the moment they decide whether to call someone else."]},{type:"h2",text:"What this screen does NOT control"},{type:"p",text:"This is the screen a developer got stuck on for an afternoon, so the list is explicit."},{type:"list",items:["**The container booking widget.** Nothing here reaches [`[haulhq_booking]`](/docs/wordpress/haulhq-booking). Dumpster rental is a separate workflow -- see [the two workflows](/docs/start-here/two-workflows).","**Your dumpster sizes.** Containers are equipment on your [Equipment screen](/docs/dumpster-rental/catalog-equipment), never form fields.","**The load sizes and their prices.** Those are [load tiers](/docs/junk-removal/load-tiers), under Settings > Pricing.","**Booking capacity, hours and blocked dates.** Per-day caps are on the [Load Tiers](/docs/junk-removal/load-tiers) tab; hours and blocked dates are under [Scheduling](/docs/business/scheduling-and-hours).","**The chatbot.** It has its own settings screen and quotes from your price book, not from this form.","**Where the form appears.** That is the shortcode on your page -- see [Which shortcodes, and where](/docs/start-here/which-shortcodes)."]},{type:"mistakes",items:[{wrong:'"I turned on instant booking in Settings > Quote Form."',right:'It is not there. The instant booking-time picker and the same-day cutoff are both under **Settings > Scheduling**, in the "Instant Booking Times" card.'},{wrong:'"Settings > Quote Form controls the form on my website."',right:"It controls the **ju
1nk removal** quote form (`[haulhq_form]`). It has no effect on the booking widget (`[haulhq_booking]`), which is what a dumpster site uses."},{wrong:'"I will add my dumpster sizes as fields on the quote form."',right:"The quote form is junk removal only. Containers live under Equipment and are sold through the booking widget."},{wrong:'"Hiding the price will get me more leads."',right:"It may get you more contacts from people who would have bounced, and fewer from people the price would have convinced. Measured on a live HaulHQ site the gated flow produced 13 leads against 45. Treat hiding the price as a test you measure, not an improvement."},{wrong:'"Same-day is not being offered and I set a cutoff."',right:"Check four things: the instant booking-time picker is on, the cutoff is not blank, your business timezone is set under Settings > General, and **Earliest a customer can book (days)** on the Load Tiers tab is 0. With it at 1 or more, today is never offered whatever the cutoff says."},{wrong:'"I unticked Allow Book Today so that size would make people call me."',right:"It stops them **self-booking**; it does not make them phone you. They get a short form -- contact details and what they need hauled -- and the lead lands with you to call back. Which is the point: you close it. The setting is on the [Load Tiers](/docs/junk-removal/load-tiers) tab, per size."},{wrong:'"I changed the success message but customers still get the old one."',right:"WordPress caches the embedded form. Reload the page with the cache cleared, or check that the page you edited is the one carrying the shortcode."}]}]},{slug:"junk-removal/load-tiers",title:"Load tiers & capacity",description:"The load sizes customers pick from, their price ranges, and the booking capacity settings that unexpectedly live on this screen.",layer:2,workflow:"both",blocks:[{type:"p",text:"Load tiers are the sizes a junk removal customer picks from -- single item, quarter truck, half truck, full truck. They are also, unexpectedly, where **booking capacity for both workflows** is configured."},{type:"callout",tone:"key",title:"Why a dumpster operator ends up on this screen",text:"Per-day booking caps, the earliest bookable day and the booking window live here for historical reasons -- capacity started life as a property of junk load tiers. They apply to **both** workflows. If you rent dumpsters and never touch load tiers otherwise, you still need this screen."},{type:"video",title:"Load tiers and booking capacity"},{type:"p",text:"You will find them under **Settings > Pricing**, on the **Load Tiers** tab -- not under Scheduling, which is where most people look first."},{type:"h2",text:"The tiers"},{type:"table",head:["Field","What it does"],rows:[["Label",'What the customer sees, e.g. "Half Truck Load"'],["Description","The plain-language explanation under the label"],["Price min / price max",'The range shown. **Leave both blank and it renders "Call for pricing".** See [Range pricing](/docs/junk-removal/range-pricing).'],["Allow Book Today","Whether a customer can self-book this size. Untick it and the size asks for a custom quote instead -- they leave their details and you get back to them. Big Job ships with it off."],["Order","Move a size up or down with the arrows beside it"]]},{type:"h2",text:"Capacity -- applies to both workflows"},{type:"table",head:["Field","What it does"],rows:[["Enforce booking capacity limits","The switch for the whole card. Off, nothing below is applied."],["Max morning bookings per day","Bookings accepted in the morning slot per day"],["Max afternoon bookings per day","Bookings accepted in the afternoon slot per day"],["Earliest a customer can book (days)","0 allows same-day; 1 means tomorrow at the earliest."],["How far out customers can book (days)","How far ahead customers can book"]]},{type:"p",text:"Hours and blocked dates are on a different screen -- see [Scheduling & business hours](/docs/business/scheduling-and-hours). Deposits have their own page under Settings > Money -- see [Deposits](/docs/money/deposits). The instant booking-time picker and the same-day cutoff are under Scheduling too, not here -- see [Your quote form](/docs/junk-removal/quote-form)."},{type:"mistakes",items:[{wrong:'"I rent dumpsters, why am I on the load tiers screen?"',right:"Because booking capacity lives here and applies to both workflows. You can leave the junk tiers alone."},{wrong:'"Everything says Call for pricing."',right:"The price min and max on those tiers are blank. Fill them in."},{wrong:'"I am looking for load tiers under Scheduling."',right:"They are under **Settings > Pricing**, on the Load Tiers tab. So is booking capacity, which is the part that catches dumpster operators out."}]}]},{slug:"junk-removal/range-pricing",title:"Range pricing: one idea, two screens",description:"Low and high prices are entered in two different places in HaulHQ and behave differently in each. This page covers both, because documenting them apart is what makes them confusing.",layer:2,workflow:"junk",blocks:[{type:"p",text:"HaulHQ asks you for a **low and a high price** in two separate places. They look like the same setting and they are not: one is shown to the public, the other drives written estimates and the chatbot. Nobody has ever been told which is which."},{type:"video",title:"Where low and high prices are used"},{type:"table",head:["Where","Fields","Who sees it"],rows:[["**Settings > Pricing > Load Tiers**","Price min / price max, per load size","The public. This is the range on the load-size cards in your form."],["**Settings > Pricing > Price Book**","Low / Default / High, per item","The chatbot, when it quotes. A customer never sees the columns."]]},{type:"h2",text:"Load tiers: the range the customer sees"},{type:"p",text:'This is the only one of the three that a visitor to your website ever reads. Each [load tier](/docs/junk-removal/load-tiers) has a price min and a price max, and the card shows that span -- "$250 to $450".'}
1,{type:"callout",tone:"warn",title:'Blank means "Call for pricing"',text:'A tier with no min and no max does not show a blank price. It renders **"Call for pricing"** -- unless the size also has **Allow Book Today** off, in which case it renders **"Custom estimate required"**. Big Job ships that way, which is why it is usually the one you see it on; a priced Big Job shows its range like any other. If every card says "Call for pricing", it is because the fields are empty, not because something is broken. Fill in only one of the two and you get "Starting at $X" or "Up to $X".'},{type:"h2",text:"Price book: low, default and high per item"},{type:"p",text:"The price book is a per-item list -- couch, mattress, refrigerator, stairs fee -- and each item carries **three** numbers rather than two."},{type:"table",head:["Field","What it means"],rows:[["**Default**","Your realistic price. The **bottom** of the range the chatbot quotes -- the least it will go."],["**High**","The **top** of the range the chatbot quotes, for a difficult job."],["**Low**","Kept on the form for your own records. The chatbot no longer uses it: the bottom of the range it quotes is its floor, and there is no lower number behind it."]]},{type:"p",text:"There is also a **Minimum Job Price**, which is the floor for a whole job regardless of what the items add up to. These numbers are internal -- a customer hears a total from the chatbot, never the columns. [Written estimates](/docs/junk-removal/estimates) are built line by line and do not read the price book."},{type:"h2",text:"The chatbot has no range setting of its own"},{type:"p",text:'There is nothing to configure. The chatbot prices a job from **your price book** -- it adds up Default and High across the items the customer describes and quotes **that range in plain dollars**: "that\'s a $375 to $425 job". It never says the words low, target, floor, high or range, and it never invents a number below the bottom of the range: if a customer pushes, it can offer the bottom for flexible timing, and past that it hands off to you. If its prices are wrong, the price book is the only place to fix them.'},{type:"mistakes",items:[{wrong:'"I set the range on my price book but my website still says Call for pricing."',right:"Different screen. The public range comes from **load tiers**, not the price book. Fill in price min and price max on each tier."},{wrong:'"I want the chatbot to quote one number rather than a range."',right:"It quotes Default-to-High as a range by design, so a customer hears a realistic figure and the top it could reach. To narrow what it says, bring Default and High closer together in the price book."},{wrong:'"I put my target in Low and the chatbot ignores it."',right:"It does. The chatbot reads **Default** and **High** only. Put the price you actually expect in Default -- that is the bottom of the range it quotes and the least it will go."},{wrong:'"My chatbot quotes higher than my website."',right:"The website shows a load-tier range; the chatbot quotes a Default-to-High range added up from the price book items the customer named. If the gap is too wide, the two screens disagree about what a job costs, and the price book is usually the one to fix."}]}]},{slug:"junk-removal/estimates",title:"Written estimates",description:"Building an estimate line by line, sending it by email and text, what the customer can do with it, and how an accepted estimate becomes a job on your schedule.",layer:2,workflow:"junk",blocks:[{type:"p",text:"A written estimate is the formal version of the quote workflow. The customer gets a document with line items and a total, they accept it from a link, and -- if you asked for one -- pay a deposit right there. It is what you send when the load-size range is not enough: a big cleanout, a commercial job, anything where the customer needs something to show someone else."},{type:"video",title:"Building and sending an estimate"},{type:"h2",text:"The path an estimate takes"},{type:"table",head:["Status","What it means"],rows:[["**Draft**","You are still building it. Nothing has been sent."],["**Sent**","It has gone out by email, by text, or both. You can resend without creating a new one."],["**Viewed**","The customer opened it. Useful before you chase."],["**Approved**","The customer accepted. The linked lead is marked converted, and a job exists or is 
1waiting on the deposit."],["**Revised**","You duplicated it after sending. The original is closed and a new draft carries the line items forward."],["**Expired**","You archived it."]]},{type:"h2",text:"Building one"},{type:"steps",items:["Open the lead and start an estimate from it, so the customer and their details come across. You can also pick a lead or customer on the form.","Add **line items** by hand -- description, quantity, unit price. The price book is not involved; it is the chatbot's reference, not the estimate's.","Set the **Job Date** if one is agreed. A date that clashes with your calendar is flagged in amber, never blocked.","Set a **Deposit (fixed amount)** if you want one paid on acceptance. It only appears once Stripe is connected, and it is capped at the estimate total.","Add **Notes to Customer** -- they appear on the estimate the customer reads. Your **internal note** on the lead never does; when the estimate is approved it is copied onto the job (see [the internal note](/docs/junk-removal/quote-workflow)).","Fill in the **Margin Calculator** if you want to see what the job nets: the Dump Fee comes from your default [disposal site](/docs/business/disposal-sites), and labour, fuel and other costs are yours to enter.","Save as a draft, then send when you are ready."]},{type:"callout",tone:"info",title:"It sends by email and text together",text:"When the customer has both an email and a mobile number, the estimate goes out on both, and the text carries the same link. Email uses the lead's address, falling back to the customer record; if neither has one you can type an address at send time and CC yourself or the office."},{type:"h2",text:"What the customer can do"},{type:"p",text:"From the link the customer can read the line items and **accept it themselves** -- no login, no account. Acceptance marks the estimate approved and the lead converted, and notifies you. If the estimate carries a deposit, the same page then offers to **pay it** by card; accepting and paying are two separate steps, so a customer who accepts and closes the page has still accepted."},{type:"callout",tone:"key",title:"Acceptance creates the job",text:"With **no deposit**, the job appears on your schedule the moment the customer accepts, and they are told they are booked. With a **deposit**, the job is created when the deposit is paid; until then the estimate shows **Deposit pending: $X** and no job exists yet. The customer gets a confirmation with your notes, what they paid and the balance, and the paid deposit is credited against the job's final invoice."},{type:"p",text:"A **Convert to job** button still appears on an approved estimate that has no job yet -- an older one, or one waiting on a deposit you would rather not wait for -- and creates the job from the line items and the customer."},{type:"h2",text:"Revising and archiving"},{type:"p",text:"You cannot edit an estimate once it is sent, deliberately -- the customer is holding a copy of it. **Duplicate** closes the original as revised and opens a new draft with the same line items, so there is a record of what changed. **Archive** closes one out that is no longer live, which keeps your list honest without deleting the history. There is no automatic expiry: an estimate stands until you archive it."},{type:"p",text:"A **PDF** of the estimate is available to you from the estimate's preview page. The customer's link is the web page; there is no PDF attached to what they receive."},{type:"mistakes",items:[{wrong:'"The customer approved the estimate but the job is not on my schedule."',right:"Check whether the estimate carried a deposit. With one, the job is created when the deposit is paid, and the estimate shows **Deposit pending** until then. Use **Convert to job** if you want the job now regardless."},{wrong:'"I need to change a price on an estimate I already sent."',right:"Use **Duplicate**. It closes the original and gives you a fresh draft with the same lines, so the customer's copy and yours never silently disagree."},{wrong:'"My estimate lines did not pick up my price book prices."',right:"They never do. Estimate lines are typed by hand; the price book is what the **chatbot** quotes from. See [Range pricing](/docs/junk-removal/range-pricing)."},{wrong:'"Can the customer pay from the estimate?"',right:"A deposit, yes -- if you set one on the estimate and Stripe is connected, the accept page offers it. The balance is paid against the job or its invoice once the work is done."},{wrong:'"I set a valid-until date and it did not expire."',right:"There is no expiry date on an estimate. Archive it when it is no longer live."}]}]}]},{label:"Dumpster rental",kind:"reference",pages:[{slug:"dumpster-rental/booking-workflow",title:"The booking workflow",description:"How a dumpster rental moves through HaulHQ: the customer books and pays, you deliver, you pick up.",layer:2,workflow:"dumpster",blocks:[{type:"p",text:"A dumpster rental is a **booking**, not a quote. The customer picks a size, dates and any add-ons, sees the price, and checks out. You are not in the middle of that transaction -- your job starts when it completes."},{type:"video",title:"A rental from booking to pickup"},{type:"h2",text:"The path"},{type:"steps",items:["The customer picks a container size, rental window and add-ons on your booking page.","They check out. If Stripe is connected they pay the **full rental total** there and then; if not, the booking is recorded and you collect later.","HaulHQ creates a **job** and reserves an **equipment unit** -- a specific container -- for those dates. An online checkout that stops before paying shows as **Awaiting payment** and releases itself after two hours -- see [Booking & checkout](/docs/dumpster-rental/booking-and-checkout).","A **delivery** trip is scheduled, and a **pickup** trip for the end of the rental.","You deliver. Your driver marks it dropped and takes photos.","You pick it up, and the rental **finishes at the scale**: the driver enters the weight ticket at the dump, any weight charge is confirmed, and the customer gets their receipt -- see [Work orders & trips](/docs/dumpster-rental/work-orders-and-trips)."]}
1,{type:"callout",tone:"key",title:"One job, two site visits",text:"This is the structural difference from junk removal. A rental is a single job with a **delivery trip and a pickup trip**, days or weeks apart. Your schedule has to hold both."},{type:"h2",text:"What has to be set up for this to work"},{type:"list",items:["Each container size added as **equipment**, with a quantity -- see [Equipment & units](/docs/dumpster-rental/catalog-equipment).","**Add-ons**, if you want to offer any. They are items in the **Add-on** category with **Show in booking flow** switched on, and they are their own step in the booking flow. Create none and your customers are simply never offered any -- see [Equipment & units](/docs/dumpster-rental/catalog-equipment).","A **service area** with cities, not just zips -- see [Service area](/docs/business/service-area).","**Hours** and **booking capacity**, which live on two different screens -- see [Scheduling & business hours](/docs/business/scheduling-and-hours).","A booking page on your website carrying [`[haulhq_booking]`](/docs/wordpress/haulhq-booking)."]},{type:"mistakes",items:[{wrong:'"I will set up my dumpsters in Settings > Quote Form."',right:"That screen is junk removal only and has no effect on rentals. Dumpsters live under **Equipment**. This is the single most common wrong turn in the product -- see [the two workflows](/docs/start-here/two-workflows)."},{wrong:'"Customers can book but I never see availability limits working."',right:"Bookable units come from the **number of units** on each equipment item. Zero means nothing is bookable; more than your real fleet oversells you."},{wrong:'"The rental ended and the container was offered to someone else before we had picked it up."',right:"Availability follows the **booked dates**, not the pickup trip. A container is offered again from the day after its end date whether or not the pickup has happened. If a customer keeps it longer, check the calendar before agreeing -- see [Work orders & trips](/docs/dumpster-rental/work-orders-and-trips)."}]}]},{slug:"dumpster-rental/catalog-equipment",title:"Equipment & units",description:"Add each container size on the Equipment screen, set the number of units that creates bookable containers, and understand how a rental is priced and what customers see.",layer:2,workflow:"dumpster",blocks:[{type:"p",text:"The **Equipment** screen is what customers can buy. For dumpster rental it holds one **equipment** item per container size. Nothing on your website renders until this is filled in."},{type:"video",title:"Setting up your container catalog"},{type:"h2",text:"Add a container size"},{type:"steps",items:["Go to **Equipment** in the sidebar and add an item in the **Equipment** tab.",'Name it the way customers say it: "20 Yard Dumpster", not "RO-20".',"Set the **base price** -- what the rental costs for the days it includes -- then the rental terms below it.","Set the **Number of units** -- how many of this size you actually own.","Add specs (dimensions, weight limit, what fits) and a photo. These show on the product page and are what make it convincing.","Save. HaulHQ creates that many **equipment units** automatically. From then on you add or retire units in the item's **Units** editor, which is the one source of truth for how many exist."]},{type:"callout",tone:"key",title:"The number of units is your real fleet, not a wish",text:"Each unit is a specific container that can be reserved for specific dates. Set it to what you own. Too high oversells you; zero makes the size unbookable while still showing on your site."},{type:"h2",text:"Rental terms: how a rental total is built"},{type:"p",text:"The **base price** is a flat amount that covers a set number of days and a set weight. Everything else is charged only when the customer goes past one of those two limits. A rental total is: **base price + delivery fee + extra days + extra weight**, plus any add-ons the customer picked and any tax you charge."},{type:"callout",tone:"warn",title:"The base price is not a daily rate",text:"It is charged **once**, no matter how long the rental runs. If you set $350 on a container and the customer keeps it three weeks, the base is still $350 -- the extra time is billed by **Overage $/day** instead. Setting your daily rate in the base price field is the most expensive mistake you can make on this screen, and it undercharges you, so no customer will ever tell you."},{type:"table",head:["Field","What it does","What happens if you leave it blank"],rows:[["Base price","The flat price of the rental, 
1covering the included days and the weight limit.","The rental is free."],["Minimum rental days","The shortest window a customer is allowed to book. A booking limit, not a price.","Defaults to 1 day."],["Included rental days","How many days the base price actually buys.","Treated as **0**, so the per-day overage is charged from the very first day."],["Overage $/day","The daily rate for each day **beyond** the included days.","Extra days are **free**."],["How weight is billed","Three choices: **Included weight, then a rate** (so many tons in the base price, a rate past them), **All weight billed** (a weigh fee on every pound), or **No weight charge**.","Defaults to no weight charge."],["Tons included in the base price",'With "included weight" chosen: how much the base price covers, checked against the scale ticket at the dump.',"Cannot be left blank once that model is chosen -- the save is refused."],["Rate past the included weight / Weigh fee rate","What you charge per **ton or per pound** -- your choice, set beside the field.","Cannot be left blank once a weight model is chosen -- the save is refused."],["Delivery fee","A flat fee added once per rental.","No delivery fee."],["Number of units","How many of this container you own. Creates the bookable units.","Nothing can be booked."]]},{type:"h3",text:"Minimum days and included days are not the same number"},{type:"p",text:"This is the pair operators mix up most, because the names are nearly identical. **Minimum rental days** is the shortest window a customer may book. **Included rental days** is how many days your base price covers. They are often the same, and when they differ it is deliberate: a container with a 3-day minimum and 7 included days means nobody can book for less than 3 days, but nobody pays extra until day 8. Set the minimum to 7 with 3 included days instead, and every customer who books your own minimum window is immediately paying four days of per-day overage."},{type:"h3",text:"The two overages are different things"},{type:"p",text:'HaulHQ has **two** separate overage charges and they are never interchangeable. **Overage $/day** bills time past the included days, and it is calculated at checkout. The **weight charge** bills weight past the included tons -- or every pound, under "All weight billed" -- and it cannot be calculated until the container is on the scale, so it is charged after pickup, when your driver records the weight at the dump. Wherever you see the word "overage" on its own, check which one is meant.'},{type:"h3",text:"Price weight by the ton or by the pound"},{type:"p",text:"Haulers quote weight both ways and HaulHQ supports both. Pick the unit beside the rate field and enter the number the way you would say it to a customer: **$95.00 per ton**, or **$0.05 per pound** -- five cents. HaulHQ does the conversion when it bills, and shows the rate back to your customer in the unit you chose, so a customer quoted five cents a pound never sees a line reading dollars per ton."},{type:"callout",tone:"info",title:"The weight limit is always in tons",text:'Only the **rate** has a unit. The included weight stays in tons whichever way you price the weight charge -- which is how the trade quotes it anyway: "2 tons included, $0.05 per pound over".'},{type:"callout",tone:"warn",title:"Half a weight rule cannot be saved",text:'The included tons and the rate are halves of one rule, and the screen refuses to save one without the other: choose "Included weight, then a rate" and both fields are required, or choose "No weight charge" and neither shows. Under "All weight billed" only the rate applies. A container set up before this rule with a limit and no rate still charges nothing for weight -- open it and pick a model.'},{type:"h2",text:"A worked example"},{type:"p",text:"A 20-yard container set up with a **$350 base price**, **3 included days**, **2 tons included**, **$30 Overage $/day**, a **$95 per-ton rate** past the included weight and a **$95 delivery fee**. The customer books it for 6 days, no closed days fall in the gap, and it comes back from the dump at 3.4 tons."},{type:"table",head:["Line","How it is worked out","Amount"],rows:[["Base price","Charged once, covers the first 3 days and the first 2 tons","$350.00"],["Delivery fee","Flat, once per rental","$95.00"],["Extra days","6 days booked - 3 included = 3 extra days x $30","$90.00"],["**Paid at checkout**","Base + delivery + extra days","**$535.00**"],["Extra weight","3.4 tons - 2 included = 1.4 tons over x $95","$133.00"],["**Rental total**","Checkout total + the weight charged after pickup","**$668.00**"]]}
1,{type:"callout",tone:"key",title:"Two moments, two charges",text:"The customer pays **$535 when they book**. The **$133** weight charge cannot exist yet -- nobody knows what the container weighs until it is on the scale. It is billed after pickup, from the weight your driver records at the dump, and it can be adjusted or waived before it is charged."},{type:"h2",text:"Closed days in the middle of a rental"},{type:"p",text:"Rental days run from delivery to pickup. When the pickup lands after a day you are closed -- a Sunday, a holiday on your blocked dates -- the customer could not have had it collected earlier, and you decide whether those days count. The setting is **Closed-day pricing policy**, in the **Dumpster Rentals** card under **Settings > Scheduling**."},{type:"table",head:["Option","What it means"],rows:[["**Don't charge for closed-day gaps** (the default)","Closed days that fall after the included period are taken out of the extra days. The customer keeps the container over your closed days at no extra cost. Industry standard."],["**Charge for all days on site**","Every day between delivery and pickup counts, closed days included. The customer pays Overage $/day for every day past the included ones."]]},{type:"p",text:"The price the customer sees at checkout already reflects your choice, and a per-day add-on is charged for every day on site either way."},{type:"h2",text:"What you cannot skip, and what can wait"},{type:"p",text:"A container needs six things to be bookable and correctly priced. Everything else makes the listing better without changing the money."},{type:"table",head:["Essential","Why"],rows:[["Name","What the customer picks from."],["Base price","Zero means you are renting it for free."],["Number of units","Zero units means nobody can book it, however good the pricing is."],["Included rental days","Blank means Overage $/day starts on day one."],["Overage $/day","Blank means every extra day is free."],["A complete weight rule","Included tons and a rate, all weight billed, or no weight charge. The screen will not save half of one."]]},{type:"list",items:["**Optional, and worth doing:** photo, dimensions, capacity, what it is suitable for, prohibited items, delivery fee, short description. These sell the container. None of them change the price.","**Minimum rental days** sits in between: it changes nothing about the price, but it stops customers booking a window that does not work for you."]},{type:"h2",text:"Add-ons"},{type:"p",text:"Add-ons are items in the **Add-on** category, and they are their own step in the booking flow -- the customer sees them after picking a size and dates. Create one, set a price, and switch on **Show in booking flow** so it appears at checkout. Driveway protection boards, a lock, or a permit fee are the usual ones. A per-day add-on is charged for every day on site, closed days included. If you never create any, your customers are simply never offered any, and nothing on the booking page says so."},{type:"callout",tone:"warn",title:"Do not use an add-on to charge an overage",text:'It is tempting to create an "Extra day" or "Overweight fee" add-on. Do not. Overages are calculated automatically from the rental terms above, and an add-on is something the **customer chooses** at checkout -- nobody chooses to pay an overweight fee. An add-on priced "per ton" is charged as a single flat amount, because the weight is not known at checkout. Charging a weight overage twice, once through the rental terms and once through an add-on, is the failure this warning exists to prevent.'},{type:"h2",text:"What the customer sees before they book"},{type:"p",text:"Your container listing and the booking flow show the size, the price, the included days, the delivery fee and the **weight limit in tons**. The customer agrees to the checkout total before paying."},{type:"callout",tone:"warn",title:"Your weight terms are shown before they book",text:"The booking page, the checkout summary and the confirmation email all state your included weight and, if you have set one, your weight overage rate and that the weight comes from the disposal facility scale ticket. **If you have not set a rate, nothing is said about weight charges** -- which is correct, because none will be made. Set both fields and your customer sees the terms before they pay."},{type:"h2",text:"Equipment is not Vehicles"},{type:"p",text:"Two different things, and they are easy to confuse. The **Equipment** screen is what you rent *to* customers -- the containers. **Settings > Vehicles** is what you haul *with* -- your trucks and trailers. A truck never appears on your website."},{type:"h2",text:"The slug"},{type:"p",text:"Each item has a **slug**, a short url-safe name like `20-yard-dumpster`. You need it for [`[haulhq_product]`](/docs/wordpress/haulhq-product) when you give a size its own page, and for the `product` attribute on [`[haulhq_booking]`](/docs/wordpress/haulhq-booking). The item shows the ready-made shortcode with a copy button -- copy that rather than typing the slug, be
1cause a wrong slug renders nothing for visitors."},{type:"h2",text:"Categories"},{type:"table",head:["Category","What it is","Example"],rows:[["Equipment","Something the customer rents and you collect","20 yard dumpster"],["Service","Work you perform. The tab exists for junk removal companies; a dumpster-only company does not see it.","Junk removal, single item pickup"],["Add-on","Something extra the customer adds at checkout","Driveway protection boards, lock, permit"]]},{type:"mistakes",items:[{wrong:'"My dumpsters page is empty but I added the shortcode."',right:'The Equipment screen is empty, or the items are not in the **Equipment** category. `[haulhq_products type="equipment"]` shows equipment only.'},{wrong:'"I set up the container but nobody can book it."',right:"The number of units is zero, so there are no bookable units. Set it to what you own."},{wrong:'"I put my trucks in the catalog."',right:"Trucks go in **Settings > Vehicles**. The Equipment screen is what customers buy or rent."},{wrong:'"I set my daily rate as the base price."',right:"The base price is charged **once** for the whole rental, not per day. Your daily rate belongs in **Overage $/day**, which bills days beyond the included days. Setting a daily rate as the base undercharges you on every long rental."},{wrong:'"I set the included tons, so heavy loads get charged."',right:"Only with a rate beside them. Pick **Included weight, then a rate** and fill both fields, or **All weight billed** with a rate. A container saved before that chooser existed, with tons and no rate, charges nothing for weight until you open it and choose."},{wrong:'"Minimum rental days and included rental days are the same thing."',right:"**Minimum** is the shortest a customer may book. **Included** is what the base price covers. If the minimum is longer than the included days, every customer pays a per-day overage on the shortest booking you allow."},{wrong:'"I made an add-on for extra days so I can charge for them."',right:"Extra days are already charged automatically by **Overage $/day** in the rental terms. An add-on is something the customer chooses at checkout, and adding one for a day or weight overage bills it twice."}]}]},{slug:"dumpster-rental/booking-and-checkout",title:"Booking & checkout",description:'How the booking widget works, what the customer sees, what controls availability, discount codes, confirmations, and what "Awaiting payment" means.',layer:2,workflow:"dumpster",blocks:[{type:"p",text:"The booking widget is the checkout. It shows what you have, on the dates you can deliver, at the price you set, and takes the booking without you touching it."},{type:"video",title:"What the customer sees when they book"},{type:"h2",text:"What the customer does"},{type:"steps",items:["Picks a container size from your Equipment screen. There is no zip step on a rental; a delivery address outside your area is yours to catch when the booking arrives.","Picks a delivery date, then a pickup date, and can say whether **morning or afternoon** suits for the delivery. That is a **preference, not a slot**: it never blocks a booking, *No preference* is a normal answer, and you see it on the delivery job. Rental length is fully flexible above your minimum -- there are no fixed packages.","Picks add-ons, if you offer any.","Enters their delivery address and contact details, and types their name to accept your **rental agreement** if you have set one under **Settings > Pricing**, on the Rental tab.","Reviews the total on **Review & Pay**. If you have a usable [discount code](/docs/money/discount-codes), a field to type one is there.","Pays the **full total** by card, if Stripe is connected. There is no deposit on a rental. Without Stripe the booking is recorded unpaid and you collect."]},{type:"callout",tone:"info",title:"Your HaulHQ page books the same way",text:"The booking flow on your HaulHQ page is the same flow as the WordPress widget: the same steps, the delivery time preference, the card payment on Review & Pay, the discount code field when you have a usable code, and your rental agreement to accept. It also asks for placement instructions and what is going in the container, because the checkout needs both. One flow, two places."},{type:"h2",text:"What controls what"},{type:"table",head:["What the customer sees","Where you set it"],rows:[["Which sizes are offered","Equipment"],["Whether a size is available on a date","The quantity on that equipment item"],["The price","The base price and rental terms on the equipment item -- see [Equipment & units](/docs/dumpster-rental/catalog-equipment)"],["Which days can be picked at all","Settings > Scheduling (business hours, blocked dates)"],["How soon, and how far ahead, a delivery can be booked","**Settings > Pricing > Load Tiers** -- Earliest a customer can book, and How far out customers can book -- not Scheduling"],["How many you can deliver on one day","Your number of units. A container is offered while no booking holds it on those dates."],["Whether closed days in the gap are charged","Settings > Scheduling, the Dumpster Rentals card -- see [Equipment & units](/docs/dumpster-rental/catalog-equipment)"],["A discount code field","[Settings > Money >
1 Discounts](/docs/money/discount-codes)"]]},{type:"callout",tone:"warn",title:"The booking window is not on the Scheduling screen",text:"The earliest bookable day and how far ahead customers can book live under **Settings > Pricing > Load Tiers**. Everyone looks under Scheduling first and does not find them. See [Scheduling & business hours](/docs/business/scheduling-and-hours)."},{type:"h2",text:"After the booking"},{type:"p",text:"The customer gets a rental confirmation email. A job is created with a delivery trip and a pickup trip, and a specific container is reserved for those dates. You see it in your dashboard immediately."},{type:"h2",text:'"Awaiting payment" on a rental'},{type:"p",text:"When a customer reaches the card step online and stops, the booking already exists and holds a container. Its delivery trip shows an **Awaiting payment** label wherever the job's status is printed. **If it is still unpaid two hours after it was made, HaulHQ releases it on its own**: the booking is cancelled, the invoice voided and the container offered again -- silently, with no message to the customer or to you. Nothing to do; the label tells you which bookings are in that window."},{type:"p",text:"If the customer pays after the release, the money is recorded, the booking stays cancelled, and 
1you get one alert asking you to refund it or rebook them by hand."},{type:"h2",text:"Booking on the customer’s behalf"},{type:"p",text:"You can create the same booking from inside HaulHQ when someone phones. It produces the same job, the same trips and the same confirmation email as a self-serve booking. Two differences: a booking you enter is refused on a day you are closed, and it takes no discount code -- enter the agreed price yourself."},{type:"mistakes",items:[{wrong:'"The widget says nothing is available and I know I have containers."',right:"Check three things in order: the number of units on the equipment item, whether the date is inside your business hours or a blocked date, and **Earliest a customer can book** and **How far out customers can book** under Pricing > Load Tiers."},{wrong:'"Customers cannot book for tomorrow."',right:"**Earliest a customer can book (days)** sets the earliest bookable day, and it is under **Pricing > Load Tiers**. Set it to 0 for same-day."},{wrong:'"A booking says Awaiting payment. Did the customer pay or not?"',right:"Not yet. They started an online checkout and stopped at the card step. Leave it: it releases itself after two hours if still unpaid, and the label disappears the moment they pay."},{wrong:'"I want the booking widget on my junk removal site."',right:"Junk removal uses [`[haulhq_form]`](/docs/wordpress/haulhq-form), which already books pickups. See [the two workflows](/docs/start-here/two-workflows)."}]}]},{slug:"dumpster-rental/work-orders-and-trips",title:"Work orders & trips",description:"How a rental becomes scheduled work: the delivery trip, the pickup trip that ends at the scale, and what actually holds a container.",layer:2,workflow:"dumpster",blocks:[{type:"p",text:"A rental is one job with **two trips**: a delivery and a pickup. They are scheduled separately because they happen days or weeks apart, and the rental closes when the pickup ends at the scale."},{type:"video",title:"Delivery and pickup on the schedule"},{type:"h2",text:"Delivery"},{type:"steps",items:["The trip appears on the schedule for the booked start date.","Assign a driver.","The driver taps **Mark Dropped** and takes photos. On a rental they are labelled **delivery** automatically.","The container is now out. Its unit is reserved for the booked dates."]},{type:"h2",text:"Pickup, which ends at the dump"},{type:"steps",items:["The pickup trip sits on the booked end date. On your job list its action reads **Weigh In**, not Complete.","The driver taps **Load Secured** at the customer's address and photographs the container -- labelled **pickup** automatically -- then drives to the scale.","At the dump the driver taps **Enter Weight Ticket**: the site, the weight, the dump fee, a photo of the ticket. Saving it offers a pre-filled dump-run record for your own costs.","The weight charge, if any, is shown for confirmation. **Confirm** closes the rental: the customer is charged and gets their receipt, with the ticket on it. See [Disposal sites, dump runs and the scale ticket](/docs/business/disposal-sites)."]},{type:"callout",tone:"warn",title:"Availability follows the booked dates, not the pickup trip",text:"A container is offered again from the day after its booked end date, whether or not the pickup has been done. An open pickup trip is work to finish, not a hold on the unit -- so if a customer keeps a container past the end date, someone else may already have it for the next day."},{type:"h2",text:"Extending a rental"},{type:"p",text:"When a customer wants to keep the container longer, open either trip of the rental and use **Extend rental** on the Rental term card. Pick the new pickup date and the extra days are priced for you: days beyond the included period at the equipment's per-day rate, under your closed-day policy, with any per-day add-ons. Confirm, and the container stays held through the new date, the pickup trip moves, the extra days go on the rental's invoice as one line, and the customer gets the rescheduled-pickup message with the new date and the amount."},{type:"p",text:"The extra days are charged to the card on file if the customer paid online; otherwise they go on the invoice as a balance due with a payment link. If the container is already booked again, the date picker tells you the day it is next needed, which is the latest that rental can run. Moving the pickup trip by hand still does **not** extend the reservation -- use Extend rental."},{type:"h2",text:"Photos"},{type:"p",text:"Delivery and pickup photos are the rental equivalent of before/after. They protect you in a damage dispute, and a featured pair appears in your website gallery. See [Photos & your gallery](/docs/business/photos-and-gallery)."},{type:"mistakes",items:[{wrong:'"Availability is wrong -- it thinks containers are out that are back in my yard."',right:"A container is held for its booking's dates and nothing else. If the yard has one back early, it is still reserved until its end date; if the calendar shows one free that is still out, the booking has ended and the pickup is late. Check the booked dates, not the trip."},{wrong:'"The driver marked the pickup and the rental did not close."',right:"A rental closes at the **scale**, not at the customer's address. Load Secured is a step; Enter Weight Ticket and Confirm are what finish it."}]}]}]},{label:"Jobs & the day",kind:"reference",pages:[{slug:"jobs/the-job",title:"The job, start to finish",description:"Every status a job can be in, every button on the job page and what each one sends, and the two different ways a job finishes: on site for junk, at the dump for a rental.",layer:2,workflow:"both",blocks:[{type:"p",text:"A **job** is one piece of work with a date, an address, a price and a driver. It is the screen you spend your day on, and it is the same screen for both of your workflows: a junk lead becomes a job the moment you book it, and a dumpster booking creates a job for each trip the rental needs. Everything below applies to both, and where the two genuinely differ it says so."}
1,{type:"callout",tone:"key",title:"Where jobs come from",text:"Junk work is **quoted** first: a lead arrives, you talk to the customer, and booking it creates the job. A rental is **booked** and paid at checkout, and the booking creates a delivery job and a pickup job. If that distinction is new, read [Two workflows](/docs/start-here/two-workflows) before this page."},{type:"video",title:"The job, start to finish"},{type:"h2",text:"The statuses"},{type:"p",text:"A job is always in exactly one status. The list below is the whole set, read from the database rather than typed out, so it cannot fall behind the product."},{type:"table",head:["Status","What it means","Still on your list?"],rows:b},{type:"callout",tone:"info",title:"On hold is not a status",text:"A job **Booked with no date** is on hold. There is no separate On hold status, because a held job is exactly that: a real job that is not on the calendar. The Jobs list shows it as On hold and tells you how long it has been waiting."},{type:"h2",text:"The moves, and what each one sends"},{type:"p",text:"The job page offers you the moves that are legal from where the job is now. It will not offer a move it would then refuse. This table is generated from the rule itself, so it is the rule."},{type:"table",head:["Move","Legal from","What it records","Texts the customer?"],rows:j},{type:"callout",tone:"warn",title:"Only one move texts the customer",text:"Moving a job **forward** to Dispatched sends the on-the-way text. Moving it **back** from In progress to Dispatched does not re-send it, so correcting a mis-tap never texts the customer twice. Nothing else in the table above sends a message at all."},{type:"p",text:"The buttons you actually press are worded for the moment rather than for the status: **Schedule**, **Start Driving**, **Mark Arrived**, then **Collect Payment** or **Job Completed**. On a dumpster trip the same buttons read in the trade's words, so a delivery ends with **Dropped** rather than a junk phrase. The rollbacks (**Back to Scheduled**, **Back to In Progress** and so on) live under the three-dot menu."},{type:"h2",text:"Putting a job on hold"},{type:"p",text:"Use **Put On Hold** when the customer wants to move the job but does not yet know to when. It clears the date and the window, takes the job off the calendar and off your capacity, and tells nobody: the customer asked for this. The job stays in your Jobs list, marked On hold with the date it went on hold."},{type:"list",items:["Give it a date with **Set a date** and it goes back to Scheduled. The customer is told the **new date**, using the rescheduled message and never the first-booking confirmation.","It is offered from Booked, Scheduled and Missed only. A job whose truck is already rolling, or whose work is done, cannot be held.","It is not offered on a dumpster rental. A rental's delivery and pickup dates belong to its booking, so holding one trip would leave the booking saying something untrue."]},{type:"h2",text:"A job you did without the app"},{type:"p",text:"You did the work, took the money on site, and never touched your phone. **Close After Trip** in the three-dot menu closes that job in one step. It walks the same chain the normal flow walks, with two deliberate differences: it does **not** send the on-the-way text, because you are not on the way any more, and it does **not** invent a start time for work it did not watch. It records your completion time and nothing else."},{type:"p",text:"It appears only once the job's date has arrived or passed. Closing a job before its day is the normal flow's job, not this one."},{type:"h2",text:"Cancelling"},{type:"p",text:"**Cancel Job** asks for a reason (customer request, customer no-show, scheduling conflict, weather or safety, duplicate, or other, which needs a note) and then cancels the job, texts the customer, and puts a junk job's lead back where it was. A cancelled job keeps its reason and stays in the record; it is never deleted."},{type:"callout",tone:"info",title:"What happens to a paid deposit",text:"That is your cancellation policy, set under **Settings > Money > Billing & Payments** on the Invoicing tab, not on the job. See [Deposits](/docs/money/deposits)."},{type:"h2",text:"Finishing the job"},{type:"p",text:">>> **This is the one place the two workflows genuinely behave differently, and it is worth reading both columns even if you only do one of them.** <<< A junk job is finished when the truck pulls away. A rental is not finished until the load has been weighed, because until the container is on the scale nobody knows the final price."},{type:"compare",junk:["The crew finishes the work and presses **Job Completed** (or **Collect Payment**, which completes the job and opens the payment sheet in one press).","If you require a photo before completion and there is none, you are asked once before it goes through.","The job reaches **Completed**. You get the in-app alert, and the review request is queued.","Money is collected on site or billed afterwards. See [Collecting payment](/docs/money/collecting-payment)."],dumpster:["On the trip that **ends** the rental, the final button reads **Finish at the Dump**, and it does not complete anything yet.","The driver records the pickup on the job: photos, load secured, and the customer is told the container is gone. The job stays open on purpose.","At the scale, the driver enters the weight. The overage over the included tonnage is calculated and confirmed.","Only then does the job reach **Completed**, and only then do the final receipt and the review request go out."]}
1,{type:"callout",tone:"warn",title:"A mid-rental trip does not end the rental",text:"Completing a delivery, a swap, a dump and return or a relocation still alerts you and still updates the container, but it does **not** ask the customer for a review, because the customer still has the container. Only the trip that ends the rental does that."},{type:"h2",text:"Dumpster trip types"},{type:"p",text:"Every dumpster job carries a trip type, shown as a badge on the job and on the schedule. The last column is the one that changes what the buttons do."},{type:"table",head:["Trip","Badge","What it is","Ends the rental?"],rows:I},{type:"h2",text:"What else is on the job page"},{type:"list",items:["**Line items** are the price, edited in place. **Add Items** scrolls you to them. A job booked online with a price range arrives with a total of $0, and pressing Collect Payment opens the pricing step first rather than dead-ending.","**Photos** taken by the crew, before and after. Featuring a pair publishes it to your website gallery. See [Photos and your gallery](/docs/business/photos-and-gallery).","**The internal note** is yours. It is never shown to the customer and never sent anywhere.","**The SMS thread** is the conversation with the customer. Since COMM-06 it also shows the automated texts we sent on your behalf, marked as automatic, so the thread is the whole outbound record rather than half of it.","**The communications log** underneath is the audit trail: every templated email and text on this job and its invoices, with what happened to each one. It is collapsed by default.","**Source** says which ad or page produced the job, when we know. It renders nothing when we do not.","**Ask for a review** appears once the work is finished. See [Review requests](/docs/business/review-requests)."]},{type:"h2",text:"Missed jobs"},{type:"p",text:"A Scheduled or Booked job whose day has passed, plus your grace period, is marked **Missed** and you are alerted. Set the grace period under **Settings > Scheduling**; setting it to never turns the whole check off. A missed job is not stuck: **Resume Job** puts it back to In progress, and it can also be held or closed after the trip."},{type:"p",text:"A rental **pickup** that runs late is never marked missed. The customer kept the container, which is not your failure, and those rentals show up as overdue rentals with the extra days the customer owes. A missed **delivery** is still a missed job."},{type:"mistakes",items:[{wrong:'"I pressed Complete on a dumpster pickup and the job is still open."',right:"That is correct, and it is the one thing about rentals that surprises everybody. A rental completes **at the dump**, not at the kerb, because the weight decides the final price. Enter the scale weight on the job's driver screen and confirm the overage; the job completes then, and that is when the receipt and the review request go out."},{wrong:'"I put a job on hold and the customer got a text."',right:"A hold sends nothing at all. The message you are thinking of goes out when you give the held job a **new date** -- that is the rescheduled message, and it is deliberate: the customer needs to know the new day."},{wrong:'"I want to mark a job complete but it is still Scheduled and I already left."',right:"Use **Close After Trip** in the three-dot menu. Walking it forward manually would text the customer that you are on the way to a job you have already done. Close After Trip is the path that skips that text."},{wrong:'"I corrected a status by mistake and now the customer has two on-the-way texts."',right:"They cannot have. Only a **forward** move to Dispatched sends it. Rolling back and forward again is idempotent for the customer, and the communications log on the job shows you exactly what went out."},{wrong:'"An invoiced job is stuck. There is no way to finish it."',right:"If the work is covered by an invoice that has already been settled, use **Close as settled** on the job. It moves the job to Paid without recording a second payment, and it refuses if the money is not actually in. If the balance is genuinely still owed, collect it instead."},{wrong:'"The job page shows six texts but I know we sent more than that."',right:"Open the **Communications** section below the thread. The thread shows the conversation; the communications log shows every templated email and text on the job and its invoices, including ones sent automatically."}]}]}]},{label:"Your website",kind:"reference",pages:[{slug:"website/which-website",title:"Which website should I use?",description:"You have three options and one of them is already live and free. The real question is whether you need WordPress at all, so here is the honest answer.",layer:2,workflow:"both",blocks:[{type:"p",text:'Most operators arrive here assuming "my website" means WordPress. It does not have to. **If you finished onboarding, you already have a live website that takes bookings**, and a good number of operators never need anything else.'},{type:"video",title:"The three website options, side by side"}
1,{type:"callout",tone:"key",title:"The short answer",text:"**Start on your free HaulHQ page. It is enough to start taking jobs today.** Move to WordPress when you have a specific reason -- your own domain, multiple pages, or a real intention to publish content. Commissioning a website you did not need, getting no leads from it, and concluding the product does not work is the outcome this page exists to prevent."},{type:"h2",text:"The three options"},{type:"table",head:["Option","Cost","What you get","What you cannot do"],rows:[["**Your HaulHQ page** *(included, already live)*","Free","One page on `yourname.haulhq.app`: quote form or booking widget, your services, service area, reviews, job photos, and the chat widget","Use your own domain. Add more pages. Run a blog."],["**HaulHQ Website** *(add-on, not yet available)*","$20/month when it exists","A multi-page site built and hosted by HaulHQ","Nothing yet -- it is not built. See below."],["**Your WordPress site** *(you host it, we plug in)*","Your hosting and domain, plus whoever builds it","Your own domain, as many pages as you like, full SEO surface. The bridge plugin supplies the forms, booking, reviews and galleries.","Nothing HaulHQ imposes -- but you own the site, the hosting and the upkeep."]]},{type:"p",text:"Already have a site that is not WordPress? You are not stuck choosing between it and your HaulHQ page: the quote form and the booking flow can be pasted into any page that accepts raw HTML. See [Three ways to put HaulHQ on your website](/docs/website/html-embed)."},{type:"h2",text:"When WordPress actually earns its keep"},{type:"list",items:["**You want your own domain.** This is the honest headline reason. Your HaulHQ page lives on a `haulhq.app` address and cannot be moved to `yourcompany.com`.","**You want more than one page.** Service pages, city pages, an about page with real depth.","**You intend to publish content.** The more you invest in writing, the more the SEO surface of a site you own is worth -- and that compounds, so it matters more the longer you do it.","**You already have a WordPress site.** Then the question is not which to use; it is [which shortcodes go where](/docs/start-here/which-shortcodes)."]},{type:"callout",tone:"warn",title:"Be honest with yourself about the third one",text:"A WordPress site earns its keep through content and links, and both take sustained work. If nobody is going to write the pages, a WordPress site is a more expensive version of the page you already have -- with hosting bills and updates attached."},{type:"h2",text:"Both at once is fine, and common"},{type:"p",text:"These are not exclusive. Plenty of operators run a WordPress site as their main presence and keep the HaulHQ page as the thing they paste into a text message or a Google Business Profile. The booking flow is the same one either way, so a job booked on either lands in the same place."},{type:"mistakes",items:[{wrong:'"My page leads with junk removal and I am a dumpster company."',right:"**Settings > Website > Landing Page**, the **Service Labels** card: *What do customers mostly come to you for?* An account that has never answered leads with dumpsters whenever dumpster rental is one of its service types, so if yours leads with junk removal, either somebody picked that or dumpster rental is not on your account."},{wrong:'"I need a website before HaulHQ is useful."',right:"You have one. Finish onboarding and `yourname.haulhq.app` is live, takes bookings, and can be linked from your Google Business Profile, your truck, or a text message. See [Your HaulHQ page](/docs/website/haulhq-page)."},{wrong:'"The free page is just a placeholder until I build a real site."',right:"It runs the same quote and booking flow the WordPress plugin embeds -- it is the same product, on one page instead of yours. What it cannot do is use your domain, carry more pages, or host a blog."},{wrong:'"I will point my domain at my HaulHQ page."',right:"HaulHQ does not host on your domain -- the page lives at `yourname.haulhq.app`. What you can do is **forward** your domain to it at your registrar, so `yourcompany.com` lands visitors on your page; see [Point your domain at your HaulHQ page](/docs/website/point-your-domain). If you want your own domain in the address bar with pages of your own, that is [WordPress](/docs/website/wordpress-site)."},{wrong:'"I am paying for the $20/month website add-on."',right:"You are not, because it does not exist yet. If you are paying anyone for a website it is your own hosting or your own developer. See [HaulHQ Website](/docs/website/haulhq-website-addon)."}]}]},{slug:"website/haulhq-page",title:"Your HaulHQ page (included, already live)",description:"The free single page every tenant gets when onboarding finishes: what it renders, where the content comes from, what makes it go live, and what it cannot do.",layer:2,workflow:"both",blocks:[{type:"p",text:"When you finish onboarding, HaulHQ publishes a public page for you at **`yourname.haulhq.app`**. There is nothing to build and nothing to pay. It is the same booking and quote machinery the WordPress plugin embeds, arranged as one page."},{type:"video",title:"A tour of your HaulHQ page"},{type:"h2",text:"What is on it"},{type:"list",items:["**A header and cover area** with your company name, logo, headline and ph
1one number.","**Your quote or booking flow** -- the junk [quote form](/docs/junk-removal/quote-form), the dumpster [booking flow](/docs/dumpster-rental/booking-and-checkout) with card payment, discount codes and your rental agreement, or both if you run both verticals.","**Your services and equipment**, from the Equipment screen.","**About your business** -- description, founded year, jobs completed, team photo, highlights, and a **work gallery** of photos you upload yourself.","**Your service area**, from the cities you set.","**Reviews and testimonials.**","**A photo gallery**, which your crew fills by taking photos on jobs.","**The chat widget**, if you have it switched on.","**Schema.org markup**, so search engines can read your business details properly."]},{type:"h2",text:"Where the content comes from"},{type:"table",head:["On the page","You set it in"],rows:[["Headline, tagline, cover photo, colors","Settings > Website"],["About text, founded year, team photo, highlights","Settings > Website"],["Testimonials","The **Reviews** page, on its Testimonials tab. Settings > Website links you there."],["Work gallery (photos you upload)","Settings > Website, the **Work Gallery** card"],["Logo, company name, phone, address","Settings > General"],["Cities you serve","[Service area](/docs/business/service-area)"],["Services and equipment","The **Equipment** screen"],["Gallery photos from jobs","[Photos & your gallery](/docs/business/photos-and-gallery)"]]},{type:"callout",tone:"info",title:"Image sizes",text:"**Cover photo: 1600 x 1200 px or larger, landscape.** **Logo: 512 x 512 px square, or 800 x 320 for a wide wordmark** -- PNG or SVG with a transparent background looks best. These are the same numbers the hint text under each field gives you."},{type:"h2",text:"What makes it go live"},{type:"p",text:'Your address works from the moment your account exists, but until you are ready it shows a **"Our website is launching soon"** placeholder with your name and phone, and it is de-indexed from search. The full page goes live when **both** of these are true:'},{type:"steps",items:["Your **service area has at least one city**. Zips alone are not enough -- this is the single most common reason a site stays dark.","You have **something to sell**: at least one active service or piece of equipment set to show on your website, **or** at least one junk load size with a price range on it."]},{type:"p",text:"The onboarding page [Get your website live](/docs/start-here/get-your-website-live) walks the same two things in order."},{type:"callout",tone:"key",title:"Why there is a gate at all",text:"Without it, every new signup would get a public, indexable page before configuring anything -- which is exactly the payoff someone signing up to spam search results wants. The gate is about configuration, not judgement: finish setup and you are live."},{type:"h2",text:"How search engines find it"},{type:"p",text:"Once your page is live, HaulHQ tells the engines about it. There is nothing to submit and nothing to configure."},{type:"list",items:["Your site serves its own **sitemap** and a robots file that points to it, so Google can crawl it on its own schedule.","Bing, Yandex and the engines behind DuckDuckGo and the AI assistants are **notified directly** (through IndexNow) when your site goes live and whenever you change what it says. Google does not take those notifications.","Selected customer sites are linked from a public page on haulhq.app, which gives crawlers a path in."]},{type:"callout",tone:"info",title:"Found is not ranked",text:"This gets your page **into** the engines. Where it ranks is up to them, your reviews, and whatever else is competing for the same search -- nothing here promises a position."},{type:"h2",text:"Share your site"},{type:"p",text:"On **Settings > General**, once your page is live, a **Share your site** block gives you the address with one-tap options: **Copy link**, **Text past customers**, **More apps** for your phone's share sheet, and Facebook, X and LinkedIn. The same block appears when you finish onboarding."},{type:"h2",text:"Add it to a phone home screen"},{type:"p",text:"Once your page is live, a phone can add it to the home screen like an app: your company name under the icon, and 
1your logo on it (a wide logo is letterboxed on your brand colour, never stretched), or your initial on your brand colour if you have no logo. The prompt is the browser's own -- **Share > Add to Home Screen** on an iPhone, **Install** or **Add to Home screen** in the menu on Android. It needs a logo (Settings > General) or a brand colour (Settings > Website); with neither set, the browser offers nothing. Handy for a repeat customer, and for you: it is the quickest way to check your own page."},{type:"h2",text:"What it cannot do"},{type:"list",items:["**It cannot use your own domain as its address.** Tenant pages are served on `yourname.haulhq.app`. You can [forward your domain](/docs/website/point-your-domain) to it, but the address bar shows the HaulHQ address once it lands. If your own domain matters in the address bar, you want [WordPress](/docs/website/wordpress-site).","**It is one page.** No service pages, no city pages, no separate about page.","**There is no blog.**","**The layout is fixed.** You control the words, the photos, two colors and -- if you do both junk removal and dumpster rental -- which of the two goes first; you do not move sections around."]},{type:"h2",text:"If you do both: which one goes first"},{type:"p",text:"A page that offers both junk removal and dumpster rental puts two cards side by side, and one of them is open when a visitor lands. **If most of your work is dumpsters, that should be the dumpster card** -- and it is, by default, for any account with dumpster rental switched on."},{type:"p",text:"To change it, go to **Settings > Website > Landing Page** and find the **Service Labels** card -- the question reads *What do customers mostly come to you for?* Whichever you pick goes first on the page **and** in the menu at the top. If you only do one of the two, the question does not appear, because there is nothing to order."},{type:"callout",tone:"key",title:"It never changes on its own",text:"This is a setting, not a guess. It does not move because last month was busier for one side, and it does not move when you add equipment or change your prices. **Your page looks the same tomorrow as it does today unless you change it** -- which also means that if your business shifts, nothing updates it for you."},{type:"p",text:"None of that stops it taking jobs. Link it from your Google Business Profile, put it on your truck, and text it to callers -- it is a working booking page, not a placeholder."},{type:"mistakes",items:[{wrong:'"My page just says the website is launching soon."',right:"That is the placeholder, and it means one of the two live conditions is missing: a service-area city, or something to sell (an on-website service or equipment item, or a priced load size). Fix that and the full page appears."},{wrong:'"I cannot find my page in Google."',right:"Until both live conditions are met the page is deliberately de-indexed. After that it has a sitemap and Google crawls it on its own timetable -- HaulHQ notifies Bing and the other engines directly, but Google does not accept those notifications."},{wrong:'"I uploaded my logo as the cover photo and it looks wrong."',right:"They are two different fields. The logo is the small mark in the corner; the cover photo is the big landscape image at the top. Both are under Settings > Website, except the logo which is under Settings > General."},{wrong:'"My gallery is empty and I cannot find where to upload."',right:"The main gallery fills from photos your crew takes on jobs -- see [Photos & your gallery](/docs/business/photos-and-gallery). If you want to upload photos yourself, that is the **Work Gallery** card under Settings > Website, which shows in the About section."}]}]},{slug:"website/haulhq-website-addon",title:"HaulHQ Website (add-on, not yet available)",description:"The planned multi-page website product. It is named here so you know it exists and know that it is not something you can turn on today.",layer:2,workflow:"both",blocks:[{type:"callout",tone:"warn",title:"This is not available yet",text:"There is nothing to switch on, nothing to buy and nothing to ask for. This page exists so that when you see the name mentioned, you know 
1what it refers to and know it is not something you are missing out on today."},{type:"p",text:"HaulHQ Website is a planned add-on: a multi-page site built and hosted by HaulHQ, at **$20 per month** when it ships, sitting between the free single page you already have and running your own WordPress installation."},{type:"video",title:"HaulHQ Website"},{type:"h2",text:"What to do in the meantime"},{type:"p",text:"The three options that exist today are the ones to choose between, and [Which website should I use?](/docs/website/which-website) walks through that honestly. In short: your free [HaulHQ page](/docs/website/haulhq-page) is enough to start taking jobs; [WordPress](/docs/website/wordpress-site) is the answer when you need multiple pages, a blog, or your own domain in the address bar (a domain can also be [forwarded](/docs/website/point-your-domain) to the free page); and if you already run a site that is not WordPress, the [HTML embed](/docs/website/html-embed) puts the quote form and booking inside it."},{type:"p",text:"Nothing you set up now is wasted either way. Your catalog, service area, reviews, photos and booking settings are the same data whichever way your website is served, so moving between them is a change of front door, not a rebuild."},{type:"mistakes",items:[{wrong:'"Should I wait for this instead of setting up WordPress?"',right:"No. It has no date. If you need your own domain or more than one page today, WordPress is the option that exists."},{wrong:'"Am I already paying $20 a month for a website?"',right:"No. The add-on does not exist yet, so nobody is billed for it. Your HaulHQ page is free and always has been."}]}]},{slug:"website/wordpress-site",title:"Your WordPress site (you host it, we plug in)",description:"Running your own WordPress site with the HaulHQ bridge plugin supplying the forms, booking, reviews and galleries. What you own, what we supply, and where to start.",layer:2,workflow:"both",blocks:[{type:"p",text:"This is the option where **you own the website** -- your domain, your hosting, your pages, your theme -- and HaulHQ plugs into it. The bridge plugin gives you shortcodes that drop the quote form, the booking widget, your products, reviews, service area and photo gallery onto any page you like."},{type:"video",title:"How the WordPress bridge fits together"},{type:"callout",tone:"key",title:"The plugin is free, and it is the whole integration",text:"There is no separate charge for connecting a WordPress site. Install the bridge plugin, paste in an API key, and the same booking machinery that runs your [HaulHQ page](/docs/website/haulhq-page) runs on yours. A job booked through a shortcode on your site is identical to one booked anywhere else."},{type:"h2",text:"What you get that the free page cannot give you"},{type:"list",items:["Your own domain.","As many pages as you want -- service pages, city pages, a real about page.","A blog, and the SEO surface that comes with publishing.","Full control of design and layout through your theme."]},{type:"h2",text:"What you take on"},{type:"list",items:["Hosting, and the bill for it.","WordPress and plugin updates, and the security that goes with them.","Actually building and writing the pages, or paying someone to."]},{type:"p",text:"That is not a warning against it, it is the trade. If you are unsure whether it is worth making, [Which website should I use?](/docs/website/which-website) is the page for that decision."},{type:"h2",text:"Where to start"},{type:"steps",items:["Get your HaulHQ side configured first -- service area, the Equipment screen, hours. The shortcodes render what is in your account, so an empty account renders empty pages.","Install and connect the plugin. See [Install the WordPress plugin](/docs/start-here/install-the-plugin).","Work out which shortcodes you need and which page each one belongs on. See [Which shortcodes, and where](/docs/start-here/which-shortcodes).","Test a real booking or quote from your phone, on the live site rather than a preview."]},{type:"callout",tone:"info",title:'Two different things are called a "hero"',text:"The shortcode pages talk about pasting a snippet into **your home page hero** -- that is your WordPress theme's vocabulary for the big area at the top of a page, and it is what your theme will call it too. HaulHQ's own **Cover Photo**, under Settings > Website, is a different thing entirely: it is the image on your HaulHQ page and it has no effect on your WordPress site."},{type:"p",text:"The full shortcode reference follows below in this section, one page per shortcode, generated from the same definitions the plugin ships with."},{type:"mistakes",items:[{wrong:'"I need WordPress before HaulHQ will work."',right:"You do not. Your [HaulHQ page](/docs/website/haulhq-page) already takes bookings. WordPress 
1is the option you pick when you want your own domain and more pages."},{wrong:'"Do I pay HaulHQ for the WordPress plugin?"',right:"No. The bridge plugin is free. What you pay for is your own hosting and domain, and whoever builds the site."},{wrong:'"I installed the plugin and my pages are still empty."',right:"The shortcodes render what is in your account. An empty Equipment screen or a service area with no cities produces pages that load and show nothing -- which reads as a plugin fault and is not one."},{wrong:'"Can I keep both my WordPress site and my HaulHQ page?"',right:"Yes, and many operators do. The HaulHQ page is a useful thing to paste into a text message or a Google Business Profile even when your main site is elsewhere."}]}]},{slug:"website/point-your-domain",title:"Point a domain you own at your HaulHQ site",description:"If you own a domain that is parked or unused, you can forward it to your HaulHQ site in about five minutes at your registrar. Choose a permanent redirect, never masked forwarding.",layer:2,workflow:"both",blocks:[{type:"p",text:"Your HaulHQ site lives at an address like `your-company.haulhq.app`. If you already own a domain -- something you bought for the business and never built anything on -- you can send it to that address yourself, at the company you bought the domain from. It takes about five minutes and costs nothing."},{type:"callout",tone:"warn",title:'Choose "permanent redirect". Never choose "masked" or "cloaked"',text:"Your registrar will offer both, usually right next to each other, and the wrong one is often already selected. **Masked forwarding traps your site inside an invisible frame: Google cannot index it, your booking form and chat widget may stop working, and it behaves badly on phones. A masked forward is worse than doing nothing at all.** If you only remember one thing from this page, remember this."},{type:"video",title:"Forwarding a domain to your HaulHQ site"},{type:"h2",text:"What this does, and what it does not do"},{type:"p",text:"Your domain will **redirect**, not display. Someone who types your domain lands on your HaulHQ site, and the address bar then shows the `haulhq.app` address -- not yours. That is normal and it is how a redirect works, but if you were expecting your own domain to stay in the address bar you will think something is broken. It is not."},{type:"list",items:['**Good for:** a domain that is parked, unused, or only ever pointed at a "coming soon" page.',"**Good for:** a domain you bought to protect the name and have never built on.","**Not for you** if the domain already runs a website you use -- forwarding it would send your existing site away. Leave it alone."]},{type:"callout",tone:"info",title:"This is not custom-domain hosting",text:"HaulHQ does not host your site on your own domain, and this page is not a preview of a feature that is coming. Forwarding is a setting at your registrar, not something HaulHQ switches on."},{type:"h2",text:"Step 1: find your HaulHQ address"},{type:"steps",items:["In HaulHQ, open **Settings -> Website**.","Your site address is at the top of the **Landing Page** tab, with a **Copy** button next to it.","Copy it. It looks like `https://your-company.haulhq.app`."]},{type:"h2",text:"Step 2: set up the forward at your registrar"},{type:"p",text:"Every registrar calls this something slightly different. Find the row for your domain, then look for the wording below. In all of them you are pasting your HaulHQ address as the destination and choosing the **permanent (301)** option."},{type:"table",head:["Where you bought the domain","What the setting is called"],rows:[["GoDaddy",'Domain Settings -> **Forwarding** -> Add forwarding. Choose "Permanent (301)".'],["Namecheap",'Domain List -> Manage -> **Redirect Domain**. Choose "Permanent (301)".'],["Squarespace (formerly Google Domains)","Domains -> your domain -> **Forwarding**. Choose a permanent redirect."],["Cloudflare",'Rules -> **Redirect Rules** -> create a rule with status **301**. (Cloudflare has no simple "forwarding" box; a redirect rule is the equivalent.)']]}
1,{type:"callout",tone:"key",title:"The two settings to get right",text:'**Type: permanent / 301.** Not temporary, and not "masked", "cloaked" or "with frame". **Destination: your full HaulHQ address**, including `https://`. Forward the plain domain and the `www` version if your registrar offers both.'},{type:"h2",text:"Step 3: check it"},{type:"steps",items:["Wait a few minutes. Some registrars apply the change instantly; others take up to an hour.","Type your domain into a browser.","You should land on your HaulHQ site, and the address bar should now show your `haulhq.app` address.","If the address bar still shows your own domain, you have a **masked** forward. Go back and change it to permanent (301)."]},{type:"mistakes",items:[{wrong:'"The address bar still shows my domain -- it worked!"',right:"That is the masked forward, and it is the outcome to avoid. Your site is inside a frame, search engines will not index it, and your booking form may not work. Change the setting to permanent (301)."},{wrong:'"I will forward my main company website to my HaulHQ page."',right:"Do not do that unless you intend to retire the existing site. A forward sends every visitor away from it. If you have a working website already, look at the WordPress plugin instead -- it puts HaulHQ booking on the site you already have."},{wrong:'"Now my domain will show up in Google instead of the haulhq.app one."',right:"The opposite. A permanent redirect tells search engines the HaulHQ address is the real one, which is what you want -- it is a single site rather than two competing copies. Your domain still works as something to say out loud and print on a truck."},{wrong:'"Forwarding changed my email."',right:"It does not. Domain forwarding affects web visitors only. Email on your domain keeps working exactly as it did."}]}]},q,...S.g]},{label:"Business settings",kind:"reference",pages:[{slug:"business/scheduling-and-hours",title:"Scheduling & business hours",description:"Business hours, time slots, the instant booking picker, reschedule rules and blocked dates -- and where booking capacity and deposits actually live, which is not on this screen.",layer:2,workflow:"both",blocks:[{type:"p",text:"Settings > Scheduling controls **when you are open** and what happens around a booking. It does not control **how much** you take, which is the single biggest source of confusion in the product."},{type:"callout",tone:"warn",title:"Booking capacity is not on this page",text:"Maximum morning and afternoon bookings per day, the earliest bookable day (`buffer_days`), and how far ahead customers can book (`booking_window_days`) all live under **Settings > Pricing > Load Tiers**. Anyone configuring availability looks here first and never finds them."},{type:"video",title:"Hours, slots and capacity"},{type:"h2",text:"What is on this screen"},{type:"table",head:["Setting","What it does"],rows:[["Business Hours","Open and close time per day. A closed day cannot be booked online. If you rent dumpsters, each open day also carries a **Drop off** and a **Pick up** tick -- see below."],["Time Slots","Slot duration, max jobs per slot and the buffer between jobs. These shape the times the **chatbot** suggests. Customers booking on your page choose Morning (8-12) or Afternoon (12-5), which are fixed."],["Booking Rules","The chatbot's minimum lead time, in hours."],["Instant Booking Times","The **Show instant booking-time picker** switch and, with it on, the **Same-day cutoff time**. Junk jobs only -- see [Your quote form](/docs/junk-removal/quote-form)."],["Reschedule & Cancellation","Whether customers can reschedule themselves, how close to the appointment, and how many times."],["Missed Job Detection","How long past a scheduled time before a job is flagged as missed."],["Blocked Dates","One-off closures: holidays, a week off, a full yard. See below."],["Dumpster Rentals","The closed-day pricing policy -- see [Equipment & units](/docs/dumpster-rental/catalog-equipment)."]]},{type:"callout",tone:"info",title:"Deposits are not on this page either",text:"They used to be. Everything about deposits -- the amount, which job sizes, weekends and far-out bookings, and where the customer is asked -- 
1is one page under **Settings > Money > Deposits**. See [Deposits](/docs/money/deposits)."},{type:"h2",text:"What is on Pricing > Load Tiers instead"},{type:"table",head:["Setting","What it does"],rows:[["Max morning / afternoon bookings per day","How many bookings you accept in each slot per day."],["Earliest a customer can book (days)","0 means same-day is allowed; 1 means tomorrow at the earliest."],["How far out customers can book (days)","How far into the future customers can book."]]},{type:"p",text:"They live there for a historical reason -- capacity was originally a property of junk load tiers -- and they apply to both workflows. See [Load tiers & capacity](/docs/junk-removal/load-tiers)."},{type:"h2",text:"The order to set these in"},{type:"steps",items:["Set your business hours honestly. Everything else is filtered by them.","Set your time slots to match how you actually dispatch.","Go to **Pricing > Load Tiers** and set your per-day caps to what you can really service.","Set **Earliest a customer can book (days)** 
1-- 0 if you take same-day work, 1 if you need a day of notice.","Add known blocked dates before you go live, not after somebody books them."]},{type:"h2",text:"Drop-off days and pickup days (dumpster rentals)"},{type:"p",text:"A dumpster rental is two trips, and they do not cost you the same. A drop-off is twenty minutes. A pickup ends at the scale, and if the dump lines are long on a Saturday that pickup costs you the day. So each open day carries two ticks: **Drop off** and **Pick up**. Untick **Pick up** on Saturday and you can still win the weekend rental without spending Saturday in a queue."},{type:"callout",tone:"key",title:"The day switch still wins",text:"The checkbox at the front of the row is the one that says whether you work at all. **A closed day is closed for both trips**, whatever the two ticks say -- the ticks only split up a day you are already open on. Leave them both ticked (which is how every account starts) and nothing changes."},{type:"list",items:["Customers are not offered a day for a trip you do not run on it. The calendar will not let them pick it and the booking is refused if they try anyway.","When the pickup date it suggests lands on a day you do not collect, it moves to the next day you do, and the customer is told why.","Your own **New Booking** screen follows the same two rules, so you cannot book yourself a pickup you would not make.","**Your rental lengths do not change.** A rental runs for whatever was booked; it is not stretched because you do not collect on Sundays.","If your closed-day pricing policy is **Don't charge for closed-day gaps**, a day you will not collect on counts as one of those gaps -- the customer is not billed for a day they could not have ended the rental on."]},{type:"h2",text:"Blocked dates"},{type:"p",text:"A blocked date is a day nobody can book online, whatever your hours say. **Add Date** adds one row; **Add US Holidays** adds the federal holidays for this year and next in one go, skipping any already on the list. The list is kept in date order, and dates that have passed are folded away under *N past dates* with a **Show** toggle and a **Clear past dates** button -- a past blocked date does nothing anywhere, so clearing them is safe. Nothing is written until you press Save."},{type:"mistakes",items:[{wrong:'"I cannot find where to limit bookings per day."',right:"It is under **Settings > Pricing > Load Tiers**, not Scheduling."},{wrong:'"Customers cannot book tomorrow and I want them to."',right:"**Earliest a customer can book (days)** is set to 1 or more. Set it to 0. Also under Pricing > Load Tiers."},{wrong:'"I closed Saturday but a Saturday booking came through."',right:"Business hours gate new online bookings, not existing ones. A junk job you create yourself bypasses them; a dumpster booking you enter by hand does not -- it is refused on a closed day. Check the date was closed *before* the booking arrived."},{wrong:'"A customer could not pick Saturday for their dumpster and I am open Saturday."',right:"Check the **Pick up** tick on Saturday under Business Hours. If it is off, Saturday is still offered for the drop-off and refused for the pickup, which is what that setting is for. The message the customer sees says so -- it does not tell them you are closed."},{wrong:'"Where did Booking Deposits go?"',right:"To its own page: **Sett
1ings > Money > Deposits**, which now holds the amount, the job sizes it applies to and the channel switches together. See [Deposits](/docs/money/deposits)."}]}]},{slug:"business/service-area",title:"Service area",description:"Set the cities and zip codes you serve. Cities are what your public pages are gated on, and missing them keeps your site dark.",layer:2,workflow:"both",blocks:[{type:"p",text:"Your service area does two jobs: it tells you when a customer is outside your area, and it supplies the city names your public pages are built around."},{type:"callout",tone:"key",title:"Add cities, not just zips",text:"Zips are what the forms check. **Cities are what the public site gates on.** A tenant who enters zips only finishes setup and stays dark, with pages that render but say nothing. Fill in both."},{type:"video",title:"Setting your service area"},{type:"h2",text:"The fastest way: your zip and how far you drive"},{type:"p",text:"You do not have to type this out. Both the onboarding wizard and **Settings > General** have **Fill these in from your ZIP and a distance** above the two fields. Give it the zip where you park the truck and how far you drive, and it fills in every zip code around you and their city names."},{type:"steps",items:["Enter your **own zip code** -- where the truck is parked -- and pick a distance.","Press **Find**. You get every zip code within that distance, closest first, with its city.","Untick anywhere you will not actually drive. Two counties is 600,000 people and one truck does not serve all of it.","Optionally press **Check real driving distance**. The distances are straight lines to start with, so a zip across a river can be twice as far to drive; this measures the road distance and unticks anything past your limit. If it cannot reach Google it tells you and leaves your list alone.","If you already have a service area, choose whether to **add to it** or **replace it**. Nothing is replaced unless you say so.","Press **Use these zip codes**, then **Save**. Nothing is stored until you save."]},{type:"callout",tone:"key",title:"Your zip is also your yard zip",text:"The zip you type here is saved as your **yard zip code** under Settings > Pricing > Travel, because it is the same fact: where the truck starts. That is all it does -- it does not switch travel fees on and it does not set a rate. If you already have a yard zip set and this one is different, you are asked before anything changes."},{type:"h2",text:"Setting it by hand"},{type:"steps",items:["Go to **Settings > General** and find Service Area.","Add every **city** you serve, spelled the way a customer would search for it.","Add the **zip codes** you cover. The quote form checks against them: a zip that is not on the list does not stop the customer, it marks the lead **unconfirmed** so you check before dispatching.","Save, then load your public site and confirm the city name appears."]},{type:"h2",text:"Where it shows up"},{type:"list",items:["The zip check at the start of the booking widget and the quote form.","Your landing page headline and local search markup.","[`[haulhq_service_area]`](/docs/wordpress/haulhq-service-area) on your WordPress site."]},{type:"mistakes",items:[{wrong:'"My site says the city is wrong, or blank."',right:"The city list is empty or misspelled. It is read directly from your service area."},{wrong:'"I put my counties in and nothing works."',right:"A county name is not a city and is not a zip code, so it gates nothing and matches nobody. The quote form compares the customer's typed zip against your **zip code** list, exactly -- a county covering 500,000 people matches zero of them. Use the zip-and-distance tool above; it turns one zip into the real zips and real city names."},{wrong:'"A customer outside my area got through and booked."',right:"By design. An out-of-area zip never blocks anyone; the lead or job carries an amber **unconfirmed** badge so you decide whether to take it. Zips are matched exactly, not by proximity, so a neighbouring zip you serve but never listed shows the same badge."}]}]},{slug:"business/photos-and-gallery",title:"Photos & your gallery",description:'Job photos become the gallery on your website. There are two separate mechanisms for that, and mixing them up is why "I set it and nothing happened".',layer:2,workflow:"both",blocks:[{type:"p",text:"This is the feature nobody discovers on their own: **photos your crew takes on jobs become the gallery on your website.** An ops action produces a marketing asset, and n
1othing in the product hints at the connection."},{type:"video",title:"From a photo on a job to a photo on your website"},{type:"h2",text:"The path"},{type:"steps",items:["A driver photographs the job in the app.","Each photo is **labelled**. Junk removal: `before` and `after`. Dumpster rental: `delivery` and `pickup`.","A before and an after of the same job form a **pair**.","You **feature** the pair.","It appears in your website gallery."]},{type:"callout",tone:"warn",title:"A photo needs a labelled partner",text:'An unpaired photo never reaches the gallery. If a job has three "after" shots and no "before", none of them will show, whatever you tick. This is the usual reason a gallery looks emptier than the job list suggests.'},{type:"h2",text:"There are TWO galleries, and they are not the same thing"},{type:"p",text:"This catches people out, because both produce what looks like the same result on the same part of the page."},{type:"table",head:["","Featured job photos","Work Gallery"],rows:[["Where it comes from","Real jobs, photographed by your crew","Photos you upload yourself, up to ten"],["Where you manage it","The job, or **Settings > Online > Gallery**","The **Work Gallery** card under **Settings > Website**"],["Where it shows","The gallery section of your page, and the WordPress gallery shortcodes","A strip inside the About section of your HaulHQ page only"],["Best for","Ongoing proof of work","Launch day, before you have jobs"]]},{type:"p",text:"They do not replace each other: your page can show both at once, in different places. If you featured photos and nothing changed in the gallery section, the pair is probably incomplete. If you uploaded photos and they are not in the gallery section, that is correct -- uploads sit in the About section."},{type:"h2",text:"Featuring is a manual step"},{type:"p",text:"A pair goes public when you feature it, from the job or from the Gallery screen. Nothing is published without that, so no photo goes public by accident -- and it means a gallery does need a minute of your attention now and then."},{type:"h2",text:"On your website"},{type:"list",items:["[`[haulhq_gallery]`](/docs/wordpress/haulhq-gallery) -- a few photos as a section inside an existing page.","[`[haulhq_gallery_page]`](/docs/wordpress/haulhq-gallery-page) -- everything, on a dedicated page."]},{type:"p",text:"Both take a `heading` attribute, and both accept `none` to render no heading at all when your page already has one."},{type:"mistakes",items:[{wrong:'"I featured photos and nothing appeared on my website."',right:"The photos are not a labelled pair. A lone before or after can be ticked but never renders. Pair it and it appears."},{wrong:'"My drivers take photos but the gallery is empty."',right:"Photos have to be labelled and paired, then **featured** from the job or the Gallery screen. Until you feature a pair, nothing is public."},{wrong:'"I did not know my job photos went on my website."',right:"They do, once featured. Nothing is published without being featured first, so no photo goes public by accident."}]}]},{slug:"business/business-info-and-branding",title:"Business info & branding",description:"Your company details, logo and colors, and where each of them shows up for customers.",layer:2,workflow:"both",blocks:[{type:"p",text:"Settings > General holds the details customers actually see: on your landing page, in confirmation emails, on invoices, and in your local search markup. Your colors are the one exception -- they live under Settings > Website."},{type:"video",title:"Company details and branding"},{type:"h2",text:"What to fill in"},{type:"table",head:["Field","Where customers see it"],rows:[["Company name","Landing page, emails, invoices, texts"],["Phone","Landing page, emails. The number customers call back."],["Email","Reply-to on customer emails"],["Address","Invoices and local search markup"],["Logo","Landing page and emails"],["Business timezone",'Not seen, but everything time-based runs in it: the same-day cutoff, "today" on the schedule, reminders'],["Brand color and accent color (Settings > Website)","Buttons and accents across your public pages"],["Service area","See [Service area](/docs/business/service-area)"]]}
1,{type:"callout",tone:"info",title:"One color drives the whole palette",text:"Set your brand color and HaulHQ derives the hover, border and background shades from it. You are not asked to pick six colors that have to agree."},{type:"mistakes",items:[{wrong:'"Customer emails come from an address that is not mine."',right:"Platform emails send from a HaulHQ address with **your** email as reply-to. That is deliberate: sending as your own domain requires verifying it, and unverified sends get rejected outright."}]}]},{slug:"business/disposal-sites",title:"Disposal sites, dump runs and the scale ticket",description:"Where your loads go and what each trip costs: the standard fee that prices estimates, the dump-run log that records what you actually paid, and how a scale ticket reaches a rental invoice.",layer:2,workflow:"both",blocks:[{type:"p",text:"A disposal site is a place you tip: a landfill, a transfer station, a recycler. It sits under **Settings > Business > Disposal Sites**, right after Vehicles, because it is the same kind of thing -- a vehicle is what you haul with, a disposal site is where the load goes."},{type:"p",text:"Each site does **two jobs**. Its **standard fee** is the cost HaulHQ assumes when it prices an estimate. And the site is the **vendor** on every dump run and scale ticket you record, so your real disposal spend is grouped by where it went."},{type:"video",title:"Disposal sites, dump runs and the ticket"},{type:"h2",text:"Setting up a site"},{type:"steps",items:["Go to **Settings > Business > Disposal Sites** and press **Add Site**.","Enter the **Site Name** and the **Standard Fee** -- what a typical trip there costs you. Address and phone are optional.","Fill in **Opening hours** for each day the site is open. Your crew sees these before they drive.","Mark one site as **Default**. It is pre-selected on estimates and on the driver's forms.","To stop using a site, untick **Active**. It stays on the list, greyed out, so old runs keep their vendor, and it can be switched back on."]},{type:"h2",text:"Opening hours, and why they are worth filling in"},{type:"p",text:"A site with hours recorded shows its status to the driver **at the moment they pick it** -- on the Log a dump run screen and on the weight ticket at the scale. Open now, with the closing time; or closed, **with the time it opens**. That last part is the useful half: knowing it opens at 7 rather than 6 is the difference between waiting twenty minutes and driving somewhere else."},{type:"callout",tone:"key",title:"Leave a day blank if you are not sure",text:'A site with no hours shows the driver **nothing at all** -- it never says "closed". That is deliberate: guessing a site is shut would send a crew somewhere else for no reason, which is the same wasted trip in the other direction. Fill in only the days you actually know.'},{type:"h2",text:"How the standard fee prices your estimates"},{type:"p",text:"When you build a written estimate, the **Dump Fee (your cost)** in its Margin Calculator card is filled in from your default site's standard fee, and you can pick a different site from the list. That cost, with labour and fuel, is what the estimate's margin is measured against. The Margin tab under **Settings > Pricing** keeps your labour rates and the green/yellow/red thresholds; the dump fee itself is set here, on the site."},{type:"callout",tone:"warn",title:"A site with no fee makes every job look more profitable than it is",text:"A standard fee of $0 is read as a free tip. Every estimate against that site shows a margin with no disposal cost in it. Put a real number on every active site, even a rough one."},{type:"h2",text:"Logging a dump run (junk removal)"},{type:"p",text:"A junk truck goes to the dump with several jobs on it, so the cost is recorded per **run**, not per job. The person at the scale is the driver, so the form is built for a phone outside the truck."},{type:"steps",items:["In the driver app, tap **Dump Run** in the bottom bar. From the dashboard, the **+** button offers **Log a dump run**.","Enter what you paid. **The fee is the only required field.**","Pick **Where** -- your default site is pre-selected, and **Not sure yet** is allowed. Ticket number, weight and a photo of the ticket are optional and can be added later.","Save. If the photo fails to upload, the run still saves; the fee is never lost to a bad signal at the gate."]},{type:"p",text:"Runs appear under **Reports > Money Out >
1 Disposal**: totals for the period, a breakdown by site, the list of runs, and a CSV export. It is a record of what disposal cost you, kept apart from what customers paid."},{type:"h2",text:"The scale ticket on a dumpster pickup"},{type:"p",text:"A rental ends at the scale, not at the customer's kerb. When the driver picks the container up, the job says **Weigh the load to finish**, and the operator's job list shows the pickup's action as **Weigh In**. One capture at the scale does everything below."},{type:"steps",items:["The driver taps **Enter Weight Ticket** and fills in the disposal site, the weight and its unit, the dump fee you were charged, the waste materials, the ticket number and a photo of the ticket.","On saving, a **Log dump run** card appears, already filled from the ticket -- same fee, weight, site, ticket number and the same photo. Tap it to record the run as your cost, or **Skip**. Saving the ticket again updates that run rather than adding a second one.","The weight is compared with the rental's included weight and the overage is shown for confirmation. It can be adjusted or waived here. **Confirm** charges the customer and sends their final receipt."]},{type:"h2",text:"Where the ticket goes"},{type:"p",text:"Once a weight charge is on a rental invoice, the ticket travels with it. The customer is being asked to pay for something they did not agree to in advance, so the evidence is on every surface where they meet the charge:"},{type:"list",items:["The invoice in your dashboard, under the line items.","The customer's payment page.","The invoice PDF and the paid-receipt PDF.","The rental receipt email, as the same sentence plus a **View the scale ticket photo** link. The photo is linked, not embedded, because most mail apps block images and a blocked image reads as a broken receipt."]},{type:"p",text:"The caption states the scale weight, what was included, and the rate as you typed it -- per ton or per pound. With no photo, the numbers stand on their own."},{type:"callout",tone:"info",title:"Correcting a weight after the customer was billed",text:'The stored weight can still be corrected, but the charge does not move. The ticket then reads *"Weight later corrected to 5,400 lbs; the charge reflects the weight at billing"* wherever it shows, and the correction is written to the job\'s activity log. If the customer was over-billed, refund the difference from the invoice with a partial refund.'},{type:"mistakes",items:[{wrong:'"My estimates say every job makes money, even the ones that lose it."',right:"Check the standard fee on your default disposal site. A site with no fee puts $0 of disposal cost into every estimate's margin."},{wrong:'"I entered the ticket against the wrong site."',right:"The run's cost is filed under that site in Money Out. Open the run and change **Where**; on a rental, edit the weight ticket and save -- the linked run updates rather than duplicating."},{wrong:'"The customer says they never agreed to a weight charge."',right:"Their booking page, checkout summary and confirmation email state the included weight and your overage rate before they pay -- if you set both fields. Then the ticket itself is on the invoice and the receipt. See [Equipment & units](/docs/dumpster-rental/catalog-equipment)."},{wrong:'"I logged the dump run and the scale ticket, so the cost is in twice."',right:"On a rental pickup the two are one record: the ticket save offers the run pre-filled, and re-saving updates it. Logging a separate run from the Dump Run screen as well would double it -- use the card on the ticket."}]}]},{slug:"business/review-requests",title:"Review requests",description:"The automatic ask goes out after the job is paid, on a delay you set, to your Google review link. You can also ask for one by hand from any finished job.",layer:2,workflow:"both",blocks:[{type:"p",text:"A review request is a text or email to the customer with your **Google review link**. HaulHQ sends one on its own after each job, and you can send one yourself from the job. Both need the same three things set up first."},{type:"video",title:"Review requests: automatic and by hand"},{type:"h2",text:"What to set up"},{type:"table",head:["Setting","Where","Notes"],rows:[["**Google Review Link**","Settings > General","Without it nothing is sent, automatically or by hand: the request has nowhere to point."],["**When to ask**","Settings > Notifications",'**After the job is paid** (the default) or **After the job is completed**. Paid means the money has actually arrived -- a cash or check payment you record, a card payment, or a settled invoice. "Invoice later" is not paid.'],["**The delay**, in hours","Settings > Notifications","Counted from whichever moment you chose above. Two hours by default. A reminder can follow the first request after a further delay."]]}
1,{type:"callout",tone:"key",title:"Why the default waits for payment",text:"A customer who has just paid is at the happiest point of the job and has nothing left to dispute. A customer who has not paid may have a reason, and a review ask lands badly. So the request waits for the money by default, and a job that is completed but never paid is never asked automatically. Switch to **After the job is completed** if you invoice later and still want the ask to go out."},{type:"h2",text:"Asking by hand"},{type:"p",text:"On any **completed, invoiced or paid** job, **Ask for a review** sends the request now, on the desktop job page and on your phone. It skips the timing rule and the paid rule -- you are standing in front of a delighted customer, or you know the invoice is fine -- and nothing else. It still needs the Google link and a phone or email on the customer, it still honours a customer who opted out of texts (they get the email, or nothing), and **each job gets one request**: a second press, or a press that the automation beat by a second, is told a request has already been sent and sends nothing."},{type:"list",items:["A job that is not finished (booked, scheduled, on the way, in progress, missed) is refused: there is nothing to review yet.","A cancelled job is refused.","The **Reviews** page shows what came back: Google reviews on one tab, testimonials you can publish to your site on another, and private feedback on the third."]},{type:"mistakes",items:[{wrong:'"Jobs are completing and nobody is being asked for a review."',right:"Two things to check. Is a **Google Review Link** set on Settings > General? Nothing sends without it. And is **When to ask** set to after payment while those jobs are being invoiced later? A completed-but-unpaid job is never asked automatically; use **Ask for a review** on the job, or switch the setting to after completion."},{wrong:'"I pressed Ask for a review and it says one was already sent."',right:"Correct: one request per job, whoever sent it. The automatic request had already gone out, or the button was pressed before. Look on the job's activity for when."},{wrong:'"The customer opted out of texts, so I cannot ask them."',right:"You can. The ask goes by email when one is on file. Only a customer with no email and a text opt-out gets nothing, and the screen tells you which channel was used."}]}]}]},{label:"Money",kind:"reference",pages:[{slug:"money/deposits",title:"Deposits",description:"One deposit amount, the job sizes and days it applies to, and where the customer is asked -- on the booking confirmation, in the email, and in the chatbot.",layer:2,workflow:"both",blocks:[{type:"p",text:"A deposit is an **offer, not a gate**. The job is booked whether or not the customer pays it. What you set here is how much to ask for, which jobs to ask on, and which channels do the asking. All of it lives on one screen: **Settings > Money > Deposits**."},{type:"callout",tone:"key",title:"Dumpster rental has no deposit",text:"A rental is **paid in full at checkout** -- the customer pays the whole booking total before you deliver, and any weight charge is billed after the scale ticket. Everything on this page is about **ju
1nk removal**. A dumpster-only company does not see the Deposits screen at all."},{type:"video",title:"Setting up deposits"},{type:"h2",text:"How much"},{type:"p",text:"One amount. The screen says it plainly: *this is the amount everywhere -- your booking form, your website and the chatbot all ask for the same figure.* There is no second deposit setting on another screen."},{type:"h2",text:"Which jobs need one"},{type:"p",text:"Your load sizes are listed as **checkboxes**, each showing your own label and price range. Tick the sizes that should ask for a deposit. Below them, *Also ask when*: **weekend** bookings, and bookings **more than N days out**."},{type:"callout",tone:"warn",title:"Nothing ticked means never ask on size",text:'An empty list is a real choice -- "I do not want a deposit on any job" -- not "every job". The weekend and days-out rules still work on their own, so you can ask on weekend work without ticking a single size.'},{type:"p",text:"A size with no price shows **No price set** next to its checkbox. It can still be ticked. The rule is about the size the customer chose, not the price, so an unpriced size works fine."},{type:"h2",text:"Where customers are asked"},{type:"p",text:"Two switches, separate on purpose -- *you can have the chatbot insist on a deposit while a confirmation email only offers a link.*"},{type:"table",head:["Switch","What happens"],rows:[["Include a payment link on booking confirmations",'The customer sees **"Pay $X now to lock in your spot"** on the booking confirmation screen the moment they book, with *"Prefer to pay on the day? No problem -- just close this."* beneath it. The confirmation email carries the same link. Paying opens in a new tab; the booking is already made.'],["The chatbot asks for a deposit before it books","The chatbot asks for the deposit as part of booking. A chat never tells us the load size, so with specific sizes ticked the chatbot asks only when a weekend or days-out rule matches. With **every** size ticked it always asks."]]},{type:"p",text:"The email never asks twice: if the customer paid on the confirmation screen, the email that follows has no payment link."},{type:"h2",text:"What you see on the job"},{type:"list",items:["**Requested** from the moment of booking, when a deposit was offered.","**Paid** once the customer pays it. The balance due on the job drops by the deposit, and Collect Payment offers the remainder.","A paid deposit never changes the job's status. The job stays scheduled until your crew completes it."]},{type:"h2",text:"Asking for a deposit on a phone booking"},{type:"p",text:"When a customer agrees a job on the phone, open the job and use **Request deposit**. Type the amount -- it is pre-filled from your setting, and the number you type always wins -- then send it by text or email. The customer gets a pay link. Owners, managers and dispatchers can do this; drivers cannot."},{type:"list",items:["An amount larger than the job total is refused, not quietly reduced.","Sending again reuses the same request -- **Resend deposit** -- so a customer is never asked for two.","The job runs either way. An unpaid deposit blocks nothing."]},{type:"callout",tone:"info",title:"The cancellation policy is elsewhere",text:"What happens to a paid deposit when the customer cancels is set under **Settings > Money > Billing & Payments**, on the Invoicing tab, not here."},{type:"mistakes",items:[{wrong:'"I set a dollar threshold and the deposit never fires on my biggest jobs."',right:"There is no dollar threshold any more, and this is why. A value rule compares against the size's price -- and the biggest size is usually the one with **no price set**, so it could never match. Tick the sizes themselves."},{wrong:'"I ticked the sizes, but the chatbot still asks on everything."',right:"The chatbot has its own switch, *The chatbot asks for a deposit before it books*, separate from the confirmation link. And a chat conversation never tells us the size, so it asks on the weekend and days-out rules only. Turn its switch off if you want it to stop."},{wrong:'"I want a deposit on dumpster rentals."',right:"A rental is paid in full at checkout, so there is nothing left to take a deposit against. Rentals booked by phone can be sent a payment link from the job instead."},{wrong:'"I cannot find deposits under Scheduling."',right:"They moved. Everything about deposits is on **Settings > Money > Deposits**. Scheduling holds hours, blocked dates and the reschedule rules."}]}]},{slug:"money/discount-codes",title:"Discount codes",description:"Private codes a customer types at the dumpster checkout: flat or percent, dated, limited to specific sizes, with usage per code. Junk removal uses promotions instead.",layer:2,workflow:"both",blocks:[{type:"p",text:"A discount code is a private word you give to a customer -- a repeat contractor, a neighbour, a referral -- that they type at checkout. You create them under **Settings > Money > Discounts**."}
1,{type:"callout",tone:"key",title:"Codes work on the dumpster checkout only",text:'A code is typed on the **Review & Pay** step of an online rental booking. Junk removal has no checkout to type one into -- junk is quoted, then booked -- so junk uses **"book online and save" promotions** on load sizes instead. They are on the other tab of the same Discounts screen and get their own page.'},{type:"video",title:"Creating and using a discount code"},{type:"h2",text:"What a code is"},{type:"table",head:["Field","What it does"],rows:[["Code","The word the customer types. Letters, numbers, dashes and underscores. Matching ignores case and spaces, so `save10` and `SAVE10` are the same code."],["Type","**Percent off** or **Dollar amount off**."],["Starts / Last day it works","Optional. Both dates are inclusive, on your calendar."],["Active","Off means the code is refused, whatever its dates say."],["Which rentals","**All rentals**, or a list of specific container sizes. A code limited to sizes is refused on any other size."]]},{type:"h2",text:"What the customer sees"},{type:"steps",items:["They reach **Review & Pay** in the booking flow. A **Discount code** field is there.","They type the code and press **Apply**. The total updates, or a sentence says why not: expired, not started yet, not valid on that size.","They pay the discounted total. The card is charged that amount in full."]},{type:"callout",tone:"info",title:"The field only appears once you have a usable code",text:"A customer never sees an empty Discount code box. It shows up on your checkout the moment one active, in-date code exists, and disappears again when none does."},{type:"h2",text:"What it comes off"},{type:"p",text:"A code reduces the **base price, delivery fee, extra days and add-ons** -- everything the customer agrees to at checkout. Tax is recalculated on the discounted figure. It **never** touches the weight charge, because that is settled after pickup from the scale ticket and is a cost passed through, not a price you set."},{type:"h2",text:"What you see"},{type:"list",items:["A visible line on the job and on the invoice: **Discount SAVE10 (10% off)**, with the negative amount, so the totals still add up.",'Usage under each code: *"Used on 3 paid bookings, $95.50 off in total."* A checkout that applied the code and never paid is shown but **not counted**.',"The code in your Stripe dashboard, on the payment's details."]},{type:"h2",text:"The rules you will hit"},{type:"list",items:["**A code that takes the total under $0.50 is refused online.** A card cannot be charged nothing, so the customer is told to contact you to book instead. A free rental is a phone call.","**A code that has been used can be turned off, but not deleted.** Its cost stays visible in your usage. A code that was never applied can be deleted.","**Bookings you make yourself do not take a code.** When you book on a customer's behalf, enter the agreed price directly.","The whole feature has a switch under **Settings > Platform > Features** (Discount codes). Off, no field shows and every code is refused."]},{type:"mistakes",items:[{wrong:'"I made a code and my checkout does not show a place to type it."',right:"Check the code is **Active** and inside its dates, and that the Discount codes feature is on. The field appears only when a customer could actually use a code."},{wrong:'"A customer used a code and the weight charge was not discounted."',right:"Correct. The weight charge is a cost from the scale, added after pickup. Codes reduce the checkout total only."},{wrong:'"I want to delete a code so it stops working."',right:"Turn it off. A used code cannot be deleted, so that its cost stays on the record. Off has the same effect on the customer."},{wrong:'"I want a junk removal customer to get $20 off for booking online."',right:"That is a **promotion**, not a code -- the Junk Removal Promotions tab on the same screen. A junk customer never types anything; the discount shows in the price they see."}]}]},{slug:"money/travel-fees",title:"Travel fees",description:"A per-mile charge for jobs far from your yard: one rate, a free radius, a maximum, and road miles measured once per ZIP. Junk is one round trip; a dumpster rental is two, charged as one line.",layer:2,workflow:"both",blocks:[{type:"p",text:"A travel fee is what you charge for the drive. It is worked out from **road miles between your yard and the job's ZIP code**, and it appears as its own line on the customer's price, the job and the invoice. Everything about it lives on one tab: **Sett
1ings > Pricing > Travel**. The switch that turns it on is under **Settings > Platform > Features** (Travel fees), and it is **off** until you turn it on."},{type:"callout",tone:"key",title:"Off means not computed",text:"With the feature off there is no travel line anywhere: not on the form, not on a booking, not on an estimate. Nothing is hidden and waiting; the fee is simply never worked out. Turning it on measures your ZIPs first, and if that cannot be done the switch stays off and the screen tells you why."},{type:"callout",tone:"info",title:'It is on your setup checklist, and "not for me" is a real answer',text:"The setup checklist (the **Setup** button in the header) carries **Decide whether to charge for the drive** under *Fine-tune and grow*. It ticks when all four settings below are filled in **and** every ZIP in your service area has a measured distance -- because a travel fee with no measured distances charges nothing on any job. If you are never going to charge for the drive, press **Not for me** and the row clears for good. That records your answer and changes no setting: it does not switch travel fees on and it does not set a rate."},{type:"p",text:"One thing worth knowing before it surprises you: **if you add a ZIP code to your service area later, the checklist row goes back to unticked.** That is not a bug. The new ZIP has no measured distance yet, so it genuinely carries no travel fee until you open this tab and re-measure. A row you pressed **Not for me** on does not come back."},{type:"video",title:"Setting up travel fees"},{type:"h2",text:"The four settings"},{type:"table",head:["Setting","What it does","Default"],rows:[["**Yard ZIP code**","Where the truck starts. Every distance is measured from here. Changing it re-measures every ZIP.","none -- required"],["**Rate per mile**","Applied to every billed mile, in both directions.","$1.00"],["**Free radius (miles)**","ZIPs within this many **one-way** miles of the yard are included: no fee.","10 miles"],["**Maximum fee**","Per job. The fee is clamped here after the rate is applied. A ZIP that hits it is marked **capped** in the table.","$150"]]},{type:"p",text:"There is **one rate for every vertical**. A dumpster rental costs more in travel than a junk pickup because it is two trips, not because it has a different rate."},{type:"h2",text:"How the fee is worked out"},{type:"p",text:"The formula, exactly as the product applies it:"},{type:"code",code:"billed miles = (one-way miles - free radius, floored at 0) x 2 x trips\nfee = billed miles x rate per mile, clamped at the maximum",caption:"Stored miles are one way. The radius comes off first, then the round trip doubles it."},{type:"callout",tone:"warn",title:"The two places people read this wrong",text:"**The free radius is subtracted BEFORE the doubling**, because it is a distance from your yard, not a number of miles driven. A ZIP 40 miles out with a 10-mile radius bills 60 miles (30 beyond the radius, there and back), not 70. **And the miles stored against each ZIP are ONE-WAY.** The table on the Travel tab shows one-way miles in its own column and the fee beside it; the fee already includes the return."},{type:"h2",text:"A worked example, on the defaults"},{type:"p",text:"With the shipped defaults ($1.00 a mile, a 10-mile free radius, a $150 maximum):"},{type:"table",head:["One-way miles","Junk removal (1 trip)","Dumpster rental (2 trips)"],rows:[["8","**included** -- inside the radius","**included**"],["25","15 beyond the radius x 2 = 30 miles = **$30**","15 x 2 x 2 = 60 miles = **$60**"],["90","80 x 2 = 160 miles = $160, **capped at $150**","320 miles, **capped at $150**"]]},{type:"h2",text:"How many trips a job is"},{type:"table",head:["Job","Trips","Where the line appears"],rows:[["Junk removal","1","One line, **Travel**, on the job."],["Dumpster rental (delivery and pickup)","**2**","One line, **Travel, delivery and pickup**, on the **delivery** trip. The pickup row carries no money, so both trips are charged together at checkout."],["Swap, dump-and-return, relocate","1 each","Its own **Travel** line on that trip's row."],["Extending a rental","0","An extension creates no trip and no travel line."]]},{type:"h2",text:"Where the customer sees it"},{type:"list",items:["**The junk quote form**: **+ Travel: $30** under the price range for the ZIP they typed, then again on the booking review step and the confirmation.","**Estimates**: a **Travel** row is added from the job's ZIP when you build one, and it goes to the job when the customer approves. Auto-estimate emails and texts carry the same label after the range.","**The dumpster checkout**: a **Travel, delivery and pickup** line on **Review & Pay**, beside the dated trips, on your HaulHQ page, the WordPress widget and a booking you enter yourself.","**Invoices and receipts** print the line as it sits on the job. **It is taxable**, like the dump fee, and its tax is worked out on its own so the totals add up."]},{type:"h2",text:"Distances are measured once, not per quote"},{type:"p",text:"Road miles come from Google, **once, when you configure**, and are stored against each ZIP in your [service area](/docs/business/service-area). No quote, booking, estimate or invoice ever calls out; the fee is arithmetic over the stored number, so it is fast and it is the same on every screen. Measuring happens when you save the yard ZIP, when you turn the feature on, and when you add or remove a ZIP on **Settings > General**. A ZIP added later is measured on that save. **Re-measure** on the Travel tab does the whole list again, and the tab shows when it last ran: *Priced 30 of 30 zips.*"}
1,{type:"callout",tone:"info",title:"The table recalculates as you type",text:"The **Fee by ZIP code** table on the Travel tab recomputes on every keystroke, sorted most expensive first, with the priciest ZIP called out above it and a column per vertical. Until you press Save it is a preview, and the screen says so. Type until the number matches what a run actually costs you."},{type:"h2",text:"ZIPs that cannot be priced"},{type:"list",items:["**A ZIP on your list with no stored distance** (measuring failed for it, or it was added and never measured) charges nothing, and the job's notes say so: *Travel fee not applied: no distance is stored for ZIP 85750. Re-measure in Pricing > Travel.* You can still add the fee by hand as a line item.","**A ZIP that is not on your service-area list** changes nothing. No fee, no note. Serving it is your call when the lead arrives.","**A job with no ZIP at all** charges nothing and says so in the notes.","**A service area built from cities with few ZIPs** gets a warning when you enable, not a refusal: travel fees price by ZIP, so a cities-mostly area has little for them to work with. Add the ZIPs you serve under Settings > General."]},{type:"p",text:"Turning the feature on is refused, with the reason on screen, when there is no yard ZIP or no US ZIP in your service area (travel fees are US-only). An entry that is not a ZIP, such as several ZIPs pasted into one line, is named so you can fix it."},{type:"h2",text:"Waiving it on one job"},{type:"p",text:"The travel line is a line item like any other. Delete it on the job and the totals recompute without it. That is a decision you make per job; the setting stays on for the next one."},{type:"mistakes",items:[{wrong:'"The radius is 10 miles and a 40-mile ZIP is billing 60 miles. It should be 70."',right:"The radius comes off the **one-way** distance before the round trip is doubled: (40 - 10) x 2 = 60. It is a distance from your yard, so it is taken off once, not once each way."},{wrong:'"I want a higher rate for dumpsters than for junk."',right:"There is one rate. A rental already bills two trips to a junk job's one, so a 25-mile rental pays double the travel of a 25-mile pickup at the same rate."},{wrong:'"I added a ZIP to my service area and jobs there have no travel fee."',right:"Check the job's notes for *no distance is stored for ZIP*. Saving the ZIP list on Settings > General measures new ZIPs; if that failed, press **Re-measure** on the Travel tab. Until a distance is stored the ZIP charges nothing rather than guessing."},{wrong:'"My service area is cities, and the Travel tab has almost nothing in its table."',right:"Travel fees price by ZIP code. Enabling on a cities-mostly area warns you for exactly this reason. Add the ZIPs you serve under Settings > General and they will be measured and priced."},{wrong:'"The table says 25 miles but the fee looks like it is for 30 or 60."',right:"The miles column is **one way**. The fee column already includes the return trip and, for a rental, both trips. 25 one-way miles with a 10-mile radius is 30 billed miles for junk and 60 for a rental."},{wrong:'"A travel fee is showing on an extension."',right:"It cannot be. Extending a rental creates no trip. If a rental shows a travel line, it is the delivery row's **Travel, delivery and pickup** line, which covers both trips of the rental at booking."}]}]},{slug:"money/collecting-payment",title:"Collecting payment",description:"The Collect Payment sheet on the job and on the driver's phone: cash, check, card, Tap to Pay, a payment link, Venmo, Cash App, Zelle, invoice later, and what each one records.",layer:2,workflow:"both",blocks:[{type:"p",text:"**Collect Payment** is the same sheet everywhere it appears: on the job page, on the driver's phone, and at the end of a completion. It shows what is owed, offers the methods you have turned on, and records the money in one place no matter which one you pick."},{type:"callout",tone:"key",title:"Every collection has an invoice behind it",text:"You do not have to create one. If the job has no invoice yet, taking a payment creates it, numbers it and marks it paid, and the customer gets a re
1ceipt. That is why the money always reconciles, and it is why there is a record even for cash. See [Invoices and receipts](/docs/money/invoices-and-receipts)."},{type:"video",title:"Collecting payment on the job"},{type:"h2",text:"Where the button is"},{type:"list",items:["On the **job page**, once the work is done. Pressing **Collect Payment** on an in-progress junk job completes it and opens the sheet in one press.","On the **driver's job screen**, so the crew can take money at the kerb without calling the office.","From an **invoice**, by sending the customer a payment link instead. That path is on [Invoices and receipts](/docs/money/invoices-and-receipts)."]},{type:"h2",text:"What is on the menu"},{type:"p",text:"The sheet only offers what you can actually complete. Everything that moves money through Stripe is hidden until Stripe Connect is connected under **Settings > Money > Billing & Payments**, and the person-to-person options appear only for the handles you have filled in there."},{type:"table",head:["Option","When it is offered","What happens"],rows:[["**Tap to Pay**","Stripe connected, the feature enabled, and 
1you are in the HaulHQ app on a phone that supports it","The customer taps a card or phone against the back of yours. Nothing else to carry."],["**Card**","Stripe connected","You key the card in yourself, with the tip step if you have tipping on."],["**QR / Pay Link**","Stripe connected","Creates the customer's payment page. Hand them the device, show the QR code, copy the link, or send it to them (below)."],["**Cash**","Always","Type what they handed you and the sheet works out the change."],["**Check**","Always","Confirm the amount and photograph the check. The photo is kept with the payment."],["**Invoice Later**","Stripe connected","Nothing is collected. The job moves to **Invoiced** so it shows under that filter, and the invoice is ready to send. It does **not** email or text the customer."],["**Paid Outside HaulHQ**","Always","You collected it somewhere we cannot see. Records the amount so the job is not left looking unpaid."],["**Venmo, Cash App, Zelle**","The handle is filled in under Settings > Money > Billing & Payments","Shows a QR code (or your uploaded Zelle code, or the handle to copy), then you confirm you received it."]]},{type:"h2",text:"How each one is recorded"},{type:"p",text:"Whatever you pick, the invoice records how it was settled and, for everything except one, a payment is written against the job. This table comes from the same lists the product uses."},{type:"table",head:["Recorded on the invoice as","Recorded as a payment","Money actually moved?"],rows:Q},{type:"callout",tone:"info",title:"Marked paid manually is the one that records no money",text:'**Mark Paid** on an invoice is a status change and nothing else: it says "this is settled", not "this is where the money came from". Use it when the payment genuinely happened outside anything we can see, and prefer **Paid Outside HaulHQ** on the job, which at least records the amount.'},{type:"p",text:"The full vocabulary a payment can carry, for anyone reading an export:"},{type:"table",head:["Method","Value in your export"],rows:N},{type:"h2",text:"The deposit comes off automatically"},{type:"p",text:"If the customer paid a deposit, the sheet shows the job total, the amount already paid and the balance due, and it collects the **balance**. You never subtract it yourself. If for any reason we cannot work out what has already been paid, the sheet says so and offers no amount at all rather than asking for the full total, because cash and checks are counted by hand off that number."},{type:"h2",text:"Sending the customer a payment link"},{type:"p",text:"On the **QR / Pay Link** screen the send button names the channels it will use: **Send text and email** when the customer has both, **Send text** or **Send email** when they have one. When there are two, **Send via Text** and **Send via Email** sit underneath for when you deliberately want just one; with only one channel on file there is nothing to choose, so the top button is the only one. The send happens on our side, so it works from a desktop, it is logged in the job's communications, and the result tells you per channel what actually went."},{type:"callout",tone:"warn",title:"Pick the top one unless you have a reason",text:"A customer who has both a phone and an email gets read on whichever they happen to look at. Choosing a single channel is guessing which one that is."},{type:"h2",text:"Tips and extra charges"},{type:"list",items:["**Tips** are offered on the card paths: the customer's own payment page and the manual card entry on the driver's phone. Turn them on and set the presets under **Settings > Money > Billing & Payments** on the Invoicing tab. Cash, check and P2P record no tip. A tip shows on the job as its own line, separate from the amount, and every tip is listed with its job under Reports. It also appears on everything the customer sees -- the invoice, the PDF, their portal and the receipt -- so the total they are shown is the figure on their card statement.","**Extra charges** are added by the crew on the driver's job screen before payment, each with a description and an amount. They join the job's line items, so the customer sees what they are paying for."]},{type:"h2",text:"A job with no price yet"},{type:"p",text:"A job booked online from a price range arrives with a total of $0, because the range was a quote and not a price. Pressing **Collect Payment** on it opens the pricing step first: the line items editor, prefilled with one editable row from the quoted range, so you set the real price and go straight on to payment. The button says **Add Items & Collect** when that is what will happen."},{type:"h2",text:"When the sheet refuses"},{type:"list",items:["The job is a **trip on a work order for an invoiced customer**. That customer is billed by one consolidated invoice against their PO, so money taken at the trip could never be matched to it. The sheet names the work order and tells you to invoice the work order instead.","The **balance could not be read**. Check the invoice for the figure and try again rather than guessing.","A **driver** is collecting on a job that is not theirs, or their role does not allow it."]},{type:"mistakes",items:[{wrong:'"I took cash but there is no invoice, so there is no record."',right:"There is. Cash creates the invoice if one does not exist, marks it paid with the amount and the change given, and sends a receipt -- by email, or by text when the customer has no email address. Open the job and look at the invoice link, or find it in Invoices."},{wrong:'"I pressed Invoice Later and the customer never got a bill."',right:"Invoice Later does not send anything. It moves the job to **Invoiced**, so you can find it under that filter on the Jobs list, and it prepares the invoice. **Sen
1d Invoice** on the invoice is what actually emails and texts the customer, and it is what starts the overdue reminders."},{wrong:'"The customer paid a deposit and the sheet is asking for the full amount."',right:"It is not, unless the deposit could not be read -- and in that case it offers no amount at all and says so. What you are looking at is the job total on the line above the balance due. The bold figure is what to collect."},{wrong:'"There is no card option on the sheet."',right:"Stripe Connect is not finished. Every card path, the payment link and Invoice Later are hidden until it is. Connect it under **Settings > Money > Billing & Payments**."},{wrong:'"I want to charge a card over the phone but Tap to Pay is not there."',right:"Tap to Pay needs the HaulHQ app on a supported phone; on a desktop it never appears. Use **Card** to key it in, or **QR / Pay Link** and send the customer the link."}]}]},{slug:"money/invoices-and-receipts",title:"Invoices and receipts",description:"Where an invoice comes from, what sending one actually sends, how overdue reminders escalate, and what your customer sees when they open it.",layer:2,workflow:"both",blocks:[{type:"p",text:"An invoice is the record of what a job cost and whether it has been paid. Most of the time you never create one by hand: collecting payment makes it for you. You reach for this screen when the customer is going to pay **later**, or when you need to prove what was charged."},{type:"video",title:"Sending and chasing an invoice"},{type:"h2",text:"Where invoices come from"},{type:"list",items:["**Collecting payment on a job** creates one if the job has none, and marks it paid.","**Generate Invoice** on a completed job creates one and moves the job to Invoiced.","**A work order** for a customer on invoiced terms bills as ONE invoice for the whole work order against their PO. Trips on it are never invoiced individually, and the product refuses if you try. See [Work orders and trips](/docs/dumpster-rental/work-orders-and-trips).","A **rental** bills at checkout, and any weight overage is invoiced after the scale ticket."]},{type:"p",text:"Numbers are allocated centrally, so two people invoicing at the same moment cannot land on the same number. The due date is the invoice date plus your **Payment Terms (days)** under **Settings > Money > Billing & Payments** on the Invoicing tab; leave it at 0 for due on receipt."},{type:"h2",text:"Sending it"}
1,{type:"p",text:"**Send Invoice** does up to two things at once, and tells you which of them happened:"},{type:"list",items:["An **email** with the line items, any deposit already credited, the total, the due date, your notes and a Pay button. It comes from HaulHQ with your company name on it and your own address as the reply-to, so it lands even if you use a Gmail address.","A **text** with the payment link, if the customer has a mobile number, they have not opted out, and you have that message switched on under **Settings > Notifications**."]},{type:"p",text:"Once sent, the button becomes **Resend**. Resending does not restart the reminder sequence. **Payment Link** opens the customer's payment page in a new tab, for reading out over the phone or pasting into a message yourself."},{type:"callout",tone:"info",title:"Invoice Later on the job is not the same thing",text:"**Invoice Later** in the Collect Payment sheet moves the job to **Invoiced** and prepares the invoice, but it does **not** send the email or the text and it does not start the reminders. If you want the customer chased, open the invoice and press **Send Invoice**."},{type:"h2",text:"Sending a receipt again"},{type:"p",text:"Once a payment has settled, the job's payment block and the invoice both carry a receipt button. It says which way it will go -- **Email receipt** when you have an email address for the customer, **Text receipt** when you do not -- because that is the rule the automatic receipt follows too, and you do not choose. Use it when a customer says they never got it, or lost it."},{type:"list",items:["It is there even when a receipt already went. A customer asking for another copy is the common case, not an error.","A customer with **no email and no phone** gets no button, and the line in its place says to add one to their record.","A customer who replied **STOP** and has no email address gets no button either. An opt-out is not something an operator can override.","If receipts are switched off for your account, nothing sends on either channel and the button says so."]},{type:"callout",tone:"info",title:"Resend is not the same button",text:"**Sen
1d Invoice** and **Resend** send a *bill* with a pay link. On a job that is already paid that asks the customer for money they have handed over, so those buttons are not offered once an invoice is settled -- the receipt button is."},{type:"h2",text:"Recording a payment"},{type:"list",items:["**Money you actually took** belongs on the job: open it and use **Collect Payment**, which records how it was paid. See [Collecting payment](/docs/money/collecting-payment).","**Mark Paid** on the invoice is the shortcut for a payment that happened somewhere we will never see. It settles the invoice, sends the receipt, and writes **no** payment record, so it will not appear in your payment reconciliation."]},{type:"h2",text:"Overdue, and what we send"},{type:"p",text:"Sending an invoice starts a reminder sequence. By default it runs at **7, 14 and 30 days**, and you can change the days or switch it off entirely on the Invoicing tab. The **first** reminder is the moment the invoice flips to Overdue."},{type:"list",items:["Each reminder is an email with the payment link, plus a text where the customer has a number and has not opted out.","The urgency escalates, and the last one in your list is marked **Final Notice**.","You get an alert too, so a customer who is not paying is not something you have to go looking for.","The sequence stops the moment the invoice is paid or voided. It never chases a settled invoice."]},{type:"p",text:"The Invoices screen leads with what is outstanding and what is overdue. Tapping the Overdue figure filters the list to it, and tapping it again clears the filter."},{type:"h2",text:"Voiding and refunding"},{type:"list",items:["**Void** cancels an unpaid invoice. It cannot be undone, it releases any work-order trips the invoice covered so they can be billed again, and it is owners and managers only. If it refuses, nothing has changed.","**Issue Refund** appears on a paid invoice, in the three-dot menu, for owners and managers. It is deliberately not next to the everyday buttons.","On a refund, HaulHQ reverses its own 1.5% processing fee in proportion to what you returned. The $1.50 completion fee stands, because the job was still completed."]},{type:"h2",text:"What your customer sees"},{type:"list",items:["The **email**, in your colours with your logo, listing the work, any deposit credited, the total and the due date.","The **payment page**, where they pay by card or bank transfer, and can add a tip if you have tipping on. If a card is saved there, it is so any dump, overage or balance adjustment on the same order can be charged, and the page says so.","A **receipt** as soon as it is paid, however it was paid. It goes by **email** when you have one for them, and by **text** when you do not -- a phone-only customer gets the amount and a link to the full receipt in their portal. It states what the card was **actually charged**, so a tip is named and the total includes it. Turn receipts off, or change the note at the foot of the email, on the Invoicing tab; the text switch is on **Settings > Notifications**, on the same row.","Their **portal link**, which lists their jobs and invoices with you."]},{type:"mistakes",items:[{wrong:'"I marked an invoice paid and now my payment report does not add up."',right:"**Mark Paid** records no money, only a settled status. Anything you actually collected should go through **Collect Payment** on the job, which records the method and the amount. Mark Paid is for money that genuinely moved somewhere HaulHQ cannot see."},{wrong:'"The customer says they never got the invoice."',right:"Check the send result, then the job's Communications section. An invoice with no email address on the customer sends only the text, and one with neither is refused with that reason. Add the missing detail to the customer and press Resend."},{wrong:'"I voided an invoice and the job still says Invoiced."',right:"Voiding cancels the invoice, not the work. If the job needs to be finished a different way, either collect on it or, when another settled invoice already covers it, use **Close as settled** on the job. See [The job, start to finish](/docs/jobs/the-job)."},{wrong:'"My invoice still says Balance Due, but the customer paid it."',right:"Fixed. The wording used to be chosen by whether a deposit had been credited rather than by whether anything was still owed, so a settled invoice with a deposit read as outstanding. A paid invoice now says **Total paid**, and when there was a tip it shows the invoice total, the tip, and the total paid, in that order."},{wrong:'"The job says No receipt sent, but the payment went through."',right:"Both are true. The money is in; the customer was just never given a record of it. The line tells you why -- usually that they have no email address and no mobile number we are allowed to text. Add one to their customer record, and the next payment receipts itself."},{wrong:'"Reminders are going out to a customer who has already paid me in cash."',right:"The sequence stops on a paid or voided invoice, so the invoice is still open. Record the cash against the job through Collect Payment; the reminders stop at the next step."},{wrong:'"I invoiced one trip of a work order and it was refused."',right:"That customer is on invoiced terms, so the whole work order bills as one invoice against their PO. Invoice the work order from its own page when the work is done."}]}]}]}];function D(e){var t,o;switch(e.type){case"p":case"h2":case"h3":return[e.text];case"steps":case"list":return e.items;case"callout":return[null!==(t=e.title)&&void 0!==t?t:"",e.text];case"code":return[e.code,null!==(o=e.caption)&&void 0!==o?o:""];case"table":return[...e.head,...e.rows.flat()];case"shortcodeTable":return[e.shortcode];case"video":return[e.title];case"mistakes":return e.items.flatMap(e=>[e.wrong,e.right]);case"compare":return[...e.junk,...e.dumpster]}}O.flatMap(e=>e.pages).map(e=>[e.slug,e]);let z=O.flatMap(e=>e.pages.map(t=>
1({slug:t.slug,title:t.title,description:t.description,group:e.label,layer:t.layer,body:[t.title,t.description,...t.blocks.flatMap(D)].join(" ").replace(/\[([^\]]+)\]\(([^)]+)\)/g,"$1").replace(/\*\*([^*]+)\*\*/g,"$1").replace(/`([^`]+)`/g,"$1").toLowerCase()})));var Y=o(18222);function M(){let[e,t]=(0,s.useState)(""),[o,i]=(0,s.useState)(!1),r=(0,s.useRef)(null),h=(0,s.useMemo)(()=>(function(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:8,o=e.toLowerCase().split(/\s+/).filter(Boolean);return o.length?z.map(e=>{let t=e.title.toLowerCase(),a=e.description.toLowerCase(),s=0;for(let n of o){if(!e.body.includes(n))return{doc:e,score:-1};t.includes(n)&&(s+=10),a.includes(n)&&(s+=4),s+=1}return 1===e.layer&&(s+=3),{doc:e,score:s}}).filter(e=>e.score>=0).sort((e,t)=>t.score-e.score).slice(0,t).map(e=>e.doc):[]})(e),[e]);return(0,s.useEffect)(()=>{function e(e){r.current&&!r.current.contains(e.target)&&i(!1)}return document.addEventListener("mousedown",e),()=>document.removeEventListener("mousedown",e)},[]),(0,a.jsxs)("div",{ref:r,className:"relative",children:[(0,a.jsxs)("div",{className:"relative",children:[(0,a.jsx)(Y.Z,{className:"pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-slate-400"}),(0,a.jsx)("input",{type:"search",value:e,onChange:e=>{t(e.target.value),i(!0)},onFocus:()=>i(!0),placeholder:"Search ".concat(z.length," pages"),"aria-label":"Search documentation",className:"w-full rounded-md border border-slate-200 bg-white py-2 pl-9 pr-3 text-sm text-slate-700 placeholder:text-slate-400 focus:border-[#1E3A5F] focus:outline-none"})]}),o&&""!==e.trim()&&(0,a.jsx)("div",{className:"absolute z-30 mt-1.5 max-h-[70vh] w-full overflow-y-auto rounded-md border border-slate-200 bg-white py-1 shadow-lg",children:0===h.length?(0,a.jsxs)("p",{className:"px-3 py-3 text-sm text-slate-500",children:["No results for “",e,"”"]}):h.map(e=>(0,a.jsxs)(n.default,{href:"/docs/".concat(e.slug),onClick:()=>{i(!1),t("")},className:"block px-3 py-2 hover:bg-slate-50",children:[(0,a.jsx)("p",{className:"text-sm font-medium text-slate-900",children:e.title}),(0,a.jsx)("p",{className:"mt-0.5 line-clamp-2 text-xs leading-relaxed text-slate-500",children:e.description}),(0,a.jsx)("p",{className:"mt-1 text-[11px] uppercase tracking-wide text-slate-400",children:e.group})]},e.slug))})]})}function L(e){let{onNavigate:t}=e,o=(0,i.usePathname)(),[h,d]=(0,s.useState)({});return(0,a.jsx)("nav",{className:"flex flex-col gap-6",children:O.map(e=>{let s="orientation"===e.kind,i=e.pages.some(e=>o==="/docs/".concat(e.slug)),u=s||i||!h[e.label];return(0,a.jsxs)("div",{className:(0,l.cn)(s&&"rounded-lg border border-[#1E3A5F]/20 bg-[#1E3A5F]/[0.03] p-3"),children:[s?(0,a.jsx)("p",{className:"mb-2 px-1 text-xs font-semibold uppercase tracking-wide text-[#1E3A5F]",children:e.label}):(0,a.jsxs)("button",{type:"button",onClick:()=>d(t=>{var o;return{...t,[e.label]:!(null!==(o=t[e.label])&&void 0!==o&&o)}}),className:"mb-1.5 flex w-full items-center justify-between px-1 text-xs font-semibold uppercase tracking-wide text-slate-500 hover:text-slate-700","aria-expanded":u,children:[e.label,(0,a.jsx)(r.Z,{className:(0,l.cn)("h-3.5 w-3.5 transition-transform",!u&&"-rotate-90")})]}),u&&(0,a.jsx)("ul",{className:"flex flex-col gap-0.5",children:e.pages.map(e=>{let s="/docs/".concat(e.slug),i=o===s;return(0,a.jsx)("li",{children:(0,a.jsx)(n.default,{href:s,onClick:t,className:(0,l.cn)("block rounded px-2 py-1.5 text-sm leading-snug transition-colors",i?"bg-[#1E3A5F] font-medium text-white":"text-slate-600 hover:bg-slate-100 hover:text-slate-900"),children:e.title})},e.slug)})})]},e.label)})})}function _(){let[e,t]=(0,s.useState)(!1);return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsxs)("div",{className:"mb-4 flex items-center gap-3 lg:hidden",children:[(0,a.jsxs)("button",{type:"button",onClick:()=>t(e=>!e),className:"inline-flex shrink-0 items-center gap-1.5 rounded-md border border-slate-200 px-3 py-2 text-sm font-medium text-slate-600","aria-expanded":e,children:[e?(0,a.jsx)(h.Z,{className:"h-4 w-4"}):(0,a.jsx)(d,{className:"h-4 w-4"}),"Docs"]}),(0,a.jsx)("div",{className:"min-w-0 flex-1",children:(0,a.jsx)(M,{})})]}),e&&(0,a.jsx)("div",{className:"mb-6 border-b border-slate-200 pb-6 lg:hidden",children:(0,a.jsx)(L,{onNavigate:()=>t(!1)})}),(0,a.jsxs)("div",{className:"hidden lg:sticky lg:top-8 lg:block lg:max-h-[calc(100vh-4rem)] lg:overflow-y-auto lg:pr-2",children:[(0,a.jsx)("div",{className:"mb-5",children:(0,a.jsx)(M,{})}),(0,a.jsx)(L,{})]})]})}},63941:function(e,t,o){"use strict";o.d(t,{MarketingNav:function(){return l}});var a=o(76029),s=o(20524),n=o(66068),i=o(59795);let r="https://dashboard.haulhq.app/signup",h=[{href:"/junk-removal-software",label:"Junk Removal"},{href:"/dumpster-rental-software",label:"Dumpster Rental"},{href:"/pricing",label:"Pricing"},{href:"/features",label:"Features"},{href:"/compare",label:"Compare"},{href:"/docs",label:"Docs"}];function d(e){return e.startsWith("/")}
1function l(){let[e,t]=(0,s.useState)(!1),o=(0,i.usePathname)();return(0,a.jsxs)("nav",{className:"sticky top-0 z-50 border-b border-slate-200 bg-white/95 backdrop-blur-xl",children:[(0,a.jsxs)("div",{className:"mx-auto flex h-14 max-w-[1280px] items-center justify-between px-5 md:h-16 md:px-6",children:[(0,a.jsxs)(n.default,{href:"/",className:"text-xl font-bold tracking-tight md:text-2xl",children:[(0,a.jsx)("span",{className:"text-[#1E3A5F]",children:"Haul"}),(0,a.jsx)("span",{className:"text-[#D97706]",children:"HQ"})]}),(0,a.jsxs)("div",{className:"hidden items-center gap-8 md:flex",children:[h.map(e=>{let t=d(e.href)&&o===e.href;return d(e.href)?(0,a.jsx)(n.default,{href:e.href,className:"text-sm font-medium transition-colors hover:text-slate-900 ".concat(t?"font-semibold text-[#1E3A5F]":"text-slate-600"),children:e.label},e.href):(0,a.jsx)("a",{href:e.href,className:"text-sm font-medium text-slate-600 transition-colors hover:text-slate-900",children:e.label},e.href)}),(0,a.jsx)("a",{href:r,className:"rounded-lg bg-[#1E3A5F] px-5 py-2.5 text-sm font-semibold text-white transition-colors hover:bg-[#152D4A]",children:"Create Free Account"})]}),(0,a.jsxs)("div",{className:"flex items-center gap-3 md:hidden",children:[(0,a.jsx)("a",{href:r,className:"rounded-md bg-[#1E3A5F] px-3.5 py-2 text-[13px] font-semibold text-white",children:"Sign Up Free"}),(0,a.jsx)("button",{onClick:()=>t(!e),className:"p-1","aria-label":e?"Close menu":"Open menu",children:(0,a.jsx)("svg",{viewBox:"0 0 24 24",fill:"none",stroke:"#334155",strokeWidth:"2",className:"h-6 w-6",children:e?(0,a.jsx)("path",{d:"M6 18L18 6M6 6l12 12",strokeLinecap:"round",strokeLinejoin:"round"}):(0,a.jsx)("path",{d:"M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5",strokeLinecap:"round",strokeLinejoin:"round"})})})]})]}),e&&(0,a.jsxs)("div",{className:"flex flex-col gap-3 border-t border-slate-200 px-5 py-3 md:hidden",children:[h.map(e=>{let s=d(e.href)&&o===e.href;return d(e.href)?(0,a.jsx)(n.default,{href:e.href,onClick:()=>t(!1),className:"py-2 text-[15px] font-medium ".concat(s?"font-semibold text-[#1E3A5F]":"text-slate-600"),children:e.label},e.href):(0,a.jsx)("a",{href:e.href,onClick:()=>t(!1),className:"py-2 text-[15px] font-medium text-slate-600",children:e.label},e.href)}),(0,a.jsx)("a",{href:"https://dashboard.haulhq.app",className:"py-2 text-[15px] font-medium text-[#1E3A5F]",children:"Log In"})]})]})}},92091:function(e,t,o){"use strict";o.d(t,{$A:function(){return i}});let a=["delivery","swap","dump_return","relocate"],s=["dumpster_delivery"];function n(e){return"dumpster_rental"===e.service_type}function i(e){return n(e)&&!function(e){var t,o;if(!n(e))return!1;let i=null!==(t=e.trip_type)&&void 0!==t?t:null;if(i)return a.includes(i);let r=null!==(o=e.job_subtype)&&void 0!==o?o:null;return!!r&&s.includes(r)}(e)}},97597:function(e,t,o){"use strict";o.d(t,{Fj:function(){return u},HR:function(){return a},Hi:function(){return y},Sy:function(){return i},Tm:function(){return h},fO:function(){return l},mh:function(){return d},n9:function(){return n},pI:function(){return c},tj:function(){return p}});let a=["booked","scheduled","dispatched","in_progress","completed"],s={booked:["scheduled"],scheduled:["booked","dispatched"],dispatched:["booked","confirmed","scheduled","in_progress"],in_progress:["dispatched","missed","completed"],completed:["in_progress"]};function n(e,t){var o;let a=arguments.length>2&&void 0!==arguments[2]?arguments[2]:new Date().toISOString();if(!(null===(o=s[t])||void 0===o?void 0:o.includes(e)))return null;let n={status:t};return"in_progress"===t&&("completed"===e?(n.actual_end=null,n.completed_at=null):n.actual_start=a),"dispatched"===t&&"in_progress"===e&&(n.actual_start=null),"completed"===t&&(n.actual_end=a,n.completed_at=a),{updates:n,fireEnRoute:"dispatched"===t&&["booked","confirmed","scheduled"].includes(e)}}let i=["scheduled","booked","missed"],r="on_hold_at";function h(e){let t=null==e?void 0:e[r];return"string"==typeof t&&t?t:null}function d(e){return"booked"===e.status&&!e.scheduled_date}function l(e,t){let o=arguments.length>2&&void 0!==arguments[2]?arguments[2]:new Date().toISOString();return i.includes(e)?{updates:{status:"booked",scheduled_date:null,scheduled_time_start:null,scheduled_time_end:null,job_c
1osts:{...null!=t?t:{},[r]:o}},fireEnRoute:!1}:null}let u=["scheduled","booked","missed","dispatched"];function c(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:new Date().toISOString();if(!u.includes(e))return null;let o="missed"===e||"dispatched"===e?["in_progress","completed"]:["dispatched","in_progress","completed"],a=e,s={};for(let e of o){let o=n(a,e,t);if(!o)return null;Object.assign(s,o.updates),a=e}return delete s.actual_start,{updates:{...s,status:"completed",actual_end:t,completed_at:t},fireEnRoute:!1,chain:o}}let p=["invoiced"];function y(e){return p.includes(e)?{updates:{status:"paid"},fireEnRoute:!1}:null}},75361:function(e,t,o){"use strict";o.d(t,{default:function(){return d}});var a=o(20524),s={xmlns:"http://www.w3.org/2000/svg",width:24,height:24,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"};let n=e=>{for(let t in e)if(t.startsWith("aria-")||"role"===t||"title"===t)return!0;return!1};var i=o(21002);let r=(0,a.createContext)({}),h=()=>(0,a.useContext)(r),d=(0,a.forwardRef)((e,t)=>{var o,r,d;let{color:l,size:u,strokeWidth:c,absoluteStrokeWidth:p,className:y="",children:m,iconNode:g,...w}=e,{size:f=24,strokeWidth:b=2,absoluteStrokeWidth:k=!1,color:v="currentColor",className:x=""}=null!==(o=h())&&void 0!==o?o:{},T=(null!=p?p:k)?24*Number(null!=c?c:b)/Number(null!=u?u:f):null!=c?c:b;return(0,a.createElement)("svg",{ref:t,...s,width:null!==(r=null!=u?u:f)&&void 0!==r?r:s.width,height:null!==(d=null!=u?u:f)&&void 0!==d?d:s.height,stroke:null!=l?l:v,strokeWidth:T,className:(0,i.z)("lucide",x,y),...!m&&!n(w)&&{"aria-hidden":"true"},...w},[...g.map(e=>{let[t,o]=e;return(0,a.createElement)(t,o)}),...Array.isArray(m)?m:[m]])})},15915:function(e,t,o){"use strict";o.d(t,{Z:function(){return d}});var a=o(20524),s=o(21002);let n=e=>e.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase(),i=e=>e.replace(/^([A-Z])|[\s-_]+(\w)/g,(e,t,o)=>o?o.toUpperCase():t.toLowerCase()),r=e=>{let t=i(e);return t.charAt(0).toUpperCase()+t.slice(1)};var h=o(75361);let d=(e,t)=>{let o=(0,a.forwardRef)((o,i)=>{let{className:d,...l}=o;return(0,a.createElement)(h.default,{ref:i,iconNode:t,className:(0,s.z)("lucide-".concat(n(r(e))),"lucide-".concat(e),d),...l})});return o.displayName=r(e),o}},36008:function(e,t,o){"use strict";o.d(t,{Z:function(){return a}});let a=(0,o(15915).Z)("chevron-down",[["path",{d:"m6 9 6 6 6-6",key:"qrunsl"}]])},18222:function(e,t,o){"use strict";o.d(t,{Z:function(){return a}});let a=(0,o(15915).Z)("search",[["path",{d:"m21 21-4.34-4.34",key:"14j7rj"}],["circle",{cx:"11",cy:"11",r:"8",key:"4ej97u"}]])},41974:function(e,t,o){"use strict";o.d(t,{Z:function(){return a}});let a=(0,o(15915).Z)("x",[["path",{d:"M18 6 6 18",key:"1bl5f8"}],["path",{d:"m6 6 12 12",key:"d8bk6v"}]])},21002:function(e,t,o){"use strict";o.d(t,{z:function(){return a}});let a=function(){for(var e=arguments.length,t=Array(e),o=0;o<e;o++)t[o]=arguments[o];return t.filter((e,t,o)=>!!e&&""!==e.trim()&&o.indexOf(e)===t).join(" ").trim()}},66068:function(e,t,o){"use strict";o.d(t,{default:function(){return s.a}});var a=o(68443),s=o.n(a)},59795:function(e,t,o){"use strict";var a=o(25794);o.o(a,"useParams")&&o.d(t,{useParams:function(){return a.useParams}}),o.o(a,"usePathname")&&o.d(t,{usePathname:function(){return a.usePathname}}),o.o(a,"useRouter")&&o.d(t,{useRouter:function(){return a.useRouter}}),o.o(a,"useSearchParams")&&o.d(t,{useSearchParams:function(){return a.useSearchParams}})},62227:function(e,t,o){"use strict";var a,s;e.exports=(null==(a=o.g.process)?void 0:a.env)&&"object"==typeof(null==(s=o.g.process)?void 0:s.env)?o.g.process:o(17100)},17100:function(e){!function(){var t={229:function(e){var t,o,a,s=e.exports={};function n(){throw Error("setTimeout has not been defined")}function i(){throw Error("clearTimeout has not been defined")}function r(e){if(t===setTimeout)return setTimeout(e,0);if((t===n||!t)&&setTimeout)return t=setTimeout,setTimeout(e,0);try{return t(e,0)}catch(o){try{return t.call(null,e,0)}catch(o){return t.call(this,e,0)}}}!function(){try{t="function"==typeof setTimeout?setTimeout:n}catch(e){t=n}try{o="function"==typeof clearTimeout?clearTimeout:i}catch(e){o=i}}();var h=[],d=!1,l=-1;function u(){d&&a&&(d=!1,a.length?h=a.concat(h):l=-1,h.length&&c())}function c(){if(!d){var e=r(u);d=!0;for(var t=h.length;t;){for(a=h,h=[];++l<t;)a&&a[l].run();l=-1,t=h.length}a=null,d=!1,function(e){if(o===clearTimeout)return clearTimeout(e);if((o===i||!o)&&clearTimeout)return o=clearTimeout,clearTimeout(e);try{o(e)}catch(t){try{return o.call(null,e)}catch(t){return o.call(this,e)}}}(e)}}function p(e,t){this.fun=e,this.array=t}function y(){}s.nextTick=function(e){var t=Array(arguments.length-1);if(arguments.length>1)for(var o=1;o<arguments.length;o++)t[o-1]=arguments[o];h.push(new p(e,t)),1!==h.length||d||r(c)},p.prototype.run=function(){this.fun.apply(null,this.array)},s.title="browser",s.browser=!0,s.env={},s.argv=[],s.version="",s.versions={},s.on=y,s.addListener=y,s.once=y,s.off=y,s.removeListener=y,s.removeAllListeners=y,s.emit=y,s.prependListener=y,s.prependOnceListener=y,s.listeners=function(e){return[]},s.binding=function(e){throw Error("process.binding is not supported")},s.cwd=function(){return"/"},s.chdir=function(e){throw Error("process.chdir is not supported")},s.umask=function(){return 0}}},o={};function a(e){var s=o[e];if(void 0!==s)return s.exports;var n=o[e]={exports:{}},i=!0;try{t[e](n,n.exports,a),i=!1}finally{i&&delete o[e]}return n.exports}a.ab="//";var s=a(229);e.exports=s}()},45130:function(e,t,o){"use strict";o.d(t,{I1:function(){return n},jG:function(){return a}});let a={booked:"actionable",scheduled:"actionable",dispatched:"actionable",in_progress:"actionable",completed:"terminal",invoiced:"terminal",paid:"terminal",cancelled:"terminal",missed:"missed"};Object.keys(a).filter(e=>
1"terminal"===a[e]),Object.keys(a).filter(e=>"actionable"===a[e]);let s=Object.fromEntries(Object.keys(a).map(e=>{let t=a[e];return[e,"actionable"===t?"remaining":"terminal"===t&&"cancelled"!==e?"done":"closed"]}));function n(e){var t;return null!==(t=s[e])&&void 0!==t?t:"remaining"}}},function(e){e.O(0,[6134,8443,2588,7561,4760,3514,9634,3603,8553,4364,1744],function(){return e(e.s=56726)}),_N_E=e.O()}]);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.