Skip to Content

Delivery

Once a quote is accepted and isActiveProject = true, the expert begins delivering against the milestones defined in the accepted expert quote. The flag is cleared again when the lead reaches delivered (or any later exit — survey_sent, completed, qualified_out), so an account leaves the active portfolio the moment delivery is done. Each milestone represents a discrete unit of work with its own percentage of the total project value, its own due date, and its own invoice.

Flow

Milestones

Milestones are defined by the expert as part of their quote and locked in when the quote is accepted. They represent the phased delivery schedule for the project.

Milestone Types

TypeDescription
timeThe milestone is triggered on a specific due date — useful for scheduled deliverables like sprint reviews or monthly reports
eventThe milestone is triggered when the expert marks a specific delivery as complete — useful for feature delivery or project phases

Milestone Constraints

  • The percentage values of all milestones on a quote must sum to exactly 100% — enforced by Milestone.validateMilestonePercentages(proposalId)
  • Milestones are ordered via the order field (minimum 1)
  • Each milestone must have a unique order within its quote
  • Each milestone is linked to exactly one invoice once work begins

Milestone Fields

FieldTypeConstraintsDescription
nameString3–100 chars, requiredMilestone label
descriptionStringMax 1000 charsWhat will be delivered
typeStringtime or eventTrigger type
orderNumber≥ 1, requiredDelivery sequence
percentageNumber0–100, whole numberShare of total project value
statusStringupcoming | in-progress | blocked | done, default upcomingDelivery state shown to the customer
ownerStringExpert | Your team | Customer, default ExpertWho is accountable for the milestone
outcomeStringMax 200 chars, optionalCustomer-facing outcome label (falls back to name)
blocksGoLiveBooleanDefault falseWhether this milestone blocks go-live
dueDateDateOptionalExpected completion date
proposalProposal refRequiredParent expert quote
expertUser refRequiredResponsible expert
invoiceInvoice refSet on completionAssociated invoice
acknowledgedAtDateNull until acknowledgedWhen the customer confirmed the milestone is done
acknowledgedByUser refSet with acknowledgedAtWho recorded the acknowledgement
acknowledgedOnBehalfBooleanDefault falseWhether a CSM or SDM recorded it for the customer

Delivery status

The expert drives delivery by advancing each milestone’s status from upcoming → in-progress → done. If work stalls, the expert can move an in-progress milestone to blocked and back to in-progress once the blocker clears (a loop transition). The milestone sub-workflow has no verification or evidence step. Reaching done is the trigger that marks a milestone complete in the flow above, and it is distinct from the milestone’s billing state — a milestone can be delivered (status = done) long before its invoice is raised, approved or paid. Billing state is read from the milestone’s invoice, never from its delivery status.

Customer acknowledgement

Reaching done is the expert’s statement that the work is finished. Acknowledgement is the customer’s agreement that it is — a separate fact, recorded beside the delivery flow and never inferred from status.

It changes nothing: no status moves, no transition is asserted, and no delivery, invoicing or workflow path depends on it. What it exists for is the vendor metrics’ definition of Delivered — every milestone complete and acknowledged by the customer, and every bill settled — which had no record behind it before. A milestone must already be done to be acknowledged, and the first acknowledgement stands: re-recording one does not move the date.

The customer acknowledges from their own workspace. A CSM or SDM can record it on the customer’s behalf, attributed to whoever recorded it and marked as such, so the measure does not stall on a customer who never signs in — the drill-down shows which of the two it was. The expert is deliberately excluded: the party that did the work is not the party that accepts it.

Delivery confirmation

Once the customer acknowledges the final milestone (the highest order), the Delivery section of the project workspace opens with one line saying who confirmed delivery and when — Delivery confirmed by Hannah Price on 20 Sep 2026. When a CSM or SDM recorded that acknowledgement on the customer’s behalf, the line names the customer and the person who recorded it: Delivery confirmed by Hannah Price on 20 Sep 2026 · recorded by Mark Jansen. Until the final milestone is acknowledged there is no line. Every milestone row also shows its due date beside its owner (Due 1 Oct 2026) whenever the milestone carries one.

Blockers, the revenue exposed and the next action

Anyone who can raise a blocker from the workspace (Raise a blocker) fills in what is blocking delivery, its owner and its impact — and, optionally, three more fields:

  • Milestone at risk — one of the engagement’s delivery milestones the customer has not yet acknowledged, or None. A linked blocker shows Revenue exposed — that milestone’s amount, the same figure its row shows — beside the milestone’s name and the blocker’s owner.
  • Next action and Due date — given together or not at all. They raise an ordinary action item on the project, marked urgent and linked to the blocker, so it also appears under Action items and completes the usual way. While it is open the blocker row reads Next: Chase store access · Due 1 Oct 2026; once it is completed the line goes.

