# MoMoney — Offer Anatomy Guide

> LLM-optimised version of https://momoney.co/anatomy.html
> Last updated: 2026-03-31

The MoMoney API delivers targeted offers that you display to players at key moments — level completions, achievements, or natural pause points in gameplay. This guide explains the structure of each offer and how to use every response field to build compelling, correctly-tracked displays.

> **Important:** The `description`, `short_description`, and `terms_and_conditions` fields contain HTML markup. Always render these as HTML rather than plain text to preserve formatting.

> **Unity developers:** Unity's UI system (TextMeshPro, uGUI) does not natively render HTML. You'll need to parse and strip HTML tags, or use a third-party library like `HtmlAgilityPack` or `SimpleHTMLParser` to convert HTML to plain text or TextMeshPro rich text tags. See the Unity Integration section below for complete examples.

---

## Display Formats

The API supports any presentation format. These two patterns are common starting points:

**Embedded**
Renders inline within your game UI at a natural pause point. Blends into the existing layout and keeps the player in context while presenting the offer.

**Modal**
Overlays the game with a focused offer panel. Captures full attention at key moments — level completions, achievements, or natural pause points in gameplay.

You are not limited to either pattern — design the layout that best fits your game's UI.

---

## Embedded — Field Map

The following callouts (A–H) correspond to visual regions in a standard embedded layout:

| Label | API Field                                           | Description                                                                                                                                                                                                                        |
| ----- | --------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| A     | `offers[].image`                                    | Main creative image. See Creative Selection below for choosing the right size.                                                                                                                                                     |
| B     | `offers[].title`                                    | Primary headline (max 90 chars). Use `offers[].short_headline` for smaller layouts.                                                                                                                                                |
| C     | `offers[].description`                              | Full offer description in HTML (max 220 chars). Use `offers[].short_description` for constrained spaces. **Render as HTML.**                                                                                                       |
| D     | `offers[].click_url` + `offers[].cta_yes`           | Positive CTA — open `click_url` when clicked. Button label from `cta_yes` (max 25 chars).                                                                                                                                          |
| E     | `offers[].click_url`                                | **Save For Later** — use `click_url` and append `&save_for_later=1`. Open in webview. **Requires `pub_user_id` in API request.** Unity: Use a third-party webview plugin like UniWebView or Vuplex. See Unity Integration section. |
| F     | `offers[].beacons.close`                            | Close/dismiss beacon — fire a GET when the player closes the offer without choosing yes or no.                                                                                                                                     |
| G     | `settings.offerwall_url` + `settings.offerwall_cta` | Redirects to the Perkswall (all offers). Button label from `offerwall_cta`.                                                                                                                                                        |
| H     | `offers[].terms_and_conditions`                     | Legal terms in HTML (typically an unordered list). May be empty — if so, assume no specific terms apply. **Render as HTML.**                                                                                                       |
| I     | `offers[].mini_text`                                | Short legal disclaimer text (max 160 chars, plain text). Display below CTAs or near offer image. May be empty.                                                                                                                     |

---

## Modal — Field Map

The modal format uses the same offer fields as the embedded layout. The lettered annotations map each field to its visual position in the overlay.

---

## Field Reference

### Text Fields

| Field                  | Format     | Max       | Ideal     | Notes                                   |
| ---------------------- | ---------- | --------- | --------- | --------------------------------------- |
| `title`                | Plain text | 90 chars  | 40 chars  | Full-size headline                      |
| `short_headline`       | Plain text | 60 chars  | 40 chars  | Use in compact layouts                  |
| `description`          | HTML       | 220 chars | 140 chars | Render as HTML                          |
| `short_description`    | HTML       | 140 chars | —         | Use in compact layouts. Render as HTML. |
| `mini_text`            | Plain text | 160 chars | —         | Legal disclaimers shown below the CTA   |
| `cta_yes`              | Plain text | 25 chars  | —         | Accept button label                     |
| `cta_no`               | Plain text | 25 chars  | —         | Dismiss button label                    |
| `terms_and_conditions` | HTML       | —         | —         | May be empty. Render as HTML.           |

For implementation details on tracking pixels, beacons, and URL handling, see the Tracking & Actions section below.

---

## Tracking & Actions

The MoMoney API includes several tracking URLs and action fields that you must implement to ensure proper attribution, reporting, and user experience. This section explains what each field does and when to use it.

