Promotions
A promotion reduces what a customer pays: a percentage or fixed amount off a line, a replacement price, free billing periods, free units, a bundle reward, or a cut or rebate once spend or usage passes a threshold. The engine evaluates it while the invoice is being built, so the discount appears as part of normal billing rather than as a manual adjustment afterwards.
A promotion is two things bolted together. Eligibility, its audience and its scope decide which customers and which lines it may touch. Its shape decides what it does to them. Between those sits the question that governs everything else on this page: how does a customer come to hold it at all?
How customers get it
This is the distribution field, and it is required at creation and frozen
once the promotion is published. The dashboard asks it as "Who gets it, and
how?" on the second step of the builder.
Available to the audience
distribution: "auto_apply". Eligible customers get it when they redeem it, or
when an operator attaches it with POST /v1/promotions/{id}/redeem. Once held,
it applies automatically at billing. No code is involved.
This is the right answer for most promotions.
Auto-enrol everyone
distribution: "auto_enroll". Every member of the audience is enrolled without
doing anything, unless they opt out. Use it for a blanket price change dressed
as a promotion, or a goodwill credit you do not want customers to have to claim.
Customers redeem a code
distribution: "coupon". The customer types a code to opt in. code becomes
required (and is refused for any other distribution), and it must be unique
within the workspace, case-insensitively.
An auto_enroll or coupon promotion needs somebody to reach: an
audience_id or a customer_includes list. Publishing one with neither is
refused.
Exact names
The values are lowercase: auto_apply, auto_enroll, coupon. There is no
DIRECT distribution. The lifecycle endpoints are
POST /v1/promotions/{id}/publish and POST /v1/promotions/{id}/end; there is no
activate, deactivate or archive. A promotion's status is draft, live
or ended.
What it does: the shape
shape is required and is what the promotion does, flattened under a kind
discriminator. It is frozen once the promotion is published.
shape.kind | What it does |
|---|---|
line_discount | discount_kind percent (with percent), fixed or override (with amount); optional first_n_periods, min_spend |
free_periods | periods billing periods free |
free_units | units of a product free, per_period or lifetime |
bundle | a reward on a target line when every leg of a set is present |
threshold | a percent cut once units or spend reach a threshold within a window |
rebate | a percent of the whole window rebated once the threshold is reached |
Each shape may carry eligibility (first_purchase, plans,
requires_verification). scope narrows the lines it applies to by
products, price_keys and tags; omit it for the whole catalogue. Money
amounts in a shape or a limit are a money table: a currency-less default
spent in whatever currency the bill is in, plus optional entries of
{currency, value} that win for the currencies they name. See
Promotion effects and Promotion rules.
When and how much: window and limits
window_start is required: the first day it may fire, inclusive, frozen
once published. A backdated start is allowed and re-bills nothing.
window_end is the last day, inclusive; omit it for open-ended. It is the one
window bound a live promotion can still move, and only inwards.
limits[] are the ceilings. Each one says what it counts (redemptions,
firings, amount or units), over which buckets (promotion, or any of
customer, subscription, price_key), per which window (lifetime,
billing_period, calendar_month, calendar_year, first_n_periods with
periods), and what happens at_ceiling to a firing that does not fit (skip
or partial). An amount ceiling takes its cap in amount; the others in
ceiling. A total budget is an amount ceiling over promotion; one use per
customer is a redemptions ceiling of "1" over customer.
Ceilings are managed on a live promotion with POST /v1/promotions/{id}/limits,
PATCH and DELETE /v1/promotions/{id}/limits/{position} (downwards only), and
GET /v1/promotions/{id}/limits/usage shows how much of each is spent.
Create it in the dashboard
Over the API, this is POST /v1/promotions followed by
POST /v1/promotions/{id}/publish; see Create it with the API.
The dashboard's builder is a six-step wizard: Goal → Audience → Rule →
Name & dates → Limits → Review. It asks for a goal first and then only shows
the fields that goal needs.
-
Open the builder
In the sidebar under Advanced, click Promotions, then Create Promotion.
-
Pick what you are trying to do
The Goal step offers presets: a discount, credit or free units, a free trial, a product or variant bundle reward, spend and usage ladders, or Build the rule yourself when none of them fit.
The goal only shapes the questions you are asked next -
Set the audience and distribution
Answer "Who gets it, and how?", then narrow the audience to Every customer or Customers matching rules. The builder shows a live count of how many customers match, which is the cheapest sanity check available.
-
Define the rule, dates and limits
The Rule step builds the shape, eligibility and scope. Name & dates sets
window_start, which is required, and optionallywindow_end. Limits caps the damage: a total budget, a cap on redemptions overall, and a cap per customer, each a row oflimits. -
Review and publish
A promotion starts as a Draft and discounts nothing. Publishing is what puts it in front of customers.
Create it with the API
name, distribution, shape and window_start are required. A promotion is
created as a draft.
Publishing validates the whole draft, freezes the rule (shape, scope,
distribution and code, window start, negotiated-price policy, exclusions) and
arms the window-end timer. After that, POST /v1/promotions/{id}/narrow is the
only edit a live promotion takes, and only in the narrowing direction: an
earlier window end, a lower ceiling, a changed audience or display text. A wider
edit is refused with PROMOTION_WOULD_WIDEN.
Try it before you publish it
POST /v1/promotions/preview runs a customer's held promotions against the bill
lines you send and returns each line's net with the chain that produced it,
including the promotions that matched and did nothing, and why. Send the draft
as promotion to evaluate it alongside them as if it were published. Nothing is
written. On a promotion with a budget, that is a great deal cheaper than finding
out from the invoices.
Dashboard and API names
| In the dashboard | In the API |
|---|---|
| "Who gets it, and how?" | distribution, required |
| Available to the audience | "auto_apply" |
| Automatically enroll everyone | "auto_enroll" |
| Customers redeem a code | "coupon", with code |
| Rule | shape, scope, shape.eligibility |
| Name & dates | name, window_start, window_end |
| Limits → total budget | a limits[] row counting amount over promotion |
| Limits → what happens when it runs out | at_ceiling: skip or partial |
| Limits → per-customer cap | a limits[] row counting redemptions over customer |
| Draft → Active | POST /promotions/{id}/publish (status live) |
| End | POST /promotions/{id}/end (status ended) |
Where promotions go next
- Publish it. A draft discounts nothing.
- Watch the budget.
GET /v1/promotions/{id}/redemptionslists who holds it,GET /v1/promotions/{id}/firingsis its ledger, andGET /v1/promotions/{id}/limits/usageshows how much of each ceiling is spent. - See what it did to a bill.
GET /v1/invoices/{id}/promotionsreturns every promotion row of an invoice in lane order,PENDINGon a draft andSETTLEDonce issued, plussavedper currency. Match a row to its invoice lines by product andprice_key.GET /v1/subscriptions/{id}/promotionslists the promotions on a subscription. - Understand stacking before running two at once.
never_withnames promotions this one may never fire alongside;negotiated_prices(excludeby default) decides whether it may cut a hand-negotiated price.
Next step
Promotions apply while an invoice is built. Continue to Invoices to see where they land.
Going deeper
A promotion is two mechanisms bolted together, and each has its own page.
| Topic | What it adds |
|---|---|
| Promotion effects | What each shape does to a bill |
| Promotion rules | Who qualifies, and which lines it touches |
Reference
| Topic | When you need it |
|---|---|
| Audiences | Reusable customer populations shared across promotions |
| Wallets | Where credit-granting promotions deposit |
| Promotions API | Every field, plus preview, redeem, narrow and end |