More Tutorials

Implement entitlement-gated offers with your own fulfillment using Chargebee Growth's Offers APIs

Growth
Personalized Offers

Overview

Many products control access to advanced features and capabilities via entitlements. When a user tries to access a gated feature, showing a clear upgrade offer at that moment can turn intent into revenue. Use Chargebee Growth's Offers APIs to make offer decisions, while keeping full control over offer fulfillment.

This tutorial explains how to implement an entitlement-gated offer flow: your application detects when a user hits a gated feature, fetches a personalized offer from Chargebee using the Offers APIs, shows it in-product, and when the user accepts, your application runs its own upgrade processing and then updates the offer fulfillment status in Chargebee. Chargebee handles offer eligibility, selection, and reporting; you keep full control over payments and provisioning.

Architecture overview

The flow splits responsibilities between your backend and Chargebee as follows.

Your application:

  • Detects when a user tries to access a gated feature
  • Requests an offer from Chargebee and renders it to the user
  • Notifies offer engagement events to Chargebee
  • Creates and updates offer fulfillments
  • Handles payment collection and feature provisioning

Chargebee:

  • Evaluates offer eligibility
  • Selects the appropriate offer
  • Records offer engagement events
  • Records fulfillment status
Entitlement-gated offers with your own fulfillment.
Entitlement-gated offers with your own fulfillment.

A typical flow:

  1. When a user tries to access a gated feature, fetch a personalized offer from Chargebee, passing the customer and subscription identifiers and the feature entitlement flag.
  2. If an offer is returned, show an in-product upgrade prompt using the offer content.
  3. For engagement tracking, notify Chargebee when the offer is viewed or dismissed. Stop here if the user dismisses the offer.
  4. When the user accepts the offer, create an offer fulfillment at Chargebee.
  5. Route the user into your upgrade or checkout flow (for example, collect payment, enable the feature flag, and provision access to the feature). You can optionally use the redirect URL provided in the offer response, if configured in the offer.
  6. When provisioning succeeds, update the offer fulfillment status in Chargebee to completed.

Before you start

For this tutorial to work, you need the following setup:

Set up your development environment

  1. Obtain a full-access API key from Chargebee Billing.
  2. Install the Chargebee client SDK for your server-side stack.
  3. Have entitlement or feature-gating logic in your application.

Set up Chargebee Billing and Growth

Users with access to Chargebee Growth must complete these steps.

  1. Have Chargebee Growth enabled and connected to your Chargebee Billing site.
  2. Ensure that the required configuration has been completed in Chargebee so the APIs return offers at runtime.

Implementation steps

Follow these steps to implement the entitlement-gated offer flow using Chargebee Growth's Offers APIs with your own checkout or provisioning.

Entitlement-gated offers sequence diagram.
Entitlement-gated offers sequence diagram.

Step 1: Detect entitlement-gated access

When a user tries to access a gated feature (for example, Advanced Analytics), your application should first check whether the feature is enabled for that user.

  • If the feature is enabled, continue as normal.
  • If the feature is not enabled, start the Growth offer flow.

This check typically runs when the user opens a gated screen, invokes a gated action, or when a permission check fails.

Step 2: Fetch the personalized offer

Fetch a personalized offer from Chargebee to see whether the user is eligible for an upgrade. Include the customer and subscription identifiers and any custom fields that are used in your Growth audience rules (for example, entitlement flags).

Call the Personalized Offers API from your backend:

curl  https://YOUR_CHARGEBEE_SUBDOMAIN.grow.chargebee.com/api/v2/personalized_offers \
     -u YOUR_CHARGEBEE_API_KEY:\
     --header 'Content-Type: application/json;charset=UTF-8' \
     --data '{
     "customer_id": "CUSTOMER_ID",
     "subscription_id": "SUBSCRIPTION_ID",
     "custom": {
       "has_advanced_analytics": false
     }
}'

If the subscriber matches one or more configured plays in Growth, Chargebee returns the offer corresponding to the play of highest priority. Example response:

{
  "personalized_offers": [
    {
      "id": "vdyjO2qlLD_715653d4",
      "offer_id": "vdyjO2qlLD",
      "content": {
        "title": "<div class=\"slate-p\">Unlock Advanced Analytics</div>",
        "description": "<div class=\"slate-p\">Get advanced dashboards, deeper insights, and custom reports.</div>"
      },
      "options": [
        {
          "id": "6cd5280e",
          "label": "Unlock now",
          "processing_type": "url_redirect",
          "redirect_url": "https://app.example.com/upgrade/analytics"
        }
      ]
    }
  ],
  "expires_at": 1732790400
}

