Coupons & discounts

Apply percentage or fixed discounts to invoices and subscriptions.

View as MarkdownInstall skills

Coupons reduce the amount owed on an invoice or subscription — once or on a recurring basis, with optional redemption limits and expiry. A coupon is a reusable template; attaching it to a customer or subscription creates a discount that VINR applies automatically when each invoice finalizes.

How discounts applyAsk

A coupon never touches money on its own. It becomes active only when attached, and VINR resolves it during the finalize step of the billing cycle — after line items are gathered but before tax is computed. The discount reduces the taxable subtotal, never the tax itself, and an invoice can never go below zero.

TermMeaning
couponThe reusable template — percentage or fixed amount, plus duration rules.
DiscountA coupon attached to a customer or subscription.
redemptionOne concrete application of a coupon to a finalized invoice (rdm_...).

Creating a couponAsk

A coupon is either a percentage off or a fixed amount off. Amounts are always integers in minor units, and a fixed coupon is bound to a single currency.

import { Vinr } from '@vinr/sdk';
const vinr = new Vinr({ secretKey: process.env.VINR_SECRET_KEY });

// 25% off for the first three invoices.
const coupon = await vinr.coupons.create({
  name: 'Launch 25',
  percentOff: 25,
  duration: 'repeating',
  durationInPeriods: 3,
  maxRedemptions: 500,           // across all customers
  redeemBy: '2026-09-30T23:59:59Z',
});                              // -> "cpn_..."
curl https://api.vinr.com/v1/coupons \
  -H "X-Api-Key: $VINR_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Flat 10 EUR",
    "amountOff": 1000,
    "currency": "EUR",
    "duration": "once"
  }'

Set exactly one of percentOff or amountOff. A fixed amountOff requires a currency; VINR rejects the coupon for any invoice in a different currency rather than converting.

Percentage vs. fixedAsk

Choose based on how you want the discount to scale with the bill.

  • Percentage (percentOff) scales with the subtotal — 25% off €20 is €5, 25% off €80 is €20. Currency-agnostic, so it works on any subscription.
  • Fixed (amountOff) removes a flat sum in one currency. On a multi-line invoice it applies to the subtotal, and if the subtotal is smaller than amountOff the invoice simply settles at €0.00 with no carryover.

Duration & redemption limitsAsk

duration controls how long a discount survives on a subscription:

Prop

Type

Two independent caps protect a coupon from overuse:

  • maxRedemptions — total times the coupon can be redeemed across all customers.
  • redeemBy — a timestamp after which the coupon can no longer be attached (already-attached repeating/forever discounts keep running).

maxRedemptions counts redemptions, not attachments. A forever coupon on a monthly subscription consumes one redemption per invoice, so set the cap with the duration in mind.

Applying to subscriptionsAsk

Attach a coupon at creation or any time after. VINR records the attachment and starts discounting from the next invoice to finalize.

// Attach when creating a subscription.
const sub = await vinr.subscriptions.create({
  customer: 'cust_8fK2a',
  price: 'price_9Qx',
  coupon: coupon.id,
});

// Or attach to an existing subscription later.
await vinr.subscriptions.update('sub_4Tb1', { coupon: 'cpn_launch25' });

// Remove an active discount (stops on the next cycle).
await vinr.subscriptions.update('sub_4Tb1', { coupon: null });

Attaching a coupon to the customer instead of the subscription discounts every current and future subscription for that customer — useful for account-wide promotions.

Stacking rulesAsk

VINR allows one discount per scope, and resolves the most specific one:

  1. A coupon on the subscription wins over a coupon on the customer.
  2. A coupon on the customer applies only to subscriptions that have no coupon of their own.

Percentage and fixed coupons do not combine on a single invoice — there is no compounding. If you need a deeper one-time reduction, issue a fixed invoice item credit instead of a second coupon.

Replacing an active coupon with subscriptions.update discards any remaining repeating periods of the old discount. There is no automatic resume.

Tracking redemptionsAsk

Each application emits an event and creates a redemption you can reconcile against finalized invoices.

{
  "type": "coupon.redeemed",
  "data": {
    "id": "rdm_2pL9",
    "coupon": "cpn_launch25",
    "invoice": "inv_77Ba",
    "subscription": "sub_4Tb1",
    "amountDiscounted": 500,
    "currency": "EUR"
  }
}

Subscribe to coupon.redeemed to track promotion spend in near real time. Verify the payload with vinr.webhooks.verify(payload, signature) against the x-vinr-signature header — see Webhooks.

Next stepsAsk

Was this page helpful?
Edit on GitHub

Last updated on

On this page