This tutorial shows you how to generate unique, personalized promo codes for each subscriber in your email sequences. Instead of using a static code like `LINKEDIN25` for everyone, you'll create individual codes with expiration dates to drive urgency and make subscribers feel special.

## Why use dynamic promo codes?

- **FOMO effect** — Time-limited codes create urgency and encourage immediate action.
- **Exclusivity** — Unique codes make subscribers feel like they're getting special treatment.
- **Tracking** — Individual codes let you track which subscribers convert.
- **Credibility** — Personalized codes feel more authentic than generic discount codes.

## How it works

1. Subscriber joins your workflow (via tag trigger or form submission).
2. Workflow webhook step sends subscriber data to your server.
3. Your server generates a unique coupon code with expiration.
4. Your server updates the subscriber's custom field with the coupon.
5. Email steps in your workflow display the personalized code using variables.

## Prerequisites

- A Lumail organization with an active workflow.
- One of the following to handle the webhook:
  - **No-code**: Make.com, Zapier, or n8n scenario
  - **Low-code**: Vercel, Cloudflare Workers, or Val.town function
  - **Custom**: Your existing backend server
- Optional: A payment provider (Stripe, LemonSqueezy) for validated coupons — or just generate unique tracking codes.

## Step 1 — Create a custom field for the coupon

Before setting up the workflow, create a custom field to store the promo code:

1. Go to your Lumail dashboard.
2. Navigate to **Settings → Custom Fields**.
3. Create a new field:
   - **Name**: `promo-code` (or `atrakt-coupon` if using Atrakt)
   - **Type**: Text

This field will store each subscriber's unique promo code.

![Screenshot](https://codelynx.mlvcdn.com/images/2026-01-05/clipboard_image_1767593393.png)

## Step 2 — Set up your workflow

Create a workflow that triggers when subscribers join your sequence:

1. Go to **Workflows** in your Lumail dashboard.
2. Create a new workflow or edit an existing one.
3. Add a **Trigger** (e.g., "Subscriber added with tag: `new-lead`").
4. Add a **Webhook Step** right after the trigger.

![create webhook step](https://codelynx.mlvcdn.com/images/2026-01-05/clipboard_image_1767593465.png)

### Configure the Webhook Step

In the webhook step configuration:

- **Webhook URL**: Your server endpoint (e.g., `https://your-app.com/api/generate-coupon`)
- **Webhook Secret**: A secret key for authentication (optional but recommended)

![configure webhook step](https://codelynx.mlvcdn.com/images/2026-01-05/clipboard_image_1767593527.png)

The webhook `POST`s this body. `payload` stays nested. When a secret is set, it is also sent as the `x-lumail-signature` header.

```json
{
  "subscriberId": "sub_123",
  "subscriberEmail": "john@example.com",
  "organizationId": "org_xyz",
  "workflowId": "wf_789",
  "stepId": "step_456",
  "secret": "your-secret",
  "payload": {}
}
```

## Step 3 — Build the coupon generation endpoint

Create a serverless function or API route that:

1. Receives the webhook payload from Lumail.
2. Generates a unique coupon code with expiration.
3. Creates the coupon in your payment provider (optional).
4. Updates the subscriber in Lumail with the coupon code.

### Example with Next.js and Stripe

Create `app/api/generate-coupon/route.ts`:

```tsx
import { NextRequest, NextResponse } from "next/server";
import Stripe from "stripe";

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);

export async function POST(request: NextRequest) {
  try {
    const body = await request.json();

    // Verify webhook secret
    if (body.secret !== process.env.LUMAIL_WEBHOOK_SECRET) {
      return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
    }

    const { subscriberEmail, subscriberId } = body;

    // Generate unique code
    const uniqueCode = `WELCOME-${subscriberId.slice(-6).toUpperCase()}`;

    // Set expiration to 7 days from now
    const expiresAt = Math.floor(Date.now() / 1000) + 7 * 24 * 60 * 60;

    // Create coupon in Stripe
    await stripe.coupons.create({
      id: uniqueCode,
      percent_off: 25,
      duration: "once",
      redeem_by: expiresAt,
      max_redemptions: 1,
    });

    // Create promotion code for the coupon
    await stripe.promotionCodes.create({
      coupon: uniqueCode,
      code: uniqueCode,
      max_redemptions: 1,
      expires_at: expiresAt,
    });

    // Calculate expiration date for display
    const expirationDate = new Date(expiresAt * 1000).toLocaleDateString(
      "en-US",
      {
        month: "long",
        day: "numeric",
        year: "numeric",
      },
    );

    // Update subscriber in Lumail with the coupon
    await fetch(`https://lumail.io/api/v1/subscribers/${subscriberEmail}`, {
      method: "PATCH",
      headers: {
        "Content-Type": "application/json",
        Authorization: `Bearer ${process.env.LUMAIL_API_TOKEN}`,
      },
      body: JSON.stringify({
        fields: {
          "promo-code": uniqueCode,
          "promo-expires": expirationDate,
        },
        triggerWorkflows: false,
      }),
    });

    return NextResponse.json({ success: true, code: uniqueCode });
  } catch (error) {
    console.error("Coupon generation failed:", error);
    return NextResponse.json(
      { error: "Failed to generate coupon" },
      { status: 500 },
    );
  }
}
```

## Step 4 — Use the promo code in your emails

After the webhook step completes, the subscriber has a `promo-code` custom field. Use it in your email content with Lumail variables:

In the editor, type `@` to insert variables — your custom fields will appear in the list.

You can also add this promo code inside the URL, in the editor you can write `https://example.com/?code={{promo-code}}`.

## Step 5 — Test the flow

1. **Deploy your webhook endpoint** to a publicly accessible URL.
2. **Add the webhook URL** to your Lumail workflow step.
3. **Trigger a test subscriber** by adding the trigger tag to a test email.
4. **Check the subscriber** in Lumail dashboard — verify the `promo-code` field is populated.
5. **Review the email** sent — confirm the code appears correctly.

### Debugging tips

- Check your server logs for webhook receipt confirmation.
- Verify the Lumail API token has permission to update subscribers.
- Ensure the custom field name matches exactly (`promo-code` is case-sensitive).
- Test with `triggerWorkflows: false` to avoid infinite loops.

## Summary

By combining Lumail's webhook steps with your backend logic:

1. **Webhook step** triggers your coupon generation endpoint.
2. **Your server** creates unique codes and updates subscriber custom fields via the [PATCH API](/docs/api-reference/api-subscribers-patch).
3. **Email steps** display personalized codes using `{{promo-code}}`.

This creates a seamless experience where each subscriber receives a unique, time-limited promo code — driving urgency, tracking conversions, and making your offers feel exclusive.

## Related Documentation

- [Webhook Step Reference](/docs/workflows/workflow-webhook-step) - Webhook configuration
- [Update Subscriber API](/docs/api-reference/api-subscribers-patch) - Update subscribers with custom fields
- [Variables in Emails](/docs/features/variables) - Use custom fields in email content
- [API Tokens](/docs/api-reference/api-tokens) - Authenticate webhook callbacks
- [Create API Token Tutorial](/docs/tutorials/create-api-token) - Generate tokens for webhooks
- [Workflows Overview](/docs/workflows) - Learn about workflow automation
