# Offer

URL: /business/nouns/offer · Id: `noun:offer`

> An Offer is a Product, under a Brand, at a price, on terms, made by a Startup.

An Offer is a [Product](/business/nouns/product), under a [Brand](/business/nouns/brand), at a [Price](/business/nouns/price), on terms, made by a [Startup](/business/nouns/startup). One Product sells as many Offers, and a [Customer](/business/nouns/customer) accepts an Offer, never a Product.

## Properties
| Property | Type | Required | Description |
| --- | --- | --- | --- |
| `price` | `money` | Yes | The amount, currency, and unit. Zero is a price. Never randomized. |
| `terms` | `text` | No | The conditions the Customer accepts, refund rights included. Never randomized. |
| `from` | `string` | No | Lineage: the slot it compiles from, as `Hypothesis#<slot>@v<version>`. |
| `activation` | `json` | No | The Event, count and window that mark a Customer activated, such as `Reminder.sent` 20 times within 7 days. |
| `credit` | `money` | No | The Credit Grant each purchase issues, for an Offer that sells prepaid credit, such as $50 of calls. |
| `atBound` | `string` | No | What a call past an Entitlement’s Limit gets: `stop`, `overage` or `upgrade` (refuse it, bill it through the Meter, or move to the next Offer). |
| `interval` | `string` | No | How often it bills: `once`, `day`, `week`, `month` or `year`. |
| `availableFrom` | `datetime` | No | When it can first be bought. Unset: from when it is published. |
| `availableUntil` | `datetime` | No | When it can last be bought. Existing Subscriptions continue past it. |
| `trial` | `json` | No | The free trial every new Customer starts with: `days`, and `card` true to take a card up front. Unset: no trial. |
| `seats` | `string` | No | How a per-seat Price counts: `manual` (the buyer sets the quantity) or `members` (people in the Customer’s Tenant, prorated as they change). |
| `uptime` | `json` | No | The monthly uptime it promises, and the share of that month’s charge credited back below each level. |
| `license` | `string` | No | What a buyer may do with the records it sells: use them `internal`ly, `display` them to its own users, or `redistribute` them. |
| `attribution` | `text` | No | The credit line a buyer shows wherever the records appear. |
| `privateTo` | [Party](/business/nouns/party) | No | The one Party who sees it and may accept it, making it a private Offer; no pricing page lists it. |

## Object
```json
{
  "$id": "https://api.sb/petpantry.shop/offers/offer_1",
  "$type": "Offer",
  "price": {
    "amount": 54,
    "currency": "USD"
  },
  "terms": "Full refund within 14 days of purchase. Delivery in 2 business days.",
  "from": "Hypothesis#approach@v1",
  "activation": {},
  "credit": {
    "amount": 54,
    "currency": "USD"
  },
  "atBound": "stop",
  "interval": "once",
  "availableFrom": "2026-10-22T12:00:00Z",
  "availableUntil": "2026-10-22T12:00:00Z",
  "trial": {
    "days": 14,
    "card": true
  },
  "seats": "manual",
  "uptime": {
    "target": 99.9,
    "credits": [
      {
        "below": 99.9,
        "percent": 10
      },
      {
        "below": 99,
        "percent": 25
      }
    ]
  },
  "license": "internal",
  "attribution": "Renewal data from Leaseline",
  "privateTo": "party_1"
}
```

## Anatomy
RouteWise (`routewise.dev`), a sample business, sells three Offers:

| Offer | Price | Features | Limits | At its bound | On sale |
| --- | --- | --- | --- | --- | --- |
| Starter (`starter`) | $49 a month | [Dashboard.ask](/business/nouns/dashboard/ask) | [Collection.query](/business/nouns/collection/query): 10,000 a calendar month | `upgrade` to Fleet | Always |
| Fleet (`fleet`) | $199 a month | [Collection.export](/business/nouns/collection/export), [Dashboard.ask](/business/nouns/dashboard/ask) | [Collection.query](/business/nouns/collection/query): 100,000 a calendar month | `overage` | Always |
| Route audit (`route-audit`) | $499 once | None. | [Document.render](/business/nouns/document/render): 1 in all; [Order.revise](/business/nouns/order/revise): 2 in all | `stop` | 2026-11-01 to 2027-01-31 |

