# Promotions

Promotion tools may not be available in all jurisdictions. Before implementing this functionality, please consult your RubyPlay account manager for further details.

The Gaming Platform (GP) can create campaigns with defined start and end dates (based on time zone), predefined players, games, currencies, campaign prices, and more.

## API Rate Limiting

The API implements a three-tier rate limiting system:

### Global Rate Limits

- **All methods** (GET, POST, PUT, PATCH, DELETE): 500 requests/second
- **GET methods only**: 1000 requests/second


### Per Endpoint Per User Rate Limits

- **Campaign participant addition** (`/campaign/*/participant` POST): 130 requests/second
- **Campaign creation** (`/campaign` POST): 60 requests/second
- **All other endpoints**: 60 requests/second


### Per Endpoint Rate Limits

- **Campaign participant addition** (`/campaign/*/participant` POST): 270 requests/second
- **Campaign creation** (`/campaign` POST): 135 requests/second


These limits represent general guidelines and may be further restricted in extreme cases to safeguard stable operations for all customers.

We recommend implementing dynamic throttling or retry mechanisms in your integration to gracefully handle rate limiting and related changes.

If you expect higher volumes, please contact support to discuss capacity planning.

## Promotion Limits and Recommendations

The following limits apply to campaign creation and management:

| Parameter | Limit | Notes |
|  --- | --- | --- |
| `name` | 255 characters | Campaign name |
| `externalCampaignId` | 512 characters | Operator-defined campaign ID |
| `requestId` | 36 characters | Idempotency key |
| `rulesUrl` | 2048 characters | URL with campaign rules |
| `betNumber` | 1500 | Maximum free rounds/features per player |
| Campaign duration | 712 days | Difference between `endDateTime` and `startDateTime` |
| Campaign end date | 742 days | Maximum period into the future for which `endDateTime` may be set |
| Players per request | 50,000 | Maximum number of player IDs per Add Player List request |
| CSV import (Back Office) | 50,000 rows / 5 MB | Maximum players per CSV import |


Exceeding any of these limits results in a `REQUEST_NOT_VALID` response. The corresponding messages
are listed on the [Campaign Validation Rules](/content/integration/public-api/promotions-api/validation-rules)
page and include the limit that was applied.

Recommendations
- Use the `requestId` parameter for idempotency to safely retry failed requests
- For large player lists, use the batch participant endpoint instead of individual additions
- Set realistic campaign durations - campaigns shorter than 24 hours are not typical business scenarios


## Brand-Level Configuration

As part of our promotional tools (Free Rounds and Awarded Features), we provide the following additional configurations related to what is displayed on pop-ups/counter. These options can be enabled or disabled on request at the brand level from the RubyPlay side:

* **Show/Hide Value of Promotion** (default: **Show**)
* **Show/Hide Final Winning Value** (default: **Show**)
* **Show/Hide Campaign/Feature Player Expiry Time** (default: **Hide**)
* **Show/Hide Play Later Button** (default: **Show**)
* **Show/Hide Bet Value (Awarded Feature only)** (default: **Hide**)
* **Show/Hide Opt-Out Button and Function** (default: **Hide**)


Opt-Out Function
The opt-out function is **not enabled by default**. In regulated markets if this needs to be handled on RubyPlay side, it should be requested.
Also note that no information is provided regarding this through integration.

## Type of Round Based on Debit/Credit Request Body

