> ## Documentation Index
> Fetch the complete documentation index at: https://docs.zbdpay.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Increasing Limits with MMP Data

> Grow per-player withdrawal limits from real monetization signals in your MMP — with or without a backend of your own.

The withdrawal limit is the ceiling on what a player can cash out, so it's also the ceiling on how engaging your rewards can be. The time-based accrual described in [Withdrawal Limits (Server)](/earn/sdk/revenue-based-earning) is a rough proxy for revenue; tying limits to **actual monetization signals from your MMP** (Singular, AppsFlyer, Adjust, and similar) is how you raise them meaningfully.

Done this way the pool is self-funding: players who generate revenue earn the right to withdraw more, and you never give away more than that player has produced.

<Note>
  It also makes rewards easy to size. If you know a player has plenty of headroom, you can confidently issue larger rewards for completing tasks, while players with a lower limit get smaller ones. This is one pattern rather than a requirement, but it keeps rewards self-balancing against revenue.
</Note>

## Raise the limit, not the balance

<Warning>
  **Never credit a player's rewards balance directly in response to a monetization event.** "+100 points for watching an ad" is effectively cash-for-task, and it's the pattern that runs into app store review problems.

  Use the monetization event to raise the **withdrawal limit** — the ceiling — and let players earn their **rewards balance** through genuine play: winning a match, completing a level, hitting a streak.
</Warning>

| Trigger                           | What it should change                               |
| --------------------------------- | --------------------------------------------------- |
| Monetization event (ad view, IAP) | **Withdrawal limit** — how much they *can* cash out |
| Genuine gameplay                  | **Rewards balance** — the points they actually earn |

This keeps your reward economy backed by real revenue while keeping the player-facing earning experience tied to play, not ads.

## Prerequisite: link the two IDs

Every approach below joins an MMP event to a ZBD player, so the two systems have to know about each other's identifiers. Do it in **both directions** — it's cheap, and it means the mapping works whichever ID ends up on the postback.

### 1. Give ZBD your player ID

Pass your own internal player identifier when you initialize the SDK, or set it later with `SetUserId`:

```csharp theme={null}
ZBDController.Instance.Init(userId, completion => { /* ... */ });
```

