Skip to content
Stove
Last Updated

Game Sanction

Understanding


When you need to block a specific user's game access due to cheating or the like, the operator can sanction that user in STOVE Partners.
Once a sanction is registered, the STOVE platform delivers a Kick event to the game server.
The game server can receive this event and immediately eject the sanctioned user from the game.

Behavior by Sanction Timing

Sanctions are applied differently depending on the user's connection state.

State Mobile PC
Connected (logged-in) state Can use normally (no sanction pop-up)
A sanction alert appears on re-login after token expiry
Can use normally (no sanction pop-up)
Pre-connection (not logged-in) state A sanction alert appears at login A sanction alert appears when the launcher starts the game

Mobile (a sanction alert appears at login)

PC (a sanction alert appears when the launcher starts the game)

In-game Kick handling is needed for connected users
ㅁ In the connected (logged-in) state, the game can't recognize the STOVE sanction status.
ㅁ When a sanction is registered, the game server must handle the Kick and the client must handle logout/exit itself.

Sanction Event Flow

Step Action
① Event delivery On sanction registration, STOVE delivers a Kick event to the URL registered for the game
② Game handling Notify of the sanction; handle the in-game Kick and STOVE Logout
③ Re-login On user re-login, the detailed sanction reason is shown in a pop-up

No separate event when a sanction is lifted
ㅁ The game only needs to handle the Kick for the sanctioned user.

Game-server environment prerequisites before integration
ㅁ We recommend an internal-network setup for the game API server, and an ACL setting is needed for STOVE ↔ game-server calls.
ㅁ If you have an Inbound ACL, allow the STOVE Event Broker's per-environment NAT IPs. (ask the STOVE technical contact for the IPs)

Development


Integration Overview

  • User-sanction (Kick event) handling flow

  • PC execution flow — checking sanction status for re-logging-in users

  • Mobile execution flow — checking sanction status for re-logging-in users

  • Additional verification flow for bypassing users

Callback API Configuration Guide

An endpoint that the CP company (developer) implements and operates directly; the STOVE Event Broker calls this URL when a Kick event occurs.


API endpoint

Item Details
URL Provided by the CP company (e.g., https://game.example.com/stove/kick)
Method POST
Protocol HTTPS (TLS 1.2 or higher required)
Content-Type application/json
Resend policy If no HTTP 200 response, the same event is resent up to 3 times

Request — Body specification

Name Type Required Description
event_reason String Y The reason the event occurred. For a game sanction, GAME_RESTRICT.
event_message String Y The event message.
event_time Long Y The event occurrence time (milliseconds).
target_users Long Array Y The list of user identifiers targeted for the Kick.

Sample — Request

bash
curl --location --request POST '{endpoint}' \
--header 'Content-Type: application/json' \
--data-raw '{
  "event_reason": "GAME_RESTRICT",
  "event_message": "Abnormal in-game trading behavior",
  "event_time": 1739145600000,
  "target_users": [ 20000000001, 20000000002, 20000000003, 20000000004, 20000000005 ]
}'

Sample — Response (Success)

http
HTTP/1.1 200 OK

{
  "code": 0,
  "message": "SUCCESS"
}

Sample — Response (Failure)

If the response code isn't 200, the STOVE Event Broker resends the same event up to 3 times.

http
HTTP/1.1 500 Internal Server Error

{
  "code": 500,
  "message": "FAILURE"
}

User Sanction-Info Lookup API

