> ## Documentation Index
> Fetch the complete documentation index at: https://voucherify-rc-lv2-guides.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Integrate loyalty v2

> Learn how to integrate with Voucherify's new loyalty v2 API

To integrate Voucherify's loyalty v2 with your platform, you'll need to complete several steps. You'll have to prepare your customer data, build the loyalty program with required modules, and create the loyalty member journey.

Loyalty v2 has a modular structure, so you can start small and scale your program up as it grows with your members. The modular structure also helps you to optimize your loyalty program with your other incentives, like discount coupons or gift cards.

## Preparation

Prepare your Voucherify account for a functional loyalty v2 integration.

<Steps>
  <Step title="Prepare the project">
    Prepare your test environment in Voucherify, for example the Sandbox project.

    Use an existing API key/token or create a new pair. Go to **Project settings** > **Application keys** to get `X-App-Id` and `X-App-Token`. Keep these keys on the server side only. See [Authentication](/guides/authentication) and [Security](/guides/security).

    Make sure the API key can call loyalty, campaign, voucher, customer, product, and order endpoints used by your integration.

    Choose the correct regional API base URL from [API overview](/guides/api-overview#choose-the-right-api-endpoint) (Europe, United States, or Asia).
  </Step>

  <Step title="Create loyalty v2 program">
    <Info>
      <Badge color="gray">User guides</Badge>

      Follow [Tutorial – Create basic program](/build/create-loyalty-campaign-v2) for a step-by-step dashboard setup. Read the [Loyalty v2 overview](/build/loyalty-overview) for the program model and rollout context.
    </Info>

    Create the loyalty v2 program by going to **Loyalty hub** > **Programs**.

    Design there the program architecture. You can start with a small program for earning and spending points. The required components are the point wallet (card definition) and an earning rule.

    Copy your v2 program ID (`lprg_...`). This is required for all member-related API calls.

    Set the program status to `ACTIVE`.

    <Warning>
      Once the program is active, you can experiment with additional earning rules, benefits, and rewards. However, you can't add or edit point wallets or add tier structures.
    </Warning>
  </Step>

  <Step title="Customer data">
    Your loyalty program members must first exist as customers in Voucherify to have a Voucherify customer ID (`cust_...`).

    You can:

    * Import your customer base with the [Customer import](/prepare/customers#data-import-and-synchronization) or [POST Import and Update Customers using CSV](/api-reference/customers/import-and-update-customers-using-csv) API endpoint.
    * Use a CDP or CRM tool to synchronize your customers with Voucherify.
  </Step>

  <Step title="Product data">
    If you plan to use products as loyalty program rewards, you have to create them in Voucherify first.

    You can:

    * Import your product database with the [Product import](/prepare/products#bulk-import-with-csv) in the Voucherify dashboard.
    * Use the API with [POST Import Products using CSV](/api-reference/products/import-products-using-csv) and [POST Update products in bulk](/api-reference/products/update-products-in-bulk) endpoints.
  </Step>

  <Step title="Data schemas: Metadata and custom events">
    If you're going to combine the loyalty v2 program with metadata (custom attributes for point wallets, loyalty members, and more) or custom events (for example, to trigger when points are earned), you'll have to create relevant schemas.

    Read more about creating [metadata schemas](/prepare/metadata#define-new-metadata-schema) and [custom events](/prepare/custom-events#define-new-custom-event).
  </Step>
</Steps>

## Acquisition

Use the [POST Create member](/api-reference/programs/create-program-member) or [POST Create member in batch](/api-reference/programs/batch-create-program-members) (asynchronous action) with the Voucherify customer ID to add the customer as a member to a loyalty v2 program.

By default, the customer is added as an `ACTIVE` member, who can immediately earn points and fully participate in the loyalty program.

Optionally, you can add the customer as an `INACTIVE` member. An inactive member will have to be activated later with the [POST Activate member](/api-reference/programs/activate-program-member) endpoint to participate in the loyalty program.

<CodeGroup>
  ```json Basic create member request lines theme={null}
  {
      "customer_id": "cust_V0uCh3r1fyId"
  }
  ```

  ```json Create inactive member request lines theme={null}
  {
      "customer_id": "cust_V0uCh3r1fyId",
      "status": "INACTIVE"
  }
  ```

  ```json Create member in batch request lines theme={null}
  [
    {
      "customer_id": "cust_V0uCh3r1fyId01"
    },
    {
      "customer_id": "cust_V0uCh3r1fyId02"
    },
    {
      "customer_id": "cust_V0uCh3r1fyId03"
    }
  ]
  ```
</CodeGroup>

The response for creating an individual member will return (see the lines in the example below):

* Loyalty program member ID with a unique member ID (`lmbr_...`) (line 2).
* The loyalty card(s) generated for the member with a unique loyalty card ID (`lcrd_...`) and the card definition (point wallet) ID. If your program uses multiple point wallets, the member will automatically receive a separate card for each card definition (`cards[].card.id` and `cards[].card.card_definition_id` in lines 16, 17, 54, and 55).

The response for creating a batch of new members returns an ID of an asynchronous action. Use the ID in the [GET Get async action](/api-reference/async-actions/get-async-action) endpoint to check the processing status of the action.

<Warning>
  <Badge color="yellow">Handle loyalty card code latency</Badge>

  Make sure your UI can handle `null` for the `cards[].card.code` property. The `code` property is generated asynchronously after member creation (lines 19 and 57).
</Warning>

In this response example, the loyalty program member receives two loyalty cards for two separate point wallets (card definitions).

<CodeGroup>
  ```json POST Create member: response example lines highlight={2,16,17,19,54,55,57} expandable theme={null}
  {
      "id": "lmbr_V0uCh3r1fyId1",
      "customer_id": "cust_V0uCh3r1fyId2",
      "program_id": "lprg_V0uCh3r1fyId3",
      "status": "ACTIVE",
      "metadata": {},
      "created_at": "2026-06-06T06:06:06.006Z",
      "updated_at": null,
      "object": "member",
      "cards": [
          {
              "member_role": "OWNER",
              "created_at": "2026-06-06T06:06:06.006Z",
              "tier_progress": null,
              "card": {
                  "id": "lcrd_V0uCh3r1fyId4",
                  "card_definition_id": "lcdef_V0uCh3r1fyId5",
                  "card_type": "INDIVIDUAL",
                  "code": null,
                  "lifetime_bucket": {
                      "points": {
                          "total": 0,
                          "earned": 0,
                          "added": 0,
                          "subtracted": 0,
                          "expired": 0,
                          "spent": 0,
                          "refunded": 0,
                          "returned": 0,
                          "locked": 0,
                          "unlocked": 0
                      },
                      "pending_points": {
                          "total": 0,
                          "activated": 0,
                          "canceled": 0
                      }
                  },
                  "balance": {
                      "points": 0,
                      "pending_points": 0
                  },
                  "next_expiration": null,
                  "next_activation": null,
                  "object": "card"
              },
              "object": "member_card"
          },
          {
              "member_role": "OWNER",
              "created_at": "2026-06-06T06:06:06.006Z",
              "tier_progress": null,
              "card": {
                  "id": "lcrd_V0uCh3r1fyId6",
                  "card_definition_id": "lcdef_V0uCh3r1fyId7",
                  "card_type": "INDIVIDUAL",
                  "code": null,
                  "lifetime_bucket": {
                      "points": {
                          "total": 0,
                          "earned": 0,
                          "added": 0,
                          "subtracted": 0,
                          "expired": 0,
                          "spent": 0,
                          "refunded": 0,
                          "returned": 0,
                          "locked": 0,
                          "unlocked": 0
                      },
                      "pending_points": {
                          "total": 0,
                          "activated": 0,
                          "canceled": 0
                      }
                  },
                  "balance": {
                      "points": 0,
                      "pending_points": 0
                  },
                  "next_expiration": null,
                  "next_activation": null,
                  "object": "card"
              },
              "object": "member_card"
          }
      ]
  }
  ```

  ```json Batch create member: response lines wrap theme={null}
  {
    "async_action_id": "aa_abc123def456"
  }
  ```
</CodeGroup>

## Discovery

Once you enroll your members, use the following endpoints so the members can learn more about your loyalty program: How they can earn and spend their points.

### Examine earning rules

Use the [POST Examine earning rules](/api-reference/examine/examine-earning-rules) endpoint to estimate how many points a customer will earn in a given scenario. This simulation calculates the precise number of points and material or digital benefits a customer can earn based on specific operational business triggers. Because this evaluation is a dry-run execution, Voucherify calculates these opportunities dynamically without writing transactions to card balances or modifying customer states.

<Note>
  The POST Examine earning rules endpoint is an estimation of points to be earned. It doesn't take into account additional earning rule triggers that may occur. For example, this can happen when a member pays for an order, earns points, and reaches a higher loyalty tier that will trigger additional points.
</Note>

You can configure the simulation engine using two distinct trigger modes:

* `ALL` mode: Simulates a parallel "what-if" scenario across all active earning rule configurations simultaneously.
* `SPECIFIC` mode: Limits evaluation to one event context, like order paid, segment entered, or custom event.

#### ALL trigger

Use the trigger type `ALL` mode to create "Ways to earn" loyalty portals or customer dashboards. This option displays all available marketing paths to your customer simultaneously.

When you pass the type `ALL`, Voucherify sets the top-level `event` parameter value to `null` (response line 2). Voucherify processes every earning rule active within the customer's program.

The simulation lists all earning rules (in the response example, the `earning_rules` array in lines 9–40) across distinct card definitions (the `memberships[].cards` array in lines 56–128, loyalty cards with names `AnnualTime`, `AutumnTime`, `SummerTime`). The response displays the total number of points to be earned on a card (`points_estimation` in line 65) and for individual earning rules that trigger the earning (`points_estimation` in lines 72 and 80). Voucherify also lists material rewards, such as the "Coffee sample Ethiopia" item, which is a `MATERIAL` benefit type (`benefits[].benefit` in lines 130–136).

<Note>
  <Badge color="blue">Order paid earning rules with proportional calculation</Badge>

  To return an estimation for order paid earning rules which calculate points proportionally, you need to pass the `customer_order_paid` object with required data, like `order.items` array.
</Note>

<CodeGroup>
  ```json Trigger: All request lines wrap expandable theme={null}
  {
      "trigger": {
          "type": "ALL"
      },
      "customer_identification": {
          "type": "member_id",
          "member_id": "lmbr_128f962dbc8c4ba5dc"
      },
      "customer_order_paid": {
          "order": {
              "items": [
                  {
                      "quantity": 1,
                      "price": 1000,
                      "amount": 1000,
                      "source_id": "vchrf-trckr-cp",
                      "name": "Voucherify trucker cap",
                      "related_object": "product",
                      "product": {
                          "metadata": {
                              "brand": "Voucherify",
                              "colour": "cobalt",
                              "category": "cap"
                          }
                      }
                  },
                  {
                      "quantity": 1,
                      "price": 15000,
                      "amount": 15000,
                      "source_id": "vchrf-tshrt",
                      "name": "Voucherify T-shirt",
                      "related_object": "product",
                      "product": {
                          "metadata": {
                              "brand": "Voucherify",
                              "colour": "ultramarine",
                              "category": "t-shirt"
                          }
                      }
                  },
                  {
                      "quantity": 1,
                      "price": 6500,
                      "amount": 6500,
                      "source_id": "vchrf-hd",
                      "name": "Voucherify hoodie",
                      "related_object": "product",
                      "product": {
                          "metadata": {
                              "brand": "Voucherify",
                              "colour": "regal crimson",
                              "category": "hoodie"
                          }
                      }
                  },
                  {
                      "quantity": 2,
                      "price": 1500,
                      "amount": 3000,
                      "source_id": "vchrf-vncnt-plsh",
                      "name": "Voucherify Vincent plushie",
                      "related_object": "product",
                      "product": {
                          "metadata": {
                              "brand": "Voucherify",
                              "colour": "cobalt",
                              "category": "plushie"
                          }
                      }
                  }
              ]
          }
      },
      "customer_segment_entered": {
          "customer": {
              "metadata": {
                  "VIP": false
              }
          }
      },
      "customer_custom_event": {
          "type": "SPECIFIC",
          "all": null,
          "specific": {
              "custom_event": {
                  "schema_id": "ms_oX8au6DostTnHdWBAVVFnTLP"
              }
          }
      }
  }

  ```

  ```json Trigger: All response lines wrap expandable highlight={2,9-40,56-128,130-136} theme={null}
  {
      "event": null,
      "customer": {
          "id": "cust_6onRrs4kCnvSVrTs5YtDPFBG",
          "source_id": "lx-rdmptr",
          "metadata": {},
          "object": "customer"
      },
      "earning_rules": [
          {
              "id": "lern_128f48ee44cc4bec34",
              "name": "EarningRule-OrderPaid",
              "metadata": {},
              "object": "earning_rule"
          },
          {
              "id": "lern_128f577bd4d47c8e12",
              "name": "EnterVipSegment",
              "metadata": {},
              "object": "earning_rule"
          },
          {
              "id": "lern_128f5822398c4bf78f",
              "name": "AutumnPaid",
              "metadata": {},
              "object": "earning_rule"
          },
          {
              "id": "lern_128f5834b30c4bf7a3",
              "name": "SummerPaid",
              "metadata": {},
              "object": "earning_rule"
          },
          {
              "id": "lern_128f57ec40cc4bf762",
              "name": "Autumn Ethiopia",
              "metadata": {},
              "object": "earning_rule"
          }
      ],
      "memberships": [
          {
              "member": {
                  "id": "lmbr_128f962dbc8c4ba5dc",
                  "customer_id": "cust_6onRrs4kCnvSVrTs5YtDPFBG",
                  "program_id": "lprg_128f58429f4c4bf7b2",
                  "metadata": {},
                  "object": "member"
              },
              "program": {
                  "id": "lprg_128f58429f4c4bf7b2",
                  "name": "AnnualProgram",
                  "metadata": {},
                  "object": "program"
              },
              "cards": [
                  {
                      "card": {
                          "id": "lcrd_128f962dbd8c4ba5e1",
                          "card_definition_id": "lcdef_128f4a88414c4bed69",
                          "card_type": "INDIVIDUAL",
                          "code": "AnnualTime-7M7ShPGfme",
                          "object": "card"
                      },
                      "points_estimation": 25650,
                      "earning_rules": [
                          {
                              "earning_rule": {
                                  "id": "lern_128f48ee44cc4bec34",
                                  "object": "earning_rule"
                              },
                              "points_estimation": 25500,
                              "object": "earning_rule_estimation"
                          },
                          {
                              "earning_rule": {
                                  "id": "lern_128f577bd4d47c8e12",
                                  "object": "earning_rule"
                              },
                              "points_estimation": 150,
                              "object": "earning_rule_estimation"
                          }
                      ],
                      "object": "card_estimation"
                  },
                  {
                      "card": {
                          "id": "lcrd_128f962dbd8c4ba5e0",
                          "card_definition_id": "lcdef_128f49963c0c4becb7",
                          "card_type": "INDIVIDUAL",
                          "code": "AutumnTime-R02hVARx33",
                          "object": "card"
                      },
                      "points_estimation": 10,
                      "earning_rules": [
                          {
                              "earning_rule": {
                                  "id": "lern_128f5822398c4bf78f",
                                  "object": "earning_rule"
                              },
                              "points_estimation": 10,
                              "object": "earning_rule_estimation"
                          }
                      ],
                      "object": "card_estimation"
                  },
                  {
                      "card": {
                          "id": "lcrd_128f962dbd8c4ba5df",
                          "card_definition_id": "lcdef_128f495f720c4bec8c",
                          "card_type": "INDIVIDUAL",
                          "code": "SummerTime-7z8dWawICd",
                          "object": "card"
                      },
                      "points_estimation": 10,
                      "earning_rules": [
                          {
                              "earning_rule": {
                                  "id": "lern_128f5834b30c4bf7a3",
                                  "object": "earning_rule"
                              },
                              "points_estimation": 10,
                              "object": "earning_rule_estimation"
                          }
                      ],
                      "object": "card_estimation"
                  }
              ],
              "benefits": [
                  {
                      "benefit": {
                          "id": "linc_128f4bba81947c8542",
                          "name": "Coffee sample Ethiopia",
                          "type": "MATERIAL",
                          "object": "benefit"
                      },
                      "earning_rules": [
                          {
                              "earning_rule": {
                                  "id": "lern_128f57ec40cc4bf762",
                                  "object": "earning_rule"
                              },
                              "object": "earning_rule_estimation"
                          }
                      ],
                      "object": "benefit_estimation"
                  }
              ],
              "object": "member_earnings_opportunity"
          }
      ],
      "object": "earnings_examine_result"
  }
  ```
</CodeGroup>

#### SPECIFIC trigger: Order paid

Use the `SPECIFIC` trigger type `customer.order.paid` inside digital shopping carts to estimate customer point collection right before final billing.

The simulation isolates rules linked directly to `PAID` transaction states. Voucherify processes the order details from your request context, including items, metadata, catalog categories, and amount values.

In the example, Voucherify resolves the `EarningRule-OrderPaid` rule (response example, `earning_rules` array, first object in lines 10–15) and returns an explicit balance projection of 25500 points for the primary `AnnualTime` loyalty card (`memberships[].cards[].points_estimation` in line 53). Also, the response lists earnings for `AutumnPaid` and `SummerPaid` earning rules (`earning_rules` array, objects in lines 16–27) that are used for the `AutumnTime` and `SummerTime` loyalty cards (`memberships[].cards[].points_estimation` in lines 74 and 95). The calculation reflects the current shopping cart properties without adding points to the active card yet.

<CodeGroup>
  ```json Trigger: Order paid request lines wrap expandable theme={null}
  {
      "trigger": {
          "type": "SPECIFIC",
          "specific": {
              "event": "customer.order.paid"
          }
      },
      "customer_identification": {
          "type": "member_id",
          "member_id": "lmbr_128f962dbc8c4ba5dc"
      },
      "customer_order_paid": {
          "order": {
              "items": [
                  {
                      "quantity": 1,
                      "price": 1000,
                      "amount": 1000,
                      "source_id": "vchrf-trckr-cp",
                      "name": "Voucherify trucker cap",
                      "related_object": "product",
                      "product": {
                          "metadata": {
                              "brand": "Voucherify",
                              "colour": "cobalt",
                              "category": "cap"
                          }
                      }
                  },
                  {
                      "quantity": 1,
                      "price": 15000,
                      "amount": 15000,
                      "source_id": "vchrf-tshrt",
                      "name": "Voucherify T-shirt",
                      "related_object": "product",
                      "product": {
                          "metadata": {
                              "brand": "Voucherify",
                              "colour": "ultramarine",
                              "category": "t-shirt"
                          }
                      }
                  },
                  {
                      "quantity": 1,
                      "price": 6500,
                      "amount": 6500,
                      "source_id": "vchrf-hd",
                      "name": "Voucherify hoodie",
                      "related_object": "product",
                      "product": {
                          "metadata": {
                              "brand": "Voucherify",
                              "colour": "regal crimson",
                              "category": "hoodie"
                          }
                      }
                  },
                  {
                      "quantity": 2,
                      "price": 1500,
                      "amount": 3000,
                      "source_id": "vchrf-vncnt-plsh",
                      "name": "Voucherify Vincent plushie",
                      "related_object": "product",
                      "product": {
                          "metadata": {
                              "brand": "Voucherify",
                              "colour": "cobalt",
                              "category": "plushie"
                          }
                      }
                  }
              ]
          }
      }
  }
  ```

  ```json Trigger: Order paid response lines wrap expandable highlight={10-27,53,74,95} theme={null}
  {
      "event": "customer.order.paid",
      "customer": {
          "id": "cust_6onRrs4kCnvSVrTs5YtDPFBG",
          "source_id": "lx-rdmptr",
          "metadata": {},
          "object": "customer"
      },
      "earning_rules": [
          {
              "id": "lern_128f48ee44cc4bec34",
              "name": "EarningRule-OrderPaid",
              "metadata": {},
              "object": "earning_rule"
          },
          {
              "id": "lern_128f5822398c4bf78f",
              "name": "AutumnPaid",
              "metadata": {},
              "object": "earning_rule"
          },
          {
              "id": "lern_128f5834b30c4bf7a3",
              "name": "SummerPaid",
              "metadata": {},
              "object": "earning_rule"
          }
      ],
      "memberships": [
          {
              "member": {
                  "id": "lmbr_128f962dbc8c4ba5dc",
                  "customer_id": "cust_6onRrs4kCnvSVrTs5YtDPFBG",
                  "program_id": "lprg_128f58429f4c4bf7b2",
                  "metadata": {},
                  "object": "member"
              },
              "program": {
                  "id": "lprg_128f58429f4c4bf7b2",
                  "name": "AnnualProgram",
                  "metadata": {},
                  "object": "program"
              },
              "cards": [
                  {
                      "card": {
                          "id": "lcrd_128f962dbd8c4ba5e1",
                          "card_definition_id": "lcdef_128f4a88414c4bed69",
                          "card_type": "INDIVIDUAL",
                          "code": "AnnualTime-7M7ShPGfme",
                          "object": "card"
                      },
                      "points_estimation": 25500,
                      "earning_rules": [
                          {
                              "earning_rule": {
                                  "id": "lern_128f48ee44cc4bec34",
                                  "object": "earning_rule"
                              },
                              "points_estimation": 25500,
                              "object": "earning_rule_estimation"
                          }
                      ],
                      "object": "card_estimation"
                  },
                  {
                      "card": {
                          "id": "lcrd_128f962dbd8c4ba5e0",
                          "card_definition_id": "lcdef_128f49963c0c4becb7",
                          "card_type": "INDIVIDUAL",
                          "code": "AutumnTime-R02hVARx33",
                          "object": "card"
                      },
                      "points_estimation": 10,
                      "earning_rules": [
                          {
                              "earning_rule": {
                                  "id": "lern_128f5822398c4bf78f",
                                  "object": "earning_rule"
                              },
                              "points_estimation": 10,
                              "object": "earning_rule_estimation"
                          }
                      ],
                      "object": "card_estimation"
                  },
                  {
                      "card": {
                          "id": "lcrd_128f962dbd8c4ba5df",
                          "card_definition_id": "lcdef_128f495f720c4bec8c",
                          "card_type": "INDIVIDUAL",
                          "code": "SummerTime-7z8dWawICd",
                          "object": "card"
                      },
                      "points_estimation": 10,
                      "earning_rules": [
                          {
                              "earning_rule": {
                                  "id": "lern_128f5834b30c4bf7a3",
                                  "object": "earning_rule"
                              },
                              "points_estimation": 10,
                              "object": "earning_rule_estimation"
                          }
                      ],
                      "object": "card_estimation"
                  }
              ],
              "benefits": [],
              "object": "member_earnings_opportunity"
          }
      ],
      "object": "earnings_examine_result"
  }
  ```
</CodeGroup>

#### SPECIFIC trigger: Customer entered segment

<Info>
  <Badge color="gray">Prerequisite: Customer segment</Badge>

  This scenario requires an earning rule that uses a customer segment as a trigger.

  Read the [Customer segments](/prepare/customer-segments) guide to learn more about customer segments.
</Info>

Use the `SPECIFIC` trigger type `customer.segment.entered` to forecast reward outcomes when a member moves into a high-tier profile segment, like a VIP club. You can display this as an achievable milestone in the member profile.

The evaluation matches metadata configurations in the customer reference object. In this case, passing the metadata key-value pair `"VIP": true` simulates a segment transition (request example, `customer_segment_entered.customer.metadata` in line 15).

Voucherify identifies the `EnterVipSegment` rule schema (response example, the `earning_rules` array, first object in lines 12–17). The response isolates the member's `AnnualTime` loyalty card and lists a milestone award valuation of 150 points (`memberships[].cards[].points_estimation` in line 43). Other purchase-dependent rules are omitted from the calculation automatically.

<CodeGroup>
  ```json Trigger: Customer entered segment request lines wrap expandable highlight={15} theme={null}
  {
      "trigger": {
          "type": "SPECIFIC",
          "specific": {
              "event": "customer.segment.entered"
          }
      },
      "customer_identification": {
          "type": "member_id",
          "member_id": "lmbr_128f962dbc8c4ba5dc"
      },
      "customer_segment_entered": {
          "customer": {
              "metadata": {
                  "VIP": true
              }
          }
      }
  }
  ```

  ```json Trigger: Customer entered segment response lines wrap expandable highlight={12-17,43} theme={null}
  {
      "event": "customer.segment.entered",
      "customer": {
          "id": "cust_6onRrs4kCnvSVrTs5YtDPFBG",
          "source_id": "lx-rdmptr",
          "metadata": {
              "VIP": true
          },
          "object": "customer"
      },
      "earning_rules": [
          {
              "id": "lern_128f577bd4d47c8e12",
              "name": "EnterVipSegment",
              "metadata": {},
              "object": "earning_rule"
          }
      ],
      "memberships": [
          {
              "member": {
                  "id": "lmbr_128f962dbc8c4ba5dc",
                  "customer_id": "cust_6onRrs4kCnvSVrTs5YtDPFBG",
                  "program_id": "lprg_128f58429f4c4bf7b2",
                  "metadata": {},
                  "object": "member"
              },
              "program": {
                  "id": "lprg_128f58429f4c4bf7b2",
                  "name": "AnnualProgram",
                  "metadata": {},
                  "object": "program"
              },
              "cards": [
                  {
                      "card": {
                          "id": "lcrd_128f962dbd8c4ba5e1",
                          "card_definition_id": "lcdef_128f4a88414c4bed69",
                          "card_type": "INDIVIDUAL",
                          "code": "AnnualTime-7M7ShPGfme",
                          "object": "card"
                      },
                      "points_estimation": 150,
                      "earning_rules": [
                          {
                              "earning_rule": {
                                  "id": "lern_128f577bd4d47c8e12",
                                  "object": "earning_rule"
                              },
                              "points_estimation": 150,
                              "object": "earning_rule_estimation"
                          }
                      ],
                      "object": "card_estimation"
                  }
              ],
              "benefits": [],
              "object": "member_earnings_opportunity"
          }
      ],
      "object": "earnings_examine_result"
  }
  ```
</CodeGroup>

#### SPECIFIC trigger: Custom event

<Info>
  <Badge color="gray">Prerequisite: Custom event schema</Badge>

  This scenario requires an earning rule that uses a custom event as a trigger.

  Read the [Custom events](/prepare/custom-events) guide to learn more about custom events.
</Info>

Use the `SPECIFIC` trigger type `customer.custom_event` to evaluate custom behaviors outside standard purchase workflows, such as writing product reviews, bringing reusable cups to a coffee shop, or using mobile apps.

The calculation evaluates rules that trigger on unique business schemas rather than order parameters or segment fields. The custom event is passed as a `schema_id` inside the `customer_custom_event` object (request example, `customer_custom_event.specific.custom_event.schema_id` in line 16).

In the example below, the earning rule (response example, the object in the `earning_rules` array, lines 10–15) awards a benefit rather than loyalty points on a card. As a result, the simulation bypasses regular point cards completely, meaning the `cards` array returns empty (line 32). Instead, the response fills the `benefits` array, which shows that once this action is completed, the member will receive the "Coffee sample Ethiopia" product (lines 34–40).

<Tip>
  <Badge color="green">"ALL" type for custom events</Badge>

  In the `customer_custom_event` you can pass `"type": "ALL"` to return a point estimation for all earning rules that are based on custom events. Use this type if your program runs many earnings that trigger when a member performs an action based on a custom event.
</Tip>

<CodeGroup>
  ```json Trigger: Custom event request lines wrap expandable highlight={16} theme={null}
  {
      "trigger": {
          "type": "SPECIFIC",
          "specific": {
              "event": "customer.custom_event"
          }
      },
      "customer_identification": {
          "type": "member_id",
          "member_id": "lmbr_128f962dbc8c4ba5dc"
      },
      "customer_custom_event": {
          "type": "SPECIFIC",
          "specific": {
              "custom_event": {
                  "schema_id": "ms_oX8au6DostTnHdWBAVVFnTLP"
              }
          }
      }
  }
  ```

  ```json Trigger: Custom event response lines wrap expandable highlight={10-15,32,34-40} theme={null}
  {
      "event": "customer.custom_event",
      "customer": {
          "id": "cust_6onRrs4kCnvSVrTs5YtDPFBG",
          "source_id": "lx-rdmptr",
          "metadata": {},
          "object": "customer"
      },
      "earning_rules": [
          {
              "id": "lern_128f57ec40cc4bf762",
              "name": "Autumn Ethiopia",
              "metadata": {},
              "object": "earning_rule"
          }
      ],
      "memberships": [
          {
              "member": {
                  "id": "lmbr_128f962dbc8c4ba5dc",
                  "customer_id": "cust_6onRrs4kCnvSVrTs5YtDPFBG",
                  "program_id": "lprg_128f58429f4c4bf7b2",
                  "metadata": {},
                  "object": "member"
              },
              "program": {
                  "id": "lprg_128f58429f4c4bf7b2",
                  "name": "AnnualProgram",
                  "metadata": {},
                  "object": "program"
              },
              "cards": [],
              "benefits": [
                  {
                      "benefit": {
                          "id": "linc_128f4bba81947c8542",
                          "name": "Coffee sample Ethiopia",
                          "type": "MATERIAL",
                          "object": "benefit"
                      },
                      "earning_rules": [
                          {
                              "earning_rule": {
                                  "id": "lern_128f57ec40cc4bf762",
                                  "object": "earning_rule"
                              },
                              "object": "earning_rule_estimation"
                          }
                      ],
                      "object": "benefit_estimation"
                  }
              ],
              "object": "member_earnings_opportunity"
          }
      ],
      "object": "earnings_examine_result"
  }
  ```
</CodeGroup>

### Examine rewards

<Info>
  <Badge color="gray">Prerequisite: Create and add rewards to point wallets</Badge>

  Created rewards must be added to relevant point wallets to be redeemable by your members. When you add the reward to a point wallet, you also define its price in points.

  You can create rewards through the dashboard or the API.
</Info>

Use the [POST Examine rewards](/api-reference/examine/examine-rewards) endpoint to verify which benefits, digital discount coupons or gift card credits, and material rewards are available to a member across their active loyalty program memberships. This simulation calculates the precise point costs using specific spending rules and evaluates stock availability in real time. Because this evaluation is a dry-run execution, the Voucherify engine processes these opportunities without writing transactions to card balances.

You can use this endpoint in your user interface to build dynamic reward catalogs or member portals that show customers exactly what rewards they can unlock with their current point balance, as well as the specific actions required to unlock restricted rewards.

The Voucherify simulation engine evaluates every reward assignment and classifies the reward under one of two distinct operational states within the `rewards` array. The endpoint returns only active rewards (response example, lines 8–30) and draft rewards are ignored.

#### AVAILABLE status

When a reward returns an `AVAILABLE` status, the loyalty program member meets all eligibility criteria for reward redemption. This status guarantees that:

* The member has an active loyalty card belonging to the required card definition.
* The current point balance on that specific loyalty card is equal to or greater than the resolved point cost.
* The reward assignment has sufficient stock and is currently active.

In the response example below, the loyalty card `AnnualTime-7M7ShPGfme` contains two `AVAILABLE` reward opportunities: the "Free item coupon" (requiring 300 points; the `memberships[].cards[].card.rewards` object lines 91–102) and the "Minas Gerais 500" material reward (requiring 150 points; the `memberships[].cards[].card.rewards` object in lines 103–114).

#### UNAVAILABLE status

When a reward returns an `UNAVAILABLE` status, the member can't redeem it at that moment. To prevent integration guesswork and support frontend user guidance, the Voucherify API provides a structured `unavailability_reasons` array detailing the exact failure point.

The evaluation engine identifies five primary reasons for reward unavailability:

* `insufficient_balance`: The loyalty card balance is lower than the required reward cost. The API returns explicit context in the `details` object showing `required`, `available`, and `missing` point values.
* `out_of_stock`: The reward assignment stock is depleted.
* `no_matching_cost`: No applicable point cost configuration is found or the cost configuration is locked.
* `no_card_for_cost`: The member profile lacks an active card for the required card definition.
* `reward_inactive`: The parent reward object is paused or inactive.

In the response example below, the "Carioca cap" reward on the loyalty card `SummerTime-7z8dWawICd` is marked `UNAVAILABLE` (line 61) due to `insufficient_balance` because the member possesses only 50 points out of the required 250 points (the `memberships[].cards[].card.rewards.unavailability_reasons` object in lines 66–75).

<Tip>
  <Badge color="green">UX best practice</Badge>

  Use the `missing` property inside the `insufficient_balance` details object to calculate and display progress bars or "points needed" callouts directly on your e-commerce checkout page or mobile application.
</Tip>

#### Examine rewards: Example request and response

Check the following request and response payloads to learn how `AVAILABLE` and `UNAVAILABLE` rewards are returned.

<CodeGroup>
  ```json Examine rewards request lines wrap theme={null}
  {
      "customer_identification": {
          "type": "member_id",
          "member_id": "lmbr_128f962dbc8c4ba5dc"
      }
  }
  ```

  ```json Examine rewards response lines wrap highlight={8-30,61,66-75,91-114} expandable theme={null}
  {
      "customer": {
          "id": "cust_6onRrs4kCnvSVrTs5YtDPFBG",
          "source_id": "lx-rdmptr",
          "metadata": {},
          "object": "customer"
      },
      "rewards": [
          {
              "id": "lrew_128f4cefbad47c863e",
              "name": "Carioca cap",
              "type": "MATERIAL",
              "metadata": {},
              "object": "reward"
          },
          {
              "id": "lrew_128f4cab04147c8608",
              "name": "Free item coupon",
              "type": "DIGITAL",
              "metadata": {},
              "object": "reward"
          },
          {
              "id": "lrew_128f4c6710947c85d1",
              "name": "Minas Gerais 500",
              "type": "MATERIAL",
              "metadata": {},
              "object": "reward"
          }
      ],
      "memberships": [
          {
              "member": {
                  "id": "lmbr_128f962dbc8c4ba5dc",
                  "customer_id": "cust_6onRrs4kCnvSVrTs5YtDPFBG",
                  "program_id": "lprg_128f58429f4c4bf7b2",
                  "metadata": {},
                  "object": "member"
              },
              "program": {
                  "id": "lprg_128f58429f4c4bf7b2",
                  "name": "AnnualProgram",
                  "metadata": {},
                  "object": "program"
              },
              "cards": [
                  {
                      "card": {
                          "id": "lcrd_128f962dbd8c4ba5df",
                          "card_definition_id": "lcdef_128f495f720c4bec8c",
                          "card_type": "INDIVIDUAL",
                          "code": "SummerTime-7z8dWawICd",
                          "object": "card"
                      },
                      "rewards": [
                          {
                              "reward": {
                                  "id": "lrew_128f4cefbad47c863e",
                                  "object": "reward"
                              },
                              "status": "UNAVAILABLE",
                              "cost": {
                                  "points": 250,
                                  "object": "reward_cost"
                              },
                              "unavailability_reasons": [
                                  {
                                      "reason": "insufficient_balance",
                                      "details": {
                                          "required": 250,
                                          "available": 50,
                                          "missing": 200
                                      },
                                      "object": "reward_unavailability_reason"
                                  }
                              ],
                              "object": "reward_estimation"
                          }
                      ],
                      "object": "card_estimation"
                  },
                  {
                      "card": {
                          "id": "lcrd_128f962dbd8c4ba5e1",
                          "card_definition_id": "lcdef_128f4a88414c4bed69",
                          "card_type": "INDIVIDUAL",
                          "code": "AnnualTime-7M7ShPGfme",
                          "object": "card"
                      },
                      "rewards": [
                          {
                              "reward": {
                                  "id": "lrew_128f4cab04147c8608",
                                  "object": "reward"
                              },
                              "status": "AVAILABLE",
                              "cost": {
                                  "points": 300,
                                  "object": "reward_cost"
                              },
                              "object": "reward_estimation"
                          },
                          {
                              "reward": {
                                  "id": "lrew_128f4c6710947c85d1",
                                  "object": "reward"
                              },
                              "status": "AVAILABLE",
                              "cost": {
                                  "points": 150,
                                  "object": "reward_cost"
                              },
                              "object": "reward_estimation"
                          }
                      ],
                      "object": "card_estimation"
                  }
              ],
              "object": "member_rewards_opportunity"
          }
      ],
      "object": "rewards_examine_result"
  }
  ```
</CodeGroup>

## Activity

Your customers need to perform activities to meet earning rules and earn points or other benefits.

### Create order

If your customers earn points or benefits when they finalize transaction in your system (`customer.order.paid` event), Voucherify relies on the condition that the [POST Create order](/api-reference/orders/create-order) request has the `status` field set to `PAID`.

When the Order API processes a payload with `status` set to `PAID` (line 3), the loyalty engine evaluates the line items and order amount to add points or benefits. Submitting orders with other statuses (such as `CREATED` or `FULFILLED`) will fail to activate the earnings.

```json Order paid request payload lines highlight={3} expandable theme={null}
{
    "source_id": "coffee_order_77129",
    "status": "PAID",
    "amount": 3500,
    "customer": {
      "source_id": "cust_espresso_fan_99",
      "email": "customer@coffeeclub.com"
    },
    "items": [
      {
        "source_id": "prod_colombian_supremo_1kg",
        "related_object": "product",
        "quantity": 1,
        "price": 3500,
        "amount": 3500,
        "product": {
          "name": "Colombian Supremo Coffee Beans",
          "metadata": {
            "roast": "medium",
            "origin": "Colombia"
          }
        }
      }
    ]
}
```

### Custom events

If your members earn points or benefits when performing specific custom activities, like subscribing to a newsletter, leaving a review, or unlocking a daily perk, use the [POST Track custom event](/api-reference/events/track-custom-event) endpoint (`/v1/events`).

The request payload must include:

* `customer.id` and/or `customer.source_id` (lines 3 and 4) to link the event to a specific customer in Voucherify database. The customer must be a member in the loyalty program.
* Name of the custom event that triggers the earning rule passed as the value of the `event` property (line 6).

<CodeGroup>
  ```json Track custom event request payload lines wrap highlight={3,4,6} theme={null}
  {
      "customer": {
          "id": "cust_6onRrs4kCnvSVrTs5YtDPFBG",
          "source_id": "lx-rdmptr"
      },
      "event": "example_custom_event_name"
  }
  ```

  When Voucherify registers the custom event, the earning rule will trigger and issue points or benefit to the member as defined in its earnings.
</CodeGroup>

### Enter segment

<Info>
  Read the [Customer segments](/prepare/customer-segments) article to learn more about customer segments and how to create them.
</Info>

If your members earn points or benefits when they join a segment of VIP customers, for example, you don't have to send any specific API requests.

The earning rule triggers automatically when Voucherify registers the member has joined the defined dynamic customer segment.

<Tip>
  <Badge color="green">Customer segment suggestions</Badge>

  You can define segments on the basis of various customer properties, like total amount of all orders, birth date, or your own custom attributes (metadata). Here are some ideas for customer segments:

  * Customers who haven't placed an order for more than 90 days.
  * Customers who have birthday today.
  * VIP customers who spent more than \$1000 USD in your store.
  * Customers with a defined metadata `"pet_owner": true`.
  * Customers who performed an activity "Collect June badge" defined as a custom event less than 5 days ago.
</Tip>

## Retention

Use the following endpoints and data to build a member profile view, which helps retaining your member base and keeps them engaged.

### Get program membership

Use the [GET program membership](/api-reference/programs/get-program-membership) endpoint (GET `/v2/loyalties/programs/{programId}/memberships/{customerId}?identification_type=member_id`) to create a member profile that will show:

* The member's identity and status in the program.
* All loyalty cards the member has in the program.
* Current point balance, pending points, and other life time point data.
* Dates for point expiration and activation.
* Current progress in the tier structure.

The response is a `membership` object that groups three top-level fields:

* `member`: The member's profile in the program (lines 3–10).
* `program`: A summary of the loyalty program the member belongs to (lines 13–17). This is useful when your integration handles several programs and needs to identify the program referred to in the response.
* `cards`: The list of loyalty cards issued to the member (starting at line 19), used to render points and tier progress.

<Tip>
  <Badge>Similar endpoints</Badge>

  The Get membership endpoint works in a similar way to [Get member](/api-reference/programs/get-program-member). Get member returns member details in the context of a loyalty program. It uses only the member ID as the path parameter and it doesn't return `tier_progress` data.

  If your program runs loyalty tiers, use the Get membership endpoint.

  Check the documentation of these endpoints to learn more about their differences and similarities.
</Tip>

The response example is used in the *Member*, *Points*, and *Tiers* sections below.

```json Get membership response wrap lines expandable highlight={3-10,13-17,19,29-47,50,51,54,55,58-60,115-151} theme={null}
{
    "member": {
        "id": "lmbr_128f962dbc8c4ba5dc",
        "customer_id": "cust_6onRrs4kCnvSVrTs5YtDPFBG",
        "program_id": "lprg_128f58429f4c4bf7b2",
        "status": "ACTIVE",
        "metadata": {},
        "created_at": "2026-06-11T15:59:41.298Z",
        "updated_at": null,
        "object": "member"
    },
    "program": {
        "id": "lprg_128f58429f4c4bf7b2",
        "name": "AnnualProgram",
        "status": "ACTIVE",
        "metadata": {},
        "object": "program"
    },
    "cards": [
        {
            "member_role": "OWNER",
            "created_at": "2026-06-11T15:59:41.303Z",
            "card": {
                "id": "lcrd_128f962dbd8c4ba5df",
                "card_definition_id": "lcdef_128f495f720c4bec8c",
                "card_type": "INDIVIDUAL",
                "code": "SummerTime-7z8dWawICd",
                "lifetime_bucket": {
                    "points": {
                        "total": 740,
                        "earned": 40,
                        "added": 700,
                        "purchased": 0,
                        "purchased_reversed": 0,
                        "subtracted": 30,
                        "expired": 300,
                        "spent": 250,
                        "refunded": 0,
                        "returned": 0,
                        "locked": 250,
                        "unlocked": 250
                    },
                    "pending_points": {
                        "total": 50,
                        "activated": 40,
                        "canceled": 0
                    }
                },
                "balance": {
                    "points": 160,
                    "pending_points": 10
                },
                "next_expiration": {
                    "points": 160,
                    "date": "2026-08-08"
                },
                "next_activation": {
                    "points": 10,
                    "type": "PERIOD_BASED",
                    "date": "2026-07-20"
                },
                "object": "card"
            },
            "object": "member_card"
        },
        {
            "member_role": "OWNER",
            "created_at": "2026-06-11T15:59:41.303Z",
            "card": {
                "id": "lcrd_128f962dbd8c4ba5e0",
                "card_definition_id": "lcdef_128f49963c0c4becb7",
                "card_type": "INDIVIDUAL",
                "code": "AutumnTime-R02hVARx33",
                "lifetime_bucket": {
                    "points": {
                        "total": 50,
                        "earned": 0,
                        "added": 50,
                        "purchased": 0,
                        "purchased_reversed": 0,
                        "subtracted": 0,
                        "expired": 0,
                        "spent": 0,
                        "refunded": 0,
                        "returned": 0,
                        "locked": 0,
                        "unlocked": 0
                    },
                    "pending_points": {
                        "total": 50,
                        "activated": 0,
                        "canceled": 0
                    }
                },
                "balance": {
                    "points": 50,
                    "pending_points": 50
                },
                "next_expiration": {
                    "points": 50,
                    "date": "2026-12-23"
                },
                "next_activation": {
                    "points": 50,
                    "type": "FIXED_DATES",
                    "date": "2026-12-23"
                },
                "object": "card"
            },
            "object": "member_card"
        },
        {
            "member_role": "OWNER",
            "created_at": "2026-06-11T15:59:41.303Z",
            "tier_progress": {
                "current": {
                    "id": "lt_128f4b46400c4bedfb",
                    "name": "Bronze",
                    "activated_at": "2026-07-13T17:22:22.626Z",
                    "expires_at": null,
                    "points": {
                        "current": 0,
                        "min": 0,
                        "max": 99
                    }
                },
                "deferred": [],
                "tier_structure": {
                    "id": "lts_128f4b460e4c4bedf6",
                    "object": "tier_structure"
                },
                "risks": [],
                "opportunities": [
                    {
                        "valid_until": null,
                        "tier_id": "lt_128f4b46558c4bedff",
                        "points": 100
                    },
                    {
                        "valid_until": null,
                        "tier_id": "lt_128f4b466a0c4bee03",
                        "points": 200
                    },
                    {
                        "valid_until": null,
                        "tier_id": "lt_128f4b46808c4bee07",
                        "points": 300
                    }
                ],
                "object": "member_tier_progress"
            },
            "card": {
                "id": "lcrd_128f962dbd8c4ba5e1",
                "card_definition_id": "lcdef_128f4a88414c4bed69",
                "card_type": "INDIVIDUAL",
                "code": "AnnualTime-7M7ShPGfme",
                "lifetime_bucket": {
                    "points": {
                        "total": 1250,
                        "earned": 250,
                        "added": 1000,
                        "purchased": 0,
                        "purchased_reversed": 0,
                        "subtracted": 1000,
                        "expired": 0,
                        "spent": 150,
                        "refunded": 0,
                        "returned": 0,
                        "locked": 10,
                        "unlocked": 10
                    },
                    "pending_points": {
                        "total": 300,
                        "activated": 250,
                        "canceled": 0
                    }
                },
                "balance": {
                    "points": 100,
                    "pending_points": 50
                },
                "next_expiration": null,
                "next_activation": {
                    "points": 50,
                    "type": "PERIOD_BASED",
                    "date": "2026-07-20"
                },
                "object": "card"
            },
            "object": "member_card"
        }
    ],
    "object": "membership"
}
```

#### Member

The `member` object (lines 2–11 of the response above) holds the profile data you use to identify the member in your system and reflect their status in the program:

* `id`: The loyalty member ID assigned by Voucherify (line 3). Store it alongside your own customer record and pass it back on subsequent calls to this endpoint.
* `customer_id`: Links the member to a Voucherify customer (line 4), which you can use to correlate loyalty activity with the customer profile in your CRM.
* `program_id`: Identifies the loyalty program the member belongs to (line 5). Combined with the top-level `program` object, this lets you support integrations that manage several programs at once.
* `status`: Shows whether the member is `ACTIVE` or `INACTIVE` (line 6). Use it to gate loyalty features in your UI. For example, hide the "Redeem points" action for inactive members.
* `metadata`: Returns any custom attributes you added to the member (line 7), such as an internal loyalty tier from another system or the enrollment channel.
* `created_at` and `updated_at` are timestamps for when the member was created and last modified (lines 8, 9).

#### Points

With the Get membership endpoint you can create a member profile that lists points available to a member. Each of the member's loyalty cards is returned in the `cards` array (starting at line 19). The details regarding points are stored in the following objects (referenced here from the first card, `SummerTime-7z8dWawICd`):

* `lifetime_bucket`: Stores historical details regarding `points` and `pending_points` (lines 29–47). For example, you can use the data to show how many points were `earned`, `spent`, `expired`, and so on.
* `balance`: Stores the details about the current number of available points (`balance.points`) for spending and the current number of `pending_points` (lines 50, 51).
* `next_expiration`: Stores the details about the number of points that are about to expire and the nearest date (lines 54, 55). If you configured point expiration, you can use this data to nudge your members to spend the points before they expire.
* `next_activation`: Stores the details about the date and number of points that are about to be activated from the pending state as well as the type of the activation (lines 58–60).

#### Tiers

If your program runs loyalty tiers, you can also use the Get membership endpoint to return the details about the current tier progress for a given loyalty card.

Use the data from the `tier_progress` object on the relevant card in the `cards` array (lines 115–151 of the response above, on the third card `AnnualTime-7M7ShPGfme`) to build a member profile that will display the active tier, and also risks and opportunities:

* `current`: Returns the loyalty tier the member is currently on, including the tier ID, name, when the member achieved it and the point range (lines 116–126).
* `risks`: Returns data for tier expiration and downgrade (line 132). In this example the array is empty, meaning the member is not scheduled to lose their current tier.
* `opportunities`: Returns the number of points the member must earn to reach higher tiers (lines 133–149).

### List transactions

Use the [GET List transactions](/api-reference/programs/list-card-transactions) endpoint to extend the member profile in your system with details about all point movements on a member's card. If your loyalty program uses several card definitions (point wallets), create a separate transaction list for each card that's governed by a given wallet.

The list card transactions endpoint lists many types (`type`) of transactions to match your business case. The transactions list the date of occurrence (`created_at`), number of points affected (`points` object), and other details. In a simple loyalty program, the key transactions types are:

* `POINTS_SPENT_ON_REWARD`: Points spent to buy a reward. The `details` object lists the number of points and the `reward.id` (lines 11–24)
* `PENDING_POINTS_ACTIVATED`: Points that were in a pending state, but they were activated manually or automatically as set in the card definition configuration (the `details` object in lines 38–47).
* `PENDING_POINTS_ADDED`: Pending points that were added to the loyalty card under the point wallet settings (the `details` object in lines 61–76).
* `POINTS_EXPIRED`: Points that expired under the card definition settings (the `details` object in lines 90–108).
* `POINTS_EARNED`: Points earned and instantly added to the loyalty card (the `details` object in the Point earned example, line 11–24).

<CodeGroup>
  ```json List card transactions response wrap expandable lines highlight={11-24,38-47,61-76,90-108} theme={null}
  {
      "data": [
          {
              "id": "lctx_129748dbafe65ae86d",
              "card_id": "lcrd_128f962dbd8c4ba5e1",
              "program_id": "lprg_128f58429f4c4bf7b2",
              "member_id": "lmbr_128f962dbc8c4ba5dc",
              "card_definition_id": "lcdef_128f4a88414c4bed69",
              "card_type": "INDIVIDUAL",
              "type": "POINTS_SPENT_ON_REWARD",
              "details": {
                  "reason": "Points spent on reward",
                  "rejection": null,
                  "metadata": {},
                  "points": {
                      "total": 150
                  },
                  "reward": {
                      "id": "lrew_128f4c6710947c85d1"
                  },
                  "reward_transaction": {
                      "id": "lrtx_129748dbafe65ae86e"
                  }
              },
              "status": "APPROVED",
              "created_at": "2026-06-17T15:29:43.104Z",
              "updated_at": "2026-06-17T15:29:45.622Z",
              "object": "card_transaction"
          },
          {
              "id": "lctx_128f9a03c6cc4ba8e7",
              "card_id": "lcrd_128f962dbd8c4ba5e1",
              "program_id": "lprg_128f58429f4c4bf7b2",
              "member_id": "lmbr_128f962dbc8c4ba5dc",
              "card_definition_id": "lcdef_128f4a88414c4bed69",
              "card_type": "INDIVIDUAL",
              "type": "PENDING_POINTS_ACTIVATED",
              "details": {
                  "reason": "Manual points activation",
                  "rejection": null,
                  "metadata": {},
                  "points": {
                      "total": 400,
                      "date": "2026-06-21",
                      "type": "PERIOD_BASED"
                  }
              },
              "status": "APPROVED",
              "created_at": "2026-06-11T16:16:26.907Z",
              "updated_at": "2026-06-11T16:16:27.291Z",
              "object": "card_transaction"
          },
          {
              "id": "lctx_128f9821be4fd14efd",
              "card_id": "lcrd_128f962dbd8c4ba5e1",
              "program_id": "lprg_128f58429f4c4bf7b2",
              "member_id": "lmbr_128f962dbc8c4ba5dc",
              "card_definition_id": "lcdef_128f4a88414c4bed69",
              "card_type": "INDIVIDUAL",
              "type": "PENDING_POINTS_ADDED",
              "details": {
                  "reason": "AnnualProgram / EarningRule-OrderPaid",
                  "rejection": null,
                  "metadata": {
                      "event_id": "evcus_128f981fced47cbe9b",
                      "member_id": "lmbr_128f962dbc8c4ba5dc",
                      "program_id": "lprg_128f58429f4c4bf7b2",
                      "earning_item_id": "lernei_128f48ee3fcc4bec33",
                      "earning_rule_id": "lern_128f48ee44cc4bec34"
                  },
                  "points": {
                      "total": 400,
                      "date": "2026-06-21",
                      "type": "PERIOD_BASED"
                  }
              },
              "status": "APPROVED",
              "created_at": "2026-06-11T16:08:13.305Z",
              "updated_at": "2026-06-11T16:08:14.170Z",
              "object": "card_transaction"
          },
          {
              "id": "lctx_129a515e9aa3f9dd93",
              "card_id": "lcrd_128f962dbd8c4ba5df",
              "program_id": "lprg_128f58429f4c4bf7b2",
              "member_id": "lmbr_128f962dbc8c4ba5dc",
              "card_definition_id": "lcdef_128f495f720c4bec8c",
              "card_type": "INDIVIDUAL",
              "type": "POINTS_EXPIRED",
              "details": {
                  "reason": "Points expired automatically",
                  "rejection": null,
                  "metadata": {},
                  "points": {
                      "total": 50
                  },
                  "date": "2026-06-09",
                  "buckets": [
                      {
                          "id": "lcpeb_1299ec455b625c31e1",
                          "points": {
                              "total": 50
                          },
                          "expiration_date": "2026-06-09",
                          "expiration_type": "ROLLING_EXPIRATION"
                      }
                  ]
              },
              "status": "APPROVED",
              "created_at": "2026-06-10T00:02:20.906Z",
              "updated_at": "2026-06-10T00:02:20.975Z",
              "object": "card_transaction"
          }
      ],
      "cursor": {
          "next": "lcrsctx_12989e71c62b6d5baf",
          "expires_at": "2026-06-18T16:32:07.896Z"
      },
      "object": "list"
  }
  ```

  ```json Point earned response wrap lines expandable highlight={11-25} theme={null}
  {
      "data": [
          {
              "id": "lctx_1298a2ae5ba25c6454",
              "card_id": "lcrd_1298a287482b6d5e90",
              "program_id": "lprg_1298a2068ad09df2dc",
              "member_id": "lmbr_1298a28746eb6d5e8d",
              "card_definition_id": "lcdef_1298a22790509df2f8",
              "card_type": "INDIVIDUAL",
              "type": "POINTS_EARNED",
              "details": {
                  "reason": "Pointexpire / EXPIRE_EARNING_RULE",
                  "rejection": null,
                  "metadata": {
                      "event_id": "evcus_1298a2abb06b6d5ec2",
                      "member_id": "lmbr_1298a28746eb6d5e8d",
                      "program_id": "lprg_1298a2068ad09df2dc",
                      "earning_item_id": "lernei_1298a23b9f509df311",
                      "earning_rule_id": "lern_1298a23baf509df312"
                  },
                  "points": {
                      "total": 50,
                      "expiration_date": "2026-06-19"
                  }
              },
              "status": "APPROVED",
              "created_at": "2026-06-18T16:40:38.510Z",
              "updated_at": "2026-06-18T16:40:39.402Z",
              "object": "card_transaction"
          }
      ],
      "cursor": null,
      "object": "list"
  }
  ```
</CodeGroup>

### List reward purchases

Use the [GET List reward purchases](/api-reference/programs/list-member-reward-purchases) endpoint to show your members the rewards they have purchased. This endpoint lists purchases for all cards the member has.

The `details` (lines 12–33 and 47–68) object lists more information about the reward purchase, like the number of points spent or the reward `type`.

```json List reward purchases response lines wrap highlight={12-33,47-68} theme={null}
{
    "data": [
        {
            "id": "lrtx_1299a9ded817f57b90",
            "card_id": "lcrd_128f962dbd8c4ba5df",
            "card_transaction_id": "lctx_1299a9ded817f57b8f",
            "program_id": "lprg_128f58429f4c4bf7b2",
            "member_id": "lmbr_128f962dbc8c4ba5dc",
            "reward_id": "lrew_128f4cefbad47c863e",
            "status": "APPROVED",
            "type": "PURCHASE",
            "details": {
                "reason": "Points spent on reward",
                "rejection": null,
                "metadata": {},
                "points": {
                    "total": 250
                },
                "result": {
                    "reward": {
                        "id": "lrew_128f4cefbad47c863e",
                        "type": "MATERIAL"
                    },
                    "quantity": 1,
                    "material": {
                        "type": "PRODUCT",
                        "product": {
                            "id": "prod_128f4cd0a94c4bef25"
                        }
                    },
                    "digital": null
                }
            },
            "created_at": "2026-06-19T11:50:32.033Z",
            "updated_at": "2026-06-19T11:50:33.387Z",
            "object": "reward_transaction"
        },
        {
            "id": "lrtx_129748dbafe65ae86e",
            "card_id": "lcrd_128f962dbd8c4ba5e1",
            "card_transaction_id": "lctx_129748dbafe65ae86d",
            "program_id": "lprg_128f58429f4c4bf7b2",
            "member_id": "lmbr_128f962dbc8c4ba5dc",
            "reward_id": "lrew_128f4c6710947c85d1",
            "status": "APPROVED",
            "type": "PURCHASE",
            "details": {
                "reason": "Points spent on reward",
                "rejection": null,
                "metadata": {},
                "points": {
                    "total": 150
                },
                "result": {
                    "reward": {
                        "id": "lrew_128f4c6710947c85d1",
                        "type": "MATERIAL"
                    },
                    "quantity": 1,
                    "material": {
                        "type": "PRODUCT",
                        "product": {
                            "id": "prod_128f4c2edf547c859e"
                        }
                    },
                    "digital": null
                }
            },
            "created_at": "2026-06-17T15:29:43.104Z",
            "updated_at": "2026-06-17T15:29:44.542Z",
            "object": "reward_transaction"
        }
    ],
    "cursor": null,
    "object": "list"
}
```

## Fulfillment

Your loyalty program needs a way for your members to spend the loyalty points they've earned. In a typical loyalty program, members can spend points to:

* Use the points as currency to pay for their orders.
* Purchase rewards.

### Pay with points

<Info>
  <Badge color="gray">Prerequisite: Settings of pay with points in the point wallet</Badge>

  The point-to-currency exchange ratio is defined in the point wallet that governs the behavior of the loyalty card.
</Info>

To allow your members to use points as currency and pay for their purchases, you'll need two endpoints:

* [POST Create order](/api-reference/orders/create-order "Create order API reference page") to create an order in Voucherify.
* [POST Pay for an order with points](/api-reference/programs/pay-for-an-order-with-points "Pay for an order with points API reference") to use the existing order ID in this endpoint.

<Note>
  <Badge color="blue">Qualifying, validating, and redeeming incentives</Badge>

  If you want to combine your loyalty program with [qualification](/api-reference/qualifications/check-eligibility), [validation](/api-reference/validations/validate-stackable-discounts), and [redemption](/api-reference/redemptions/redeem-stackable-discounts) of incentives, these should happen before the POST Pay with points endpoint is used.

  If you use the redemption endpoint (POST `v1/redemptions`), it can create an order in Voucherify if an `order` object is passed. You can then skip the POST Create order endpoint, but you'll need to retrieve the order ID from the `v1/redemptions` response.
</Note>

The POST Pay for an order with points endpoint has:

* Two required fields:
  * The `card_id` string of the loyalty card (`lcrd_...`) whose points will be used in the transaction (line 2 in the examples).
  * The `order` object that contains the `id` or `source_id` of the order to be paid (line 4).
* Two optional fields:
  * The `mode` string that defines if the request is used to simulate the transaction or to actually pay for the order (line 6). Pass the following values:
    * `DRY_RUN` to perform a simulation of the transaction. No points are spent from the member loyalty card and no transaction records are created.
    * `TRANSACTION` to create an actual transaction. Points are spent to lower the total amount of the order.
  * The `payment_limit` object that defines a limit on the transaction by providing a `type` (line 6):
    * `CARD_BALANCE`: Default setting that allows the automatic payment up to the maximum of the total balance of the loyalty card. This setting is used when no `payment_limit` object is sent.
    * `POINTS_LIMIT`: Uses the `points_limit` object to define how many points will be spent in the transaction. In the example below, the amount to be spent equals `100` points (line 10). If the card has fewer points than the set value for `max`, all available points will be used.
    * `AMOUNT_LIMIT`: Uses the `amount_limit` object to define how much of the order amount can be paid for with points. The `max` value is defined in the smallest currency amount (line 10), so `100` means \$1, for example.

See the request examples below.

<CodeGroup>
  ```json Pay with points request: Card balance limit lines wrap expandable highlight={2,4,6,8} theme={null}
      {
          "card_id": "lcrd_128f962dbd8c4ba5e1",
          "order": {
              "id": "ord_12b4cdf5f30c158825"
          },
          "mode": "TRANSACTION",
          "payment_limit": {
              "type": "CARD_BALANCE"
          }
      }
  ```

  ```json Pay with points request: Point limit lines wrap expandable highlight={2,4,6,8,10} theme={null}
      {
          "card_id": "lcrd_128f962dbd8c4ba5e1",
          "order": {
              "id": "ord_12b4cdf5f30c158826"
              },
          "mode": "TRANSACTION",
          "payment_limit": {
              "type": "POINTS_LIMIT",
              "points_limit": {
                  "max": 100
              }
          }
      }
  ```

  ```json Pay with points request: Amount limit lines wrap expandable highlight={2,4,6,8,10} theme={null}
      {
          "card_id": "lcrd_128f962dbd8c4ba5e1",
          "order": {
              "id": "ord_12b4cdf5f30c158827"
          },
          "mode": "TRANSACTION",
          "payment_limit": {
              "type": "AMOUNT_LIMIT",
              "amount_limit": {
                  "max": 2000
              }
          }
      }
  ```
</CodeGroup>

The response differs depending on the `mode` used:

* `200` for the `DRY_RUN` mode.
* `202` for the `TRANSACTION` mode. Usually, returns a `PENDING` transaction to be processed asynchronously.

The table below describes how individual schema properties differ between the two execution response structures.

| JSON Property                     | `DRY_RUN` mode (simulation)                     | `TRANSACTION` mode (live)               | Notes                                                                               |
| :-------------------------------- | :---------------------------------------------- | :-------------------------------------- | :---------------------------------------------------------------------------------- |
| `status`                          | `"DRY_RUN"`                                     | `"TRANSACTION_CREATED"`                 | Indicates the processing method used by Voucherify.                                 |
| `message`                         | `"Dry run mode. No transaction was created..."` | `"Pay with points transaction created"` | Returns a mode-specific descriptive system notification.                            |
| `transaction.id`                  | *Omitted*                                       | `"lotx_12b8dbf787c67de367"`             | Unique identifier generated only for live, persistent records.                      |
| `transaction.card_transaction_id` | `null`                                          | `"lctx_12b8dbf787c67de366"`             | Populates with a tracking ID during live balance changes.                           |
| `transaction.status`              | `"SIMULATED"`                                   | `"PENDING"`                             | Reflects workflow state tracking (mock engine vs. active processing queue).         |
| `transaction.created_at`          | *Omitted*                                       | `"2026-07-13T17:22:19.296Z"`            | Generated exclusively when the state transaction object is written to the database. |

The transaction details regarding the point or amount limit and the actual number of points spent (or to be spent for the `DRY_RUN`) are returned in the `details.payment` object (lines 18–22).

<CodeGroup>
  ```json Pay with points response: Card balance type lines wrap expandable highlight={16-21} theme={null}
      {
          "transaction": {
              "id": "lotx_12b8861fdd0dd35462",
              "program_id": "lprg_128f58429f4c4bf7b2",
              "member_id": "lmbr_128f962dbc8c4ba5dc",
              "card_id": "lcrd_128f962dbd8c4ba5e1",
              "card_definition_id": "lcdef_128f4a88414c4bed69",
              "card_transaction_id": "lctx_12b8861fdd0dd35461",
              "order_id": "ord_12b4cdf5f30c158825",
              "status": "PENDING",
              "type": "PAY_WITH_POINTS",
              "details": {
                  "reason": "Points spent on order payment",
                  "rejection": null,
                  "metadata": {},
                  "payment": {
                      "amount": 200,
                      "points_spent": 200,
                      "exchange_ratio": 1
                  }
              },
              "created_at": "2026-07-13T11:07:16.212Z",
              "updated_at": null,
              "object": "order_transaction"
          },
          "status": "TRANSACTION_CREATED",
          "message": "Pay with points transaction created"
      }
  ```

  ```json Pay with points response: Point limit type lines wrap expandable highlight={18-22} theme={null}
      {
          "transaction": {
              "id": "lotx_12b884c72b0dd352b9",
              "program_id": "lprg_128f58429f4c4bf7b2",
              "member_id": "lmbr_128f962dbc8c4ba5dc",
              "card_id": "lcrd_128f962dbd8c4ba5e1",
              "card_definition_id": "lcdef_128f4a88414c4bed69",
              "card_transaction_id": "lctx_12b884c72b0dd352b8",
              "order_id": "ord_12b4cdf5f30c158826",
              "status": "PENDING",
              "type": "PAY_WITH_POINTS",
              "details": {
                  "reason": "Points spent on order payment",
                  "rejection": null,
                  "metadata": {
                      "points_limit": 12
                  },
                  "payment": {
                      "amount": 12,
                      "points_spent": 12,
                      "exchange_ratio": 1
                  }
              },
              "created_at": "2026-07-13T11:01:23.245Z",
              "updated_at": null,
              "object": "order_transaction"
          },
          "status": "TRANSACTION_CREATED",
          "message": "Pay with points transaction created"
      }
  ```

  ```json Pay with points response: Amount limit type lines wrap expandable highlight={18-22} theme={null}
      {
          "transaction": {
              "id": "lotx_12b8939fd78dd3657f",
              "program_id": "lprg_128f58429f4c4bf7b2",
              "member_id": "lmbr_128f962dbc8c4ba5dc",
              "card_id": "lcrd_128f962dbd8c4ba5e1",
              "card_definition_id": "lcdef_128f4a88414c4bed69",
              "card_transaction_id": "lctx_12b8939fd78dd3657e",
              "order_id": "ord_12b4cdf5f30c158827",
              "status": "PENDING",
              "type": "PAY_WITH_POINTS",
              "details": {
                  "reason": "Points spent on order payment",
                  "rejection": null,
                  "metadata": {
                      "amount_limit": 2000
                  },
                  "payment": {
                      "amount": 2000,
                      "points_spent": 2000,
                      "exchange_ratio": 1
                  }
              },
              "created_at": "2026-07-13T12:06:15.135Z",
              "updated_at": null,
              "object": "order_transaction"
          },
          "status": "TRANSACTION_CREATED",
          "message": "Pay with points transaction created"
      }
  ```
</CodeGroup>

If the loyalty card has no points, Voucherify returns error code `423` – "Card balance is zero".

```json Pay with points response: No balance – error 423 lines wrap expandable highlight={} theme={null}
    {
        "code": 423,
        "key": "zero_card_balance",
        "message": "Card balance is zero",
        "details": "Operation requires a positive card balance",
        "request_id": "v-12b8894b3ec1a59873"
    }
```

### Purchase reward

Use the [POST Purchase reward](/api-reference/programs/purchase-a-reward-with-points) endpoint to create a store where your loyalty program members can buy rewards.

In the request body, send the reward ID (`reward_id`) to be purchased. Since rewards are assigned to specific card definitions, Voucherify will subtract the required number of points from a loyalty card that's governed by the card definition.

Use the `details` object (response lines 11–19) to display how many points were spent on the reward purchase. If the loyalty program uses several card definitions, use the `card_id` (line 4) to tie the purchase to a specific loyalty card.

<CodeGroup>
  ```json Purchase reward: Request lines wrap theme={null}
  {
      "reward_id": "lrew_128f4cefbad47c863e"
  }
  ```

  ```json Purchase reward: Response lines wrap expandable highlight={4,11-19} theme={null}
  {
    "transaction": {
      "id": "lrtx_1299a9ded817f57b90",
      "card_id": "lcrd_128f962dbd8c4ba5df",
      "card_transaction_id": "lctx_1299a9ded817f57b8f",
      "program_id": "lprg_128f58429f4c4bf7b2",
      "member_id": "lmbr_128f962dbc8c4ba5dc",
      "reward_id": "lrew_128f4cefbad47c863e",
      "status": "PENDING",
      "type": "PURCHASE",
      "details": {
        "reason": "Points spent on reward",
        "rejection": null,
        "metadata": {},
        "points": {
          "total": 250
        },
        "result": null
      },
      "created_at": "2026-06-19T11:50:32.033Z",
      "updated_at": null,
      "object": "reward_transaction"
    },
    "status": "TRANSACTION_CREATED",
    "message": "Reward purchase transaction created"
  }
  ```
</CodeGroup>

## Cancellation

Your loyalty program should also support situations when a member wants to stop participating in the program, have their member profile deleted altogether, or cases when you need to temporarily suspend member activity when suspecting program fraud.

### Deactivate

Use the [POST Deactivate member](/api-reference/programs/deactivate-program-member) endpoint to suspend a member's participation in the program.

A suspended member can't earn points or spend them on rewards or orders.

<Tip>
  <Badge color="green">Activating member again</Badge>

  If you need to reactivate the member, so that they can participate in the loyalty program again, use the [POST Activate member](/api-reference/programs/activate-program-member) endpoint.
</Tip>

### Delete

If you need a mechanism for deleting member data, use the [DELETE Member](/api-reference/programs/delete-program-member) endpoint to soft-delete the member and all their associated cards within the program.

<Note>
  <Badge color="blue">Customer data in Voucherify</Badge>

  The DELETE Member endpoint deletes only the data regarding the member profile. However, the member still has a customer profile in Voucherify.

  Read how to [delete customer](/prepare/customers#delete-customers) data from Voucherify.
</Note>
