Legacy Coupon Server
Legacy Coupon Server is for existing integrations that use the old coupon/voucher model. In this model, Loyal Guru does not act as the new discount engine. It serves coupon and voucher information, while the POS or external system applies the discount according to the coupon fields it receives.
When to use this pageβ
Use this guide only for companies that have not migrated their coupon integration to the current engine. New implementations should use New Engine Promo Engine or New Engine Coupon Server.
Responsibility splitβ
| System | Responsibility |
|---|---|
| Loyal Guru | Stores coupon/voucher state, exposes validation data, tracks selection/redemption/rollback depending on the integration. |
| POS / external system | Interprets coupon fields, validates any local pricing constraints, and applies the discount amount to the receipt. |
Technical flowβ
The legacy flow is POS-led. Loyal Guru validates and returns voucher data, but the POS compacts the returned vouchers, interprets the legacy coupon fields, and applies the discount on the receipt.
Voucher validation endpointsβ
| Scenario | Endpoint | POS behavior |
|---|---|---|
| Customer identifies and wants to use selected vouchers | GET /customers/{id}/vouchers/selected | Read the Customer's selected vouchers, compact the response, and decide which discounts can be applied locally. |
| Customer presents an individual coupon or voucher code | GET /vouchers/{code} with required customer_id and location_id query params (ValidateAVoucher operation) | Validate the coupon/voucher data for that Customer and store, compact the returned voucher, and apply the discount locally using the key field. |
The validation call always needs both the Customer and the location to resolve eligibility β this is the same pattern the current engine uses on GET /discounts (customer_id/customer_code plus location_id/location_code).
For endpoint schemas and HTTP behavior, use the API Reference.
Fields the POS must interpretβ
Legacy coupons can include fields that determine how the POS should apply the benefit. The exact available fields depend on the legacy configuration, but integrations commonly need to handle:
- Discount type and value:
discount,discount_type. - Product or product-family restrictions:
product_ids,excluded_product_ids,features(internal taxonomy),purchased_product_ids/purchased_affectation. - Store or country restrictions:
location_ids. - Payment method restrictions:
payment_methods,payment_method_categories_1/_2,payment_method_discount,payment_method_discount_type. - Validity dates and redemption limits:
available_from,available_to,available_redemptions/available_redemtions,max_redemptions,min_money,max_money,min_units,max_units,cumulative. - Points-based vouchers:
points,points_money,has_points. - Periodical-publication restrictions when configured for that company.
- Voucher ownership (
selected,frozen_redemptions) and redemption state (status,priority,applies_to) for targeted deliveries. key: the field the POS uses to know which local discount logic to apply β not the same as the vouchercode.
Legacy integrations may also read/write custom1βcustom11 and custom1fβcustom8f on the Customer profile. These are deprecated in favor of the attributes object β see Custom fields β Attributes.
Voucher lifecycle endpointsβ
Beyond read/validate, the legacy flow exposes the same generic voucher-lifecycle endpoints used across both engines (the vouchers entity is shared). These do not change when a company migrates to the new engine β only the campaign/eligibility read side changes (see How this differs from current engine).
Select / Deselectβ
Selecting a voucher marks it so it is automatically applied on the Customer's next purchase; deselecting reverses that.
| Endpoint | Scope | Use it for |
|---|---|---|
POST /vouchers/{code}/select | Single voucher | Customer or POS explicitly opts in to use one voucher on the next purchase. |
PUT /vouchers/select | Bulk (max 50 vouchers per call, body { vouchers: [{ code }] }) | Pre-select a batch of vouchers, for example after a campaign delivery. |
POST /vouchers/{code}/deselect | Single voucher | Customer or POS cancels the selection before redemption. |
PUT /vouchers/deselect | Bulk (max 50 vouchers per call) | Bulk-cancel selections. |
GET /customers/{id}/vouchers/selected (see table above) is how the POS later reads which vouchers are currently selected for that Customer.
Freeze / Unfreezeβ
Freezing puts a voucher on hold: a frozen voucher is not printed nor redeemable until it is unfrozen. Use it to protect a voucher that is in the middle of an incomplete transaction or under fraud review.
| Endpoint | Use it for |
|---|---|
POST /vouchers/{code}/freeze | Temporarily block a voucher from being printed or redeemed. |
POST /vouchers/{code}/unfreeze | Release the hold and make the voucher printable/redeemable again. |
Rollbackβ
POST /vouchers/rollback reverts a redeemed voucher back to its previous state and increases available_redemptions by 1. It accepts a list, identifying each voucher either by id or by code + customer_id. It fails (400) when the voucher is not currently in redeemed status, has expired (voucher_expired), or its availability date is in the future (voucher_out_date).
Use this to undo a redemption after a cancelled sale, a failed payment, or a return β it is the legacy equivalent of the current engine's rollback step in the redeem/rollback pair.
Printβ
POST /vouchers/print returns the batch of vouchers (or messages) the POS should print on the receipt for a given customer_id, location_id, and size. It supports exclude_codes (skip specific codes already handled), activity_id, and a debug flag to preview without marking vouchers as printed.
Ordering is fixed and always applied in this order:
- Campaign priority (if any) β campaigns with priority print first.
- Voucher priority within the same campaign (if any).
- Voucher availability β soonest to expire prints first.
By default, printing marks the vouchers as "printed" so the same batch is not returned again on the next call; use debug: true to inspect the batch without changing state. The response reuses the same field set described in Fields the POS must interpret (discount, discount_type, min_money/max_money, min_units/max_units, points, has_points, payment_methods, features, etc.).
How this differs from current engineβ
| Topic | Legacy Coupon Server | Current Engine Promo Engine |
|---|---|---|
| Final discount calculation | POS/external system | Loyal Guru |
| Campaign rules | Legacy coupon fields | rule_conditions, rule_application, rule_effects |
| Runtime cart evaluation | Not the new discount engine | POST /carts/simulate / POST /carts/realtime |
| Recommended for new integrations | No | Yes |
Migration noteβ
When migrating from legacy coupon server to LE 2025, confirm whether the new target model is Promo Engine or Coupon Server. Do not map legacy coupon fields one-to-one without validating the new campaign rule model and voucher lifecycle.
Troubleshootingβ
| Symptom | What to check |
|---|---|
| The POS cannot find selected vouchers for an identified Customer | Confirm the POS is sending the same Customer ID that owns the selected vouchers and that the vouchers are still active. |
| An individual coupon is returned but no discount is applied | Check that the POS compacted the voucher response and mapped the legacy coupon fields to its local pricing rules. |
| Voucher validation succeeds but checkout redemption fails later | Check voucher status, redemption limits, date window, store scope, and whether another checkout already consumed the voucher. |
| POS discount differs from Loyal Guru reporting | Confirm that the POS applied the same legacy coupon fields that were returned during validation and sent the final usage/rollback state back to Loyal Guru. |