A lookup API called on the game server after verifying the user's token validity, to block bypass entry. You can check the user's sanction status with guid and game_id, and if sanctioned, the sanction code and sanction-period info are returned. (the sanction reason isn't returned)


Identifier terminology (TBD — to be moved to a separate guide page once finalized)

memberNo The unique identifier of a STOVE account. Only one is assigned per user. A game-independent global identifier.
guid A user identifier the platform issues per game. Even the same memberNo has a different guid per game.
Conceptually a mapping of memberNo + game_id → guid.
game_id The key that identifies the game (determined at Partners registration). Used as a path parameter of this API.

※ This definition is currently being organized, and once the official identifier guide page is ready, this box will be replaced with a link to that page.


API endpoint

Item Details
URL GET /mmember/v1.0/signin/game/status/{guid}/{game_id}
Host (Live) https://api.onstove.com
Host (Sandbox) https://api.gate8.com

Request — Header

Name Type Required Description
Content-Type application/json Y Resource media type.
Authorization String Y The platform authentication token. In the form Bearer {API AccessToken}.
caller-id String Y A header identifying the API caller. There's no fixed rule; the game server may define it freely and share it with STOVE in advance.
Used as an identifier for monitoring metrics (call volume, failures, tracing).
Example (recommended): {service_id}_SERVER, {service_id}_HOME
service_id is the Game ID registered in Partners. We recommend not changing the value once set.

Request — Path Params

Name Type Required Description
guid String Y Game User ID.
game_id String Y The game identifier (the Game ID registered in Partners).

Request — Query Params

Name Type Required Description
guid_yn String Y (guid game) For a guid game, Y is required.

Response — Body

Name Type Required Description
response_code int Y The response code (success: 0).
response_message String Y The response message (success: Success).
value.RESTRICT Object N Sanction info. Included only when sanctioned.

Response — Sanction info (value.RESTRICT)

Name Type Example Description
memberNo Long 1234 The user's unique number (guid).
nickname String AUTONICK##9515338 The character nickname.
banDay int 2 The remaining sanction period (days).
banCd String B026 The sanction code (defined in each game's master data).
banStartDt Long 1649053320000 The sanction start date (milliseconds).
banEndDt Long 1649312520000 The sanction end date (milliseconds).

Response — Response code (response_code)

All responses come back as HTTP 200, and you should branch on the response_code value to handle the business result.

Code Constant name Meaning / handling guide
0 PALMPLE_OK Normal response. If value.RESTRICT is present, sanctioned; if absent, no sanction.
10003 PALMPLE_ERR_NO_DATA No sanction info. Treat as a normal user.
10122 PALMPLE_ERR_BAN_MEMBER Sanctioned user. In-game Kick and STOVE Logout handling required.
90001 AUTH_ACCESS_TOKEN_INVALID Token verification failed. Check that the caller-id/Authorization headers or the path's guid/game_id match, then retry.

Sample — Request

bash
curl --location 'https://api.gate8.com/mmember/v1.0/signin/game/status/12345/{game_id}?guid_yn=Y' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {API accessToken}' \
--header 'caller-id: {caller-id}'

Sample — Response (no sanction)

json
{
  "response_code": 0,
  "response_message": "Success"
}

Sample — Response (sanctioned)

json
{
  "response_code": 0,
  "response_message": "Success",
  "value": {
    "RESTRICT": {
      "memberNo": 1234,
      "nickname": "AUTONICK##9515338",
      "banDay": 2,
      "banCd": "B026",
      "banStartDt": 1649053320000,
      "banEndDt": 1649312520000
    }
  }
}

Troubleshooting

SituationCauseRecommended handling
Callback event not receivedThe Callback URL isn't registered or a wrong URL is registeredAsk the STOVE technical contact to re-check the registered URL
Callback event not receivedThe STOVE NAT IP is blocked by the ACL/firewallCheck that the per-environment NAT IP is in the Inbound allow list
The same event received 3 timesThe Callback API responds with something other than 200 (4xx/5xx/timeout)Guarantee a 200 response even when the handling logic throws (idempotent handling)
Lookup API response_code=90001The caller-id/Authorization header is missing or has a typoCheck that all three headers (Content-Type, Authorization, caller-id) are included
Lookup API response_code=90001The path's guid/game_id doesn't match the token's permissionsCheck the game_id permission when reissuing the API AccessToken
Duplicate Kick handling for one userMultiple Callback instances handle the same eventWe recommend dedup using the event_time+memberNo key
Kick persists even after the sanction is liftedLifting a sanction sends no separate event (expected behavior)Re-call the lookup API at game entry to check the latest status

Sample Code

This example is a reference implementation for the area the game server operates itself. Supplement the dependencies, exception handling, and logging to fit your actual environment.


1) Receiving the Callback API — Kick event handler

java
@RestController
@RequestMapping("/stove")
public class StoveKickController {

    private final UserKickService kickService;

    public StoveKickController(UserKickService kickService) {
        this.kickService = kickService;
    }

    /** The endpoint that receives the Kick event called by the STOVE Event Broker */
    @PostMapping(value = "/kick", consumes = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<Map<String, Object>> onKickEvent(@RequestBody KickEvent event) {
        if (!"GAME_RESTRICT".equals(event.getEventReason())) {
            // Respond 200 to unknown events to prevent a resend loop.
            return ResponseEntity.ok(Map.of("code", 0, "message", "IGNORED"));
        }

        kickService.kickAll(event.getTargetUsers(), event.getEventMessage(), event.getEventTime());

        // Normal receipt → return 200 OK (on a non-200 response, the STOVE Event Broker resends up to 3 times)
        return ResponseEntity.ok(Map.of("code", 0, "message", "SUCCESS"));
    }

    @Getter @Setter
    public static class KickEvent {
        @JsonProperty("event_reason")  private String eventReason;
        @JsonProperty("event_message") private String eventMessage;
        @JsonProperty("event_time")    private Long eventTime;
        @JsonProperty("target_users")  private List<Long> targetUsers;
    }
}

2) Looking up user sanction info — REST call

java
@Component
public class StoveBanInfoClient {

    private static final String HOST_LIVE    = "https://api.onstove.com";
    private static final String HOST_SANDBOX = "https://api.gate8.com";

    private final RestClient restClient;

    @Value("${stove.api.access-token}") private String apiAccessToken;
    @Value("${stove.caller-id}")        private String callerId;
    @Value("${stove.profile:live}")     private String profile;

    public StoveBanInfoClient(RestClient.Builder builder) {
        this.restClient = builder.build();
    }

    /** Look up sanction status. Returns Optional.empty() for a normal user. */
    public Optional<BanInfo> getBanInfo(String guid, String gameId) {
        String host = "sandbox".equalsIgnoreCase(profile) ? HOST_SANDBOX : HOST_LIVE;
        String url  = host + "/mmember/v1.0/signin/game/status/" + guid + "/" + gameId + "?guid_yn=Y";

        BanResponse res = restClient.get()
                .uri(url)
                .header(HttpHeaders.AUTHORIZATION, "Bearer " + apiAccessToken)
                .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                .header("caller-id", callerId)
                .retrieve()
                .body(BanResponse.class);

        if (res == null) return Optional.empty();

        switch (res.responseCode) {
            case 0:                                       // Normal
                return res.value != null ? Optional.ofNullable(res.value.RESTRICT) : Optional.empty();
            case 10003:                                   // No sanction info
                return Optional.empty();
            case 10122:                                   // Sanctioned user
                return res.value != null ? Optional.ofNullable(res.value.RESTRICT) : Optional.empty();
            case 90001:                                   // Token verification failed
                throw new IllegalStateException("STOVE accessToken / caller-id / path mismatch — check settings");
            default:
                throw new IllegalStateException("Unexpected response_code: " + res.responseCode);
        }
    }

    @Getter @Setter
    public static class BanResponse {
        @JsonProperty("response_code")    public int responseCode;
        @JsonProperty("response_message") public String responseMessage;
        public ValueBlock value;
    }

    @Getter @Setter
    public static class ValueBlock { public BanInfo RESTRICT; }

    @Getter @Setter
    public static class BanInfo {
        public Long memberNo;
        public String nickname;
        public int banDay;
        public String banCd;
        public Long banStartDt;
        public Long banEndDt;
    }
}

3) Usage flow at the game-entry point

