Self-service AWS CodePipeline platform — developers ship compliant CI/CD pipelines in minutes via dashboard, CLI, CDK, or AI prompt, while platform teams enforce policy-as-code guardrails, governance, and per-team isolation.
This is the reference for operator-granted discounts — the price-only reductions a system admin mints, issues, and an account redeems, all realized as Stripe customer-balance usage credits rather than provider coupons. It covers the discount-code format, the mint → issue → redeem lifecycle, per-provider handling (Stripe in-app vs. AWS Marketplace private offers), and the admin/self-service API. For the automatic, composition-based credits earned from add-on combos, see the note under Everything is a usage credit.
Discounts are price-only adjustments an operator grants on top of an account’s subscription — a temporary price reduction (one-time or recurring) or a standing balance, both realized as a usage credit that offsets future costs. They never change entitlements, quotas, or tier; they only change the bill, and they are never forwarded to the provider as a coupon — billing owns the reduction. For the caps and tiers themselves, see feature tiers and add-on bundles.
Discounts are controlled by BILLING_DISCOUNTS_ENABLED (on by default; set false to hide the surface). On Stripe they realize as a customer-balance credit. On AWS Marketplace — which has no customer-balance primitive — they realize by withholding reported metered usage, active only when BILLING_METERING_ENABLED is also on (see Applying a discount by provider); otherwise Marketplace accounts are rejected.
Every discount is authored in a compact, human-readable form and issued (when handed to a customer) as an opaque, unforgeable token.
Authoring form — what an operator types when minting:
value : unit : kind [ : campaign ]
| Field | Values | Notes |
|---|---|---|
value |
positive integer | percent points, or whole dollars (stored as cents) |
unit |
dollar | percent |
aliases $ / % |
kind |
onetime | recurring | credit |
see below |
campaign |
optional label | e.g. summer24, for reporting |
Examples: 50:percent:onetime · 25:dollar:recurring · 100:dollar:credit · 50:percent:onetime:summer24
Issued token — a customer never sees the authoring form. Mode-B issuance seals the discount into an opaque AES-256-GCM token (v1.<base64url>), non-deterministic (a fresh token every time) and unforgeable: only a holder of the signing key can mint one that decodes, so a guessed or hand-crafted string is rejected. Signing keys are versioned (BILLING_DISCOUNT_KEYS) so they can be rotated — the highest version mints, older keys still decode.
A discount is never forwarded to the provider as a coupon object. Every kind resolves to a usage credit — a temporary price reduction billing owns, banked as a balance and applied against future costs. The only difference between the kinds is how much and how often the credit is granted:
| Kind | Grant | Realized as |
|---|---|---|
onetime |
a one-time credit = the reduction (percent-of-plan or dollars), granted once | a usage credit consumed by the next invoice |
recurring |
the reduction re-granted every period (a standing rule) until removed | a usage credit topped up each cycle |
credit |
a one-time credit of the value, drawn down over time | a usage-credit balance |
The credit is realized on the customer’s balance at the provider (Stripe posts a negative customer-balance transaction, applied to upcoming invoices) — there is no coupon and no per-subscription discount object. A percent discount is resolved to dollars from the plan price at grant time, so a recurring percent discount tracks plan changes — each period’s credit is recomputed from the then-current price. A subscription may hold one standing recurring rule at a time; credits accumulate freely.
Combo discounts are a second, automatic source of usage credits: holding a qualifying set of add-ons (e.g. the Analytics Suite or Team Growth Bundle) grants a recurring credit for the bundled saving, using the same balance mechanism. They are composition-based rather than operator-granted — see Combo pricing.
How a discount actually reaches an account depends on the billing provider. The in-app discount surface described here is Stripe-only; AWS Marketplace discounts are handled on the AWS side.
This is the flow this document describes end to end:
POST /billing/admin/discounts with value:unit:kind./apply a direct grant to { targetOrgId }) or Mode B (/token, hand the customer an opaque token or public alias).billing:manage).recurring rule re-grants each period.Nothing is sent to Stripe as a coupon — billing owns the reduction and only mirrors it to the customer balance.
Marketplace has no customer-balance primitive, so in-app credits are realized differently: by withholding reported metered usage. When both BILLING_DISCOUNTS_ENABLED and BILLING_METERING_ENABLED are on, the provider reports usageCreditSupport: 'metered' and the same mint → redeem flow applies to Marketplace accounts; a banked credit is drawn down on the metering cycle. When metering is off, the provider reports usageCreditSupport: 'none' and the routes reject these accounts (DISCOUNTS_UNSUPPORTED, HTTP 409) — a credit is never accepted unless the mechanism that realizes it is running (no banking without realization).
How metered realization works (per metering cycle, gated + default-off):
YYYY annual / YYYY-MM monthly), the standing recurring discount + any active combo credits are re-granted onto the local balance (Marketplace has no invoices to drive Stripe’s reconciler, so the cycle drives it).units − withheld to BatchMeterUsage, where the withheld units’ value (at the dimension’s configured price, AWS_MARKETPLACE_DIMENSION_PRICE_MAP, cents per unit per cycle) is drawn from the balance. Whole units only; the remainder carries forward.unprocessed === 0), emitting credit_consumed (and credit_exhausted at zero). Multi-pod safe via atomic conditional updates.Set BILLING_METERING_DRAWDOWN_DRYRUN=true to validate first: the cycle logs the intended withholding but reports full quantities and leaves the balance untouched.
Known limitation. Withholding offsets metered add-on usage only — never the base plan contract line. So a recurring plan-percent discount on an account with little/no metered add-on usage realizes only partially (or not at all); the un-drawable surplus is surfaced via a warn + billing_marketplace_credit_unrealizable_total metric, not silently lost. For plan-level or contract pricing, use an AWS Marketplace private offer:
ResolveCustomer) — the discount lives entirely in AWS, so no in-app discount record is created and it doesn’t surface as a discount/credit line in the billing dashboard (which reads the local ledger, not AWS pricing).Generation (mint the record) and issuance (deliver it) are separate steps, both system-admin only.
SUMMER50) and delivers it out-of-band (email, landing page). The customer redeems it themselves. Best for promos.Redemption happens two ways:
billing:manage pastes a token/alias on their own billing page. They can only ever discount their own account.A discount bound to a targetOrgId is redeemable only by that account; an untargeted (public) discount is redeemable by anyone, subject to maxRedemptions, redeemBy, and tier restrictions.
Because a token only seals the discount id, one discount can back many tokens — re-issuing mints a fresh string against the same record and shared redemption counter. Revoking (isActive: false) invalidates every token for that discount at once, since redemption always validates the live record, not the string. Revoking does not strip a discount already applied to a subscription — remove those explicitly.
A promotion is the marketing counterpart to a discount: where a discount is redeemed (a code someone enters) or manually granted, a promotion auto-grants a usage credit when an org hits a trigger, bounded by a campaign budget. It’s a grant source — it reuses the same usage-credit machinery (creditBalanceCents / creditLedger, drawn down on the invoice or by Marketplace metered withholding), so there’s no new realization path.
Gated by BILLING_PROMOTIONS_ENABLED (same opt-out default as discounts — on unless set to false), which additionally requires BILLING_DISCOUNTS_ENABLED (discounts off ⇒ promotions off).
Triggers. A promotion fires on a lifecycle event evaluated against the org’s subscription:
subscription_created — signup / first-subscription campaigns (with firstSubscriptionOnly).plan_change — upgrade / conversion campaigns.manual — admin-only; granted via POST /admin/promotions/:id/grant.referral — two-sided (see Referrals below).Eligibility (trigger.conditions, all-must-match): tiers, intervals, firstSubscriptionOnly. Plus an active window (startsAt/endsAt).
Budget & safety. Each grant atomically reserves from budgetCents (a guarded $inc), then applies the credit idempotently per (promotion, org) via a creditLedger.dedupeKey; any failure after reservation compensates ($inc -cents) — so concurrent triggers can never overspend and the bias is always under-spend. spentCents/grantsCount are a reconciled advisory cache; the ledger (Σ promo:<id> entries) is the source of truth (see GET /admin/promotions/:id/spend, which returns both and their drift). A promotion never grants when the provider can’t realize a usage credit (usageCreditSupport === 'none') — it warns instead of banking an unrealizable credit. perOrgCapCents clamps a single grant; maxGrants caps total grants.
Value. unit: 'dollar' (cents) or 'percent' (percent of the current plan price, resolved at grant time). kind: 'onetime' grants once; kind: 'recurring' re-grants each billing period, re-granted from the periodic reconcile/metering path with period-keyed idempotency (a redelivered invoice never double-grants) and stopped when the promo is revoked / out of window / over budget or the org is no longer eligible.
Batch activation & backfill. POST /admin/promotions/:id/activate grants across the existing eligible base now (idempotent per org, budget-bounded — skips are logged, never silent). A backfill cron (BILLING_PROMOTION_BACKFILL_INTERVAL_MS, leader-locked) periodically does the same for every active promo, so a grant dropped by a transient failure or a campaign activated after an org’s signup still lands.
Clawback. A grant is reversed (ledger row pulled, balance reduced, budget released, promotion_clawback emitted) if the subscription cancels within the clawback window (BILLING_PROMOTION_CLAWBACK_WINDOW_MS, default 7d) — defusing signup-grab-churn. Revoking a promotion (isActive: false, or DELETE) stops future auto-grants; credits granted earlier and outside the clawback window stay.
A referral promotion is two-sided. A new org subscribes with a referral code (= the referrer’s org id) via referralCode on POST /billing/subscriptions:
value), and a pending Referral is recorded.referrerValue (or the same as the referee if unset). Gating on first payment defeats fake-referral farming.Guards: no self-referral, a referee is referred at most once (unique), the referrer must be a real subscribed org, and both grants flow through the shared budget + idempotency machinery (referee keyed per org, referrer keyed per pair). A referral whose referrer grant can’t be funded (budget) still marks qualified — it won’t retry forever.
All routes are under /billing and gated by BILLING_DISCOUNTS_ENABLED.
| Method | Path | Gate | Purpose |
|---|---|---|---|
POST |
/admin/discounts |
system admin | Mint a discount (ceiling-checked) |
POST |
/admin/discounts/:id/token |
system admin | Mode B — issue / re-issue an opaque token |
POST |
/admin/discounts/:id/apply |
system admin | Mode A — direct grant to { targetOrgId } |
GET |
/admin/discounts · /:id |
system admin | List (filter by campaign/active/target) / inspect |
PUT · DELETE |
/admin/discounts/:id |
system admin | Edit / revoke |
POST |
/subscriptions/:id/discounts |
billing:manage |
Self-service redeem a token or alias |
DELETE |
/subscriptions/:id/discounts/:discountId |
billing:manage |
Stop a standing recurring discount (granted credits persist) |
GET |
/events |
billing:read |
The caller’s own billing events — credit applied/consumed/exhausted, discounts, combos (own org only) |
Every mutation writes a local billing_events row and mirrors to the central audit trail (billing.discount.generate / .issue / .apply / .remove / .revoke, plus billing.credit.consumed / .exhausted and billing.combo.expired for usage-credit realization), attributing both the acting party and the affected account. Tokens, signing keys, and aliases are never logged or audited — only the discount id, kind, and value. An account can review its own credit movement via GET /billing/events (billing:read).
BILLING_DISCOUNT_MAX_PERCENT / BILLING_DISCOUNT_MAX_CENTS cap the magnitude an operator can mint.maxRedemptions before mutating the subscription, so concurrent redemptions can’t exceed the cap; a failed apply compensates the reservation.BILLING_DISCOUNT_KEYS as a sealed secret.| Variable | Default | Description |
|---|---|---|
BILLING_DISCOUNTS_ENABLED |
true |
Master switch — set false to 404 the discount routes |
BILLING_DISCOUNT_KEYS |
— | Secret. Versioned AES-256-GCM keys v1:<base64-32B>,v2:… |
BILLING_DISCOUNT_MAX_PERCENT |
100 |
Ceiling on a percent discount |
BILLING_DISCOUNT_MAX_CENTS |
10000000 |
Ceiling on a dollar/credit discount, in cents |
See Environment Variables → Billing.
GET /billing/summary). This is where an account sees the effect of its discounts.BILLING_METERING_ENABLED is on (credits realize by withholding metered usage, priced via AWS_MARKETPLACE_DIMENSION_PRICE_MAP); default-off, so enable + validate with BILLING_METERING_DRAWDOWN_DRYRUN first. Withholding offsets metered add-on usage only — for plan-level/contract pricing use AWS Marketplace private offers.