Notice that the response includes a processing_type of url_redirect and a redirect_url. That means the offer is configured for fulfillment by your application, typically by redirecting the user to the redirect_url after they accept the offer.

When no offer is returned

If the response has an empty personalized_offers list, do not show an upgrade prompt. An empty response means the user isn't eligible (for example, the request parameters don't match any active plays, or the offer was already accepted or dismissed).

Step 3: Render the in-product upgrade prompt

If a personalized offer is returned, use the offer content to show an upgrade prompt inside your application. Depending on how prominent you want the message, you can use one of these UI patterns:

  • A banner at the top of the dashboard
  • A modal
  • An inline callout near the gated feature or upgrade area

Use the offer content as follows to render the upgrade prompt:

This ensures that the offer is displayed to the user dynamically based on the current configuration in Chargebee Growth.

Here are some examples of how to render the upgrade prompt:

An in-app banner showing a personalized offer.
An in-app banner showing a personalized offer.
An in-app modal showing a personalized offer.
An in-app modal showing a personalized offer.

Step 4: Track offer interactions

To measure engagement in Growth, record the following offer events:

  • Record a viewed event each time the offer is displayed.
  • Record a dismissed event when the user closes or ignores the offer (for example, "Maybe later").

Send these events from your backend after the corresponding UI action using the Offer Events API with the personalized_offer_id from the Personalized Offers response:

curl  https://YOUR_CHARGEBEE_SUBDOMAIN.grow.chargebee.com/api/v2/offer_events \
     -u YOUR_CHARGEBEE_API_KEY:\
     --header 'Content-Type: application/json;charset=UTF-8' \
     --data '{
     "type": "viewed",
     "personalized_offer_id": "vdyjO2qlLD_715653d4"
}'

These events are for reporting; the dismissed event also affects offer cooldown. They do not affect fulfillment behavior.

Step 5: Create offer fulfillment on CTA click

When the user selects the acceptance CTA ("Unlock now"), call your backend to create an offer fulfillment. Use the Offer Fulfillments API with the personalized_offer_id and option_id from the offer returned in Step 2.

curl  https://YOUR_CHARGEBEE_SUBDOMAIN.grow.chargebee.com/api/v2/offer_fulfillments \
     -u YOUR_CHARGEBEE_API_KEY:\
     --header 'Content-Type: application/json;charset=UTF-8' \
     --data '{
     "personalized_offer_id": "vdyjO2qlLD_715653d4",
     "option_id": "6cd5280e"
}'

The API returns a response like the following:

{
  "offer_fulfillment": {
    "id": "507c340b",
    "personalized_offer_id": "vdyjO2qlLD_715653d4",
    "option_id": "6cd5280e",
    "processing_type": "url_redirect",
    "status": "in_progress"
  }
}

This records acceptance and sets the fulfillment to in_progress.

Step 6: Redirect and run your entitlement or billing logic

After creating the fulfillment, redirect the user to the URL returned in the personalized offer response to run your own upgrade logic. For example:

  • Collect payment from the customer and update the subscription.
  • Enable feature flags or entitlements.
  • Provision access to the feature.

This step is entirely under your control and can involve multiple systems.

Step 7: Update the offer fulfillment status

When your provisioning or checkout has finished successfully, update the offer fulfillment in Chargebee:

  • Set status to completed when the user has successfully received the upgrade.
  • Set status to failed when provisioning or payment fails. If status is failed, you must include a failure_reason to explain why the fulfillment did not succeed.

Use the Update an Offer Fulfillment API:

curl  https://YOUR_CHARGEBEE_SUBDOMAIN.grow.chargebee.com/api/v2/offer_fulfillments/507c340b \
     -u YOUR_CHARGEBEE_API_KEY:\
     -X POST \
     --header 'Content-Type: application/json;charset=UTF-8' \
     --data '{
     "status": "completed"
}'

The API returns a response like the following:

{
  "offer_fulfillment": {
    "id": "507c340b",
    "personalized_offer_id": "vdyjO2qlLD_715653d4",
    "option_id": "6cd5280e",
    "processing_type": "url_redirect",
    "status": "completed"
  }
}

Error handling

Multiple CTA clicks by the same user

As a best practice, disable the offer acceptance CTA button in the UI immediately after it's clicked. If there's a duplicate click and you request a new offer fulfillment, the API returns an error because multiple fulfillments aren't allowed for the same personalized_offer_id.

Handle the error as follows:

  1. Fetch the offer_fulfillment.id saved earlier in Step 5.
  2. Retrieve the fulfillment status and act on its current status.
  3. If offer_fulfillment.status is completed, inform the user that the offer has already been fulfilled.
  4. If offer_fulfillment.status is failed, check the offer_fulfillment.error.code to determine the reason.

