The future is agentic.

Tutorials

Birthday coupons and spend limits with Commerce Layer.

Learn how to use Commerce Layer’s Rules Engine to create birthday discounts, limit eligibility with customer metadata, and manage dynamic spend thresholds with precision.

SB
Sara
· February 24, 2026

It’s common practice to reward customers with a birthday coupon voucher. These coupons are usually generated ahead of time and can be redeemed only within a predefined validity window (for example: the birthday month, the exact birthday date, or a few days around it).

Below, we outline a couple of approaches to manage this use case using the Commerce Layer Rules Engine.

Leverage customer metadata fields or tags

As you know, Commerce Layer is not a CRM. However, in the customers resource, you can access every customer who has initiated at least one order by providing an email address.

In Commerce Layer, customers are primarily identified by their email address and enriched with a set of attributes, including their order-based status:

  • Prospect — no orders
  • Acquired — one order
  • Repeat — more than one order

These values are automatically calculated by Commerce Layer. Customers also have a registration status (registered or guest) and fields that help link customers to external CRM systems, such as profile_id, which can be used to uniquely identify a customer across connected services.

You can also rely on metadata, which are customizable fields designed to store additional custom information.

The Rules Engine can leverage on any of the fields listed above to activate promotions: you can create discounts for registered customers only, or for customers that have never placed an order before or use customer metadata and customer tags.

For example:

  • If you store a customer’s first and last name as metadata, you can create a promotion that applies only to customers whose name is "John".
  • You can use tags to segment customers into tiers such as "bronze", "silver", or "gold", and apply different discount percentages based on the assigned tag.

Similarly, you can store a customer’s birthday date as metadata and configure a promotion that triggers only when an order is placed during the matching period.

Below is an example about how to set the metadata field for this purpose:

{
  "id": "OopZhmqLlQ",
  "type": "customers",
  "email": "john.doe@example.com",
  ...
  "metadata": { "birthday_date": "1978-02-28"},
  ...
}

Building a birthday promotion

In the following examples, we created some birthday flex promotions that apply a fixed discount of 5 EUR only if the customer places an order within a defined period around their birthday. You can adjust the validity logic to apply only on the exact month or day, or within a custom window (e.g., 3 days before and 3 days after), depending on your business requirements.

Example 1

Promo valid only on the exact month

This is the JSON rule that triggers the promotion only if the customer places and order within the month of their birthday:

{
  "rules": [
    {
      "id": "promo-birthday-month-today",
      "name": "5 EURO discount on customer birthday month (today)",
			"conditions": [
        {
          "field": "order.customer.metadata.birthday_date",
          "group": "d457b413-0070-4696-8091-bb428ac67e87",
          "value": "{{month(today)}}",
          "matcher": "eq"
        }
      ],
      "actions": [
        {
          "type": "fixed_amount",
          "value": 500,
          "groups": [ "d457b413-0070-4696-8091-bb428ac67e87" ],
          "selector": "order"
        }
      ]
    }
  ]
}
Example 2

Promo valid only on the exact day

If you want to restrict the promotion to the customer’s exact birthday day, you can adjust the condition as follows:

{
  "rules": [
    {
      "id": "promo-birthday-today",
      "name": "5 EURO discount on customer birthday (today)",
			"conditions": [
        {
          "field": "order.customer.metadata.birthday_date",
          "group": "d457b413-0070-4696-8091-bb428ac67e87",
          "value": "{{birthday}}",
          "matcher": "eq"
        }
      ],
      "actions": [
        {
          "type": "fixed_amount",
          "value": 500,
          "groups": [ "d457b413-0070-4696-8091-bb428ac67e87" ],
          "selector": "order"
        }
      ]
    }
  ]
}

If the customer in the example (born on February 28th) tries to use the coupon on February 10th, the promotion won’t match. This is visible in the order checkpoint response:

{
  "id": "promo-birthday-today",
  "name": "5 EURO discount on customer birthday (today)",
  "priority": 0,
  "match": false,
  "conditions_logic": "and",
  "conditions": [
    {
      "field": "order.customer.metadata.birthday_date",
      "group": "d457b413-0070-4696-8091-bb428ac67e87",
      "value": "{birthday}",
      "matcher": "eq",
      "match": false,
      "matches": [],
      "scope": "any"
    }
  ],
  "actions": []
}

