How to integrate your business with the ESCS

Why should you?

  • Integration with ESCS allows you to offer to your customers p2p wagering and competitions on any game connected to the ESCS.

  • ESCS automates the whole process and connects to your payment processing.

  • ESCS takes a small transaction fee for the service and gives 50% to the publisher.

Step 1 - Registration

  1. Register in the client.escs.io and create a game - as if you are a game publisher. Take a look here https://docs.escs.io/integration-step-1. Skip the part with scores and game modes.

  2. Generate a secret key and save it like shown below. Remember your public key and secret key. You will need them later.

  3. Add a payment method to your account. Take a look below.

Step 2 - oAuth preparations

  1. Login with your credentials from the client.escs.io to the account.escs.io and create and oAuth app, choosing ESCS as an oAuth provider like shown below.

Step 3 - API integration flow

The API endpoints

  1. For the integration you will need following API endpoints from the ESCS:

    1. Gives you a list of all the games available in the ESCS for you: https://api.escs.io/rpc/GetGames

    2. Gives you a list of available scores for the selected game: https://api.escs.io/rpc/GetGameScoreTypes

    3. Gives you a list of all available game modes for the selected game: https://api.escs.io/rpc/GetGameModes

    4. This is the endpoint to set the metadata: https://api.escs.io/rpc/SetPlayerGameMetadata

    5. This is the endpoint to create the wager: https://api.escs.io/rpc/CreateWagerTournament

    6. This is where you submit the wager data after the wager was completed: https://api.escs.io/rpc/CreateWagerTournamentBet

    7. This is the endpoint, where you get the players scores after they completed the game: https://api.escs.io/rpc/SubscribeToEvent

    You can find examples in our Postman: https://escs-postman.postman.co/workspace/Team-Workspace~9d567d98-0b92-43ca-8e23-5b0695b63bb3/collection/22121254-04dd1595-2295-4fa1-93c1-557f070a938e?action=share&creator=32809010

Getting games

Let your users select a game, game mode and score type. In general it is up to you how you present this part to your users – via the corresponding API endpoints ESCS provides you the catalog of games, game modes and scores. You can design the front-end part completely on your own or use some of the ESCS templates.

Getting a list of games. To get the list of the available games call this endpoint. https://api.escs.io/rpc/GetGames Use the following parameters.

export type RpcRequest<HandleInputDTO>;

export interface HandleInputDTO {
  context: AuthServiceContext;
  filters?: HandleInputDTOFilters;
}

export interface HandleInputDTOFilters {
  offset?: number;
  limit?: number;
}

export type AuthServiceContext = {
  type: AuthServiceContextTypeEnum.BY_GAME_SECRET_KEY;
  gameId: ObjectIdLike; //your game id from the Step 1
  secretKey: string; // your secret key from the Step 1
};

export const enum AuthServiceContextTypeEnum {
  BY_GAME_SECRET_KEY = 'BY_GAME_SECRET_KEY'
}

As a response you are getting a list of games.

export type RpcResponse<HandleOutputDTO>;

export interface HandleOutputDTO {
  count: number;
  limit?: number;
  offset: number;
  rows: GameModelProperties[];
}

export interface GameModelProperties {
  _id: ObjectIdLike; //Game ID
  name: string; //Game name
  description: string; //Description of the game
  logo?: string; //Logo of the game
}

The relevant parameters for you are _id, name, description and the logo. Use this data to display the list of games to your users. To display the logo take the file name from the response and attach it to https://storage.escs.io/file/ . Example: https://storage.escs.io/file/10b57d14-458a-4e60-9449-9b0d7e0827d0.

Getting game scores

After user chooses a game you can get a list of available scores for this game via a following endpoint: https://api.escs.io/rpc/GetGameScoreTypes For the request you really need only the gameId of the game selected by the user.

export type RpcRequest<HandleInputDTO>;

export interface HandleInputDTO {
  context?: AuthServiceContext;
  filters?: HandleInputDTOFilters;
}

