Acceptance, decline, and counter-offers
When you send a quote, the client gets an email with a link to the hosted quote page at /quotes/<token>. From there they have three actions: accept, decline, or request changes.
What the client sees
The hosted quote page shows:
- Your org's logo and accent color (their portal looks like yours).
- The full quote — title, line items, totals, deposit info, expiration date.
- Three buttons: Accept Quote, Decline, Request Changes.
- A "Download PDF" link for their records.
No account is needed to view or act on a quote — the signing token in the URL grants temporary access.
Acceptance flow
When the client clicks Accept:
- The contract status moves to
signedwithsignedAtset to now andsignedByNameset to the name the client typed. Your saved signature was already on the quote from the moment you sent it, so this single action finalizes the document for both sides. - Based on the quote's billing mode, the appropriate Stripe invoice fires (deposit, full, or draft).
- A Project is auto-created with
budget = quote totaland tied to the contract. - The client lands on
/quotes/<token>/acceptedwith numbered next steps: pay the deposit via the hosted Stripe link, then click "Create portal account" to set up portal access (separate step, prevents bouncing between payment and account creation).
You get an email at your support address (or login email if support_email is unset) letting you know the quote was accepted, plus an in-portal notification.
Project auto-creation
By default, accepting a quote spawns a Project automatically so the work has a home for time tracking, notes, and document sharing. Turn this off under Settings, then Quotes, with the Create a project automatically when a quote is accepted checkbox.
The project's name matches the quote's title. If you don't want a project (e.g., for one-off product sales), turn the setting off.
Decline flow
When the client clicks Decline:
- The contract status moves to
declined. They can optionally leave a reason. - You get a notification (in-portal + email) with their reason if provided.
- The hosted quote page replaces the accept/decline buttons with a "This quote was declined" message.
Declined quotes stay in your Contracts list under the Declined filter. Nothing is deleted, so you can go back and read what you quoted before you price the next one. There's no one-click "quote this again" button. If you expect to reuse a quote's shape, tick Save as template when you send it, and every later quote can start from that saved version.
Counter-offer / request changes
When the client clicks Request Changes:
- They get a free-form text box to describe what they want changed (price, scope, timing).
- The contract status moves to
changes_requested. - You get a notification with their message.
- The hosted quote page is locked — they can't accept until you respond.
How you respond
Open the contract from /admin/contracts and a Client requested changes panel shows what they asked for, when, and who sent it. Underneath it you get:
- Create change order, which starts a new document tied back to this one. Use it when the scope itself is changing.
- A Resolution box, which is how you close the request out. Pick I edited the quote with the requested changes or No changes needed, ask the client to reconsider, add an optional note to the client, and click Resolve & email client.
Resolving puts the quote back in sent status, gives it a fresh signing window if the old one had already expired, and emails the client a link straight back to the quote so they can accept it.
There is no "revise this quote" button that reopens the composer with the original loaded. A quote's line items are locked once it's sent, so if the price or the scope has to change, send a new quote from the quote composer and void the old one from its contract page.
Counter-offer caps
To prevent infinite back-and-forth, there's a cap on how many counter-offer rounds a client can request on one quote. Default is 3. Change it under Settings, then Quotes, in the Counter-offer limit field. Set it to 0 for no cap.
After the cap, the "Request Changes" button on the client's hosted page is disabled — they can only accept or decline.
Expiration
If you set an expiration date on the quote and the client doesn't act before it, the contract status moves to expired automatically (via the cron sweep that runs hourly).
After expiration, the client can still view the quote but can't accept. Send them a new quote, or set an Acceptance grace period (days) under Settings, then Quotes, to give every quote a few extra days past its expiry date. Default is 0, which means the expiry is strict.
Voiding a quote
If you need to pull back a quote before the client acts, open the contract page and click Void. The status moves to voided, the hosted page shows "This quote was voided," and the client can't accept anymore.
Voided quotes are kept in your contracts list for record-keeping. They don't fire any invoices and don't create projects.
What the client sees after accepting
After paying the deposit (if any), the client clicks "Continue to your portal" and lands at /dashboard. Their first portal experience shows:
- The project that was auto-created from the quote
- The signed contract with the option to download it
- An invoice receipt for the deposit (if applicable)
- A pending balance invoice (if your billing mode is deposit+draft, they'll see this once you finalize and send it later)