### Tracking Pixel (Required)

Every offer contains a `pixel` URL that **must** be fired as an HTTP GET request as soon as the offer becomes visible to the player. This is the primary signal for impression attribution, campaign optimisation, and revenue reporting.

> **Critical:** Fire the pixel **once per impression**, immediately on display. Firing it multiple times causes duplicate impression data. Never delay or batch pixel firing.

### Click URL (Required)

When a player taps the accept CTA (e.g., "Claim Offer"), open `offers[].click_url` in the device's default browser or in-app browser. This redirects the player to the advertiser's landing page and tracks the click event.

### Beacons (Optional but Recommended)

Beacons are simple GET requests that track player interactions. Fire these to improve campaign optimisation and reporting accuracy:

- `beacons.no_thanks_click` — Fire when the player taps the dismiss CTA (e.g., "No Thanks")
- `beacons.close` — Fire when the player closes the offer panel without clicking any CTA

### Offerwall URL (Optional)

If you display a "See All Offers" or similar CTA, open `settings.offerwall_url` in the device's default browser. This redirects to the Perkswall where players can browse all available offers.

> **Platform-specific examples:** See the Platform-Specific Integration Guides section below for complete implementation examples for Unity, React Native, and other game engines.

---

## Creative Selection

Each offer includes a primary `image` URL plus a `creatives` array with multiple variants at different sizes and aspect ratios.

### Choosing a creative

| Scenario                   | Strategy                                                  |
| -------------------------- | --------------------------------------------------------- |
| Default / fallback         | Use the creative where `is_primary: true`                 |
| Square layout (tile, icon) | Find creative with `aspect_ratio` close to `1:1`          |
| Portrait / mobile card     | Find creative where `aspect_ratio < 1` (taller than wide) |
| Landscape / banner         | Find creative where `aspect_ratio > 1` (wider than tall)  |

### On-the-fly image resizing

Append query parameters to any image URL:

| Parameter           | Example                       | Effect                                      |
| ------------------- | ----------------------------- | ------------------------------------------- |
| `?width=N`          | `image.jpg?width=300`         | Resize to 300px wide, maintain aspect ratio |
| `?height=N`         | `image.jpg?height=200`        | Resize to 200px tall, maintain aspect ratio |
| `?aspect_ratio=W:H` | `image.jpg?aspect_ratio=16:9` | Crop and resize to the given aspect ratio   |

---

## Sample API Response

```json
{
  "data": {
    "session_id": "sess_abc123",
    "count": 3,
    "privacy_url": "https://example.com/privacy",
    "settings": {
      "offerwall_url": "https://track.example.com/offerwall",
      "offerwall_cta": "See All Offers"
    },
    "offers": [
      {
        "id": 10542,
        "campaign_id": 3021,
        "advertiser_name": "StreamPlus",
        "title": "Get 3 Months Free StreamPlus!",
        "short_headline": "3 Months Free Streaming",
        "description": "<p>Sign up today and enjoy ad-free streaming on all your devices.</p>",
        "short_description": "<p>Premium streaming, on us.</p>",
        "click_url": "https://track.example.com/click/10542",
        "cta_yes": "Claim Offer",
        "cta_no": "No Thanks",
        "image": "https://cdn.example.com/offers/streamplus.jpg",
        "pixel": "https://track.example.com/impression/10542",
        "beacons": {
          "close": "https://track.example.com/close/10542",
          "no_thanks_click": "https://track.example.com/reject/10542"
        },
        "terms_and_conditions": "<ul><li>New subscribers only</li><li>Cancel anytime</li></ul>",
        "mini_text": "Terms apply. New subscribers only.",
        "creatives": [
          {
            "id": 8801,
            "url": "https://cdn.example.com/creatives/streamplus-banner.jpg",
            "width": 600,
            "height": 314,
            "type": "image",
            "is_primary": true,
            "aspect_ratio": "1.91:1"
          },
          {
            "id": 8802,
            "url": "https://cdn.example.com/creatives/streamplus-square.jpg",
            "width": 400,
            "height": 400,
            "type": "image",
            "is_primary": false,
            "aspect_ratio": "1:1"
          }
        ]
      }
    ]
  }
}
```

---

## Platform-Specific Integration Guides

This section provides platform-specific implementation examples for popular game engines and frameworks. Each guide covers tracking pixels, URL handling, beacons, and platform-specific considerations.