export interface HandleInputDTOFilters {
  conditions?: HandleInputDTOFiltersConditions;
}

export interface HandleInputDTOFiltersConditions {
  gameId?: QueryCondition<ObjectIdLike>;//id of the game selected by the user
  status?: QueryCondition<GameScoreTypeModelStatusEnum>;
}

export const enum GameScoreTypeModelStatusEnum {
  ACTIVE = 'ACTIVE'
}

export type AuthServiceContext = {
  type: AuthServiceContextTypeEnum.BY_GAME_SECRET_KEY;
  gameId: ObjectIdLike; //your game id from the step 1
  secretKey: string; // your secret key from the step 1
};

export const enum AuthServiceContextTypeEnum {
  BY_GAME_SECRET_KEY = 'BY_GAME_SECRET_KEY'
}

As a response you are getting a list for the scores that are available in this game.

export type RpcResponse<HandleOutputDTO>;

export interface HandleOutputDTO {
  count: number;
  limit?: number;
  offset: number;
  rows: GameScoreTypeModelProperties[];
}

export interface GameScoreTypeModelProperties {
  _id: ObjectIdLike; //score id
  gameId: ObjectIdLike;
  identifier: string; //unique identifier
  name: string; //name of the score
  description: string; //description of the score
  sorting: -1 | 1; // -1 means lower values are better, 1 means higher values are better
}

To present scores to your users you just need the name and the description. When the score is selected you need the score id ("_id") to send to the ESCS later. The scores are set up by the game developer and this makes them self explanatory to users. Let your users choose one of the available scores.

Getting game modes

The list of the available game modes for this game you are getting via a following endpoint: https://api.escs.io/rpc/GetGameModes

To get the list of the game modes you just need a gameId of the game that the user selected.

export type RpcRequest<HandleInputDTO>;

export interface HandleInputDTO {
  context?: AuthServiceContext;
  filters?: HandleInputDTOFilters;
}

export interface HandleInputDTOFilters {
  offset?: number;
  limit?: number;
  conditions?: HandleInputDTOFiltersConditions;
}

export interface HandleInputDTOFiltersConditions {
  gameId?: QueryCondition<ObjectIdLike>; //id of the game selected by the user
  status?: QueryCondition<GameModeModelStatusEnum>;
}

export const enum GameModeModelStatusEnum {
  ACTIVE = 'ACTIVE'
}

export type AuthServiceContext = {
  type: AuthServiceContextTypeEnum.BY_GAME_SECRET_KEY;
  gameId: ObjectIdLike; //your game id from the step 1
  secretKey: string; // your secret key from the step 1
};

export const enum AuthServiceContextTypeEnum {
  BY_GAME_SECRET_KEY = 'BY_GAME_SECRET_KEY'
}

In the response you are getting a list of the game modes that the game developer set up for this game.

export type RpcResponse<HandleOutputDTO>;

export interface HandleOutputDTO {
  count: number;
  limit?: number;
  offset: number;
  rows: GameModeModelProperties[];
}

export interface GameModeModelProperties {
  _id: ObjectIdLike; //id of this game mode - you need this later
  gameId: ObjectIdLike;
  name: string; //name of the game mode
  description: string; //description of the game mode
  options: GameModeModelOptions; //important options
  type: GameModeModelTypeEnum;
}

export interface GameModeModelOptions {
  setParameters: string; //specific parameters - you dont need these
  maxSetDuration: number; //max duration of one game
  minParticipantsInSet: number; //min number of players in one game
  maxParticipantsInSet: number; //max number of players in one game
  penaltyPoints: number; //if the user does not finish the game for any reason, user gets penalty points
}

export const enum GameModeModelTypeEnum {
  SINGLE = 'SINGLE', //means in the game this is a single player game mode
  MULTIPLAYER = 'MULTIPLAYER' //means in the game this is a multiplayer game mode
}

Read more on the game modes here https://docs.escs.io/tournament-series/game-mode-and-scores. The game modes are set up by the game publishers and should be recognizable by the players. You can display just the name and the description of the game mode, or also additional parameters, like the minimal number of the participants in the game. Let the user select one of the game modes.

