---
title: "The Deal Service"
url: https://services.dev/docs/examples/sales/deal
captured: [sales-build.txt, sales-deal.txt, sales-launch.txt]
---
# The Deal Service

URL: https://services.dev/docs/examples/sales/deal

> services/deal.ts, step by step. AI sources, qualifies, books, meets and negotiates; a warm reply sends the booking to a setter; the deal's value, a buying committee or the seller's ask sends the meeting and everything after it to one closer. The gate holds the meeting to someone attending and the close to the seller's floor, and the closer works the account in the seller's name.

*sales's words: deal is a job, account is a subject, gig is a task, closer is a member, seller is a client, seller is a buyer (examples/sales/brand.ts).*

Each step is AI's until its `escalate` names a role, so a person books, meets or closes only where one pays off.

## The policy

`humans_from`, `setter` and `stall_days` bind the mechanics, so the seller or its agent moves them. `floor` binds the merits: only the seller sets it.

```bash
cat services/deal.ts
```

```txt title="magic values: none"
  policy: {
    floor: { word: 'number', default: 0.8, min: 0.1, max: 1, binds: 'merits', note: 'the least share of list a close may take' },
    humans_from: { word: 'money', default: 50_000, note: 'a closer takes deals at or above this value' },
    setter: { word: ['ai', 'on_warm', 'person'], default: 'on_warm', note: 'who books the meeting' },
    stall_days: { word: 'number', default: job => (job.input.motion === 'B2C' ? 7 : 14), min: 3, max: 90 },
```

An order past a bound hears every violation at once, with `next.fill` naming each field:

```txt
→ POST /deals {"account":{"name":"Northwind Freight"},"value":80000,"offer":"https://harbor.example/route-planner","policy":{"floor":1.2,"stall_days":2},"max_usd":12000,"key":"northwind-2"}
```

```txt title="magic values: none"
  "code": "policy_invalid",
  "reason": "The policy breaks 2 bounds: floor must be at most 1; stall_days must be at least 3.",
    "fill": ["policy.floor", "policy.stall_days"],
```

## The steps

| Step | Who | What moves it on |
|---|---|---|
| `source` | AI | finds the contacts at the account |
| `qualify` | AI | fit goes to `book`; no fit ends the deal lost, `no_fit` |
| `book` | AI, or a setter on `warm` | a meeting on the account's calendar; the setter's gig stays open until it is held |
| `meet` | AI, or a closer from `humans_from`, `committee` or `person` | a meeting the guard accepts as held, which pays the setter $250 |
| `propose`, `negotiate` | AI, or the same closer | the proposal, then the terms and redlines, each committed at the gate; the gig stays claimed |
| `close` | AI, or the same closer | a committed `closed_value` with the seller's invoice is won; none is lost, with its reason |
| `nurture` | a nurturer, routed | `gone_dark` brings it here; revived returns to the step it left, dead ends it lost, `dark` |

A closer's span is one gig, from the meeting to the close, and only the close fulfills it:

```bash
cat services/deal.ts
```

```txt title="magic values: none"
const setter = job => job.policy.setter === 'person' || (job.policy.setter === 'on_warm' && job.signals.warm)
const person = job => job.input.value >= job.policy.humans_from || job.signals.committee || job.signals.person
    closer: { pool: closers, span: ['meet', 'propose', 'negotiate', 'close'], revive: { first_call: '24h' },
  signals: {
    warm: job => job.record.replies > 0, committee: { inbound: true }, person: { inbound: true },
    gone_dark: { silent: job => `${job.policy.stall_days}d`, of: 'account', goto: 'nurture' } },
    book: { by: 'ai', escalate: { to: 'setter', when: setter } },
    meet: { by: 'ai', escalate: { to: 'closer', when: person }, when: job => job.input.motion !== 'B2A',
    propose: { by: 'ai' }, negotiate: { by: 'ai' },
    nurture: { by: 'nurturer', routed: true, next: job => (job.output.revived ? job.record.interrupted : 'lost') },
```

## The held meeting