See [Integration](/earn/sdk/integration#initialize-the-sdk).

### 2. Give your MMP the ZBD player ID

Read the ZBD-side identifier with `Utils.GetUserId()` and set it on your MMP's SDK as a customer/user identifier or custom property, so it comes back on their postbacks:

```csharp theme={null}
using ZBD;

// After Init has succeeded — the ID isn't available before the player is registered
string zbdUserId = Utils.GetUserId();

if (!string.IsNullOrEmpty(zbdUserId))
{
    // Set it on your MMP's SDK — the exact call depends on the MMP.
    // AppsFlyer, Adjust and Singular each expose a customer user ID
    // and/or custom-data field for exactly this purpose.
    MyMmp.SetCustomerUserId(zbdUserId);
}
```

<Note>
  `Utils.GetUserId()` returns an empty string until the SDK has registered the player, so call it after `Init` succeeds rather than at startup. Check the exact field name against your MMP's own documentation — the concept is the same everywhere, but the method isn't.
</Note>

Without at least one of these links there's nothing to match an MMP event against, and none of the options below will work.

## Choose your integration

Three ways to get monetization data to ZBD, depending on what you have to build with:

<Tabs>
  <Tab title="Your backend (recommended)">
    The most capable option, and the only one that gives you full control over which events count and how much they're worth.

    <Steps>
      <Step title="Your MMP sends a postback">
        When a player monetizes — a rewarded ad view, a completed IAP — your MMP fires a server-to-server postback to your backend with the player identifier and the revenue amount.
      </Step>

      <Step title="Resolve the MMP user to a ZBD player">
        Look up the SDK user from the identifier in the postback, using the internal player ID you set at `Init`.

        *Example: MMP user `1234` maps to ZBD player `1234`, who just monetized \$1.00 from a rewarded ad.*
      </Step>

      <Step title="Decide how much of that revenue to grant">
        Pick a share of the revenue to convert into withdrawal headroom — 10–20% is the usual starting range. So \$1.00 monetized at 10% → \$0.10 of withdrawal limit.
      </Step>

      <Step title="Convert to your reward currency">
        Withdrawal limits are denominated in your reward currency, so convert the dollar figure using your app's configured point value. There's no live conversion endpoint yet, but the value is stable enough to hardcode.

        For example, at a point value of \$0.001, \$0.10 becomes **100 points**.

        <Note>
          The point value is configured per app — confirm yours with your ZBD Customer Success Manager rather than assuming a rate. See [Reward Economics](/earn/sdk/reward-economics).
        </Note>
      </Step>

      <Step title="Increase the withdrawal limit">
        Call the withdrawal limit endpoint with the converted amount. See [Withdrawal Limits (Server)](/earn/sdk/revenue-based-earning#increasing-limits-based-on-actual-revenue) for the full request.

        ```bash theme={null}
        curl --request POST \
          --url https://api.zbdpay.com/api/v1/rewards/user/<rewardsUserId>/withdrawal-limit \
          --header 'Content-Type: application/json' \
          --header 'x-api-key: <x-api-key>' \
          --data '{
            "amount": 100,
            "currency": "POINT"
          }'
        ```
      </Step>
    </Steps>
  </Tab>

  <Tab title="MMP posts to ZBD">
    No backend? Your MMP can post directly to ZBD instead. Your Customer Success Manager provides a postback URL that you add to your MMP's configuration, and ZBD raises the limit when events arrive.

    <Warning>
      **Coverage varies by MMP, and it's rarely complete.** Not every MMP supports custom server-to-server postbacks to a third party, and those that do often won't send every event type. With AppsFlyer, for example, we've been able to receive in-app purchase events but **not** ad-revenue events — which is usually the larger share of a rewarded game's revenue.

      Check what your MMP can actually send before relying on this. If ad revenue can't be forwarded, you'll be raising limits from a fraction of the real signal.
    </Warning>

    Ask your Customer Success Manager for a postback URL and share which MMP you use — the event coverage and the exact setup depend on it.
  </Tab>

  <Tab title="Periodic file upload">
    The fallback when you have neither a backend nor usable MMP postbacks: send ZBD a **CSV or JSON file** of players who monetized, and we raise their withdrawal limits in batch on a schedule that suits you.

    Each row needs the player's internal ID (the one you set at `Init`) and the amount of revenue — or the limit increase you want applied. Agree the format and cadence with your Customer Success Manager.

    It's the least responsive option, since limits move on your upload schedule rather than in real time, but it's a legitimate way to keep limits tied to real revenue without building anything.
  </Tab>
</Tabs>

## Sizing rewards to available headroom

Once limits track revenue, you can check a player's state before issuing a reward and scale it to what they can actually withdraw. Read it wherever you send rewards from:

<Tabs>
  <Tab title="From your game (client)">
    Most games send rewards client-side, so check the player's state with `GetBalance` in Unity:

    ```csharp theme={null}
    ZBDController.Instance.GetBalance(callback =>
    {
        if (!callback.success) return;

        long balance = callback.balance;                 // what they've earned
        long headroom = callback.withdrawalLimit;        // what they can withdraw right now

        // Size the reward to the headroom available
        int reward = headroom > balance ? largeReward : smallReward;
        ZBDController.Instance.SendReward(reward, result => { /* ... */ });
    });
    ```

    See [Player Balance & Limits](/earn/sdk/user-balance) for the full call, including maintenance handling.
  </Tab>

  <Tab title="From your server">
    If you deliver rewards server-side, read the same two values from the API before issuing one:

    ```bash theme={null}
    curl --request GET \
      --url https://api.zbdpay.com/api/v2/rewards/users/<rewardsUserId>/balance \
      --header 'x-api-key: <x-api-key>'
    ```

    ```json theme={null}
    {
      "success": true,
      "message": "Success.",
      "data": {
        "withdrawableLimit": 6835,
        "rewardsBalance": 9324,
        "currency": "POINT"
      }
    }
    ```

    <Note>
      The field names differ between the two: the client returns `balance` and `withdrawalLimit`, the API returns `rewardsBalance` and `withdrawableLimit`. Same two values.
    </Note>
  </Tab>
</Tabs>

Either way you learn two things: what the player currently has, and what they're allowed to cash out. Players with more headroom can be given larger rewards, because you know the revenue is there to back them.

Keeping the two roughly aligned matters — a player who can see a large balance they can't withdraw disengages. See [Best Practices](/earn/sdk/best-practices).