A milestone that is not one of this engagement’s, or a next action without its due date (or the reverse), is refused before anything is written. Blockers raised without these fields — and every blocker raised before they existed — read exactly as before. The link is set when the blocker is raised; it is not edited afterwards.

FieldOnTypeDescription
milestoneBlockerMilestone refOptional — the delivery milestone the blocker puts at risk
blockerAction itemBlocker refOptional — the blocker this item is the next action on

The expert’s delivery surface

Once the quote is accepted and the project is active, the Delivery section of the project workspace is the expert’s record of the engagement — and the place they work from. The Expert quotes section disappears for them at that point: it was their own-quote view and their edit surface, and both stop meaning anything the moment the quote is awarded. Every other party keeps their Expert quotes section throughout.

Each milestone row offers the expert exactly the moves the status machine above allows from where that milestone stands — Start on upcoming, Mark complete or Mark blocked on in-progress, Resume on blocked, and nothing on done. Beside them sits Raise invoice, on a delivered milestone that has none yet.

Milestones are locked in when the quote is accepted. Nothing on this surface creates, re-orders or re-weights them; only their delivery status moves.

Each action runs through an authenticated, tenant-scoped server action that re-checks the expert’s capability (milestone.start, milestone.complete, milestone.block, milestone.unblock, invoice.submit) and, below that, the service’s owning-expert filter — so an expert can only ever advance or bill a milestone that is theirs, whatever the browser sends.

The change is reflected on the customer’s dashboard on the next read: project progress shown to the customer is the percentage-weighted sum of done milestones (which, since percentages sum to 100, reaches 100% when every milestone is done).

Customer action items

Delivery often depends on the customer doing something — approving integration access, confirming a data mapping, signing off a phase. The delivery team (Expert, CSM, or SDM) raises these as action items on the project, and the customer sees and clears them from the “Your actions” strip on their dashboard.

Each action item is scoped to a project (lead) and the customer reads only their own project’s open items — the one their dashboard shows, which stays reachable after delivery (see After delivery), so an item still open when a project finishes can still be cleared.

FieldTypeConstraintsDescription
labelStringMax 200 chars, requiredWhat the customer must do
dueDateDateRequiredWhen it’s needed by
urgencyStringurgent | upcomingDrives ordering and emphasis on the dashboard
statusStringopen | completed, default openOpen items show on the dashboard; completing removes them
blocksGoLiveBooleanDefault falseWhether clearing this item gates go-live
blockerBlocker refOptionalSet when the item was raised as a blocker’s next action

Open items are sorted by urgency then due date, with dueInDays derived at read time. Completing an item — from the dashboard or the team surface — records who completed it and when, and drops it from the customer’s list.

Change control

During delivery a project’s scope sometimes shifts in ways that move the timeline or cost — an integration expanded, a phase brought forward. The delivery team (Expert, CSM, or SDM) records these as change-control entries on the project, and the customer sees them in the “Changes to your project” section of their dashboard, newest first — a transparent log of how and why the plan changed.

Each entry is scoped to a project (lead) and the customer reads only their own project’s entries. Impacts are stored as signed numbers (0 = no change) so the dashboard can format them (“+2 days”, ”+£500”) and render the cost in the project’s currency.

FieldTypeConstraintsDescription
changeStringMax 500 chars, requiredWhat changed
timelineImpactDaysNumberSigned, default 0Days added or removed; 0 shows as “No change”
costImpactAmountNumberSigned, default 0Cost added or removed in the project currency; 0 shows as “No change”
atDateDefaults to nowWhen the change was recorded

Entries are recorded explicitly by the team — they are not auto-derived from milestone or scope edits — and the customer view is read-only (no approval workflow).

Customer activity feed

Throughout delivery the customer sees a running feed of what has happened on their project, drawn from the platform’s Activity audit log. The dashboard’s latest-update panel shows the most recent events, and a full feed lives at /customer/activity, newest first.

The feed is deliberately customer-facing: only status changes and creation events — a status reached, or a project, milestone, invoice or expert quote record created — are shown. Internal field edits, deletions, assignments and comments are filtered out. Entry titles come from a customer-facing label map rather than the platform’s internal action names, so the feed reads “Requirements approved”, not “Brd approved”.

Delivery events reach the feed as well as commercial ones: a milestone moving to a new delivery status, and a blocker being raised or cleared, are each recorded as status changes on the project. A blocker owned by a Partner or Vendor still produces an entry, but its label is withheld — the same rule the risk card follows, for the same reason.

Go-live impact tags