Described in the [Rubyplay Seamless Wallet API](/content/integration/seamless-wallet-api/general/overview#type-of-round-based-on-debitcredit-request-body).

## Important Note

The legacy URL format (`/api/v1/freeround/campaign/...`) is still supported for backward compatibility. However, we ****strongly recommend**** using the new unified format:

`/api/v1/campaign/...`

This ensures consistency across all campaign types and simplifies integration.

## Campaign Creation Flow

The following diagram illustrates the primary flow for creating and managing promotional campaigns:

```mermaid
sequenceDiagram
    participant Operator
    participant RubyPlay API
    participant Player

    Note over Operator,Player: Campaign Setup Phase
    Operator->>RubyPlay API: Get Games (retrieve available games and bet sizes)
    RubyPlay API-->>Operator: Game list with bets, buy feature multipliers

    Operator->>RubyPlay API: Create Campaign (type, games, bet, players, dates)
    RubyPlay API-->>Operator: Campaign created (campaignId)

    opt Add more participants
        Operator->>RubyPlay API: Add Participants (campaignId, playerIds)
        RubyPlay API-->>Operator: Participants added
    end

    Note over Operator,Player: Campaign Active Phase
    Player->>RubyPlay API: Launch game
    RubyPlay API-->>Player: Display promotion popup
    Player->>RubyPlay API: Accept promotion

    loop For each free round/feature
        Player->>RubyPlay API: Play round
        RubyPlay API->>Operator: Debit request (freeRound=true)
        Operator-->>RubyPlay API: Debit response
        RubyPlay API->>Operator: Credit request (winnings)
        Operator-->>RubyPlay API: Credit response
        RubyPlay API-->>Player: Round result
    end

    RubyPlay API-->>Player: Promotion completed
```

## Free Round Campaign Creation

When creating a **Free Round** campaign, the `bet` value must represent any supported game bet.

Info
- **Configured bet** – any bet value from the game's configured bet list that player can choose from for regular gameplay. For example,`[0.5, 1, 1.5, 2]`
- **Supported bet** – any bet value that is technically supported by game. The lower bound is the game's minimum supported bet, the upper bound is the maximum game configured bet. For example, `[0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2]`


There are 3 approaches to determine the correct bet value.

### Approach 1: Using Free Round Configurations Endpoint (Recommended)

This is the simplest approach. The endpoint returns supported game bets that can be used directly.

```mermaid
sequenceDiagram
    participant Operator
    participant RubyPlay API

    Operator->>RubyPlay API: GET /api/v1/free-round-configurations<br/>?gameId=rp_102&currencyCode=EUR&betGte=0.1&betLte=10
    RubyPlay API-->>Operator: Available configurations with supported bets

    Note over Operator: Response example:<br/>{ "gameId": "rp_102", "bet": 1.0, "currencyCode": "EUR" }

    Operator->>RubyPlay API: POST /api/v1/campaign<br/>{ type: "FREE_ROUND", gameIds: ["rp_102"],<br/>  bet: 1, currencyCode: "EUR", ... }
    RubyPlay API-->>Operator: Campaign created
```

**Example Request:**

```bash
GET /api/v1/free-round-configurations?gameId=rp_102&currencyCode=EUR&betGte=0.1&betLte=10
```

**Example Response:**

```json
{
  "content": [
    { "gameId": "rp_102", "bet": 0.10, "currencyCode": "EUR" },
    { "gameId": "rp_102", "bet": 0.20, "currencyCode": "EUR" },
    { "gameId": "rp_102", "bet": 0.30, "currencyCode": "EUR" },
    { "gameId": "rp_102", "bet": 0.40, "currencyCode": "EUR" }
  ]
}
```

### Approach 2: Using Get Games Endpoint

The endpoint returns configured game bets that can be used directly.

```mermaid
sequenceDiagram
    participant Operator
    participant RubyPlay API

    Operator->>RubyPlay API: GET /api/v1/game?id=rp_102
    RubyPlay API-->>Operator: Game configuration

    Note over Operator: Response includes:<br/>betSizes: { EUR: [0.1, 0.2, 0.3, 0.4, 0.5] }

    Note over Operator: Use any value from betSizes

    Operator->>RubyPlay API: POST /api/v1/campaign<br/>{ type: "FREE_ROUND", gameIds: ["rp_102"],<br/>  bet: 0.1, currencyCode: "EUR", ... }
    RubyPlay API-->>Operator: Campaign created
```

### Approach 3: Using Pre-stored Information

If you have already stored game information (bet sizes) in your system, you can get bet directly without additional API calls.

```mermaid
sequenceDiagram
    participant Operator System
    participant Operator
    participant RubyPlay API

    Operator System-->>Operator: Retrieve stored game data<br/>(betSizes)

    Note over Operator: Stored data:<br/>rp_102: bets=[0.1, 0.2, 0.3, 0.4]

    Note over Operator: Use any value from bets

    Operator->>RubyPlay API: POST /api/v1/campaign<br/>{ type: "FREE_ROUND", gameIds: ["rp_102"],<br/>  bet: 0.1, currencyCode: "EUR", ... }
    RubyPlay API-->>Operator: Campaign created
```

## Awarded Feature Campaign Creation

When creating an **Awarded Feature** campaign, the `bet` value must represent the **total cost of the Buy Feature** (base bet × price multiplier), not the base bet alone.

There are 3 approaches to determine the correct bet value.

### Approach 1: Using Awarded Feature Configurations Endpoint (Recommended)

This is the simplest approach. The endpoint returns pre-calculated feature costs that can be used directly.

```mermaid
sequenceDiagram
    participant Operator
    participant RubyPlay API

    Operator->>RubyPlay API: GET /api/v1/awarded-feature-configurations<br/>?gameId=rp_140&currencyCode=EUR&betGte=5&betLte=100
    RubyPlay API-->>Operator: Available configurations with feature costs

    Note over Operator: Response example:<br/>{ "gameId": "rp_140", "bet": 50.0, "currencyCode": "EUR" }

    Operator->>RubyPlay API: POST /api/v1/campaign<br/>{ type: "AWARDED_FEATURE", gameIds: ["rp_140"],<br/>  bet: 50, currencyCode: "EUR", ... }
    RubyPlay API-->>Operator: Campaign created
```

**Example Request:**

```bash
GET /api/v1/awarded-feature-configurations?gameId=rp_140&currencyCode=EUR&betGte=5&betLte=100
```

**Example Response:**

```json
{
  "content": [
    { "gameId": "rp_140", "bet": 5.0, "currencyCode": "EUR" },
    { "gameId": "rp_140", "bet": 7.5, "currencyCode": "EUR" },
    { "gameId": "rp_140", "bet": 50.0, "currencyCode": "EUR" },
    { "gameId": "rp_140", "bet": 100.0, "currencyCode": "EUR" }
  ]
}
```

### Approach 2: Using Get Games Endpoint with Multiplier Calculation

If you prefer to calculate the feature cost yourself, retrieve the game's Buy Feature multiplier from the Get Games endpoint and multiply it by the base bet.

```mermaid
sequenceDiagram
    participant Operator
    participant RubyPlay API

    Operator->>RubyPlay API: GET /api/v1/game?id=rp_140
    RubyPlay API-->>Operator: Game configuration with buyFeatures

    Note over Operator: Response includes:<br/>buyFeatures: [{ buyFeatureType: "freespin",<br/>priceMultiplier: 50 }]<br/>betSizes: { EUR: [0.1, 0.2, 0.5, 1.0, 2.0] }

    Note over Operator: Calculate: 1.0 EUR × 50 = 50 EUR

    Operator->>RubyPlay API: POST /api/v1/campaign<br/>{ type: "AWARDED_FEATURE", gameIds: ["rp_140"],<br/>  bet: 50, currencyCode: "EUR", ... }
    RubyPlay API-->>Operator: Campaign created
```

**Calculation Formula:**

```
Feature Cost = Base Bet × Price Multiplier

Example: 1.0 EUR × 50 = 50 EUR
```

### Approach 3: Using Pre-stored Information

If you have already stored game information (bet sizes and multipliers) in your system, you can calculate the feature cost directly without additional API calls.

```mermaid
sequenceDiagram
    participant Operator System
    participant Operator
    participant RubyPlay API

    Operator System-->>Operator: Retrieve stored game data<br/>(betSizes, priceMultiplier)

    Note over Operator: Stored data:<br/>rp_140: multiplier=50, bets=[0.1, 0.5, 1.0, 2.0]

    Note over Operator: Calculate: 1.0 EUR × 50 = 50 EUR

    Operator->>RubyPlay API: POST /api/v1/campaign<br/>{ type: "AWARDED_FEATURE", gameIds: ["rp_140"],<br/>  bet: 50, currencyCode: "EUR", ... }
    RubyPlay API-->>Operator: Campaign created
```

Important
For **Awarded Feature** campaigns, always use the **total feature cost** (base bet × multiplier) in the `bet` field.

For example, if you want to award a feature with a 1 EUR base bet on a game with a 50× multiplier, the `bet` value should be **50 EUR**, not 1 EUR.