An Entitlement bounded by a Limit has `kind` `limit`; one without has `kind` `feature`. The `kind` is computed, never set.

| `atBound` | A call past the Limit |
| --- | --- |
| `stop` | Refused: the Verb replies 402 `payment-required`. |
| `overage` | Allowed: each unit past the Limit is billed through the Meter. |
| `upgrade` | Refused with 402 `payment-required`, naming the next Offer, which `Entitlement.check` returns as `upgrade`. |

[Offer.publish](/business/nouns/offer/publish) puts each Offer on every buy surface: `checkout`, `paymentLink`, `pricingTable`, `portalEntry`, `mcpBuyTool`, `http402`.

## Relationships
- <a id="rel-accepted-by"></a>An Offer **is accepted by** many [Customers](/business/nouns/customer): a party becomes a [Customer](/business/nouns/customer) by accepting any Offer, at any price including zero. `acceptedBy`, inverse [`accepts`](/business/nouns/customer#rel-accepts).
- <a id="rel-branded-by"></a>An Offer **is branded by** one [Brand](/business/nouns/brand): each Offer carries exactly one [Brand](/business/nouns/brand), whose name, voice and logo the buyer sees. `brandedBy`, inverse [`brands`](/business/nouns/brand#rel-brands).
- <a id="rel-coupons"></a>An Offer **has** many [Coupons](/business/nouns/coupon): a [Coupon](/business/nouns/coupon) applies only to the Offers it names; an Offer may accept several Coupons. `coupons`, inverse [`discounts`](/business/nouns/coupon#rel-discounts).
- <a id="rel-funnels"></a>An Offer **has** many [Funnels](/business/nouns/funnel): each [Funnel](/business/nouns/funnel)’s path ends at one Offer, after its landing [Page](/business/nouns/page), opt-in and [Sequence](/business/nouns/sequence). `funnels`, inverse [`leadsTo`](/business/nouns/funnel#rel-leads-to).
- <a id="rel-governed-by"></a>An Offer **is governed by** one [Outcome Contract](/business/nouns/outcome-contract): an [Outcome Contract](/business/nouns/outcome-contract) warrants the result an Offer sells: its done state, acceptance, assurance and remedy. `governedBy`, inverse [`governs`](/business/nouns/outcome-contract#rel-governs).
- <a id="rel-grants"></a>An Offer **grants** many [Entitlements](/business/nouns/entitlement): an accepted Offer grants [Entitlements](/business/nouns/entitlement), each a [Verb](/business/nouns/verb) on a [Noun](/business/nouns/noun), optionally bounded by a [Limit](/business/nouns/limit). `grants`, inverse [`grantedBy`](/business/nouns/entitlement#rel-granted-by).
- <a id="rel-includes"></a>An Offer **includes** one [Product](/business/nouns/product): each Offer sells one [Product](/business/nouns/product); one Product sits inside many Offers at different prices and terms. `includes`, inverse [`includedIn`](/business/nouns/product#rel-included-in).
- <a id="rel-offered-by"></a>An Offer **is offered by** one [Startup](/business/nouns/startup): each Offer has one maker, which earns its revenue and answers for its terms. `offeredBy`, inverse [`offers`](/business/nouns/startup#rel-offers).
- <a id="rel-ordered-in"></a>An Offer **is ordered in** many [Orders](/business/nouns/order): an [Order](/business/nouns/order) commits to buying one or more Offers once. `orderedIn`, inverse [`buys`](/business/nouns/order#rel-buys).
- <a id="rel-priced-at"></a>An Offer **is priced at** one [Price](/business/nouns/price): every Offer embeds one [Price](/business/nouns/price): amount, currency, unit and tiers; zero is a Price. `pricedAt`, inverse [`priceOf`](/business/nouns/price#rel-price-of).
- <a id="rel-promoted-by"></a>An Offer **is promoted by** many [Ads](/business/nouns/ad): each [Ad](/business/nouns/ad) sells one Offer. `promotedBy`, inverse [`promotes`](/business/nouns/ad#rel-promotes).
- <a id="rel-pursued-in"></a>An Offer **is pursued in** many [Deals](/business/nouns/deal): a [Deal](/business/nouns/deal) asks one buyer to accept one or more Offers. `pursuedIn`, inverse [`pursues`](/business/nouns/deal#rel-pursues).
- <a id="rel-quoted-in"></a>An Offer **is quoted in** many [Quotes](/business/nouns/quote): a [Quote](/business/nouns/quote) prices one or more Offers for one buyer. `quotedIn`, inverse [`proposes`](/business/nouns/quote#rel-proposes).
- <a id="rel-reviews"></a>An Offer **has** many [Reviews](/business/nouns/review): a [Review](/business/nouns/review) evaluates one Offer; Reviews of a [Product](/business/nouns/product) or [Brand](/business/nouns/brand) roll up through its Offers. `reviews`, inverse [`evaluates`](/business/nouns/review#rel-evaluates).
- <a id="rel-sold-in"></a>An Offer **is sold in** one [Motion](/business/nouns/motion): each Offer sells through one [Motion](/business/nouns/motion), which says who buys and who acts for them. `soldIn`, inverse [`sells`](/business/nouns/motion#rel-sells).
- <a id="rel-subscriptions"></a>An Offer **has** many [Subscriptions](/business/nouns/subscription): a [Subscription](/business/nouns/subscription) bills one recurring Offer each [Period](/business/nouns/period) until canceled. `subscriptions`, inverse [`renews`](/business/nouns/subscription#rel-renews).
- <a id="rel-targets"></a>An Offer **targets** many [Personas](/business/nouns/persona): an Offer speaks to and is priced for named [Personas](/business/nouns/persona). `targets`, inverse [`targetedBy`](/business/nouns/persona#rel-targeted-by).
- <a id="rel-waitlist"></a>An Offer **has** one [Waitlist](/business/nouns/waitlist): a [Waitlist](/business/nouns/waitlist) admits its [Leads](/business/nouns/lead) to one Offer once that Offer opens. `waitlist`, inverse [`awaits`](/business/nouns/waitlist#rel-awaits).

## Lifecycle

| State | Meaning | Moves to |
| --- | --- | --- |
| `draft` (first) | The Offer is being written: no party can see or accept it. | `published` by [Offer.publish](/business/nouns/offer/publish) |
| `published` | Parties can see the Offer, accept it and subscribe to it. | `withdrawn` by [Offer.withdraw](/business/nouns/offer/withdraw) |
| `withdrawn` | The Offer takes no new Customers; every Order and Subscription it made stays in force. | None: it ends here. |

## Verbs
| Verb | Description | Subject | Event | Class |
| --- | --- | --- | --- | --- |
| [Offer.accept](/business/nouns/offer/accept) | Accept records that a party takes an Offer, which makes that party a Customer of the Startup. | [Customer](/business/nouns/customer) | [`Offer.accepted`](/business/events/Offer.accepted) | None. |
| [Offer.grant](/business/nouns/offer/grant) | Grant adds an Entitlement to an Offer: a Verb its Customers may call, optionally bounded by a Limit. | [Product Lead](/agents/product/product-lead) | [`Offer.granted`](/business/events/Offer.granted) | Two-way: `try`, `send`, `do` |
| [Offer.publish](/business/nouns/offer/publish) | Publish makes an Offer available for parties to accept. | [Product Lead](/agents/product/product-lead) | [`Offer.published`](/business/events/Offer.published) | None. |
| [Offer.subscribe](/business/nouns/offer/subscribe) | Subscribe accepts a recurring Offer as a Subscription that renews each period until it ends. | [Billing Specialist](/agents/finance/billing-specialist) | [`Offer.subscribed`](/business/events/Offer.subscribed) | None. |
| [Offer.trial](/business/nouns/offer/trial) | Trial starts a Customer on an Offer free for a set number of days, with or without a card, converting or ending by its terms. | [Billing Specialist](/agents/finance/billing-specialist) | [`Offer.trialed`](/business/events/Offer.trialed) | Two-way: `try`, `send`, `do` |
| [Offer.withdraw](/business/nouns/offer/withdraw) | Withdraw closes a published Offer to new Customers and leaves every existing Order and Subscription in force. | [Product Lead](/agents/product/product-lead) | [`Offer.withdrawn`](/business/events/Offer.withdrawn) | None. |
| <a id="create"></a>[Offer.create](/business/nouns/offer/create) | Creates an Offer. Inherited from [Noun](/business/nouns/noun). | None. | `Offer.created` | Two-way: `try`, `send`, `do` |
| <a id="get"></a>[Offer.get](/business/nouns/offer/get) | Returns one Offer by id. Inherited from [Noun](/business/nouns/noun). | None. | None. | Read: `try`, `do` |
| <a id="list"></a>[Offer.list](/business/nouns/offer/list) | Returns Offers, newest first, one page at a time. Inherited from [Noun](/business/nouns/noun). | None. | None. | Read: `try`, `do` |
| <a id="update"></a>[Offer.update](/business/nouns/offer/update) | Changes the given properties of an Offer. Inherited from [Noun](/business/nouns/noun). | None. | `Offer.updated` | Two-way: `try`, `send`, `do` |
| <a id="delete"></a>[Offer.delete](/business/nouns/offer/delete) | Deletes an Offer; its history is kept. Inherited from [Noun](/business/nouns/noun). | None. | `Offer.deleted` | One-way: `send`, `do` |

## Permissions
Which Access level may call each Verb. One-way Verbs wait their delay window.

| Operation | `owner` | `admin` | `member` | `viewer` |
| --- | --- | --- | --- | --- |
| `Offer.create` | Yes | Yes | Yes | No |
| `Offer.get` | Yes | Yes | Yes | Yes |
| `Offer.list` | Yes | Yes | Yes | Yes |
| `Offer.update` | Yes | Yes | Yes | No |
| `Offer.delete` | Yes | Yes | Yes | No |
| `Offer.accept` | Yes | Yes | Yes | No |
| `Offer.grant` | Yes | Yes | Yes | No |
| `Offer.publish` | Yes | Yes | Yes | No |
| `Offer.subscribe` | Yes | Yes | Yes | No |
| `Offer.trial` | Yes | Yes | Yes | No |
| `Offer.withdraw` | Yes | Yes | Yes | No |

## Stripe
**Product** (`product`). The source of truth is db.sb, and Stripe holds a copy written from it.

| Property | Stripe field | Source of truth |
| --- | --- | --- |
| `price` | `default_price` | db.sb |

## Call it

**Ask**

```text
Publish the offer “RouteWise API”.
```

**API**

```bash
curl -X POST https://api.sb/routewise.dev/offers/offer_1/publish \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY"
```

```json
{
  "api": "https://api.sb/routewise.dev",
  "offer": {
    "$id": "https://api.sb/routewise.dev/offers/offer_1",
    "$type": "Offer",
    "price": {
      "amount": 25,
      "currency": "USD"
    }
  },
  "action": {
    "$id": "https://api.sb/routewise.dev/actions/action_1",
    "operation": "Offer.publish",
    "status": "done"
  },
  "links": {
    "self": "https://api.sb/routewise.dev/actions/action_1"
  }
}
```

**SDK**

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

const offer = await $.Offer.publish({ offer: 'offer_1' })
```

**CLI**

```bash
npx startup-builder do Offer.publish --json '{"offer":"offer_1"}'
```

**MCP**

```json
{
  "name": "do",
  "arguments": {
    "code": "return await $.Offer.publish({ offer: 'offer_1' })"
  }
}
```

## Example
A dental bookkeeping Startup sells one Product as two Offers: $600 a month for monthly closes, or $2,400 once for catch-up books. Both carry its one Brand.