Each entry carries the go-live impact of the thing it is about — No impact, Minor delay or Delays go live. The tag is computed when the feed is read, never stored, and it is scored by the same per-signal predicates the health engine uses, so an entry and the go-live strip can never disagree about the same milestone, blocker or invoice:

The entry is aboutTag
A milestone that blocksGoLive, is past due and is not doneDelays go live
An invoice that is unpaid and past its due dateDelays go live
A milestone past due and not done that does not block go liveMinor delay
A blocker that is still openMinor delay
Anything else — including a subject the project no longer carriesNo impact

Those are the severity-2 and severity-1 signals from the table below, read one subject at a time instead of folded into a project-wide worst-wins score. A blocker since cleared, or a milestone since completed, therefore reads No impact on its old entries: the feed records that it happened, not that it still hurts.

Project messaging

Throughout delivery the customer and their delivery team share a single message thread scoped to the project. The customer dashboard’s “Talk to your team” card shows the latest message and a waiting on you / waiting on team signal derived from who sent last, and the full thread lives at /customer/messages, where the customer reads the history and posts a new message.

Each message is scoped to the project (lead) and carries its sender, a senderRole of customer or team, and its text. When the customer posts, every delivery-team participant (the project’s expert and delivery manager) is notified in real time over the platform’s existing Ably channel. The customer reads and posts only on their own project — the one their dashboard shows, or the project a workspace message is sent from; non-participants cannot reach the thread. The thread stays open after the project finishes, so the customer can still answer a blocker nudge.

FieldTypeConstraintsDescription
senderUser refRequiredThe user who wrote the message
senderNameStringMax 200 chars, requiredDisplay name shown in the thread and preview
senderRoleStringcustomer | teamWhich side of the thread the message is from
textStringMax 4000 chars, requiredThe message body
blockerBlocker refOptionalThe go-live blocker this message addresses, when it is outreach about a specific blocker
outreachPartyStringcustomer | expert | vendor | partnerWhich party the outreach concerns — set on every blocker-tagged message
outreachDirectionStringchase | replyWhich way it runs: the CSM’s follow-up (stored as chase), or the party followed up with answering

On the delivery side, a CSM working the Go live control dashboard sends outreach on a specific go-live blocker, and the blocker’s owner decides who receives it. The dashboard leads with Blocked customers, one row per open blocker (its subtitle counts them; a party filter that matches none says so plainly): the CSM can follow up with the blocking party from the row itself, or from the blocker’s conversation card further down. Either way it is the same outreach on the same thread. The conversation card shows the latest message, where the follow-up stands (Follow-up required before anything is sent, Follow-up completed · awaiting reply after a send, Responded once the party followed up with has replied since the CSM’s last follow-up, No follow-up needed on a blocker the team owns), and when the CSM last followed up (Last contacted, or Not yet contacted). Outreach reuses this same thread — the CSM posts as team, tagged with the blocker it addresses and the party it concerns, rather than a parallel collection — and is scoped to the CSM’s portfolio.

Blocker ownerWho is followed up withHow they receive it
CustomerThe project’s customerThe thread, plus an in-app notification
ExpertThe delivering expertThe thread, plus an in-app notification
VendorThe project’s vendorThe thread, plus an in-app notification
PartnerThe partner contactEmail — partners hold no platform account
Your teamNobodyThe card shows the blocker and its state, and offers no composer — the CSM is the team

The expert answers from their own dashboard, where Blockers waiting on you lists the open blockers they own on their own projects with the team’s latest message and a reply box. Their answer lands on the same blocker-tagged thread, and it is that answer — not a message from anyone else on the thread — that moves the CSM’s card to responded. The vendor is followed up with in exactly the same way — the thread, plus an in-app notification — but has no such card: go-live blockers left the vendor overview, which reports on their book of work, so there is no vendor-side reply box today. A partner is emailed instead: the CSM records a contact name and address on the blocker the first time they write, and the mail carries the whole follow-up, since there is nothing for a partner to sign in to. That card reads Follow-up completed · awaiting reply by email rather than claiming a response it could never receive.

Following up is not the only pressure a CSM can apply from that row. Escalating a blocker records the escalation against it — the count is what the dashboard’s already escalated figure counts, and it carries into the delivering expert’s quality record. Clearing a blocker closes the shared record, so the customer’s and the expert’s views of that project stop showing it too.

Only the customer’s own CSM — the CSM on the blocked project — can follow up, escalate or clear its blockers. The rule is enforced on the server, not just by greying out the row: at All customers scope another CSM’s rows are visible but their actions are refused with Only this customer’s CSM can act on this blocker., and so is a blocker on a project with no CSM. There is no admin override: an admin cannot act on another CSM’s blockers. When an escalate or clear does not go through — refused, already cleared by someone else, or the connection dropped — the reason shows beside that row’s actions until the next attempt. A follow-up sent from either place updates both: the row’s Last contacted line and the conversation card’s latest message move together, without a reload.