On the customer’s actual birthday date, the promotion matches and the discount is applied:

{
  "id": "promo-birthday-today",
  "name": "5 EURO discount on customer birthday (today)",
  "priority": 0,
  "match": true,
  "conditions_logic": "and",
  "conditions": [
    {
      "field": "order.customer.metadata.birthday_date",
      "group": "d457b413-0070-4696-8091-bb428ac67e87",
      "value": "{birthday}",
      "matcher": "eq",
      "match": true,
      "matches": [
        {
          "order": "Nlalhbbvom",
          "customer": "OopZhmqLlQ",
          "group": "d457b413-0070-4696-8091-bb428ac67e87"
        }
      ],
      "scope": "any"
    }
  ],
  "actions": [
    {
      "resources": [
        {
          "resource_type": "orders",
          "id": "Nlalhbbvom",
          "group": "d457b413-0070-4696-8091-bb428ac67e87",
          "quantity": null,
          "value": 500,
          "action_type": "fixed_amount"
        }
      ]
    }
  ]
}

The promotion is activated via a coupon code BIRTHDAY-PROMO and can be redeemed only once per customer:

Example 3

Promo valid only on a custom date range

Assume you want to mark a milestone occasion — such as a customer’s 50th birthday — by offering a 50% discount. The discount should be valid during a specific time window: from seven days before to seven days after the customer’s 50th birthday.

Below is an example about how to set the metadata field for this purpose:

{
  "id": "OopZhmqLlQ",
  "type": "customers",
  "email": "john.doe@example.com",
  ...
  "metadata": { "50_birthday": "2026-02-23"},
  ...
}

This is the JSON rule that triggers the desired promotion:

{
  "rules": [
    {
      "id": "promo-50-birthday",
      "name": "50% on 50th birthday",
			"conditions": [
        {
          "field": "order.customer.metadata.50_birthday",
          "matcher": "gteq_lteq",
          "group": "ffaba434-a1fa-4654-ba21-4e80f183e959",
          "value": "{{range(today, 7.days)}}"
        }
      ],
      "actions": [
        {
          "type": "percentage",
          "selector": "order.line_items",
          "groups": [ "ffaba434-a1fa-4654-ba21-4e80f183e959" ],
          "value": 0.5
        }
      ]
    }
  ]
}

Discounts with spend limits

Using the same approach, you can rely on customer metadata to define spending thresholds that determine whether a customer can continue benefiting from a promotion.

For example, you can store a total_spent value inside customer metadata. This field should be updated every time an order is approved or canceled by an external process, using the Commerce Layer Metrics API to compute the customer’s total spend.

Here's an example query to the /stats endpoint:

{
  "stats": {
    "field": "order.total_amount_with_taxes",
    "operator": "sum"
  },
  "filter": {
    "order": {
      "mode": "test",
      "date_from": "2025-01-01T00:00:00Z",
      "date_to": "2026-12-23T23:59:59Z",
      "date_field": "current_date",
      "returned": false,
      "statuses": {
        "in": [ "approved" ]
      },
      "currency_codes": {
        "in": [ "EUR" ]
      }
    },
    "customer": {
      "emails": {
        "in": [ "<CUSTOMER_EMAIL_HERE>" ]
      }
    },
    "meta": {
      "payload": true
    }
  }
}

This way, customer.metadata.total_spent can be kept updated every time the customer places a new order.

At this point, you can extend your promotion by adding a condition that checks whether order.customer.metadata.total_spent is lower than a defined threshold (e.g., 100 EUR). If the metadata value exceeds that limit, the promotion will no longer match and the discount will not be applied.

For example:

  • If the customer’s total spend reaches 100 EUR, the promotion won’t apply.
  • If the customer’s total spend is lowered to 1 EUR, the promotion will match and the discount will be calculated correctly.

Try It Out

If you want to start experimenting with advanced promotion logic, request a free trial directly from your Dashboard in the Promotions app.

We’ll help you get started with the Rules Engine and tailor it to your business needs.