guides

The tender lifecycle, in five stages.

Every guide on this site sits somewhere in the same pipeline: find the work, decide whether it's worth bidding, price it properly, submit it cleanly, then learn from what the result actually tells you. Start wherever your next deadline is.

02 · qualify

The bid/no-bid decision

3 guides

The smallest stage, and the one that saves the most hours when it's done properly. Score fit and win probability before pricing a single line item.

spotlight

The best bid/no-bid scoring matrix solution

A good bid/no-bid matrix turns gut-feel into a repeatable, defensible decision. Score each opportunity against the factors that actually predict a win — strategic fit, client relationship, margin potential, delivery risk, resource availability and competition — then weight them to reflect how your business really wins work. BidNext helps you build this once and apply it to every tender, so your team stops chasing bids you can't win and focuses effort where the odds are strongest. Start from the guides below, adapt the template to your trade, and pair it with on-site data so the scores reflect reality rather than wishful thinking.

Understanding GMP Contracts

A Guaranteed Maximum Price (GMP) contract caps the total a client pays while the contractor is reimbursed for actual costs up to that ceiling. It blends the transparency of cost-plus with the budget certainty of a lump sum — the owner is protected from runaway costs, and shared-savings clauses often reward the contractor for coming in under the agreed maximum.

Why they work: GMP contracts encourage open-book collaboration, give both sides clear visibility of where the money goes, and align incentives around efficient delivery. The trade-off is that they demand accurate, well-documented estimates and disciplined cost tracking from day one.

How BidNext helps: BidNext lets contractors build GMP bills of quantities on real site data, keep rate build-ups defensible, and maintain the open-book transparency clients expect — so your guaranteed maximum reflects reality rather than guesswork. Explore the pricing guides above to put these principles into practice.

Once it's priced, EstimateNext keeps the numbers defensible.

Price your next bid with EstimateNext

', …), // where dollar-quote and dollar-amp are special replace tokens; and it lives in a // JS template literal that eats backslash escapes. So keep this block free of the // dollar sign and of backslash escapes: exact-match CTA texts (no regex end-anchor) // plus a space-only collapse regex. var cnxCtaTexts = ['book a demo','request a demo','get a demo','book demo','schedule a demo','talk to sales','talk to us','talk to the team','get in touch','contact us','contact sales','contact the team','contact']; function cnxIsContactCta(el){ if (el.id && el.id.indexOf('cnx-') === 0) return false; if (el.hasAttribute && el.hasAttribute('data-cnx-contact')) return true; var raw = ((el.getAttribute && el.getAttribute('href')) || '').toLowerCase(); var hh = raw.split('?')[0].split('#')[0]; if (raw === '#contact' || hh === '/contact' || hh === 'contact' || hh === 'contact.html' || (hh.length >= 8 && hh.lastIndexOf('/contact') === hh.length - 8) || (hh.length >= 13 && hh.lastIndexOf('/contact.html') === hh.length - 13)) return true; var t = (el.textContent || '').replace(/ +/g, ' ').trim().toLowerCase(); if (t.length > 24) return false; for (var k = 0; k < cnxCtaTexts.length; k++) { if (t === cnxCtaTexts[k]) return true; } return false; } document.addEventListener('click', function (e) { var el = e.target; while (el && el !== document) { if (el.tagName === 'A' || el.tagName === 'BUTTON') break; el = el.parentNode; } if (!el || el === document) return; if (cnxIsContactCta(el)) { e.preventDefault(); e.stopPropagation(); openModal(); } }, true); if (closeBtn) closeBtn.addEventListener('click', closeModal); if (scrim) scrim.addEventListener('click', function(e){ if(e.target === scrim) closeModal(); }); if (form) form.addEventListener('submit', function(e){ e.preventDefault(); var note = document.getElementById('cnx-note'), err = document.getElementById('cnx-err'), btn = document.getElementById('cnx-submit'); note.style.display='none'; err.style.display='none'; if (form.website_url && form.website_url.value){ note.style.display='block'; return; } // honeypot var payload = { name: form.name.value, email: form.email.value, company: form.company.value, phone: form.phone.value, message: form.message.value, subscribe_newsletter: form.subscribe_newsletter && form.subscribe_newsletter.checked, turnstile_token: form.turnstile_token ? form.turnstile_token.value : '', _token_ts: tsEl ? tsEl.value : tokenTs() }; if (!payload.name || !payload.email || !payload.company || !payload.phone || !payload.message){ err.textContent='Please fill in all required fields.'; err.style.display='block'; return; } if (payload.phone.replace(/[^0-9]/g, '').length < 7){ err.textContent='Please enter a valid phone number.'; err.style.display='block'; return; } btn.disabled = true; btn.textContent = 'Sending…'; post(payload).then(function(d){ if (d && d.success){ form.style.display='none'; note.style.display='block'; } else { err.textContent = (d && d.message) || 'Something went wrong — please try again.'; err.style.display='block'; btn.disabled=false; btn.textContent='Send message'; } }).catch(function(){ err.textContent='Network error — please try again.'; err.style.display='block'; btn.disabled=false; btn.textContent='Send message'; }); }); // --- Progressive enhancement: wire a site's OWN contact form to the CRM --- // Targets forms that look like contact forms (have an email field AND a // textarea/message field), aren't ours, and aren't opted out. Field mapping is // heuristic; company falls back to the entered name so the CRM never rejects. function pick(f, res){ for (var i=0;i