java
public class GameEntryService {
    private final StoveBanInfoClient banClient;
    private final GameSessionService sessionService;

    public void onPlayerEnter(String guid, String gameId) {
        banClient.getBanInfo(guid, gameId).ifPresentOrElse(
            ban -> sessionService.kick(ban.memberNo, ban.banCd, ban.banEndDt),
            ()  -> sessionService.allow(guid)
        );
    }
}

Frequently Asked Questions



Q1. Can a connected user keep playing even after being sanctioned?
A. Yes; in the connected (logged-in) state on Mobile/PC, the STOVE platform's sanction status isn't recognized immediately.
Therefore, you must separately implement user-Kick handling on the game server side and logout or exit handling on the client side.
Configure a Callback API that receives Kick events, and handle the user immediately when an event is received.
Q2. Can I receive a separate event when a sanction is lifted?
A. No, no separate event is delivered when a sanction is lifted.
A Kick event is delivered only on sanction registration, and the game only needs to handle the Kick request for the sanctioned user.
Q3. What happens if the Callback API can't respond with HTTP 200?
A. If the HTTP response code isn't 200, the STOVE platform resends the same event 3 times by default.
When implementing the Callback API, be sure to respond with HTTP 200 on successful receipt.
Q4. How do I block sanctioned users who enter the game by bypassing the client?
A. At the game-entry point, after verifying the user's token validity, additionally call the sanction-status lookup API (GET /mmember/v1.0/signin/game/status/{guid}/{game_id})
to check the sanction status. If sanctioned, perform in-game Kick and STOVE Logout handling.
Client info is vulnerable to packet hijacking and tampering, so you must perform additional verification on the server side.
Q5. Can I also check the sanction reason in the user sanction-info lookup API?
A. No, the sanction reason isn't included in the response.
When sanctioned, the response includes the sanction code (banCd), sanction period (banStartDt, banEndDt), remaining sanction period (banDay), nickname (nickname), and member number (memberNo).
Q6. How do I register the Callback API URL?
A. The URL-registration menu within Partners is currently in preparation.
If you provide the Callback API URL info to the game technical contact, we'll register it through technical support.



Need to contact us directly? stove.developers@smilegate.com