The important parameter for you is: maxParticipantsInSet - the maximum possible number of players in one game. The maxParticipantsInSet is the maximum number of the participants in the wager.

oAuth integration

After the user selected the game, game modes and scores for the wager and entered additional parameters that are required by your system, the user presses something like a button “Create wager”. After that you need to guide the user through the following steps.

Connect user’s account in your system to the ESCS account system - the following description resembles classic oAuth flow.

Present to a user a call to action “Please connect your [YOUR SERVICE NAME] account the ESCS account to proceed. Login or register to the ESCS on the next screen.” Redirect user as following

https://account.demo.escs.io/oauth?app_id={{OAUTH_APP_ID}}&code={{OAUTH_SECRET_KEY}}&redirect_url={{YOUR_REDIRECT_URL}}&service_name={{YOUR_SERVICE_NAME}}&scope=CURRENT_SESSION_INFO

//OAUTH_APP_ID: you oAuth app id from the Step 2
//OAUTH_SECRET_KEY: you secret key from the Step 2

//YOUR_REDIRECT_URL: url in your system where escs account system needs to redirect the user after the login

//YOUR_SERVICE_NAME: the name of your service that will be shown to the user

Player logs in or creates an escs account.

account.escs.io redirects the user back to the YOUR_REDIRECT_URL that was sent by you with a one time code that you will use to get the user’s information from the ESCS backend.

{{YOUR_REDIRECT_URL}}?result=success&code={{ONE_TIME_CODE}}

[/rpc/OAuthAuthorizationCodeGrant] Possible error codes:
- APPLICATION_NOT_FOUND
- HOSTNAME_NOT_ALLOWED
- ACCESS_DENIED
- INSUFFICIENT_PERMITS
- INTERNAL_APPLICATION_ERROR

Pass the one time code from the previous step to your backend and then get user’s ESCS id from the ESCS backend via the following 2 steps. While the user is waiting you should show a screen with a loader and something like “Please wait…”

// Back-end to back-end request
// Exchanging one-time code for authToken
POST https://oauth.demo.escs.io/rpc/OAuthAuthorizationCodeExchange

// =======================================================
// Request
interface RpcRequest {
  id: string;   // //any unique string
  params: HandleInputDTO;
}

interface HandleInputDTO {
  code: string; //the one-time code that you received
  application: {
    _id: string; //your oauth application id
    secretKey: string; //your secret key for your oauth app
  };
}

// =======================================================
// You will get the following response

type RpcResponse = {
  id: string;
  error: {
    code: string;
    params?: {
      [key: string]: any;
    };
    debug?: {
      [key: string]: any;
    };
  };
} | {
  id: string;
  result: HandleOutputDTO;
};

interface HandleOutputDTO {
  session: { //you need these tokens
    authToken: string; 
    refreshToken: string;
  };
}
// =======================================================

/*
Possible error codes:
- APPLICATION_NOT_FOUND
- CODE_NOT_FOUND
- CODE_EXPIRED
- ACCESS_DENIED
- INSUFFICIENT_PERMITS
- INTERNAL_APPLICATION_ERROR
*/

Take the auth token and send the request to receive the user ESCS id.

// Back-end to back-end request
// Get session info and user's id
POST https://oauth.demo.escs.io/rpc/GetCurrentSessionInfo

// =======================================================
// Request
interface RpcRequest {
  id: string;  //any unique string
  params: HandleInputDTO;
}

interface HandleInputDTO {
  context: AuthServiceContext;
}

interface AuthServiceContext {
  type: 'USER';
  authToken: string; //the token that you received
}


// =======================================================
// Response
type RpcResponse = {
  id: string;
  result: HandleOutputDTO;
};

interface HandleOutputDTO {
  session: {
    _id: string;
    userId: string; //this is the id that you need
  };
}

After you received user’s id (”userId” in the above) you need to save it in your system. You will match the scores based on this id later.

