> ## 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.

# Error Handling

> How to handle every failure state the ZBD Earn SDK can return, and what your game should do in each case.

The SDK surfaces three types of failure: initialization failures, reward delivery failures, and maintenance mode. Each requires a different response. The guiding principle is the same in all cases: **never let a ZBD failure break your game.**

## Initialization failures

`Init` can fail for several reasons, and **when it fails the modal cannot be shown** — the rewards web app never loaded, so `ShowModal()` has nothing to display. That means the player-facing messaging is yours: read the failure reason off the `completion` object and surface your own message (or hide the rewards UI). Never fail silently, and never leave the player staring at a rewards button that does nothing.

```csharp theme={null}
ZBDController.Instance.Init(completion =>
{
    if (completion.success)
    {
        // SDK ready, proceed normally
    }
    else if (completion.maintenance)
    {
        // Not an error — ZBD is in maintenance. See Maintenance Mode section below.
        ShowMaintenanceMessage();
    }
    else if (completion.type == "region")
    {
        // Unsupported region — no retry path.
        // Hide all Earn UI/functionality, or show a "not available in your region" message.
        HideEarnUI();
    }
    else
    {
        // Init failed — the modal is unavailable, so show your own message
        // based on completion.error and let gameplay continue as normal.
        ShowRewardsUnavailableMessage(completion.error);
        Debug.LogWarning("ZBD init failed: " + completion.error);
    }
});
```

### Common init failure causes

| Cause | What happens | What to do |
| - | - | - |
| VPN / ad blocker / private DNS blocking device verification | `completion.success = false` | Show your own actionable message — ask the player to disable the blocker or VPN and try again. Note: VPN *policy* detection does not fail init — it places the player in limited earnings mode instead. See [Security & Fraud Prevention](/embedded-rewards/security). |
| No network connection | `completion.success = false` | Show your own message with a retry affordance, and retry `Init` when connectivity returns. Do not prevent gameplay. |
| Unsupported region | `completion.success = false`, `completion.type = "region"` | No retry path. Hide all Earn-related UI and functionality, or show a message that Earn isn't available in their region. See [Global Support](/embedded-rewards/coverage). |
| Attestation failure | `completion.success = false` | Show your own message. Indicates a potentially modified device or app — keep the wording generic rather than accusatory. |
| ZBD maintenance | `completion.maintenance = true` | See [Maintenance Mode](#maintenance-mode) below. |

<Warning>
  Do not tie SDK initialization to any core gameplay logic. If `Init` fails, the game must continue working normally. Players who cannot earn rewards should not be blocked from playing.
</Warning>

<Note>
  `ShowModal()` is only useful once `Init` has succeeded. After a failed init the rewards web app isn't loaded, so calling it does nothing — which is why every init-failure row above puts the message in your hands.
</Note>

## Maintenance mode

The ZBD platform occasionally enters maintenance mode. This is surfaced as a `maintenance` flag on SDK responses. It is not an error.

```csharp theme={null}
public class ZBDInitResponse
{
    public bool success;
    public bool maintenance;
    public string userId;
    public string error;
    public string type;
}
```

Maintenance mode can begin at any time, including mid-session. Handle it on every SDK response, not just during initialization.

| Scenario | Correct behavior |
| - | - |
| `Init` returns `maintenance = true` | If the player has not started earning yet: hide all rewards-related UI and do not mention ZBD. If the player has already been earning: surface a message that rewards are temporarily unavailable. |
| `SendReward` returns `maintenance = true` | Silently skip the reward. Do not show an error to the player. |
| `GetBalance` returns `maintenance = true` | Do not update the displayed balance. Retry when the next natural trigger occurs. |

<Note>
  Maintenance periods are temporary. Do not log them as errors or alert your on-call. They are expected operational events.
</Note>

## Reward delivery failures

Reward delivery can fail independently of initialization. The most common causes are network interruptions and maintenance mode.

```csharp theme={null}
ZBDController.Instance.SendReward(amount, completion =>
{
    if (completion.success)
    {
        // Reward delivered, update UI
    }
    else if (completion.maintenance)
    {
        // Silently skip, do not surface to player
    }
    else
    {
        // Delivery failed, silently skip and optionally log
        Debug.LogWarning("Reward delivery failed: " + completion.error);
    }
});
```

<Warning>
  Do not show reward failure messages to players. A failed reward is invisible to the player. The gameplay moment should feel the same whether the reward was delivered or not.
</Warning>

## Full error scenario matrix

| Scenario | SDK response | Correct behavior |
| - | - | - |
| Init — VPN/ad-blocker interference | `success = false` | Your own message asking them to disable it and retry. Game continues. |
| Init — no network | `success = false` | Your own message; retry when connectivity returns. Game continues. |
| Init — unsupported region | `success = false`, `type = "region"` | Hide rewards UI (or show "not available in your region"). No retry path. |
| Init — maintenance | `maintenance = true` | Hide rewards UI for new players. Message existing earners. |
| Init — attestation failure | `success = false` | Your own generic message. Game continues. |
| App backgrounded mid-reward | n/a | Handle gracefully on foreground return. Do not retry automatically. |
| No internet during reward trigger | `success = false` | Skip or queue the reward. Do not crash. |
| `SendReward` — maintenance | `maintenance = true` | Silently skip. No player-facing message. |
| `SendReward` — network error | `success = false` | Silently skip. Log internally. |
| `GetBalance` — maintenance | `maintenance = true` | Do not update balance display. |
| iOS vs. Android behavior difference | Varies | Always test both platforms separately. SDK behavior can differ between them. |

## Platform differences

Test iOS and Android separately. The SDK's attestation behavior, WebView rendering, and modal presentation can differ between platforms in ways that are not always obvious during development.

Pay particular attention to:

* **Android back button**: Handle the back button to close the modal if it's open. See [Integration](/embedded-rewards/integration#android-back-button).
* **iOS foreground transitions**: The modal may need explicit handling when the app returns from background.
* **VPN detection**: VPN behavior differs between iOS and Android network stacks. A VPN that passes on one platform may be flagged on the other.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.