Features
RTP management
RTP (return to player) is the share of the stakes that the games pay back over time. The platform lets you steer it between 60 % and 100 %: for the whole project and, optionally, for each player. Use this page to understand which level a round gets and to manage player levels over the API.
How a round gets its RTP#
The level is resolved before the debit, inside the same transaction as the paid round, and then stays fixed for that round. A change applies from the next round; started rounds finish at their own level.
- Default. A project without saved RTP settings plays at 90 % (the provider RTP). The games' natural mathematics is used only if an owner explicitly saves "no provider RTP".
- Provider RTP set, personal RTP off. Every player's rounds are played at the project's provider RTP. Manual player levels are ignored.
- Personal RTP on. Each player has their own level (below).
Free-spin rounds go through the same logic and count in the adaptive window with their nominal bet. Target-outcome rounds are marked and do not count in the window.
Project settings#
Set in the admin panel under RTP & players (the platform owner edits the project settings; see Admin).
| Setting | Meaning |
|---|---|
| Provider RTP | The project's RTP, 60–100 %. Default 90 %. It is the ceiling for automatic levels and the starting level of each new player. Already registered players keep their start. |
| Personal RTP | Switches on per-player levels. Off means every player plays at the provider RTP. |
| Adaptive rule | Optional automatic correction of a player's level (below). |
| Bet limits per game | Minimum and maximum bet per game and currency (Bet limits). |
Personal RTP of a player#
- A new player is bound at their first round to the provider RTP that is current at that moment (start level = current level).
- The level can be changed manually (admin panel or API, 60–100 %, up to two decimals, with a reason kept in the player's journal). A manual level freezes the adaptive rule for that player until you return them to
auto. - In
automode the player plays at their current level, capped by the provider RTP at the time. If the provider RTP is lowered below a player's level, the player plays at the provider RTP. - A manual level is not capped by the provider RTP.
- The journal of every player records who changed the level, when and why (binding, rule steps, manual changes).
Adaptive rule#
The rule watches the realised return of a player's last settled rounds. If a player is too lucky it lowers their level a little; when the luck ends it brings them back, but never above their starting level.
| Parameter | Meaning | Default |
|---|---|---|
| Window, rounds | How many recent settled rounds are examined (50–100 000). | 1000 |
| Lower if player RTP is above, % | Window RTP above this lowers the level. | 150 % |
| Restore if below, % | Window RTP below this raises the level back. | 90 % |
| Step, p.p. | Change per step (0.1–20 percentage points). | 2 p.p. |
| Not below start minus, p.p. | The level never drops further than this below the player's start (0–40). | 6 p.p. |
| Minimum turnover | Per currency: the rule ignores windows with a smaller total stake. | none |
At most one step is made per window. The level never goes below 60 %. Players with a manual level are not touched.
On short samples the observed RTP can differ from the target; this is normal.
POST /playerRtp.do#
Manage a player's level and personal bet limits. Every action needs partner, api_key and an allowlisted IP. The request is not idempotency-key aware and is not rate limited. Common fields:
| Field | Type | Required | Description |
|---|---|---|---|
partner, api_key | string | yes | Your project id and API key. |
action | enum | yes | get, set, auto or bet-limit. |
player_id | string | yes | The player. Only letters, digits and _ . : @ -, 1–128 characters. This is stricter than launch: a player id with other characters can play but cannot be managed here. |
provider | enum | no | endorphina, pragmatic or nolimitcity. Default endorphina, so pass it explicitly. |
Every action answers {"status": "ok", "player": {…}}; the player object is described under get.
Errors that apply to every action: INVALID_ACTION (400) for an unknown action, INVALID_PLAYER (400) for a player_id outside the allowed characters, RTP_PROVIDER_UNSUPPORTED (400) for another provider.
get#
/playerRtp.doactiongetReturns a player's state for one provider.
Request#
curl -s -X POST https://notell-api.com/playerRtp.do \
-H 'Content-Type: application/json' \
-d '{"partner":"demo-casino","api_key":"YOUR_API_KEY","action":"get","player_id":"player-1024","provider":"pragmatic"}'{
"partner": "demo-casino",
"api_key": "YOUR_API_KEY",
"action": "get",
"player_id": "player-1024",
"provider": "pragmatic"
}The common fields above are the only ones.
Response#
{
"status": "ok",
"player": {
"partner": "demo-casino",
"player": "player-1024",
"nickname": null,
"bound": true,
"provider": "pragmatic",
"startBps": 9000,
"currentBps": 8800,
"mode": "auto",
"roundsSinceAdjust": 120,
"providerBps": 9000,
"effective": {"bps": 8800, "source": "player"},
"betLimits": [{"currency": "EUR", "minMinor": "10", "maxMinor": "5000"}],
"journal": [
{
"provider": "pragmatic",
"actor": "bind",
"reason": "bound before the first round by a level change",
"fromBps": null,
"toBps": 9000,
"fromMode": null,
"toMode": "auto",
"windowRounds": null,
"windowRtpPpm": null,
"at": "2027-02-15T10:00:00.000Z"
}
],
"providers": [
{"provider": "pragmatic", "startBps": 9000, "currentBps": 8800, "mode": "auto", "roundsSinceAdjust": 120, "effective": {"bps": 8800, "source": "player"}, "updatedAt": "2027-02-15T10:00:00.000Z"}
]
}
}| Field | Type | Description |
|---|---|---|
player.partner, player.player | string | Your project id and the player id. |
player.nickname | string or null | Nickname of a player of the platform's own site, otherwise null. |
player.bound | boolean | The player already has a level for this provider. When false, startBps and currentBps are 0 and effective is null. |
player.provider | string | The provider this view is for. |
player.startBps, player.currentBps | integer | Start and current level in basis points (9450 = 94.50 %). |
player.mode | enum | auto or manual. |
player.roundsSinceAdjust | integer | Rounds since the last rule step. |
player.providerBps | integer or null | The project's provider RTP in basis points, or null if the project has no provider RTP. |
player.effective | object or null | {bps, source}: the level that applies if personal RTP is on. source is player, provider-cap (the provider RTP caps the level) or manual. |
player.betLimits | array<object> | The player's limits per currency, {currency, minMinor, maxMinor} in minor units (strings). |
player.journal | array<object> | Latest changes, newest first, up to 100. |
player.providers | array<object> | The player's rows for each provider that has one. |
Errors#
| Code | HTTP | When |
|---|---|---|
INVALID_PLAYER | 400 | player_id is missing or has characters outside A-Z a-z 0-9 _ . : @ -. |
RTP_PROVIDER_UNSUPPORTED | 400 | provider is not one of the three providers. |
set#
/playerRtp.doactionsetSets a manual level (mode manual). The player is bound to the provider on first use.
Request#
curl -s -X POST https://notell-api.com/playerRtp.do \
-H 'Content-Type: application/json' \
-d '{"partner":"demo-casino","api_key":"YOUR_API_KEY","action":"set","player_id":"player-1024","provider":"pragmatic","rtp":94.5,"reason":"VIP agreement"}'{
"partner": "demo-casino",
"api_key": "YOUR_API_KEY",
"action": "set",
"player_id": "player-1024",
"provider": "pragmatic",
"rtp": 94.5,
"reason": "VIP agreement"
}| Field | Type | Required | Description |
|---|---|---|---|
rtp | number | yes | Percent, 60–100, at most 2 decimals, as a JSON number (94.5, not a string). |
reason | string | no | Up to 200 characters, kept in the journal. Default: manual change. |
Response#
{"status": "ok", "player": {…}} with mode: "manual", currentBps: 9450 and effective.source: "manual". The object is described under get.
{
"status": "ok",
"player": {
"partner": "demo-casino",
"player": "player-1024",
"nickname": null,
"bound": true,
"provider": "pragmatic",
"startBps": 9000,
"currentBps": 9450,
"mode": "manual",
"roundsSinceAdjust": 0,
"providerBps": 9000,
"effective": {"bps": 9450, "source": "manual"},
"betLimits": [],
"journal": [],
"providers": []
}
}(journal and providers are shortened here; the real answer lists them.)
Errors#
| Code | HTTP | When |
|---|---|---|
RTP_LEVEL_INVALID | 400 | rtp is not a number or has more than 2 decimals. |
RTP_LEVEL_OUT_OF_RANGE | 400 | rtp is below 60 or above 100. |
INVALID_REASON | 400 | reason is not a string of up to 200 characters. |
RTP_POLICY_REQUIRED | 400 | The project has no provider RTP (it was explicitly switched off). |
auto#
/playerRtp.doactionautoReturns the player to automatic mode: the adaptive rule resumes from the current level. The player is bound to the provider on first use.
Request#
curl -s -X POST https://notell-api.com/playerRtp.do \
-H 'Content-Type: application/json' \
-d '{"partner":"demo-casino","api_key":"YOUR_API_KEY","action":"auto","player_id":"player-1024","provider":"pragmatic","reason":"VIP period over"}'{
"partner": "demo-casino",
"api_key": "YOUR_API_KEY",
"action": "auto",
"player_id": "player-1024",
"provider": "pragmatic",
"reason": "VIP period over"
}| Field | Type | Required | Description |
|---|---|---|---|
reason | string | no | Up to 200 characters, kept in the journal. Default: manual change. |
Response#
{"status": "ok", "player": {…}} with mode: "auto". The object is described under get.
Errors#
| Code | HTTP | When |
|---|---|---|
INVALID_REASON | 400 | reason is not a string of up to 200 characters. |
RTP_POLICY_REQUIRED | 400 | The project has no provider RTP (it was explicitly switched off). |
bet-limit#
/playerRtp.doactionbet-limitSets or removes a player's own bet limit for one currency. The limit applies together with the project limits; the smallest maximum wins (Bet limits).
Request#
curl -s -X POST https://notell-api.com/playerRtp.do \
-H 'Content-Type: application/json' \
-d '{"partner":"demo-casino","api_key":"YOUR_API_KEY","action":"bet-limit","player_id":"player-1024","provider":"pragmatic","currency":"EUR","min_minor":"10","max_minor":"5000"}'{
"partner": "demo-casino",
"api_key": "YOUR_API_KEY",
"action": "bet-limit",
"player_id": "player-1024",
"provider": "pragmatic",
"currency": "EUR",
"min_minor": "10",
"max_minor": "5000"
}| Field | Type | Required | Description |
|---|---|---|---|
currency | string | yes | ISO 4217 code, three capital letters. |
min_minor | string or null | yes | Minimum bet per spin in minor units, digits only, at least "1". "10" = 0.10. |
max_minor | string or null | yes | Maximum bet per spin in minor units, digits only, not below min_minor. "5000" = 50.00. |
Both limits are required: send both as strings, or both as null to remove the limit.
Response#
{"status": "ok", "player": {…}}; betLimits shows the new limit. The object is described under get.
Errors#
| Code | HTTP | When |
|---|---|---|
INVALID_CURRENCY | 400 | currency is not three capital letters. |
INVALID_BET_LIMIT | 400 | min_minor / max_minor are not both strings of digits (or both null), min_minor is below 1, or max_minor is below min_minor. |
See also#
- Bet limits: how player limits combine with project limits.
- Target outcomes and Free spins: how their rounds count.
- Admin: where project RTP settings and player levels are edited.