After the account is connected process the payment for the user. You can process the payment also before connecting the accounts - it is up to you.

Create a wager and set the metadata

Now create the wager and set the metadata in the ESCS system - we strongly recommend to do it only after the payment for the user has been successfully processed.

First, create the wager. Use this endpoint. https://api.escs.io/rpc/CreateWagerTournament

export type RpcRequest<HandleInputDTO>;

export interface HandleInputDTO {
  context: AuthServiceContext;
  wager: {
    gameId: ObjectIdLike; //id of the game selected by the user
    creatorEscsUserId: string; // ESCS user id that created the wager
    tournament: {
      name: string; //any name that you want
      description?: string; //optional description
      options: TournamentModelOptions; //this is a JSON - see description below
    };
  };
}

export type AuthServiceContext = {
  type: AuthServiceContextTypeEnum.BY_GAME_SECRET_KEY;
  gameId: ObjectIdLike; //your game id from the step 1
  secretKey: string; // your secret key from the step 1
};

export const enum AuthServiceContextTypeEnum {
  BY_GAME_SECRET_KEY = 'BY_GAME_SECRET_KEY'
}

As a parameter in the "options" above, use this JSON template. It is a template for the simple one match wager (kind of a bracket). Leave everything as it is and set only the following values (see also comments in the code).

  •  "gameModeId" - you need to enter here the ID of the selected game mode

  • "gameScoreType" - enter the selected score id
  • "maxNumberOfPlayersForTournament" - this value needs to be between the minPlayersInSet and playersInSet. Where playersInSet is equal to the value maxParticipantsInSet from the selected game mode. And the minPlayersInSet is equal to the minParticipantsInSet

  • "winnersOfTournaments" - you can set up here how to determine the winners of the wager. topPercent = 50 means that top 50% by tournament points will be considered winners. topNumerical = 5 means that top 5 players by tournament points will be considered winners. You can let the wager creator decide this. The standard parameter for most cases would be topPercent = 50.

  • "maxNumberOfSetsInMatch" - enter 1, 3, or 5 depending of whether you want a best of 1, best of 3, or best of 5 match.

  • "matchEndCondition": - calculate this value according to this formula :(playersInSet-1)*rounddown(maxNumberOfSetsInMatch/2+1)
    This gives basically the same result as if someone wins 3 sets in a best of five match. This value is in match points.

  • "matchPoints" - enter this value based on the formula playersInSet-1, playersInSet-2,...,0. Comma separated.

  • "tournamentPoints" - enter this value based on the formula playersInSet-1, playersInSet-2,...,0. Comma separated. 

  • "maxDuration" - set this parameter equal or higher than 
    maxNumberOfSetsInMatch * (waitTimeForPlayer + maxSetDuration + breakBetweenSets) - breakBetweenSets
    This is the maximum allowed duration of the match. 

  • iterationQualificationConditions - here you set up a key and value that will be used for the players metadata. In general any unique string for identifier and value - need to stay the same for the whole wager and all participants.
