URL: /revenue/offers · Id: `page:/revenue/offers`

# Offers and trials: the floor and the trial

> Offers and trials: the contribution floor that refuses a thin price, the free trial, and the first Order with the ARR it adds.

An [Offer](https://startupbuilder.dev/business/nouns/offer) is what you sell at a price. Publishing one checks the price against your target margin, then trials and first [Orders](https://startupbuilder.dev/business/nouns/order) run on it.

## The record
| Offer | Price | ARR per Customer | Trial | Status |
| --- | --- | --- | --- | --- |
| `offer_1` | `$29 a month` | `+$347 ARR` | 14 days, no card | published |
| `offer_2` | `$2,400 a year` | `+$2,393 ARR` | none | published |
| `offer_3` · Starter | `$9 a month` | `+$108 ARR` | none | draft: refused below |

```json title="The Offers on leaseline.app · sample"
[
  {
    "$id": "https://api.sb/leaseline.app/offers/offer_1",
    "$type": "Offer",
    "price": {
      "amount": 29,
      "currency": "USD",
      "unit": "month"
    },
    "interval": "month",
    "trial": {
      "days": 14,
      "card": false
    },
    "status": "published"
  },
  {
    "$id": "https://api.sb/leaseline.app/offers/offer_2",
    "$type": "Offer",
    "price": {
      "amount": 2400,
      "currency": "USD",
      "unit": "year"
    },
    "interval": "year",
    "status": "published"
  },
  {
    "$id": "https://api.sb/leaseline.app/offers/offer_3",
    "$type": "Offer",
    "price": {
      "amount": 9,
      "currency": "USD",
      "unit": "month"
    },
    "interval": "month",
    "status": "draft"
  }
]
```

ARR follows what the Customer earned: a $2,400 annual prepay adds $2,393, never 13 times its month, and a one-time Order counts 13 times for four weeks, then ends.

## Run it

```text title="Publish, trial, first Order · Ask"
Publish our $9 Starter plan, start a 14-day trial of the $29 plan for Kestrel Rentals, and place a first Order on the $2,400-a-year plan. Charge no one.
```

```ts title="Publish, trial, first Order · SDK"
import { $ } from 'startup-builder'

await $.Offer.publish({ offer: 'offer_3' }, { mode: 'try' })   // Starter, $9 a month
// 422 · Starter did not publish: at $9 a month the median Customer’s cost of $6.10 leaves 32 %, under your 60 % target margin.
// contribution-floor: Raise the price, or cut the cost each Customer carries, then publish again. · then Offer.update
const trial = await $.Offer.trial({ offer: 'offer_1', customer: 'customer_44', days: 14 }, { mode: 'try' })   // the $29 plan, no card up front
const { order } = await $.Order.place({ offer: 'offer_2' }, { mode: 'try' })   // the $2,400-a-year plan, paid up front: +$2,393 ARR, never 13 times it
return await $.Metric.query({ metric: 'first-order-arr', shape: 'trend', window: '28d' })   // the mean ARR a new Customer adds · Not live yet, gaps #4
```

```json title="Publish, trial, first Order · Response · sample"
{
  "rows": [
    {
      "week": "2026-10-19",
      "value": 347,
      "customers": 6
    },
    {
      "week": "2026-10-12",
      "value": 347,
      "customers": 2
    }
  ]
}
```

`+$347 ARR per new Customer · ■■■□ Likely · n 24 Customers in 28 days`

`$.Metric.query({ metric: 'first-order-arr', shape: 'trend', window: '28d' })` · sample

## Refuses when

```json title="422 · sample"
{
  "type": "https://startupbuilder.dev/errors/refused",
  "title": "Refused",
  "status": 422,
  "detail": "Starter did not publish: at $9 a month the median Customer’s cost of $6.10 leaves 32 %, under your 60 % target margin.",
  "checks": [
    {
      "check": "contribution-floor",
      "status": "fail",
      "observed": "32 % margin at $9, target 60 %",
      "doc": "/code/refusals#check-contribution-floor",
      "fix": "Raise the price, or cut the cost each Customer carries, then publish again.",
      "then": "Offer.update"
    }
  ]
}
```

| Check | On | Refuses | Then |
| --- | --- | --- | --- |
| [`contribution-floor`](/code/refusals#check-contribution-floor) | <Op verb="Offer.publish" /> | a price that leaves the median Customer’s cost under your target margin | <Op verb="Offer.update" /> |

## Plays that use it
| Play | What it does |
| --- | --- |
| [Closer handoff](/plays/closer-handoff) | Hands a large Deal that stalled twice to a commissioned closer, paid only on collected revenue, and tests it against the Deals your agent keeps. |
| [Data residency](/plays/data-residency) | Data residency answers where a buyer’s records live, sends the eu Quote with its annex, and holds acceptance until the region reads back. |
| [Paid pilot](/plays/paid-pilot) | Runs your product on the buyer’s own data for a fee, against pass marks no higher than they already saw, signed before it starts. |
| [Deposits before you build](/plays/reserve-drop) | Sells refundable deposits for what you have not built, for 72 hours after one creator post, and builds only past a pass mark set first. |
| [Risk-reversal offers](/plays/risk-reversal-offers) | Takes the risk off the buyer: a written results-or-free bar, a 14-day top-tier trial with no card, a one-press refund and free data moves. |
| [Switch kit](/plays/switch-kit) | Wins your rival’s users with an importer tested on its exports and a dry run they see first, stating no rival fact without a source. |
| [Trust pack](/plays/trust-pack) | Sends the trust page and prefilled security answers with every large Quote, and releases the security report only after a click-through Agreement. |