> **Don't see your platform?** We're continuously adding more platform guides. If you need help integrating MoMoney with a specific game engine or framework, contact us at hello@momoney.co and we'll provide guidance or add a dedicated guide.

### Unity (C#)

Unity requires special handling for several MoMoney integration tasks. This guide provides reference code examples for firing tracking pixels, opening URLs, firing beacons, and rendering HTML content.

#### Firing the Tracking Pixel

Every offer contains a `pixel` URL that must be fired as an HTTP GET request as soon as the offer becomes visible. This is critical for impression tracking and revenue attribution.

**Unity — Fire tracking pixel with error handling**

```csharp
// Fire immediately when the offer UI becomes visible
if (offer != null && !string.IsNullOrEmpty(offer.pixel) &&
    Uri.IsWellFormedUriString(offer.pixel, UriKind.Absolute))
{
    StartCoroutine(FirePixel(offer.pixel));
}

private IEnumerator FirePixel(string url)
{
    using (UnityWebRequest req = UnityWebRequest.Get(url))
    {
        req.timeout = 60; // seconds — prevents indefinite hangs

        yield return req.SendWebRequest();

        if (req.result != UnityWebRequest.Result.Success)
        {
            Debug.LogWarning($"Pixel fire failed: {req.error}");
        }
    }
}
```

#### Opening URLs in Unity

When a player taps any CTA (accept, Save For Later, etc.), you need to open the `click_url`. You can choose to open it in the device's default browser using Unity's built-in `Application.OpenURL()`, or in an in-app webview to keep players in your game.

Opening in a webview provides a better user experience (players stay in your game), but Unity does not include a built-in webview component—you'll need a third-party plugin.

> **Webview plugins for Unity:** If you want to open Save For Later URLs in an in-app webview, you'll need a third-party plugin such as:
>
> - **gree/unity-webview** — https://github.com/gree/unity-webview (free, iOS/Android) — used in the example below
> - **UniWebView** — https://uniwebview.com (paid, iOS/Android)
> - **Vuplex** — https://vuplex.com (paid, multi-platform)

**Unity — Opening click_url in browser (standard offer acceptance)**

```csharp
// When player taps "Claim Offer" or similar CTA
public void OnAcceptOfferClicked()
{
    if (offer != null && !string.IsNullOrEmpty(offer.click_url) &&
        Uri.IsWellFormedUriString(offer.click_url, UriKind.Absolute))
    {
        Application.OpenURL(offer.click_url);
    }
    else
    {
        Debug.LogWarning("Cannot open offer: click_url is missing");
    }
}
```

**Unity — Opening Save For Later URL in webview using gree/unity-webview**

```csharp
// Install: Package Manager → Add package from git URL:
// https://github.com/gree/unity-webview.git
//
// InAppBrowser is a thin wrapper around WebViewObject (gree/unity-webview) that
// creates a fullscreen webview with a navigation toolbar (Back, Forward, Refresh,
// Stop, Close) and handles Android back-button dismissal automatically.
// See the Display Example in the API Reference for full source code.

// When player taps "Save For Later"
public void OnSaveForLaterClicked()
{
    if (offer == null || string.IsNullOrEmpty(offer.click_url) ||
        !Uri.IsWellFormedUriString(offer.click_url, UriKind.Absolute))
    {
        Debug.LogWarning("Cannot open Save For Later: click_url is missing or invalid");
        return;
    }

    // Append save_for_later=1 — use ? or & depending on whether a query string exists
    string saveUrl = offer.click_url.Contains("?")
        ? offer.click_url + "&save_for_later=1"
        : offer.click_url + "?save_for_later=1";

    InAppBrowser.Open(saveUrl);
}
```

**Unity — Opening offerwall_url in browser**

```csharp
// When player taps "See All Offers" or offerwall CTA
public void OnOfferwallClicked()
{
    if (settings == null || string.IsNullOrEmpty(settings.offerwall_url) ||
        !Uri.IsWellFormedUriString(settings.offerwall_url, UriKind.Absolute))
    {
        Debug.LogWarning("Offerwall URL is missing or invalid");
        return;
    }

    Application.OpenURL(settings.offerwall_url);
}
```

#### Firing Beacons in Unity

Beacons are simple GET requests that track player actions. Fire `beacons.no_thanks_click` when the player dismisses an offer, and `beacons.close` when they close the offer panel without interacting.