Simple N vs N Wager Template
 "name": "Wager tournament",
    "description": "test",
    "descriptionForPrizes": "",
    "additionalPrizePool": 0,
    "playersType": "singles",
    "matchmakingByScore": false,
    "options": {
        "maxNumberOfPlayersForTournament": 10, //this value needs to be between the minPlayersInSet and playersInSet
        "winnersOfTournaments": {
            "type": "topPercent", // enter topPercent or topNumerical here.
            "value": [
                {
                    "type": "VALUE",
                    "value": 50 //the value here means that top 50% are winners. If you choose topNumerical then a value 5 would mean that top 5 players by tournament points are winners
                }
            ]
        },
        "iterations": [
            {
                "gameModeId": "629513138202b00008dcec2c", //you need to enter here the ID of the selected game mode
                "gameScoreType": "61f934970f48ad00089c3a9c", //enter the selected score id
                "matchParameters": {
                    "defaultTemplate": "bestOfOne",// bestOfOne, bestOfThee, bestOfFive
                    "minPlayersInSet": 0,
                    "playersInSet": 0, //this value is provided by the selected game mode
                    "maxNumberOfSetsInMatch": 1, // Enter 1, 3, or 5 depending of whether you want a best of 1, best of 3, or best of 5 match.
                    "matchEndCondition": 1, //calculate this value according to this formula (playersInSet-1)*rounddown(maxNumberOfSetsInMatch/2+1), which gives basically the same result as if someone wins 3 sets in a best of five match. This value is in match points.
                    "matchPoints": "1,0",// enter this value based on the formula playersInSet-1,playersInSet-2,...,0. 
                    "tournamentPoints": "1,0", //enter this value based on the formula playersInSet-1,playersInSet-2,...,0
                    "maxSetDuration": 300,
                    "setPointsAggregate": "sum",
                    "notifyPlayerMinsBeforeMatchStart": 5,
                    "waitTimeForPlayer": 180,
                    "penaltyPoints": 200,
                    "modeType": "MULTIPLAYER",
                    "breakBetweenSets": 10,
                    "ingameGlobalMetadata": ""
                },
                "maxDuration": 480, // set this parameter equal to maxNumberOfSetsInMatch * (waitTimeForPlayer + maxSetDuration + breakBetweenSets) - breakBetweenSets
                "show": true,
                "drawMode": {
                    "type": "ALL"
                },
                "breakDurationAfterIteration": 60,
                "gamePrizesForMatchWinners": "",
                "exitConditions": {
                    "type": "runThisIterationXTimes",
                    "value": 1
                },
                iterationQualificationConditions: {										          
                    "type": "PLAYER_METADATA",						  
                    "gameId": "", //your game id from the step 1							  
                    "metadataCondition": "EQ",							  
                     "values": [							    
                       {		
//in general any unique string for key and value - need to stay the same for the whole wager					      
                        "value": "", //you can use just 1 as a value. Let's call it VALUE1							      
                        "condition": "EQ",							      
                        "identifier": "" //any unique string, let's call it STRING1					    
                       }							  
                     ]							
                },
                "winnersOfIterationConditions": {
                    "type": null, 
                    "value": null
                },
                "awaitMaxIterationDuration": false,
                "resetTournamentPoints": false,
                "iterationMode": "BRACKETS",
                "matchmakingByScore": false,
                "matchmakingAlgorithm": "random",
                "defaultMatchPoint": null,
                "defaultTournamentPoint": null,
                "groupByMetadata": false,
                "metadata": [],
                "metadataCondition": "eq",
                "winnersOfIterationSeriesCondition": {
                    "type": null,
                    "value": null
                }
            }
        ],
        "tournamentTemplate": "bestOfOne",
        "distributionOfIngamePrizes": null,
        "distributionOfPrizeMoney": null,
        "registrationEndBeforeStart": 0,
        "qualificationForTournaments": {
            "type": null,
            "value": null
        },
        "maxNumberIterationInTournament": null,
        "enableSignUpPhase": false,
        "signUpPhaseTime": 60,
        "showAdsOnImIn": false
    }

After you created the wager, you are getting the following response with a tournament id and a deep link.

export type RpcResponse<HandleOutputDTO>;

export interface HandleOutputDTO {
  tournament: number;
  deepLink: string;
}

You need to save both. The deep link will get users directly in to the game lobby to play their match. Before showing the deep link to any user, add ESCS user id to the link in the following manner. deepLink + %26userAccountId%3D{escsUserAccountId} Here is an example how it should look like: https://someurl/?escsplayer=custom-championships%2F%3FinviteCode%3D4IAIH0NF%26userAccountId%3D{escsUserAccountId} This will allow ESCS to offer a better user experience to the user.

After you succesfully created the wager, you need to set the metadata for the user - so that the user is able to participate in the wager.

https://api.escs.io/rpc/SetPlayerGameMetadata

export type RpcRequest<HandleInputDTO>;