The setter is paid for the meeting, not the booking: the pay waits on `meet.held`, the guard that reads the calendar for someone from the account staying 10 minutes. [A meeting nobody attended](https://services.dev/docs/examples/sales/branches.md#a-meeting-nobody-attended) is refused, rebooked and paid once.

```bash
cat services/deal.ts
```

```txt title="magic values: none"
    setter: { pool: closers, pay: { usd: 250, on: 'meet.held' } },
      guard: { held: (m, job) => m.attended(job.input.account) >= 10 || 'no one from the account stayed 10 minutes' } },
```

## How a closer reaches the account

`talk` puts the closer in the client's place: she writes from the seller's domain and calls from its number, so the account deals with the seller alone:

```bash
cat services/deal.ts
```

```txt title="magic values: none"
      talk: { as: 'client', mail: 'client_domain', calls: 'bridged' } },
```

```bash
npx services.dev check --connections   # every outside system the sales folder reads, and what stands in for it on localhost
```

```txt title="magic values: none"
services/deal.ts:31   roles.closer.talk.mail     the seller's sending domain      https://mail.example.test/sellers/{seller}
services/deal.ts:31   roles.closer.talk.calls    the seller's calling number      https://calls.example.test/bridge
```

Each touch renews the claim, and only the account's own reply resets `gone_dark`:

```txt
→ GET /gigs/tsk_7Hq5/thread   # as mbr_7Hq4, on closers.deals
```

```txt title="200 · tsk_7Hq5 · Northwind Freight · touches 4 to 10, since the claim · each renewed it · magic values: none"
  touch 4   9 Nov 11:30   mail · dana@harbor.example confirms the meeting the setter booked
  touch 5  10 Nov 11:00   meeting · bridged from Harbor's number · ops@northwind.example stayed 34 min · held
  touch 6  11 Nov 16:00   mail · dana@harbor.example → ops@northwind.example · proposal-northwind.pdf
  touch 7  13 Nov 10:15   reply · ops@northwind.example · "the CFO wants three years priced" · resets gone_dark
  touch 8  18 Nov 14:10   mail · redlines on the MSA, a three-year term
  touch 9  25 Nov 16:20   call · bridged · 31 min · they asked for 40% off
  touch 10  3 Dec 09:45   reply · "approved: year one at $74,000, net 30"
  hold      yours while Northwind talks · released only by gone_dark, an end or giving it back
  sent as   Harbor Labs · mail from harbor.example, calls from Harbor's number · Northwind never sees closers.deals
```

## The floor

`close.guard` refuses a close below the floor, from AI or a closer alike. The terms and the floor freeze at the claim, and a refused close leaves the gig claimed:

```bash
cat services/deal.ts
```

```txt title="magic values: none"
      guard: { closed_value: (v, job) => v >= job.input.value * job.policy.floor || 'below the floor you set' } },
```

```txt
→ POST /gigs/tsk_7Hq5/complete {"closed_value":50000,"notes":"they asked for 40% off for a three-year term"}
```

```txt title="magic values: none"
  "code": "guard_refused",
  "reason": "closed_value: below the floor you set ($50,000 < $64,000). The gig stays yours: propose again, or give it back.",
  "gig": { "id": "tsk_7Hq5", "status": "claimed", "hold": "renewed by each touch", "terms": "frozen at the claim, 9 Nov" },
```

```txt
→ POST /gigs/tsk_7Hq5/complete {"closed_value":74000,"term":36,"contract":"msa-northwind.pdf","pay_link":"https://invoice.stripe.com/i/acct_…H4rb/in_…8841","notes":"three years, the first at list less 7.5%"}
```

```txt title="200 · tsk_7Hq5 · done · closer · close · committed · closed_value $74,000 · won · nothing is charged until Northwind pays in_…8841 · magic values: none"
← 200 · tsk_7Hq5 · done · closer · close · committed · closed_value $74,000 · won · nothing is charged until Northwind pays in_…8841
```

When the account's best is under the floor, the closer asks, and only the seller answers, for this deal alone:

```txt
→ POST /gigs/tsk_7Hu4/ask {"to":"floor","value":0.7,"why":"their budget is fixed at $58,000 for year one, on a three-year term"}
```

```txt title="202 · ask_7Hu4 · to Harbor Labs · set the floor on job_7Hu4 · the gig stays yours while they decide · magic values: none"
← 202 · ask_7Hu4 · to Harbor Labs · set the floor on job_7Hu4 · the gig stays yours while they decide
```

```txt
→ POST /portal/deals/job_7Hu4/policy {"floor":0.7,"for":"deal"}   # Priya, in Harbor Labs' own session
```

```txt title="200 · job_7Hu4 · floor 0.7 · this deal only · $56,000 · your other deals keep 0.8 · magic values: none"
← 200 · job_7Hu4 · floor 0.7 · this deal only · $56,000 · your other deals keep 0.8
```

```txt
→ POST /gigs/tsk_7Hu4/complete {"closed_value":58000,"term":36,"contract":"msa-coho.pdf","pay_link":"https://invoice.stripe.com/i/acct_…H4rb/in_…5806"}
```

```txt title="200 · tsk_7Hu4 · done · closer · close · committed · closed_value $58,000 · won · over the floor you set for this deal ($58,000 ≥ $56,000) · magic values: none"
← 200 · tsk_7Hu4 · done · closer · close · committed · closed_value $58,000 · won · over the floor you set for this deal ($58,000 ≥ $56,000)
```

## The ends

`won` charges once the account's payment proves it. `lost` charges nothing and records one of five reasons, which the console counts without reading the deal. An end is forever, and [`again`](https://services.dev/docs/examples/sales/branches.md#started-again) starts a new deal.

```bash
cat services/deal.ts
```

```txt title="magic values: none"
    contract: 'file?', pay_link: 'url', notes: 'text', lost: ['no_fit', 'price', 'timing', 'competitor', 'dark'] },
  ends: { won: { charge: true }, lost: { charge: false, by: ['client', 'brand'] } },
  outcome: { amount: 'closed_value', proof: 'payment', match: { customer: 'account', metadata: 'deal' },
    window: { bank: '60d', card: '120d' }, tail: '90d', due: '90d', collect: job => (job.output.billing === 'monthly' ? 'each' : 'first') },
  price: { usd: 0, share: '5%', of: 'closed_value', on: 'won', ai: 40,
    lines: { referral: { share: '1%', of: 'closed_value', to: 'referrer', on: 'won' } } },
```

[The money](https://services.dev/docs/examples/sales/money.md) follows a won deal's dollars. [Policy](https://services.dev/docs/service/policy.md), [signals](https://services.dev/docs/service/signals.md) and [ends](https://services.dev/docs/service/ends.md) cover each key for any Service.

---

- Up: [The sales example](https://services.dev/docs/examples/sales.md)
- Previous: [What the brand gets](https://services.dev/docs/examples/sales/surfaces.md)
- Next: [The closers' Pool](https://services.dev/docs/examples/sales/pool.md)