User abandons after accepting

If the user closes the tab or leaves before your flow finishes, the fulfillment can stay in in_progress. Run a reconciliation job that periodically checks in_progress fulfillments and marks them failed or retries provisioning according to your business rules. If the offer fulfillment is not marked as completed or failed within 7 days, Chargebee will mark it as failed with the error.code external_fulfillment_failed.

Provisioning failures

You may encounter provisioning failures due to internal errors, downstream failures, or payment failures in your system. When you know the upgrade did not complete, update the fulfillment status to failed and include a failure_reason.

Same offer accepted by different users

Calling the Offer Fulfillments API succeeds only once for the same base offer_id when the offer originates from the same Play.

If you're a B2B business with multiple users per subscription, the same offer from the same Play can be seen by different signed-in users at the same time. In this case, after the first user accepts the offer, the Offer Fulfillments API returns the following error for subsequent users:

Fulfillment is already in progress/completed. Retrieve the fulfillment to know the latest status.

Show a message to the user such as, "This offer has already been accepted."

Summary

In this tutorial, you learned how to:

  • Detect gated feature access and fetch a personalized offer from Chargebee.
  • Use the offer content to render an in-product upgrade prompt.
  • Record offer events (viewed, dismissed) for reporting.
  • Create an offer fulfillment when the user accepts the offer.
  • Run your own entitlement or checkout logic.
  • Update the offer fulfillment status to completed or failed after provisioning.
  • Handle duplicate clicks, abandonment, provisioning failures, and B2B scenarios where the same offer is accepted by different users.

Appendix

Details

Required setup in Chargebee

This section describes the setup required in both Chargebee Billing and Chargebee Growth to enable entitlement-gated offers to be returned by Chargebee APIs. The steps below configure an expansion offer that targets subscribers without the gated feature, shows copy such as "Unlock Advanced Analytics", and uses URL redirect for fulfillment by your application.

Administrators or growth marketers with access to Chargebee Growth and Billing typically handle this configuration.

1. Create and map custom fields in Growth

  1. In Growth, navigate to Settings > Setup.
  2. Under Field mappings, select Map fields.
  3. Scroll down to Field Mapping and select the button View & map passed data. A modal appears.
  4. On the modal, under Custom Chargebee Retention fields, select the +add a new field button and enter the following details:
    1. Field Label: "Has advanced analytics".
    2. Data Type: "Boolean".
    3. JS Name: "custom.has_advanced_analytics".
  5. Select the Save changes button. The modal closes.
  6. Scroll down and fill in the form above the Add custom field button:
    1. Name: "Has advanced analytics".
    2. Type: "Boolean".
    3. Source Field dropdown: Under API & Chargebee.js, select Has advanced analytics.
  7. Select the Add custom field button.
  8. Repeat for each custom field, then select the Save changes button.

2. Create an audience in Growth

  1. In Growth, navigate to People > Audiences.
  2. Create a new audience with the following rule (or rules that match your use case):
    1. Property: "Has advanced analytics" (or the label for your mapped field).
    2. Operator: "Is".
    3. Value: "false".
  3. Select the Done button to save the audience.

3. Create the offer in Growth

Define the entitlement-gated offer and how CTAs are handled.

  1. In Growth, navigate to Offers > In-app.
  2. Create a new offer with the following configuration:
    1. Offer objective: Expansion.
    2. Offer category: Subscription change.
    3. Under Button > Settings:
      1. Set Button processing to URL redirect.
      2. Set Button action > Choose button offer subcategory to the appropriate value. In this example, you may select Upgrade or Addon. This is for reporting purposes only and does not affect offer fulfillment.
    4. Select the < button and navigate to Button > Accept step.
      1. Set the Success action to Link.
      2. Set Button action > Link destination to URL and in the text box, enter the URL you will redirect the user to after they accept the offer.
    5. Select Done and then Publish now to save the offer.
    6. In the right pane, set the offer title, description, and button labels (for example, "Unlock Advanced Analytics" and "Unlock now").
    7. Select Done and then Publish now to save the offer.

4. Create a play in Growth

  1. In Growth, navigate to Plays > Expansion (or Acquisition, as appropriate).
  2. Create a new play.
  3. Select the in-app offer play type.
  4. Select the audience created in the previous step.
  5. For Trigger, select Any page load.
  6. For Action, select Show offer.
  7. Select the offer created in the previous step.
  8. Set the play priority.
  9. Select the Publish Play button.
Was this tutorial helpful ?
Need more help?

We're always happy to help you with any questions you might have! Click here to reach out to us.