Skip to content

Game Startup

All this info is accurate as of MCEDU v1.21.10. Also

Early Start

  • Sends a get request to https://client.discovery.minecraft-services.net/api/v1.0/discovery/MinecraftEdu/builds/1.21.10 for any new updates to the API links & the PlayFab TitleID, which is 6955F.
  • Sends a post request to https://login.minecrafteduservices.com/initialPacks. Sometimes it tells you to download stuff, othertimes it doesn't. If you're building a program for MCEDU, you can safely ignore this part. This request wants a correlation vector, which should be the same for all requests in a session. It's just a UUIDv4.
Click to view request/response structure
Request Body Response

{
  "build": 12110000,
  "clientVersion": 685,
  "correlationVector": "[insert Vector]",
  "displayVersion": "1.21.10",
  "locale": "en_US",
  "osVersion": "10.0.26100",
  "platform": "Windows Desktop Build (Win32)(x64)",
  "platformCategory": "desktop"
}
No response body.

Playfab API

  • It sends a post request to https://6955f.playfabapi.com/Client/LoginWithCustomID. The most important thing for auth is the custom ID. If you don't have one, make one up. All the examples I've seen start with MCPF then have a random mix of 32 numbers and capital letters. If it's your first time using a customID, set createAccount to true.
Click to view request/response structure
Request Response
  {
    "CreateAccount": null,
    "CustomId": "[insert customID]",
    "EncryptedRequest": null,
    "InfoRequestParameters": {
      "GetCharacterInventories": false,
      "GetCharacterList": false,
      "GetPlayerProfile": true,
      "GetPlayerStatistics": false,
      "GetTitleData": false,
      "GetUserAccountInfo": true,
      "GetUserData": false,
      "GetUserInventory": false,
      "GetUserReadOnlyData": false,
      "GetUserVirtualCurrency": false,
      "PlayerStatisticNames": null,
      "ProfileConstraints": null,
      "TitleDataKeys": null,
      "UserDataKeys": null,
      "UserReadOnlyDataKeys": null
    },
    "PlayerSecret": null,
    "TitleId": "6955F"
  }
{
  "code": 200,
  "status": "OK",
  "data": {
    "SessionTicket": "[insert SessionTicket]",
    "PlayFabId": "[insert PlayfabID]",
    "NewlyCreated": false,
    "SettingsForUser": {
      "NeedsAttribution": false,
      "GatherDeviceInfo": true,
      "GatherFocusInfo": true
    },
    "LastLoginTime": "",
    "InfoResultPayload": {
      "AccountInfo": {
        "PlayFabId": "[insert PlayfabID]",
        "Created": "UTCTimestamp",
        "TitleInfo": {
          "Origination": "CustomId",
          "Created": "UTCTimestamp",
          "LastLogin": "UTCTimestamp",
          "FirstLogin": "UTCTimestamp",
          "isBanned": false,
          "TitlePlayerAccount": {
            "Id": "[insert EntityID]",
            "Type": "title_player_account",
            "TypeString": "title_player_account"
          }
        },
        "PrivateInfo": {},
        "CustomIdInfo": {
          "CustomId": "[insert CustomID]"
        }
      },
      "UserInventory": [],
      "UserDataVersion": 0,
      "UserReadOnlyDataVersion": 0,
      "CharacterInventories": [],
      "PlayerProfile": {
        "PublisherId": "[insert publisherID]",
        "TitleId": "6955F",
        "PlayerId": "[insert PlayfabID]"
      }
    },
    "EntityToken": {
      "EntityToken": "[insert EntityToken]",
      "TokenExpiration": "UTCTimestamp",
      "Entity": {
        "Id": "[insert EntityID]",
        "Type": "title_player_account",
        "TypeString": "title_player_account"
      }
    },
    "TreatmentAssignment": {
      "Variants": [],
      "Variables": []
    }
  }
}
  • It sends a post request to https://6955f.playfabapi.com/Authentication/GetEntityToken. This returns an entity token, which can be decoded from base64 to see the data it contains. It is never used from my testing however.
Click to view request/response structure
Request Response

