Build with Estii

Build and edit a deal

Find where a deal's money is, correct estimates, import a proposal, build scope, and reorganise it.

Every command here follows the grammar in how commands work: a preview first, --yes to apply.

Find a deal by name

You remember roughly what a deal is called, but not how it is spelt or whether it is open, closed, or archived.

estii deal search "platfrom migration"
estii deal search migration --status won
estii deal show "Banking Platform Migration"

search looks at every deal in the space, archived deals and templates included, and tolerates typos. The best name matches come first, then the most recently worked on. A deal can also match on its account or description, below every name match, and matched_on says which. --status narrows to the deals deal list --status would return.

Find what a deal's money is in, before touching it

The index prices every level of the deal, so one call tells you which line item carries the value and the next tells you what is inside it.

estii deal find nd_abc123 --kind feature --json
estii deal inspect nd_abc123 features/ft_7 --json
estii deal find nd_abc123 --kind estimate --json
estii deal set nd_abc123 estimates/es_4 amount=5d --dry-run

Units follow whatever the work is measured in, so a line item reads as days, or as licences, or as a count of fixed prices where nothing beneath it carries a unit. Where a line item mixes them, the figure covers the dominant part and is marked with a +; under --json, total.omitted says what it left out, so a script can act on the qualifier. A deal whose structure is incomplete still lists every id, with the totals left off rather than reported as zero.

Correct an estimate

Someone quoted three days where it should have been five.

estii deal list --status draft
estii deal find nd_abc123 --kind estimate
estii deal set nd_abc123 estimates/es_4 amount=5d --dry-run
estii deal set nd_abc123 estimates/es_4 amount=5d --yes

find gives you the path and what the line is currently worth. An amount names its own unit, so 5d is five days and 4h is four hours. A bare number against a role, a stream, or anything else measured in time or data is rejected, and the message names the units it takes. Resources that count whole things, like licences or a fixed fee, take a bare number. Recurrence is its own field: period=week.

Set how a deal bills and presents

The deal's own settings are the ones a person changes on the deal in the app: how it bills and on what terms, its rate card and rounding, its exchange rates, and the theme and slides its proposal uses.

estii deal set nd_abc123 milestone_kind=quarters milestone_terms="Net 30 from invoice" --dry-run
estii deal set nd_abc123 milestone_kind=quarters milestone_terms="Net 30 from invoice" --yes
estii deal set nd_abc123 theme_id=Default --yes

A rate card and a theme are named rather than given by id. Settings go in their own call, apart from the deal's name, owner, dates, currency and opportunity, because the two are written differently; a call that mixes them is refused before anything changes. Price, cost and margin are worked out from the deal's content, so setting one is refused. Margin bands, discount limits and contingency belong to the space: change them there, and estii deal update brings the deal up to date.

Turn a proposal you already have into a deal

If the scope is already written down — a pricing spreadsheet, a proposal, an RFP — hand the file over instead of typing it back in.

estii deal import proposal.xlsx --yes --json
estii run wait rn_abc123 --json
estii deal show nd_new123

Composing takes minutes, so the first command returns as soon as the work starts and gives you its run id. run wait blocks until it finishes, and stops after ninety seconds: exit 0 means it finished and the result names the deal it built, 9 means it is still going and you can wait again, 8 means it failed and says why.

estii run list
estii run show rn_abc123
estii run cancel rn_abc123 --yes

run list shows what is running in the space and run show walks the steps one by one with what each produced, which is where to look if a composition went somewhere you did not expect. run cancel stops one and throws away what it uploaded; a run that failed keeps everything, so you can still see what happened.

Spreadsheets, PDF, Word, HTML, plain text, markdown, CSV, JSON and YAML are all read, up to 20MB. The deal you get is the deal the app would build from the same file.

Build a deal's scope from nothing

Start with the deal itself. Each create reports the nodes it made, so the id for the next call comes from the last one rather than a lookup.

estii deal create name="Acme Phase 2" account="Acme Corp" --yes --json
estii deal add nd_abc123 sections/sc_2/features name="Onboarding" --yes --json
estii deal add nd_abc123 features/ft_7/tasks name="Workshop" --yes --json
estii deal add nd_abc123 tasks/tk_9/estimates resource="Senior Developer" amount=5d --yes --json
estii deal show nd_abc123

The deal arrives usable: creating one also creates its first phase, that phase's category and the category's section, and the response lists all four with requested naming the deal. Read the section's id from that response and the next call has somewhere to put a line item. The account is a name rather than an id, and one the space does not hold is created and reported.

--from says what the deal starts from, and takes one reference for every kind of start: a phase template, a deal template, an existing deal to copy, or blank for an empty one. Leave it off and the space's default applies. When copying, --without estimates leaves the numbers behind and keeps the structure. A copy is a separate deal unless you ask for a variant, below.

