Offers
An Offer is a plan your customers buy. Leaseclock Pro costs $29 a month and allows 200 Units. The same file sets the pricing page, the Stripe product and price, the limit the API enforces and the upgrade prompt in the app.
The file
Each Offer is one file in your product's Offer folder. This is Leaseclock Pro, as Leaseclock pushes it:
---
name: Leaseclock Pro
price: { amount: 29, currency: USD, unit: month }
limits: { Unit: 200 }
personas: Property manager, Leasing coordinator
status: Live
---Push it with npx saas.dev push, and Leaseclock is on sale.
What it sets
| Where | What Leaseclock Pro puts there |
|---|---|
| leaseclock.com/pricing | A card reading "Leaseclock Pro, $29 a month", "Up to 200 units" and "For property managers and leasing coordinators", with a Subscribe button |
| Your Stripe account | A product named Leaseclock Pro with a monthly price of $29.00 USD |
| Stripe Checkout | "Subscribe to Leaseclock Pro, $29.00 per month", in Leaseclock's teal |
| The API | A 402 limit-reached error for any Unit past a customer's 200th |
| The app | "140 of 200 Units" on Northgate Rentals' Billing screen, and the upgrade prompt when a customer reaches the limit |
Docs and llms.txt | The plan, its price, its limits and who it is for, so a customer's agent can compare plans for its user |
The pricing page, checkout and Stripe read the price from one line, so they always agree.
Keys
| Key | In Leaseclock Pro | What it does |
|---|---|---|
name | Leaseclock Pro | The plan's name on the pricing page, at checkout, in Stripe and on every invoice |
price.amount | 29 | The price in whole units of the currency. 29 charges $29.00, and 29.50 charges $29.50 |
price.currency | USD | Any three-letter currency code Stripe accepts |
price.unit | month | The billing period, month or year. Stripe renews the subscription once per period |
limits | { Unit: 200 } | The most records of each named Noun one customer may hold. A Noun left out has no limit |
personas | Property manager, Leasing coordinator | Who the plan is for, shown on the pricing card and in your docs |
status | Live | Draft sells in Test only, Live sells in Test and Live, and Retired stops new sales |
$id | leaseclock.com/offers/pro | The Offer's address. It ends in the file name, pro |
Limits
A limit counts the records one customer holds in its workspace. Northgate Rentals manages 140 apartments, so it holds 140 Units and has room for 60 more on Leaseclock Pro. A Unit that is deleted stops counting.
The API enforces each limit, so the app, the CLI, the SDK and your customers' agents all stop at the same place. Here is the request that would add a customer's 201st Unit:
curl -X POST https://leaseclock.com/api/units \
-H "Authorization: Bearer $LEASECLOCK_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "label": "9A", "building": "Birch House" }'{
"error": {
"code": "limit-reached",
"message": "Leaseclock Pro includes 200 Units, and this workspace holds 200.",
"noun": "Unit",
"limit": 200,
"offer": "leaseclock.com/offers/pro",
"docs": "https://leaseclock.com/docs/errors#limit_reached"
}
}The request saves nothing. An import that would pass the limit is rejected whole and saves no records. Your customers read about the error on your own docs at leaseclock.com/docs/errors, and you read about it in Errors.
Add a free plan
An Offer with an amount of 0 is a free plan. To let property managers start Leaseclock without a card, add a second file:
---
name: Leaseclock Starter
price: { amount: 0, currency: USD, unit: month }
limits: { Unit: 10 }
personas: Independent landlord
status: Live
---New customers start on Leaseclock Starter without visiting checkout. The pricing page shows both cards, cheapest first.
The upgrade prompt
When a customer reaches a limit, the button that would pass it opens the upgrade prompt in place of the form. The prompt states the limit, how many records the customer holds, and the smallest Live Offer that allows more, with an Upgrade button that opens Stripe Checkout. The new limit applies the moment Stripe confirms the payment.
A Leaseclock Starter customer who adds an eleventh Unit sees:
Leaseclock Starter includes 10 Units
Upgrade to Leaseclock Pro for up to 200 Units, $29 a month.
Upgrade · Not now
When no Live Offer allows more, the prompt states the limit and the count, with no Upgrade button.
Change an Offer
Edit the file and push it again. To raise Leaseclock Pro to $35 a month:
- price: { amount: 29, currency: USD, unit: month }
+ price: { amount: 35, currency: USD, unit: month }The pricing page and the next checkout show $35. SaaS.Dev adds a new $35.00 price to the Stripe product, and every customer already subscribed keeps the $29.00 price they bought.
A new limit applies to every customer on the Offer on the next request. Raise Unit to 250 and each Pro customer can add 50 more. Lower it, and a customer over the new limit keeps every record it has and adds no more until it is under the limit.
A new name renames the Stripe product, the pricing card and the next invoice. The $id stays the same, so subscriptions carry over.
To stop selling an Offer, set status: Retired. It leaves the pricing page, the upgrade prompt and checkout, and every current subscription renews as before.
Questions
Can a plan have a monthly and a yearly price?
Yes. Write price as a list: price: [{ amount: 29, currency: USD, unit: month }, { amount: 290, currency: USD, unit: year }]. The pricing page gets a Monthly and Yearly switch, and Stripe gets one price for each.
What can a customer do before it subscribes?
With a free Offer Live, it starts on that plan. Without one, every limit is zero until it subscribes, so a new customer's first New Unit opens the prompt for Leaseclock Pro. Nouns with no limit stay open.
Can I give a discount?
Yes. Create a promotion code in your Stripe dashboard. Checkout accepts it, and the Offer stays as written.
Does SaaS.Dev take a share of what customers pay?
No. Customers pay you through your own Stripe account, and Stripe charges its standard card rate. See Stripe for checkout, subscriptions and payouts.
Which Nouns can carry a limit?
Any Noun in your product. limits: { Unit: 200, Lease: 400 } limits both. A Noun left out of limits has no cap.