{
    "Entity": {
        "Id": "[insert PlayfabID]",
        "Type": "master_player_account"
    }
}
{
    "code": 200,
    "status": "OK",
    "data": {
        "EntityToken": "[insert new EntityToken]",
        "TokenExpiration": "UTCTimestamp",
        "Entity": {
            "Id": "[insert PlayfabID]",
            "Type": "master_player_account",
            "TypeString": "master_player_account"
        }
    }
}
  • It sends a post request to https://6955f.playfabapi.com/Client/GetUserPublisherData
Click to view request/response structure
Request Response
Headers:

x-authorization: [insert SessionTicket]
Body:
{"Entity":{"Id":"[insert PlayfabID]","Type":"master_player_account"}}
Body:
{"code":200,"status":"OK","data":{"Data":{},"DataVersion":0}}
  • Then it sends two unimportant Client/WritePlayerEvent post messages, basically telling them your hardware info & that you started up the game. Probably for Playfab statistics.

Minecraft Auth

  • Now it gets the more important tokens. It sends a post request to https://authorization.franchise.minecraft-services.net/api/v1.0/session/start. The UserAgent must be set to libhttpclient/1.0.0.0, or the request will fail. You also need a deviceID, which is another 32 character string of random numbers & letters, all lowercase. I recommend using the same one, even with different sessions. This gives you the MCToken, very important. As far as I can tell, memory is always 2 gibibytes in bytes.
Click to view request/response structure
Request Response
Headers:

User-Agent: libhttpclient/1.0.0.0
Body:
{
    "device": {
        "applicationType": "MinecraftPE",
        "capabilities": null,
        "gameVersion": "1.21.10",
        "id": "[insert DeviceID]",
        "memory": "[2147483647 as string]",
        "platform": "Win32",
        "playFabTitleId": "6955F",
        "storePlatform": "uwp.store",
        "treatmentOverrides": null,
        "type": "Win32"
    },
    "user": {
        "language": "en",
        "languageCode": "en-US",
        "regionCode": "US",
        "token": "[insert SessionTicket]",
        "tokenType": "PlayFab"
    }
}
Body:
{
    "result": {
        "authorizationHeader": "MCToken [insert jwt]",
        "validUntil": "UTCTimestamp",
        "issuedAt": "UTCTimestamp",
        "treatments": [
            "List of all treatments applied to the MCToken. I'm not listing them, most of them aren't related to MCEDU"
        ],
        "configurations": {
            "minecraft": {
                "id": "Minecraft",
                "parameters": {
                    "random parameter": "true/false",
                    "another one": 100
                }
            }
        },
        "treatmentContext": "This has data, but it's irrelevant, so get it yourself if you want to check it out."
    }
}
  • You need to get another token! This one is slightly important for some things. It sends a post request to https://login.minecrafteduservices.com/v2/signin. User agent must be libhttpclient/1.0.0.0. You need a microsoft access token in order to send requests to this endpoint. Here's the steps. Thanks to bundabrg to making them.

  • Create a OAuth2 authorization link with client ID b36b1432-1a1c-4c82-9b76-24de1cab42f2, the auth URL should be https://login.microsoftonline.com/common/oauth2/authorize, the redirect URI should be https://login.microsoftonline.com/common/oauth2/nativeclient, and the resource should be https://meeservices.minecraft.net.

  • Open that link and sign in with a valid account. You will be redirected to a blank page that starts with https://login.microsoftonline.com/common/oauth2/nativeclient. Copy paste everything, because that link is your authorization response.

  • Fetch an OAuth2 token using the link https://login.microsoftonline.com/common/oauth2/token, the authorization response, & make sure that includeClientID==True. This will return some JSON. Something along the lines of token['accessToken'] and you have the access token.

Alternatively, use the Web Account Manager and MSAL Broker to emulate the actual process MCEDU goes through.

Click to view request/response structure
Request Response
Headers:

User-Agent: libhttpclient/1.0.0.0
Body:
{
    "accessToken": "[insert MSFT ACCESS token]",
    "build": 12110000,
    "clientVersion": 685,
    "correlationVector": "[insert Vector]",
    "displayVersion": "1.21.10",
    "identityToken": "[insert MSFT ACCESS token]",
    "locale": "en_US",
    "osVersion": "10.0.26100",
    "platform": "Windows Desktop Build (Win32)(x64)",
    "platformCategory": "desktop",
    "requestId": "[random uuid4]"
}
Body:
{
    "response": "[insert eduToken jwt]"
}

And that's the end of startup!