URL: /capabilities/keep-and-grow/activated-customers · Id: `page:/capabilities/keep-and-grow/activated-customers`

# Activated customers

> New customers who reach the week-1 event your offer declares within 14 days of signing up, billed per customer who does.

New customers who reach the week-1 event your [offer](https://startupbuilder.dev/business/nouns/offer) declares within 14 days of signing up, billed per customer who does.

```text title="Run it · Ask"
Get this month’s new customers to their first reconciled ledger within 14 days, $40 each, and show me the plan first.
```

```ts title="Run it · SDK"
import { $ } from 'startup-builder'

const result = await $.try.Activation.deliver({ goal: 'Get this month’s new customers to their first reconciled ledger within 14 days, $40 each, and show me the plan first.', budget: { amount: 720, currency: 'USD' } })
```

```json title="Run it · Response"
{
  "mode": "try",
  "stored": false,
  "estimate": "About 16¢ per 10 new customers",
  "bills": "$40 an activated customer",
  "sample": {
    "customer": "Kestrel Lane Homes",
    "signed": "January 6",
    "event": "first reconciled ledger",
    "on day": "4",
    "state": "activated, accepted"
  }
}
```

- **Kind:** Service
- **Price:** $40 per activated customer
- **Your time:** 20 minutes
- **Approval:** Goes out on its own
- **Deepens:** [`process:onboarding`](https://startupbuilder.dev/workflows/processes/onboarding)
- **Default limits:** [What waits for you](/capabilities/agents#what-waits)

## What you get
- **10 new customers**, as [`noun:customer`](https://startupbuilder.dev/business/nouns/customer) records
- `activated[]` · Each customer who reached the event: the day it fired, counted from signup, and the note that came before it.
- `stalled[]` · Each customer past day 14 without it, with the step they stopped at; none is billed.
- `bill` · Accepted activations times the price.

**Connect once:** [Stripe](/code/integrations/stripe), [Gmail](/code/integrations/gmail). **How long:** 14 days from each signup.

## Trigger
It runs when you call it, or on any Trigger you set.

```ts
await $.Workflow.create({ verb: 'Activation.deliver', trigger: { schedule: 'weekly' } })
```

## Steps
Your agent does these steps, one row per step; each step’s check is part of its row and its price.

| Step | Verb | Returns | Price | Approval |
| --- | --- | --- | --- | --- |
| 1. Read the offer’s week-1 event | `Collection.query` | The activation Event the offer declares; none refuses with `activation-event` | Free | Runs on its own |
| 2. Score each new customer every day | [`Customer.score`](/capabilities/keep-and-grow/silence-watch) | Each new customer’s usage against the step before the event, flagged when it stalls | Free | Runs on its own |
| 3. Write the day-1, day-3 and day-7 notes | [`Content.draft`](/code/apps/sites-and-content/draft) | A note that names the one step between them and the event, with their own numbers | About 12¢ | Runs on its own |
| 4. Send each note | [`Message.schedule`](/code/apps/mailboxes/schedule) | Each note sent; the notes stop the day the customer reaches the event | About 1.5¢ | Goes out on its own |
| 5. Save anyone stuck at day 10 | [`Customer.save`](/capabilities/keep-and-grow/save-and-win-back) | One note with the offer your Policy allows, such as a 20-minute setup session | About 2.1¢ | Runs on its own |
| 6. Read the event as it fires | [`Metric.query`](/code/apps/analytics-and-experiments/query) | Each customer `activated` on the day the event fires, inside 14 days of signup | Free | Runs on its own |

Calls: `Collection.query`, then `Customer.score`, then `Content.draft`, then `Message.schedule`, then `Customer.save`, then `Metric.query`.

About 16¢ per 10 new customers, each time it runs, capped at $1.00. Its own steps cost 14¢; the compositions it orders add 2.1¢.

| How it runs | What it says |
| --- | --- |
| **Count the event your offer declares, never a login** | A login or an opened email is not activation. The event is the first thing a paying customer does, declared on the offer, read from your own product’s Events. |
| **Price it against what a customer is worth** | A customer at $149 a month is worth the $795 CAC that plan can afford. $40 is 5% of that, billed only for the customers who reach the event. |
| **Stop writing the day it fires** | The notes stop the moment the event fires, so an activated customer never gets a nudge for a step they took. |

## Example: give it a goal
> Get this month’s new customers to their first reconciled ledger within 14 days, $40 each, and show me the plan first.

The plan it shows runs 6 steps, priced before it spends anything; Run it above shows the result it returns.

## When something fails

| When | Then |
| --- | --- |
| The offer declares no activation Event | It refuses with `activation-event` before anything runs; declare the event on the offer, then order again. |
| A customer has an open support conversation | The notes pause and the customer is handed to a person with the context; the 14 days keep running. |
| A customer activates, then refunds within 30 days | The activation is credited and the refund reason is written to the customer. |

Each step retries three times, then falls through to the next route; one item's failure lands in `skipped` with its reason, never failing the call. Before a step would cross `maxCost` it stops and returns what it has, with a `resume` link. Each step's idempotency key is the call's key plus the step and the item: `leaseline-activation-deliver-1-<step>-<item>`.

## What it promises
| Term | This Service |
| --- | --- |
| Done | A new customer reaches the activation Event your offer declares within 14 days of signing up. |
| Counted by | `Activation.delivered` |
| Billed | $40 an activated customer, on acceptance |
| Target | Every new customer ordered in the month. |
| Accepted when | The event firing accepts it. A customer who refunds within 30 days of activating is credited. |
| Credited | A customer who refunds within 30 days of activating. |
| Remedy | A customer stuck at day 10 gets a save note under your Policy at no charge; one who never reaches the event is never billed. |
| Evaluator | Code reads the offer’s activation Event from your product’s own Events, with its time against the signup. |
| Window | 14 days from each signup; the refund check runs 30 days past the event. |
| Assurance | `measured` |
| Oversight | `none`: agents and code do every step |

You pay $40 for each activated customer you accept. Behind it, the agents spend about 16¢ in calls for 10 new customers, never more than $1, shown beside your bill. That spend is inside the $40: you are billed for accepted units only, never for the calls.

**January: 18 new Leaseline customers, every one ordered.**

| Count | What happened |
| --- | --- |
| 18 | Ordered |
| 18 | Signed up and ordered. |
| 3 | Stuck at day 10, so each got a save note offering a setup session; 2 then activated. |
| 13 | Reached their first reconciled ledger within 14 days; the median was day 4. |
| 5 | Not activated by day 14: never billed, each with the step they stopped at. |
| 1 | Refunded within 30 days of activating: credited. |
| **12** | **Accepted and billed: 12 × $40 = $480** |

## Change it
| Ask | Compiles to |
| --- | --- |
| Count the second reconciled ledger instead of the first. | [`Activation.deliver`](/capabilities/keep-and-grow/activated-customers) |
| Offer a setup call at day 5 instead of day 10. | [`Customer.save`](/capabilities/keep-and-grow/save-and-win-back) |
| Show me where the stalled ones stopped. | [`Metric.query`](/code/apps/analytics-and-experiments/query) |

```ts
import { $, Service } from 'startup-builder'

export default Service({
  name: 'activation-deliver',
  verb: 'Activation.deliver',
  input: { offer: 'string', within: 'string', customers: "'new' | string[]", policy: 'string' },
  maxCost: 1,
  orders: ['Customer.score', 'Customer.save'],                               // each one's cost rolls up into this call
  oversight: { posture: 'none' },
  contract: {
    done: 'Activation.delivered', accept: { by: 'event', within: '14d' }, assurance: 'measured',
    bills: { usd: 40, per: 'activated customer' }, credits: ['refund-within-30d'], remedy: 'save-note-at-day-10',
  },
  execute: async (ctx, { offer, within = '14d', policy }) => {
    const event = await ctx.step('event', () => $.Collection.query({ from: 'offer', id: offer, fields: ['activationEvent'] }))
    if (!event.activationEvent) return ctx.refuse('activation-event')
    on.Customer.created(async (customer, $) => {
      for (const day of [1, 3, 7]) await ctx.at(customer, `${day}d`, () => $.Content.draft({ kind: 'onboarding-note', customer, step: event.next, maxCost: 0.005 }))
      await ctx.at(customer, '10d', () => $.Customer.save({ customer: customer.$id, policy }), { unless: event.activationEvent })
    })
  },
})
```

Extend it with `extends: 'Activation.deliver'` and `base()`, or [compose your own](/code/compose) from these steps.

## Works with
**Plays that order it:** [Run it beside the old tool](/plays/run-beside).

| Orders | Kind | Runs, each at its own price | Rolls up |
| --- | --- | --- | --- |
| [Silence watch](/capabilities/keep-and-grow/silence-watch) | Workflow | 14 × Free | Free |
| [Save and win back](/capabilities/keep-and-grow/save-and-win-back) | Agent task | 3 × About 0.7¢ a customer | About 2.1¢ |

It serves [Keep and grow customers](/capabilities/keep-and-grow). It deepens [`process:onboarding`](https://startupbuilder.dev/workflows/processes/onboarding).

## For agents

| Fact | Value |
| --- | --- |
| Guide | `Activation.deliver` |
| Guide approval | `none` |
| Goal | Get this month’s new customers to their first reconciled ledger within 14 days, $40 each, and show me the plan first. It may change counts, the order of steps, soft filters and the route a read takes, and it stops early when the goal is met. It never relaxes your words, raises its own budget, answers its own approval, calls a Play or acts outside your standing approval. |
| Retry | Three times with backoff, then the next route; the same idempotency key never charges, sends or posts twice |
| Idempotency key | `leaseline-activation-deliver-1` |
| Lookups | A `find:` input asks unless exactly one record fits |

Step 4 reaches people outside your records and goes out on its own within your limits.

| Step | Function | Doer | Checker | Assurance | Inputs |
| --- | --- | --- | --- | --- | --- |
| 1. Read the offer’s week-1 event | `code` | Code | Code reads the Event name from the offer’s declared fields | `measured` | `$0.offer` |
| 2. Score each new customer every day, by [Silence watch](/capabilities/keep-and-grow/silence-watch) | `code` | Code | Code scores usage by the declared rule and reads each flag back | `measured` | `$0.customers` |
| 3. Write the day-1, day-3 and day-7 notes | `agentic` | The [Onboarding](/capabilities/agents/onboarding) agent | Adversarial review by a second model: Pass, or held: every number from their usage, every step one the product has | `graded` | `$1.event, $2.usage` |
| 4. Send each note | `code` | Code | Code reads the result back from where it landed | `measured` | `$3.passed` |
| 5. Save anyone stuck at day 10, by [Save and win back](/capabilities/keep-and-grow/save-and-win-back) | `agentic` | The [CSM](/capabilities/agents/csm) agent | A second model checks each note against the customer’s record and your Policy | `graded` | `$2.stalled, $0.policy` |
| 6. Read the event as it fires | `code` | Code | Code reads the activation Event from your product’s own Events, timed against the signup | `measured` | `$1.event, $0.within` |

Code does steps 1, 2, 4 and 6; The [Onboarding](/capabilities/agents/onboarding) agent does step 3; and the [CSM](/capabilities/agents/csm) agent does step 5. A second model checks the 2 graded steps, never the one that wrote it, so the whole call is `graded`.

## Under the hood

| Kind | What it touches |
| --- | --- |
| Systems | [Analytics and experiments](/code/apps/analytics-and-experiments), [Mailboxes](/code/apps/mailboxes), [sites and content](/code/apps/sites-and-content) |
| Nouns | [`noun:collection`](https://startupbuilder.dev/business/nouns/collection), [`noun:customer`](https://startupbuilder.dev/business/nouns/customer), [`noun:content`](https://startupbuilder.dev/business/nouns/content), [`noun:action`](https://startupbuilder.dev/business/nouns/action), [`noun:message`](https://startupbuilder.dev/business/nouns/message), [`noun:metric`](https://startupbuilder.dev/business/nouns/metric) |
| Events | `Collection.queried`, `Content.drafted`, `Action.evaluated`, `Message.scheduled`, `Metric.queried` |

## Reference

| Name | Type | Default | Meaning |
| --- | --- | --- | --- |
| `offer` | `Offer` | Required | The offer whose declared activation Event counts, such as Leaseline’s first reconciled rent ledger. |
| `within` | `string` | `'14d'` | Days from signup in which the event must fire to count. |
| `customers` | `'new' \| Customer[]` | `'new'` | Who is ordered: every new customer this month, or the ones you name. |
| `policy` | `Policy` | `your retention Policy` | What a day-10 save note may offer without asking: a setup session, a pause, or a credit up to its amount. |

Emits one Event named for its Verb. Refuses with [`max-cost`](/code/refusals#max-cost) before it spends, and with the Data calls and Accounts groups ([refusals](/code/refusals)).