A phase arrives usable the same way: creating one also creates the category and section it needs, and the response lists all three with requested naming the phase. To start from a template instead of an empty phase, pass template="Delivery"; a name the space does not hold is rejected and the message lists the ones it has.

estii phase-template list names the phase templates the space holds and estii phase-template show "Delivery" reports what one of them creates: its categories, the sections under each, and the items each section is seeded with. Worth a look before building the same structure by hand. Deal templates are deals: estii deal list --templates lists them, and deal show reads one.

Author a phase template, then start a phase from it

When the same shape of phase keeps being built by hand, make it a template once.

estii phase-template add --data '{"name": "Migration", "categories": [{"name": "Move", "sections": [{"name": "Data", "features": ["Extract", "Load"]}, {"name": "Cutover"}]}]}' --dry-run
estii phase-template add --data '{"name": "Migration", "categories": [{"name": "Move", "sections": [{"name": "Data", "features": ["Extract", "Load"]}, {"name": "Cutover"}]}]}' --yes
estii phase-template show Migration
estii phase-template set Migration categories/Move allow_recurring=false --yes
estii deal add nd_abc123 phases template=Migration --yes

The whole template lands in one write, and show reads back what a phase started from it will hold. Nodes under a template are addressed the way a deal's are, as categories/<ref>, sections/<ref> and features/<ref>. The space keeps at most one default template: is_default=true moves it, is_default=false unsets it, and the default cannot be removed while it is the default. Removing a template changes no deal, since a phase already started from it holds its own copy.

Reorganise a deal's scope

Scope rarely lands in the right shape first time.

estii deal find nd_abc123 --kind feature
estii deal move nd_abc123 features/ft_7 --before ft_3 --yes
estii deal move nd_abc123 features/ft_7 --to sections/sc_5 --yes
estii deal copy nd_abc123 phases/ph_1 --yes --json
estii deal remove nd_abc123 features/ft_9 --yes

move either repositions a node among its siblings or hands it to a new parent, never both in one call: moving it elsewhere puts it last, so ordering it is a second move. copy takes everything beneath the node with it. remove takes everything beneath it too, so removing a line item removes its tasks and their estimates.

Rescope with a way back

Before pulling scope around, leave yourself somewhere to return to.

estii deal snapshot nd_abc123 --name "Before rescope"
estii deal remove nd_abc123 estimates/es_4 --yes
estii deal add nd_abc123 tasks/tk_9/estimates resource="Senior Dev" amount=5d --yes
estii deal show nd_abc123
estii deal restore nd_abc123 "Before rescope" --yes

The version comes first, before anything changes. restore takes the version by name or id, and saves the current state on its way back if the deal has left draft. A restore leaves the deal in the opportunity it is in now.

Price an alternative beside the original

The buyer asks what the deal looks like delivered by 1 March. Rather than edit the deal they have already seen, make a variant of it and reshape that.

estii deal create --from nd_abc123 as=variant name="By 1 March" original="Full scope" --dry-run
estii deal create --from nd_abc123 as=variant name="By 1 March" original="Full scope" description="Phase one delivered by 1 March" --yes --json
estii opportunity list
estii deal compare nd_new123 --variant nd_abc123

An opportunity groups the deals that price one piece of work, and each deal in it is a variant with a short name of its own, up to 20 characters. A deal that is not in one yet joins a new opportunity, named after it unless opportunity= says otherwise, and original is the variant name it takes there. The variant is a full copy placed directly below the original, so the opportunity keeps counting the original: it counts as its most advanced variant by status, then the one highest in order. Reshape the variant with the same verbs as any deal, then deal compare --variant sets its price, margin, effort and dates against the original's, with what each difference means; --detailed beside it reads the detailed comparison kept for the pair.

as=replacement makes the same copy and closes the original as abandoned, with Superseded as the reason. Only a deal still open can be replaced. Deals that already exist are grouped with deal set: opportunity= names the opportunity, creating it when the space has none by that name, name= gives the variant name, and opportunity=null takes a deal back out. Joining and leaving work at any status short of archived. Variants share one account, so a deal that joins takes the opportunity's, and setting it on one variant sets it on all of them. Each variant counts as an active deal on your plan.

Price a change before making it

estii role estimate "Senior Developer" 10d
estii role estimate "Developer" 10d
estii deal add nd_abc123 adjustments kind=discount method=percent value=10% reason="Launch" --dry-run
estii deal add nd_abc123 adjustments kind=discount method=percent value=10% reason="Launch" --yes
estii deal show nd_abc123

The two estimates price a resource swap against your live rate card without touching the deal. Once an adjustment is on the deal, deal show reports the price your scope adds up to and the price after the discount, each with its own margin.