Because outreach rides the project’s one thread, a follow-up to the expert, the vendor or a partner is visible to the customer alongside everything else the team has written. That is deliberate. What the customer sees is the message: the thread renders its sender, side and text, never the blocker it is tagged to or who owns it — so the wording of a partner- or vendor-owned blocker their risk card withholds stays withheld.

Computed project health

The customer dashboard’s headline signals — the status-summary boxes (on track / minor issues / at risk), the go-live strip (status, days to go-live, go-live date, delivery confidence, and the reasons behind any risk) and the risk badge (Low / Medium / High) — are computed by the platform, not entered by anyone. They derive deterministically from the project’s real milestones, blockers, due dates and invoices, so the customer always sees an honest read on whether their project will land on time.

The engine computes a single worst-signal-wins severity for the customer’s active project, then maps that one score to every badge so they can never disagree:

SeverityTriggered whenOverall statusRiskGo-live confidenceGo-live status
2A go-live-blocking milestone is overdue (blocksGoLive, past its due date, not done), or an unpaid invoice is overdueAt riskHighLowAt risk
1A non-blocking milestone is overdue, or at least one blocker is openMinor issuesMediumMediumOn track
0None of the aboveOn trackLowHighOn track

The go-live date and the days-to-go-live count come from the project’s planned end date (lead.endDate). Each contributing signal adds a plain-language line to the reasons list shown on the go-live strip (for example, “Milestone ‘Core API build’ is overdue and blocks go live” or “Open blocker: waiting on API key”), and the headline delta is a derived label — “On track, no change”, “Minor issues — being monitored”, or the top reason when at risk.

Because the bands fall out of a pure function (computeProjectHealth), the same inputs always produce the same health, and the thresholds above are reproducible by inspection. Health is read-only for the customer — there is no manual override.

What the customer sees on the risk card

Alongside those badges, the customer’s dashboard lists the project’s open blockers — each with its label, the party that owns clearing it, and its impact. The list is limited to the parties the customer deals with: blockers owned by Customer, Your team or Expert. Blockers raised on internal surfaces against a Partner or Vendor are written for internal readers, so they are withheld from the customer’s list. When a project has no customer-facing blockers open, the card says so rather than showing anything invented.

Withholding the wording does not withhold the signal. Every open blocker — internal ones included — still feeds the health engine, so the risk badge always reflects the true picture. One consequence is deliberate: a project whose only open blocker is partner- or vendor-owned reads minor issues / medium risk while the customer’s list stays empty. Hiding an internal note must never make a project look safer than it is.

Computed Values

One virtual field is derived on the milestone itself:

VirtualSourceDescription
amountproposal.price × (percentage / 100)Monetary value of this milestone

Billing state is read from the milestone’s invoice, not from the milestone. Whether a milestone has been billed, approved or settled comes from its linked invoice’s status, isApproved and paid fields — see invoicing. Surfaces that need both join the two.

After delivery

When a project leaves delivery its isActiveProject flag clears, but the customer does not lose it. If they have no other project in delivery, their dashboard, its six figure pages, the workspace, the activity feed and the message thread all keep showing the project that finished most recently — a lead whose quote was accepted and that has since reached delivered, survey_sent, completed or qualified_out. It is marked Completed (delivered, survey sent or completed) or Closed (qualified out after the quote was accepted); the data is the same as it was during delivery. A request qualified out before any quote was accepted was never a project and is not shown. A project still in delivery always wins, and a new quote waiting on the customer shows above a finished project.

On the expert’s and vendor’s side, a finished project stays in their blocker inbox while a blocker they own on it is still open, so the follow-up can still be answered; once it is resolved, the project leaves the inbox.

Delivery Completion

A project is considered delivered when:

  1. All milestones have been marked complete (status = done)
  2. All associated invoices have been paid (invoice.paid = true)

At this point the lead is marked as Delivered and the platform issues a CSAT survey to the customer. The lead moves to survey_sent, and the customer sees it waiting under Awaiting your feedback on their satisfaction page until they respond — submitting the survey moves the lead to completed and clears the prompt. CSAT scores feed into vendor reporting and expert performance metrics. On the project workspace, the Customer satisfaction card shows the overall score out of 10 — as the survey collects it — with the customer’s written response, the delivery expert the feedback is about and the engagement value.

Closed Lost

If a project is terminated before completion — due to a dispute, quality issue, or mutual agreement — the lead is marked Closed Lost. This is tracked as a negative transition in the lead’s status workflow and reported separately in the vendor funnel dashboard.

Last updated on