**Unity — Firing beacons.no_thanks_click**

```csharp
// When player taps "No Thanks" or dismiss CTA
public void OnNoThanksClicked()
{
    if (offer?.beacons?.no_thanks_click != null &&
        Uri.IsWellFormedUriString(offer.beacons.no_thanks_click, UriKind.Absolute))
    {
        StartCoroutine(FireBeaconAndClose(offer.beacons.no_thanks_click));
    }
    else
    {
        CloseOfferPanel();
    }
}

private IEnumerator FireBeacon(string url)
{
    using (UnityWebRequest req = UnityWebRequest.Get(url))
    {
        req.timeout = 60; // seconds — prevents indefinite hangs

        yield return req.SendWebRequest();

        if (req.result != UnityWebRequest.Result.Success)
        {
            Debug.LogWarning($"Beacon fire failed: {req.error}");
        }
    }
}

private IEnumerator FireBeaconAndClose(string url)
{
    using (UnityWebRequest req = UnityWebRequest.Get(url))
    {
        req.timeout = 60;

        yield return req.SendWebRequest();

        if (req.result != UnityWebRequest.Result.Success)
        {
            Debug.LogWarning($"Beacon fire failed: {req.error}");
        }
    }

    CloseOfferPanel();
}
```

**Unity — Firing beacons.close**

```csharp
// When player closes the offer panel without clicking any CTA
public void OnOfferPanelClosed()
{
    if (offer?.beacons?.close != null &&
        Uri.IsWellFormedUriString(offer.beacons.close, UriKind.Absolute))
    {
        StartCoroutine(FireBeacon(offer.beacons.close));
    }
}
```

#### Rendering HTML Content in Unity

The `description`, `short_description`, and `terms_and_conditions` fields contain HTML markup. Unity's TextMeshPro and uGUI do not natively render HTML.

For most in-game UI, **stripping HTML tags or converting to TextMeshPro rich text** is the practical approach. Choose based on your layout needs:

| Approach                             | Best for                                                                                            | Implementation                                                                                                 |
| ------------------------------------ | --------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
| **Strip HTML tags**                  | Simple layouts with minimal formatting                                                              | Use regex or a parser to remove all HTML tags and display plain text                                           |
| **Convert to TextMeshPro rich text** | Preserving basic formatting (bold, italic, lists). Note: TMP does not support clickable hyperlinks. | Parse HTML and map tags to TMP rich text tags (`<b>`, `<i>`, etc.). Strip or convert `<a>` tags to plain text. |

**Unity — Strip HTML tags (simple approach)**

```csharp
using System.Text.RegularExpressions;
using System.Net;

private static readonly Regex AnchorTagRegex = new Regex(
    "<a\\b[^>]*>([\\s\\S]*?)</a>",
    RegexOptions.Compiled | RegexOptions.IgnoreCase);

private static readonly Regex HtmlTagRegex = new Regex(
    "<.*?>",
    RegexOptions.Compiled | RegexOptions.Singleline);

private static readonly Regex LineBreakRegex = new Regex(
    @"<(br|/p|/div|/li)\s*/?>",
    RegexOptions.Compiled | RegexOptions.IgnoreCase);

private static readonly Regex ScriptStyleRegex = new Regex(
    @"<(script|style)[^>]*>[\s\S]*?</\1>",
    RegexOptions.Compiled | RegexOptions.IgnoreCase);

private static readonly Regex WhitespaceRegex = new Regex(
    @"\s+",
    RegexOptions.Compiled);

public string StripHtml(string html)
{
    if (string.IsNullOrEmpty(html)) return "";

    // 1. Remove script/style content
    html = ScriptStyleRegex.Replace(html, "");

    // 2. Preserve line breaks
    html = LineBreakRegex.Replace(html, "\n");

    // 3. Handle anchors
    string stripped = AnchorTagRegex.Replace(html, "$1");

    // 4. Remove all remaining tags
    stripped = HtmlTagRegex.Replace(stripped, "");

    // 5. Decode entities
    stripped = WebUtility.HtmlDecode(stripped);

    // 6. Normalize whitespace
    stripped = WhitespaceRegex.Replace(stripped, " ");

    return stripped.Trim();
}

// Usage
if (offer != null && !string.IsNullOrEmpty(offer.description))
{
    descriptionText.text = StripHtml(offer.description);
}
```

**Unity — Convert HTML to TextMeshPro rich text (advanced)**