export interface HandleInputDTO {
  context: AuthServiceContext;
  gameId: ObjectIdLike; //your game id from the step 1
  player: {
    _id: string; // ESCS user id 
    type: 'ESCS_ACCOUNT_USER_ID';
  };
  metadata: PlayerGameMetadataModelMetadata;
}

export interface PlayerGameMetadataModelMetadata {
  productIds: ObjectIdLike[]; //needs to stay empty
  params: { //needs to stay empty
    key: string;
    value: string | number;
  }[];
  qualification: { 
//in general any unique string for key and value - need to stay the same for the whole wager
    key: string; //needs to be the string STRING1 from the wager creation
    value: string | number; //the VALUE1
  }[];
}

export type AuthServiceContext = {
  type: AuthServiceContextTypeEnum.BY_GAME_SECRET_KEY;
  gameId: ObjectIdLike; //your game id from the step 1
  secretKey: string; // your secret key from the step 1
};

export const enum AuthServiceContextTypeEnum {
  BY_GAME_SECRET_KEY = 'BY_GAME_SECRET_KEY'
}

You get the response with data - means everything is fine. You do not need any of the data from the response.

Getting wager results

Now subscribe to the event to receive user’s scores after they finished to play the wager. https://api.escs.io/rpc/SubscribeToEvent

export type RpcRequest<HandleInputDTO>;

export interface HandleInputDTO {
  context?: AuthServiceContext;
  event: PubSubEventModelEvent;
  action: PubSubEventModelActionHttpRequest;
  lifetime?: number; // Subscription lifetime (in seconds) - any time period that you want to wait for the results. Something big.
}

export interface PubSubEventModelEvent {
  type: PubSubEventModelEventTypeEnum.TOURNAMENT_END;
  tournamentId: ObjectIdLike; //tournament id that you received when you created the wager
}

export interface PubSubEventModelActionHttpRequest {
  type: PubSubEventModelActionTypeEnum.HTTP_REQUEST;
  url: string; //your url where ESCS will send the data
}

export type AuthServiceContext = {
  type: AuthServiceContextTypeEnum.BY_GAME_SECRET_KEY;
  gameId: ObjectIdLike; //your game id from the step 1
  secretKey: string; // your secret key from the step 1
};

export const enum AuthServiceContextTypeEnum {
  BY_GAME_SECRET_KEY = 'BY_GAME_SECRET_KEY'
}

When the wager is finished you’ll get the following response - this is not the response to the request above, this is the data you receive after the tournament end, which is what you need.

interface EventData {
  eventType: 'TOURNAMENT_END';
  payload: {
    tournamentId: ObjectIdLike;
    results: {
      players: {
        _id: ObjectIdLike;
        escsUserId: string;
        isWinner: boolean;
        matchPoints?: number;
        tournamentPoints?: number;
      }[];
    }[];
  };
}

You need the EventData. Based on the escsUserId and isWinner you can match the user to the user in your system and distribute the winnings.

After the wager is created you should show to the user, that created the wager a link to invite other players to play. This link should lead to your service/website.

Joining new users to the wager

You need to guide any new user that joins the wager through the steps of connecting their account and you need to set the user’s metadata as described above.

Only after any new user paid for the wager and connected their account in your system to the ESCS account, show to the user the deep link to the wager as described above.

Sending the wager data to the escs

After the wager was successfully completed you need to call the following endpoint https://api.escs.io/rpc/CreateWagerTournamentBet

export type RpcRequest<HandleInputDTO>;

export interface HandleInputDTO {
  context: AuthServiceContext;
  bet: {
    tournamentId: ObjectIdLike;
    betAmount: number; // in cents 
	};
}

export type AuthServiceContext = {
  type: AuthServiceContextTypeEnum.BY_GAME_SECRET_KEY;
  gameId: ObjectIdLike;
  secretKey: string;
};

tournamentId is the id that you got when you created the wager. betAmount is the total amount of the wager (the sum of all user payments for this wager) in US dollar cents.

Now you are done. :-)

Last updated