Coupons and promotion codes

Discount codes in Áskell are made up of two objects that work together:

  • A coupon (coupon) defines the discount itself: a fixed amount (amount_off and currency) or a percentage (percent_off), how long it applies (duration: forever, once or repeating with duration_in_months) and limits such as max_redemptions and redeem_by.

  • A promotion code (promotion_code) is the code the customer enters (code), and it points to a coupon. You can create many codes for the same coupon, restrict a code to a single customer, and set an expiry date (expires_at) and a minimum amount in restrictions.

Both objects can be created, fetched, updated and deleted through the V2 API. All calls require a secret API key. Legacy token authentication is not supported on these endpoints:

Authorization: Api-Key your-secret-api-key

Full documentation of fields and responses is in our Swagger documentation under V2 Coupons at /api/swagger/v2/.

Coupons

Endpoint

Description

GET /api/v2/coupons/

Lists coupons. Filters: valid, duration, currency, name. page_size turns on pagination.

POST /api/v2/coupons/

Creates a coupon.

GET /api/v2/coupons/{couponId}/

Retrieves a single coupon.

PATCH /api/v2/coupons/{couponId}/

Partially updates a coupon.

DELETE /api/v2/coupons/{couponId}/

Deletes a coupon (soft delete).

Example of creating a 20% coupon that applies for three months:

{
  "name": "Sumartilboð",
  "percent_off": "20",
  "duration": "repeating",
  "duration_in_months": 3,
  "max_redemptions": 100,
  "redeem_by": "2026-12-31T23:59:59Z"
}

Rules:

  • Exactly one of amount_off (together with currency) or percent_off must be set. To switch from one to the other with PATCH, send the old field as null in the same call.

  • duration_in_months is required when duration is repeating and must not be set otherwise.

  • redeem_by must be in the future unless the coupon has already expired; an expired coupon can still be updated and deleted, but a valid coupon cannot be made to expire by setting a date in the past.

  • Unknown fields in POST and PATCH are rejected with 400. metadata and restrictions can be sent as null to clear them.

  • id and times_redeemed cannot be changed. id can be supplied on creation (letters, digits, underscores, hyphens and periods, at most 128 characters); otherwise it is generated automatically.

  • currency must be a valid three-letter ISO 4217 code. max_redemptions and duration_in_months must be 1 or higher.

  • A coupon that has been redeemed (times_redeemed > 0) cannot be deleted. Use redeem_by or max_redemptions to close it.

Promotion codes

Endpoint

Description

GET /api/v2/promotion-codes/

Lists promotion codes. Filters: code, coupon, active, valid, customer_reference.

POST /api/v2/promotion-codes/

Creates a promotion code.

GET /api/v2/promotion-codes/{promotionCodeId}/

Retrieves a single promotion code.

PATCH /api/v2/promotion-codes/{promotionCodeId}/

Partially updates a promotion code.

DELETE /api/v2/promotion-codes/{promotionCodeId}/

Deletes a promotion code (soft delete).

Example of creating a code that a single customer can redeem only once:

{
  "coupon": "coup_5f3a1c2b9d8e4f6a7b8c9d0e",
  "code": "VELKOMIN",
  "customer_reference": "customer-123",
  "max_redemptions": 1,
  "expires_at": "2026-12-31T23:59:59Z",
  "restrictions": {
    "minimum_amount": "1000",
    "minimum_amount_currency": "ISK"
  }
}

Rules:

  • coupon must be a coupon on the same account.

  • code is 4-50 characters (letters, digits, hyphens and underscores) and is always stored in uppercase. If it is omitted, a code is generated automatically. The code must be unique among the account’s active codes; inactive (active: false) and deleted codes free the code up for reuse.

  • The customer is given with customer (ID) or customer_reference, not both. null removes the customer binding.

  • restrictions supports only a minimum amount: minimum_amount (greater than 0) and minimum_amount_currency (ISO 4217 code) must be sent together. An empty {} removes the restriction.

  • A promotion code that has been redeemed cannot be deleted. Set active to false instead.

Applying codes to subscriptions is documented with the subscription endpoints: see POST /api/v2/subscription-contracts/{contractId}/apply-code/ for V2 contracts and POST /api/subscriptions/{subscription_id}/apply-code/ for legacy subscriptions.