```csharp
using System.Text.RegularExpressions;
using System.Net;

private static readonly Regex BoldRegex = new Regex(
    "<strong\\b[^>]*>([\\s\\S]*?)</strong>",
    RegexOptions.Compiled | RegexOptions.IgnoreCase);

private static readonly Regex ItalicRegex = new Regex(
    "<em\\b[^>]*>([\\s\\S]*?)</em>",
    RegexOptions.Compiled | RegexOptions.IgnoreCase);

private static readonly Regex ParaRegex = new Regex(
    "<p\\b[^>]*>([\\s\\S]*?)</p>",
    RegexOptions.Compiled | RegexOptions.IgnoreCase);

private static readonly Regex ListRegex = new Regex(
    "<li\\b[^>]*>([\\s\\S]*?)</li>",
    RegexOptions.Compiled | RegexOptions.IgnoreCase);

private static readonly Regex AnchorRegex = new Regex(
    "<a\\b[^>]*>([\\s\\S]*?)</a>",
    RegexOptions.Compiled | RegexOptions.IgnoreCase);

private static readonly Regex BreakRegex = new Regex(
    "<br\\s*/?>",
    RegexOptions.Compiled | RegexOptions.IgnoreCase);

private static readonly Regex StripRegex = new Regex(
    "<.*?>",
    RegexOptions.Compiled | RegexOptions.Singleline);

private static readonly Regex MultiNewline = new Regex(
    "\\n{3,}",
    RegexOptions.Compiled);

private static readonly Regex MultiSpace = new Regex(
    "[ \\t]+",
    RegexOptions.Compiled);

public string HtmlToTMP(string html)
{
    if (string.IsNullOrEmpty(html)) return "";

    string tmp = html;

    // 1. Convert formatting
    tmp = BoldRegex.Replace(tmp, "<b>$1</b>");
    tmp = ItalicRegex.Replace(tmp, "<i>$1</i>");

    // 2. Structure
    tmp = ParaRegex.Replace(tmp, "$1\n");
    tmp = ListRegex.Replace(tmp, "• $1\n");
    tmp = BreakRegex.Replace(tmp, "\n");

    // 3. Anchors
    tmp = AnchorRegex.Replace(tmp, "$1");

    // 4. Strip remaining tags
    tmp = StripRegex.Replace(tmp, "");

    // 5. Decode entities
    tmp = WebUtility.HtmlDecode(tmp);

    // 6. Normalize whitespace
    tmp = MultiNewline.Replace(tmp, "\n\n");
    tmp = MultiSpace.Replace(tmp, " ");

    return tmp.Trim();
}

// Usage
if (offer != null && !string.IsNullOrEmpty(offer.description))
{
    descriptionText.text = HtmlToTMP(offer.description);
}
```

> **Tip:** For production use, consider using a dedicated HTML parsing library like **HtmlAgilityPack** (available via NuGet) for more robust HTML handling. The regex examples above work for simple cases but may not handle all edge cases.

#### PerksWallet Integration

The Save For Later button (Label E) is powered by PerksWallet. To enable this feature:

1. **Pass `pub_user_id` in every API request** — This unique identifier links the saved offer to the player's PerksWallet
2. **Append `&save_for_later=1` to `click_url`** when the player taps Save For Later
3. **Open the URL in a webview** (not the default browser) to keep players in your game

See the API Reference for `pub_user_id` parameter details: https://momoney.co/docs.html#request-body

### React Native

Use standard `fetch()` for pixels and beacons. Open URLs with `Linking.openURL()`. For Save For Later, use `react-native-webview`.

> **Need more details?** [Contact us](mailto:hello@momoney.co) and we'll provide a complete guide.

### Solar2D (Lua)

Use `network.request()` for pixels and beacons. Open URLs with `system.openURL()`. For webviews, use the native webview plugin.

> **Need more details?** [Contact us](mailto:hello@momoney.co) and we'll provide a complete guide.

### Godot (GDScript)

Use `HTTPRequest` nodes for pixels and beacons. Open URLs with `OS.shell_open()`. For webviews, use platform-specific plugins.

> **Need more details?** [Contact us](mailto:hello@momoney.co) and we'll provide a complete guide.

---

## See Also

- [API Reference (Markdown)](https://momoney.co/llms/docs.md) — endpoint, authentication, parameters, and platform examples
- Contact: hello@momoney.co
