Deep Dive Into Halo Infinite Operations API
Table of Contents
With the launch of Halo Infinite in 2021, 343 Industries introduced the concept of battle passes in Halo. The concept itself is not new in the gaming industry per-se, but it’s new to Halo. It’s effectively a limited time opportunity for players to earn in-game rewards during what is referred to as a season (although that term becomes a bit overloaded now).
Since launch, this concept evolved into what is now known as operations. You might even also remember events that were batches of cosmetic rewards that were split into multiple weeks that required coming back to get the full collection of armor bells and whistles. We went from 100 levels per season, to 50, and now 20, with significantly shorter times between operations and with different classes or rewards, but that’s for another post.
In this post, I explore the API behind operations and how you can get data about what’s available in each of them.
In-game experience #
When you log into Halo Infinite, you will see operations once you press the Y button on your controller.
You can go as far back as Season 01: Heroes of Reach, from the days of the Halo Infinite Multiplayer Technical Preview.
Every single operation as well as season and event has a set of items that are rendered once you navigate to any of the cards:
Unlike other experiences within the game, the player cannot get item details from this view - they just see what is awarded by progressing through the levels. And of course, from the switcher itself they can choose what operations they want to activate at any given moment (assuming that those are available for them.)
Exploring the API #
As I was building a new version of OpenSpartan Workshop, I wanted to make sure that customers have the ability to not only look at their match stats but also analyze their progression through the many seasonal (hah) events. As of soon-to-be-released version 1.0.4, I did get it working through my private Halo Infinite API wrapper library:
Part of the process of actually wrapping the REST API was connecting the dots across several API requests that are not immediately obvious to an outside observer like myself.
Before you continue, I should mention that using the API requires authentication. You can read about the process in one of the earlier (like, January of 2022 kind of "earlier") blog posts.
Operations available to a player #
It all starts with the request to get the operations available for a player - a standard GET
request to the following endpoint:
https://economy.svc.halowaypoint.com
/hi
/players
/xuid(YOUR_XUID)
/rewardtracks
/operations
This will return a bunch of relevant data tailored to a player for which we know the XUID (in this case, myself) - it’s what we saw earlier in the game itself.
{
"ActiveOperationRewardTrackPath": "RewardTracks/Operations/S07Op01.json",
"OperationRewardTracks": [
{
"RewardTrackPath": "RewardTracks/Operations/battlepass-lonewolves.json",
"TrackType": "Operation",
"CurrentProgress": {
"Rank": 100,
"PartialProgress": 0,
"IsOwned": true,
"HasReachedMaxRank": true
},
"PreviousProgress": null,
"IsOwned": true,
"BaseXp": null,
"BoostXp": null
},
{
"RewardTrackPath": "RewardTracks/Operations/battlepass-noblesacrifice.json",
"TrackType": "Operation",
"CurrentProgress": {
"Rank": 100,
"PartialProgress": 0,
"IsOwned": true,
"HasReachedMaxRank": true
},
"PreviousProgress": null,
"IsOwned": true,
"BaseXp": null,
"BoostXp": null
},
{
"RewardTrackPath": "RewardTracks/Operations/battlepass-WinterBreak.json",
"TrackType": "Operation",
"CurrentProgress": {
"Rank": 30,
"PartialProgress": 0,
"IsOwned": true,
"HasReachedMaxRank": true
},
"PreviousProgress": null,
"IsOwned": true,
"BaseXp": null,
"BoostXp": null
},
{
"RewardTrackPath": "RewardTracks/Operations/OperationGenBase.json",
"TrackType": "Operation",
"CurrentProgress": {
"Rank": 0,
"PartialProgress": 0,
"IsOwned": false,
"HasReachedMaxRank": false
},
"PreviousProgress": null,
"IsOwned": false,
"BaseXp": null,
"BoostXp": null
},
{
"RewardTrackPath": "RewardTracks/Operations/S03BattlePass.json",
"TrackType": "Operation",
"CurrentProgress": {
"Rank": 100,
"PartialProgress": 0,
"IsOwned": true,
"HasReachedMaxRank": true
},
"PreviousProgress": null,
"IsOwned": true,
"BaseXp": null,
"BoostXp": null
},
{
"RewardTrackPath": "RewardTracks/Operations/S04BattlePass.json",
"TrackType": "Operation",
"CurrentProgress": {
"Rank": 100,
"PartialProgress": 0,
"IsOwned": true,
"HasReachedMaxRank": true
},
"PreviousProgress": null,
"IsOwned": true,
"BaseXp": null,
"BoostXp": null
},
{
"RewardTrackPath": "RewardTracks/Operations/S05OpPassL01.json",
"TrackType": "Operation",
"CurrentProgress": {
"Rank": 50,
"PartialProgress": 0,
"IsOwned": true,
"HasReachedMaxRank": true
},
"PreviousProgress": null,
"IsOwned": true,
"BaseXp": null,
"BoostXp": null
},
{
"RewardTrackPath": "RewardTracks/Operations/S05OpPassM01.json",
"TrackType": "Operation",
"CurrentProgress": {
"Rank": 20,
"PartialProgress": 0,
"IsOwned": true,
"HasReachedMaxRank": true
},
"PreviousProgress": null,
"IsOwned": true,
"BaseXp": null,
"BoostXp": null
},
{
"RewardTrackPath": "RewardTracks/Operations/S05OpPassM02.json",
"TrackType": "Operation",
"CurrentProgress": {
"Rank": 20,
"PartialProgress": 0,
"IsOwned": true,
"HasReachedMaxRank": true
},
"PreviousProgress": null,
"IsOwned": true,
"BaseXp": null,
"BoostXp": null
},
{
"RewardTrackPath": "RewardTracks/Operations/S06OpPassM01.json",
"TrackType": "Operation",
"CurrentProgress": {
"Rank": 20,
"PartialProgress": 0,
"IsOwned": true,
"HasReachedMaxRank": true
},
"PreviousProgress": null,
"IsOwned": true,
"BaseXp": null,
"BoostXp": null
},
{
"RewardTrackPath": "RewardTracks/Operations/S06OpPassM02.json",
"TrackType": "Operation",
"CurrentProgress": {
"Rank": 20,
"PartialProgress": 0,
"IsOwned": true,
"HasReachedMaxRank": true
},
"PreviousProgress": null,
"IsOwned": true,
"BaseXp": null,
"BoostXp": null
},
{
"RewardTrackPath": "RewardTracks/Operations/S06OpPassM03.json",
"TrackType": "Operation",
"CurrentProgress": {
"Rank": 20,
"PartialProgress": 0,
"IsOwned": true,
"HasReachedMaxRank": true
},
"PreviousProgress": null,
"IsOwned": true,
"BaseXp": null,
"BoostXp": null
},
{
"RewardTrackPath": "RewardTracks/Operations/S07Op01.json",
"TrackType": "Operation",
"CurrentProgress": {
"Rank": 20,
"PartialProgress": 0,
"IsOwned": true,
"HasReachedMaxRank": true
},
"PreviousProgress": null,
"IsOwned": true,
"BaseXp": null,
"BoostXp": null
},
{
"RewardTrackPath": "RewardTracks/Operations/S07Op02.json",
"TrackType": "Operation",
"CurrentProgress": {
"Rank": 0,
"PartialProgress": 0,
"IsOwned": false,
"HasReachedMaxRank": false
},
"PreviousProgress": null,
"IsOwned": false,
"BaseXp": null,
"BoostXp": null
},
{
"RewardTrackPath": "RewardTracks/Operations/S07Op03.json",
"TrackType": "Operation",
"CurrentProgress": {
"Rank": 0,
"PartialProgress": 0,
"IsOwned": false,
"HasReachedMaxRank": false
},
"PreviousProgress": null,
"IsOwned": false,
"BaseXp": null,
"BoostXp": null
}
],
"ScheduledOperationRewardTrackPath": "RewardTracks/Operations/S07Op01.json"
}
Few things of interest here:
ActiveOperationRewardTrackPath
is the currently selected operation that the player activated. Recall that in the battle pass switcher you can own several operations that can be activated, one at any time.ScheduledOperationRewardTrackPath
returns the currently scheduled operation. At the time of me writing this blog post, it’s Banished Honor.OperationRewardTracks
contains available operations for a player. Each operation has pointers to its metadata as well as player progression against it. That is - you can tell at which level you are out of the entire set of ranks.
I see some operations here that are listed in the API response but are not available in the game. What's up with that?
There are two operations that we see in the API that are not yet reflected in the game UI - RewardTracks/Operations/S07Op02.json
(Tenrai IV) and RewardTracks/Operations/S07Op03.json
(Spartan Surplus). I am not entirely sure why these are included in the schedule right now given that they will be launched later in the year, but I can tell you that what you see in the response is primarily driven by the release ID for the clearance.
Recall that in one of my previous articles I mentioned that you need to get the flight ID (also known as the clearance) for some of the API requests. Since a few Halo Infinite releases back, a new query parameter was added to the clearance request endpoint: release=<release_id>
. The release ID here can be 1.5
, 1.6
, or 1.7
(from what I’ve seen so far). Depending on which release you’re requesting clearance for, this will influence the list of operations returned by the aforementioned economy
endpoint. 1.7
, which is what the current retail version of Halo Infinite uses, returns the events that you see in the API but not in the game. That’s the way in which those folks on Reddit can tell what’s coming in new operations.
But this is all that is available to me as a player. I know for a fact that there were more operations in the past that are not captured above. For that, we dive even deeper into the API.
Operations and events available globally #
In addition to pulling operations that are available to a given player, we also can look at what operations and events are available globally. To do that, we will issue a request against the gamecms-hacs
endpoint, where we can obtain the full season calendar:
https://gamecms-hacs.svc.halowaypoint.com
/hi
/progression
/file
/calendars
/seasons
/seasoncalendar.json
This, in turn, will give us data like this:
{
"Seasons": [
{
"CsrSeasonFilePath": "Csr/Seasons/CsrSeason1-2.json",
"OperationTrackPath": "RewardTracks/Operations/battlepass-noblesacrifice.json",
"SeasonMetadata": "Seasons/Season1.json",
"StartDate": {
"ISO8601Date": "2021-06-20T17:00:00.000Z"
},
"EndDate": {
"ISO8601Date": "2022-05-03T17:00:00.000Z"
}
},
{
"CsrSeasonFilePath": "Csr/Seasons/CsrSeason2-3.json",
"OperationTrackPath": "RewardTracks/Operations/battlepass-lonewolves.json",
"SeasonMetadata": "Seasons/Season2.json",
"StartDate": {
"ISO8601Date": "2022-05-03T17:00:00.000Z"
},
"EndDate": {
"ISO8601Date": "2022-11-08T17:00:00.000Z"
}
},
{
"CsrSeasonFilePath": "Csr/Seasons/CsrSeason2-3.json",
"OperationTrackPath": "RewardTracks/Operations/battlepass-WinterBreak.json",
"SeasonMetadata": "Seasons/Season-Winter-Break-22.json",
"StartDate": {
"ISO8601Date": "2022-11-08T17:00:00.000Z"
},
"EndDate": {
"ISO8601Date": "2023-03-07T17:00:00.000Z"
}
},
{
"CsrSeasonFilePath": "Csr/Seasons/CsrSeason3-1.json",
"OperationTrackPath": "RewardTracks/Operations/S03BattlePass.json",
"SeasonMetadata": "Seasons/Season3.json",
"StartDate": {
"ISO8601Date": "2023-03-07T17:00:00.000Z"
},
"EndDate": {
"ISO8601Date": "2023-06-20T17:00:00.000Z"
}
},
{
"CsrSeasonFilePath": "Csr/Seasons/CsrSeason4-1.json",
"OperationTrackPath": "RewardTracks/Operations/S04BattlePass.json",
"SeasonMetadata": "Seasons/Season4.json",
"StartDate": {
"ISO8601Date": "2023-06-20T17:00:00.000Z"
},
"EndDate": {
"ISO8601Date": "2023-10-17T17:00:00.000Z"
}
},
{
"CsrSeasonFilePath": "Csr/Seasons/CsrSeason5-1.json",
"OperationTrackPath": "RewardTracks/Operations/S05OpPassL01.json",
"SeasonMetadata": "Seasons/Season5-Op1.json",
"StartDate": {
"ISO8601Date": "2023-10-17T17:00:00.000Z"
},
"EndDate": {
"ISO8601Date": "2023-11-14T17:00:00.000Z"
}
},
{
"CsrSeasonFilePath": "Csr/Seasons/CsrSeason5-1.json",
"OperationTrackPath": "RewardTracks/Operations/S05OpPassM01.json",
"SeasonMetadata": "Seasons/Season5-Op2.json",
"StartDate": {
"ISO8601Date": "2023-11-14T17:00:00.000Z"
},
"EndDate": {
"ISO8601Date": "2023-12-19T18:00:00.000Z"
}
},
{
"CsrSeasonFilePath": "Csr/Seasons/CsrSeason5-1.json",
"OperationTrackPath": "RewardTracks/Operations/S05OpPassM02.json",
"SeasonMetadata": "Seasons/Season5-Op3.json",
"StartDate": {
"ISO8601Date": "2023-12-19T18:00:00.000Z"
},
"EndDate": {
"ISO8601Date": "2024-01-30T18:00:00.000Z"
}
},
{
"CsrSeasonFilePath": "Csr/Seasons/CsrSeason6-1.json",
"OperationTrackPath": "RewardTracks/Operations/S06OpPassM01.json",
"SeasonMetadata": "Seasons/Season6-Op1.json",
"StartDate": {
"ISO8601Date": "2024-01-30T18:00:00.000Z"
},
"EndDate": {
"ISO8601Date": "2024-03-05T18:00:00.000Z"
}
},
{
"CsrSeasonFilePath": "Csr/Seasons/CsrSeason6-1.json",
"OperationTrackPath": "RewardTracks/Operations/S06OpPassM02.json",
"SeasonMetadata": "Seasons/Season6-Op2.json",
"StartDate": {
"ISO8601Date": "2024-03-05T18:00:00.000Z"
},
"EndDate": {
"ISO8601Date": "2024-04-02T18:00:00.000Z"
}
},
{
"CsrSeasonFilePath": "Csr/Seasons/CsrSeason6-1.json",
"OperationTrackPath": "RewardTracks/Operations/S06OpPassM03.json",
"SeasonMetadata": "Seasons/Season6-Op3.json",
"StartDate": {
"ISO8601Date": "2024-04-02T18:00:00.000Z"
},
"EndDate": {
"ISO8601Date": "2024-04-30T18:00:00.000Z"
}
},
{
"CsrSeasonFilePath": "Csr/Seasons/CsrSeason7-1.json",
"OperationTrackPath": "RewardTracks/Operations/S07Op01.json",
"SeasonMetadata": "Seasons/Season7-Op1.json",
"StartDate": {
"ISO8601Date": "2024-04-30T18:00:00.000Z"
},
"EndDate": {
"ISO8601Date": "2024-06-04T18:00:00.000Z"
}
},
{
"CsrSeasonFilePath": "Csr/Seasons/CsrSeason7-1.json",
"OperationTrackPath": "RewardTracks/Operations/S07Op02.json",
"SeasonMetadata": "Seasons/Season7-Op2.json",
"StartDate": {
"ISO8601Date": "2024-06-04T18:00:00.000Z"
},
"EndDate": {
"ISO8601Date": "2024-07-02T18:00:00.000Z"
}
},
{
"CsrSeasonFilePath": "Csr/Seasons/CsrSeason7-1.json",
"OperationTrackPath": "RewardTracks/Operations/S07Op03.json",
"SeasonMetadata": "Seasons/Season7-Op3.json",
"StartDate": {
"ISO8601Date": "2024-07-02T18:00:00.000Z"
},
"EndDate": {
"ISO8601Date": "2024-07-30T18:00:00.000Z"
}
}
],
"Events": [
{
"RewardTrackPath": "RewardTracks/Events/Rituals/ritualWotWSamurai.json",
"StartDate": {
"ISO8601Date": "2021-09-18T21:30:00.00Z"
},
"EndDate": {
"ISO8601Date": "2021-11-07T17:00:00.00Z"
}
},
{
"RewardTrackPath": "RewardTracks/Events/Rituals/ritualWinterGiveaway.json",
"StartDate": {
"ISO8601Date": "2021-11-16T17:00:00.00Z"
},
"EndDate": {
"ISO8601Date": "2021-12-03T17:00:00.00Z"
}
},
{
"RewardTrackPath": "RewardTracks/Events/Rituals/ritualWinterGiveaway.json",
"StartDate": {
"ISO8601Date": "2021-12-21T17:00:00.00Z"
},
"EndDate": {
"ISO8601Date": "2022-01-04T17:00:00.00Z"
}
},
{
"RewardTrackPath": "RewardTracks/Events/Rituals/ritualWotWSamurai.json",
"StartDate": {
"ISO8601Date": "2022-01-04T17:00:00.00Z"
},
"EndDate": {
"ISO8601Date": "2022-01-11T17:00:00.00Z"
}
},
{
"RewardTrackPath": "RewardTracks/Events/Rituals/ritualLoneWolf1.json",
"StartDate": {
"ISO8601Date": "2022-04-27T18:00:00.00Z"
},
"EndDate": {
"ISO8601Date": "2022-04-28T18:00:00.00Z"
}
},
{
"RewardTrackPath": "RewardTracks/Events/Rituals/ritualLoneWolf1.json",
"StartDate": {
"ISO8601Date": "2022-05-03T18:00:00.00Z"
},
"EndDate": {
"ISO8601Date": "2022-05-17T18:00:00.00Z"
}
},
{
"RewardTrackPath": "RewardTracks/Events/Rituals/ritualEagleStrike.json",
"StartDate": {
"ISO8601Date": "2022-05-24T18:00:00.00Z"
},
"EndDate": {
"ISO8601Date": "2022-05-31T18:00:00.00Z"
}
},
{
"RewardTrackPath": "RewardTracks/Events/Rituals/ritualEagleStrike.json",
"StartDate": {
"ISO8601Date": "2022-06-14T18:00:00.00Z"
},
"EndDate": {
"ISO8601Date": "2022-06-21T18:00:00.00Z"
}
},
{
"RewardTrackPath": "RewardTracks/Events/Rituals/ritualEagleStrike.json",
"StartDate": {
"ISO8601Date": "2022-07-05T18:00:00.00Z"
},
"EndDate": {
"ISO8601Date": "2022-07-12T18:00:00.00Z"
}
},
{
"RewardTrackPath": "RewardTracks/Events/Rituals/ritualLoneWolf2.json",
"StartDate": {
"ISO8601Date": "2022-07-19T18:00:00.00Z"
},
"EndDate": {
"ISO8601Date": "2022-08-02T18:00:00.00Z"
}
},
{
"RewardTrackPath": "RewardTracks/Events/Rituals/ritualEagleStrike.json",
"StartDate": {
"ISO8601Date": "2022-08-16T18:00:00.00Z"
},
"EndDate": {
"ISO8601Date": "2022-08-23T18:00:00.00Z"
}
},
{
"RewardTrackPath": "RewardTracks/Events/Rituals/ritualJointFire.json",
"StartDate": {
"ISO8601Date": "2022-08-23T18:00:00.00Z"
},
"EndDate": {
"ISO8601Date": "2022-09-24T18:00:00.00Z"
}
},
{
"RewardTrackPath": "RewardTracks/Events/Rituals/S03Fracture.json",
"StartDate": {
"ISO8601Date": "2022-09-26T17:00:00.00Z"
},
"EndDate": {
"ISO8601Date": "2022-10-25T18:00:00.00Z"
}
},
{
"RewardTrackPath": "RewardTracks/Events/Rituals/S03Narrative1.json",
"StartDate": {
"ISO8601Date": "2022-10-25T18:00:00.00Z"
},
"EndDate": {
"ISO8601Date": "2022-12-19T18:00:00.00Z"
}
},
{
"RewardTrackPath": "RewardTracks/Events/Rituals/ritualWinterContingencyII.json",
"StartDate": {
"ISO8601Date": "2022-12-20T18:00:00.00Z"
},
"EndDate": {
"ISO8601Date": "2023-01-03T18:00:00.00Z"
}
},
{
"RewardTrackPath": "RewardTracks/Events/Rituals/ritualJointFire.json",
"StartDate": {
"ISO8601Date": "2023-01-17T18:00:00.00Z"
},
"EndDate": {
"ISO8601Date": "2023-01-31T18:00:00.00Z"
}
},
{
"RewardTrackPath": "RewardTracks/Events/Rituals/S04Narrative1.json",
"StartDate": {
"ISO8601Date": "2023-02-27T18:00:00.00Z"
},
"EndDate": {
"ISO8601Date": "2023-03-07T18:00:00.00Z"
}
},
{
"RewardTrackPath": "RewardTracks/Events/Rituals/S03Narrative1.json",
"StartDate": {
"ISO8601Date": "2023-03-07T18:00:00.00Z"
},
"EndDate": {
"ISO8601Date": "2023-03-21T18:00:00.00Z"
}
},
{
"RewardTrackPath": "RewardTracks/Events/Rituals/S03Fracture.json",
"StartDate": {
"ISO8601Date": "2023-03-21T18:00:00.00Z"
},
"EndDate": {
"ISO8601Date": "2023-04-11T17:00:00.00Z"
}
},
{
"RewardTrackPath": "RewardTracks/Events/Rituals/S03Fracture.json",
"StartDate": {
"ISO8601Date": "2023-04-11T18:00:00.00Z"
},
"EndDate": {
"ISO8601Date": "2023-04-18T18:00:00.00Z"
}
},
{
"RewardTrackPath": "RewardTracks/Events/Rituals/S03Narrative2.json",
"StartDate": {
"ISO8601Date": "2023-05-02T18:00:00.00Z"
},
"EndDate": {
"ISO8601Date": "2023-05-16T18:00:00.00Z"
}
},
{
"RewardTrackPath": "RewardTracks/Events/Rituals/S03Fracture.json",
"StartDate": {
"ISO8601Date": "2023-05-23T18:00:00.00Z"
},
"EndDate": {
"ISO8601Date": "2023-05-30T18:00:00.00Z"
}
},
{
"RewardTrackPath": "RewardTracks/Events/Rituals/S03Fracture.json",
"StartDate": {
"ISO8601Date": "2023-06-13T18:00:00.00Z"
},
"EndDate": {
"ISO8601Date": "2023-06-20T18:00:00.00Z"
}
},
{
"RewardTrackPath": "RewardTracks/Events/Rituals/S04Narrative1.json",
"StartDate": {
"ISO8601Date": "2023-06-20T18:00:00.00Z"
},
"EndDate": {
"ISO8601Date": "2023-07-04T18:00:00.00Z"
}
},
{
"RewardTrackPath": "RewardTracks/Events/Rituals/S04Fracture.json",
"StartDate": {
"ISO8601Date": "2023-07-04T18:00:00.00Z"
},
"EndDate": {
"ISO8601Date": "2023-07-11T18:00:00.00Z"
}
},
{
"RewardTrackPath": "RewardTracks/Events/Rituals/S04Casual.json",
"StartDate": {
"ISO8601Date": "2023-07-18T18:00:00.00Z"
},
"EndDate": {
"ISO8601Date": "2023-08-01T18:00:00.00Z"
}
},
{
"RewardTrackPath": "RewardTracks/Events/Rituals/S04Fracture.json",
"StartDate": {
"ISO8601Date": "2023-08-08T18:00:00.00Z"
},
"EndDate": {
"ISO8601Date": "2023-08-15T18:00:00.00Z"
}
},
{
"RewardTrackPath": "RewardTracks/Events/Rituals/S04Narrative2.json",
"StartDate": {
"ISO8601Date": "2023-08-22T18:00:00.00Z"
},
"EndDate": {
"ISO8601Date": "2023-09-05T18:00:00.00Z"
}
},
{
"RewardTrackPath": "RewardTracks/Events/Rituals/S04Fracture.json",
"StartDate": {
"ISO8601Date": "2023-09-12T18:00:00.00Z"
},
"EndDate": {
"ISO8601Date": "2023-09-19T18:00:00.00Z"
}
},
{
"RewardTrackPath": "RewardTracks/Events/Rituals/S04Fracture.json",
"StartDate": {
"ISO8601Date": "2023-09-26T18:00:00.00Z"
},
"EndDate": {
"ISO8601Date": "2023-10-03T18:00:00.00Z"
}
}
],
"CareerRank": {
"RewardTrackPath": "RewardTracks/CareerRanks/careerrank1.json"
}
}
There is a bit more content here than what we saw for a player - we now have three containers:
Seasons
contains information about large-scale seasons. That is - your usual suspects, like Heroes of Reach, Lone Wolves, and Infection, among others. Each season has:- An associated
CsrSeasonFilePath
CSR season metadata pointer, applicable for ranked games. It determines the “rank reset” season boundary. This file is not available through the CMS with standard Spartan authorization, from what I’ve experienced. OperationTrackPath
, that contains a breakdown of ranks and rewards for the operation.SeasonMetadata
, which points to a JSON file that contains basic information about the operation, such as background images (we’ll get to that in a second).StartDate
- when the operations starts.EndDate
- when the operation ends.
- An associated
Events
is all about those one-off events that we’ve seen in past announcements, like Cyber Showdown. There is much less metadata captured for these (e.g., there is no associated CSR season).CareerRank
is the one pointer to the current career rank progression.
Erm... You mentioned Cyber Showdown, but that event is not actually returned in the API. Is that... right?
Indeed, there are three events that are missing from the API response:
- Cyber Showdown (
RewardTracks/Events/Rituals/ritualsynthwave.json
) - Noble Intentions (
RewardTracks/Events/Rituals/ritualNobleIntentions.json
) - Tactical Ops (
RewardTracks/Events/Rituals/ritualOlympus.json
)
Why they are not included in the API I don’t know, but I have baked these into the OpenSpartan Workshop settings API to make sure that when the tool boots, users will get to see the progression against those (now old) events anyway.
Now, to the data we’ve seen above. Let’s first take a look at OperationTrackPath
. We can plug the value of this property into the gamecms-hacs
endpoint, like this:
https://gamecms-hacs.svc.halowaypoint.com
/hi
/Progression
/file
/RewardTracks
/Operations
/S07Op01.json
This will yield the event ranks along with pointers to all rewards that can be obtained in each:
{
"TrackId": "S07Op01",
"XpPerRank": 1000,
"Ranks": [
{
"Rank": 1,
"FreeRewards": {
"InventoryRewards": [
{
"InventoryItemPath": "Inventory/spartan/Emblems/3849623-SpartanEmblem.json",
"Amount": 1,
"Type": "SpartanEmblem"
}
],
"CurrencyRewards": []
},
"PaidRewards": {
"InventoryRewards": [],
"CurrencyRewards": []
}
},
{
"Rank": 2,
"FreeRewards": {
"InventoryRewards": [],
"CurrencyRewards": [
{
"CurrencyPath": "Currency/Currencies/softcurrency.json",
"Amount": 500
}
]
},
"PaidRewards": {
"InventoryRewards": [],
"CurrencyRewards": []
}
},
{
"Rank": 3,
"FreeRewards": {
"InventoryRewards": [],
"CurrencyRewards": [
{
"CurrencyPath": "Currency/Currencies/softcurrency.json",
"Amount": 500
}
]
},
"PaidRewards": {
"InventoryRewards": [],
"CurrencyRewards": []
}
},
{
"Rank": 4,
"FreeRewards": {
"InventoryRewards": [],
"CurrencyRewards": [
{
"CurrencyPath": "Currency/Currencies/softcurrency.json",
"Amount": 1000
}
]
},
"PaidRewards": {
"InventoryRewards": [],
"CurrencyRewards": []
}
},
{
"Rank": 5,
"FreeRewards": {
"InventoryRewards": [
{
"InventoryItemPath": "Inventory/Armor/KneePads/3877283-ArmorKneePad-Mark-VII.json",
"Amount": 1,
"Type": "ArmorKneePad"
}
],
"CurrencyRewards": []
},
"PaidRewards": {
"InventoryRewards": [],
"CurrencyRewards": []
}
},
{
"Rank": 6,
"FreeRewards": {
"InventoryRewards": [],
"CurrencyRewards": [
{
"CurrencyPath": "Currency/Currencies/softcurrency.json",
"Amount": 500
}
]
},
"PaidRewards": {
"InventoryRewards": [],
"CurrencyRewards": []
}
},
{
"Rank": 7,
"FreeRewards": {
"InventoryRewards": [],
"CurrencyRewards": [
{
"CurrencyPath": "Currency/Currencies/softcurrency.json",
"Amount": 500
}
]
},
"PaidRewards": {
"InventoryRewards": [],
"CurrencyRewards": []
}
},
{
"Rank": 8,
"FreeRewards": {
"InventoryRewards": [],
"CurrencyRewards": [
{
"CurrencyPath": "Currency/Currencies/softcurrency.json",
"Amount": 1000
}
]
},
"PaidRewards": {
"InventoryRewards": [],
"CurrencyRewards": []
}
},
{
"Rank": 9,
"FreeRewards": {
"InventoryRewards": [],
"CurrencyRewards": [
{
"CurrencyPath": "Currency/Currencies/softcurrency.json",
"Amount": 2500
}
]
},
"PaidRewards": {
"InventoryRewards": [],
"CurrencyRewards": []
}
},
{
"Rank": 10,
"FreeRewards": {
"InventoryRewards": [
{
"InventoryItemPath": "Inventory/Armor/WristAttachments/3877287-ArmorWristAttachment-Mark-VII.json",
"Amount": 1,
"Type": "ArmorWristAttachment"
}
],
"CurrencyRewards": []
},
"PaidRewards": {
"InventoryRewards": [],
"CurrencyRewards": []
}
},
{
"Rank": 11,
"FreeRewards": {
"InventoryRewards": [],
"CurrencyRewards": [
{
"CurrencyPath": "Currency/Currencies/softcurrency.json",
"Amount": 500
}
]
},
"PaidRewards": {
"InventoryRewards": [],
"CurrencyRewards": []
}
},
{
"Rank": 12,
"FreeRewards": {
"InventoryRewards": [],
"CurrencyRewards": [
{
"CurrencyPath": "Currency/Currencies/softcurrency.json",
"Amount": 500
}
]
},
"PaidRewards": {
"InventoryRewards": [],
"CurrencyRewards": []
}
},
{
"Rank": 13,
"FreeRewards": {
"InventoryRewards": [],
"CurrencyRewards": [
{
"CurrencyPath": "Currency/Currencies/softcurrency.json",
"Amount": 1000
}
]
},
"PaidRewards": {
"InventoryRewards": [],
"CurrencyRewards": []
}
},
{
"Rank": 14,
"FreeRewards": {
"InventoryRewards": [],
"CurrencyRewards": [
{
"CurrencyPath": "Currency/Currencies/softcurrency.json",
"Amount": 2500
}
]
},
"PaidRewards": {
"InventoryRewards": [],
"CurrencyRewards": []
}
},
{
"Rank": 15,
"FreeRewards": {
"InventoryRewards": [
{
"InventoryItemPath": "Inventory/Armor/ChestAttachments/3877285-ArmorChestAttachment-Mark-VII.json",
"Amount": 1,
"Type": "ArmorChestAttachment"
}
],
"CurrencyRewards": []
},
"PaidRewards": {
"InventoryRewards": [],
"CurrencyRewards": []
}
},
{
"Rank": 16,
"FreeRewards": {
"InventoryRewards": [],
"CurrencyRewards": [
{
"CurrencyPath": "Currency/Currencies/softcurrency.json",
"Amount": 500
}
]
},
"PaidRewards": {
"InventoryRewards": [],
"CurrencyRewards": []
}
},
{
"Rank": 17,
"FreeRewards": {
"InventoryRewards": [],
"CurrencyRewards": [
{
"CurrencyPath": "Currency/Currencies/softcurrency.json",
"Amount": 1000
}
]
},
"PaidRewards": {
"InventoryRewards": [],
"CurrencyRewards": []
}
},
{
"Rank": 18,
"FreeRewards": {
"InventoryRewards": [
{
"InventoryItemPath": "Inventory/Armor/ShouldersLeft/3877284-ArmorLeftShoulderPad-Mark-VII.json",
"Amount": 1,
"Type": "ArmorLeftShoulderPad"
},
{
"InventoryItemPath": "Inventory/Armor/ShouldersRight/3877284-ArmorRightShoulderPad-Mark-VII.json",
"Amount": 1,
"Type": "ArmorRightShoulderPad"
}
],
"CurrencyRewards": []
},
"PaidRewards": {
"InventoryRewards": [],
"CurrencyRewards": []
}
},
{
"Rank": 19,
"FreeRewards": {
"InventoryRewards": [],
"CurrencyRewards": [
{
"CurrencyPath": "Currency/Currencies/softcurrency.json",
"Amount": 2500
}
]
},
"PaidRewards": {
"InventoryRewards": [],
"CurrencyRewards": []
}
},
{
"Rank": 20,
"FreeRewards": {
"InventoryRewards": [
{
"InventoryItemPath": "Inventory/Armor/Helmets/3877282-ArmorHelmet-Mark-VII.json",
"Amount": 1,
"Type": "ArmorHelmet"
},
{
"InventoryItemPath": "Inventory/Armor/visors/012-001-06efb646.json",
"Amount": 1,
"Type": "ArmorVisor"
}
],
"CurrencyRewards": []
},
"PaidRewards": {
"InventoryRewards": [],
"CurrencyRewards": []
}
}
],
"Name": "Banished Honor",
"Description": "",
"OperationNumber": 7001,
"DateRange": "",
"IsRitual": false,
"SummaryImagePath": "",
"WeekNumber": null,
"BackgroundImagePath": "progression/RewardTracks/HIMPS7_BH_UI_EVENT_WIDGET_986x248.png",
"BattlePassImage": "progression/RewardTracks/S7_BH_SpartanMinor.png",
"BattlePassBundleImage": "progression/RewardTracks/S7_BH_SpartanMajor.png"
}
Most of the details here are self-explanatory, but pay attention to DateRange
. Notice that it’s missing here. If you are just obtaining the operation metadata from this manifest, you will be missing quite a bit of data, hence why we also need to look inside the SeasonMetadata
property, which takes us here:
https://gamecms-hacs.svc.halowaypoint.com
/hi
/Progression
/file
/Seasons
/Season7-Op1.json
The JSON response, if all things go well, should resemble this:
{
"DateRange": "April 30th, 2024 - June 3rd, 2024",
"Name": "Banished Honor",
"Logo": "progression/ScreenBackgrounds/Base_Placeholder.png",
"Number": 7001,
"Description": "Placeholder Short Description",
"SummaryBackgroundPath": "progression/ScreenBackgrounds/Summary_Background.png",
"ChallengesBackgroundPath": "progression/ScreenBackgrounds/challenges_background.png",
"BattlePassLogoImage": "progression/ScreenBackgrounds/battlepass_logo.png",
"SeasonLogoImage": "seasons/insignia_Evergreen_256.png",
"RitualLogoImage": "progression/ScreenBackgrounds/ritual_logo.png",
"StorefrontBackgroundImage": "progression/ScreenBackgrounds/storefront_background.png",
"CardBackgroundImage": "progression/Switcher/Season_Switcher_S7_BH.png",
"NarrativeBlurb": "Placeholder Long Description",
"BattlePassSeasonUpsellBackgroundImage": "progression/ScreenBackgrounds/Summary_Background_S7_BH.png",
"ProgressionBackgroundImage": "progression/ScreenBackgrounds/progression_background.png",
"SeasonBackgroundLogo": "seasons/insignia_Evergreen_2160.png"
}
Much richer and more details, albeit things like descriptions are missing. Nonetheless, literally every detail here can be used to render details in your custom experience (which is what I do in OpenSpartan Workshop).
The same metadata, however, is not available for events (also known as rituals). For those, you will need to obtain the full rank breakdown by issuing a GET
request to the gamecms
endpoint, like this:
https://gamecms-hacs.svc.halowaypoint.com
/hi
/Progression
/file
/RewardTracks
/Events
/Rituals
/ritualOlympus.json
You will get a response like this:
{
"TrackId": "ritualOlympus",
"XpPerRank": 1,
"Ranks": [
{
"Rank": 1,
"FreeRewards": {
"InventoryRewards": [
{
"InventoryItemPath": "inventory/spartan/actionposes/101-000-menu-stance-o-7ba7d54b.json",
"Amount": 1,
"Type": "SpartanActionPose"
}
],
"CurrencyRewards": []
},
"PaidRewards": {
"InventoryRewards": [],
"CurrencyRewards": []
}
},
{
"Rank": 2,
"FreeRewards": {
"InventoryRewards": [
{
"InventoryItemPath": "Inventory/Armor/Visors/012-001-aca35a00.json",
"Amount": 1,
"Type": "ArmorVisor"
}
],
"CurrencyRewards": []
},
"PaidRewards": {
"InventoryRewards": [],
"CurrencyRewards": []
}
},
{
"Rank": 3,
"FreeRewards": {
"InventoryRewards": [
{
"InventoryItemPath": "Inventory/Weapon/Charms/201-201-2033c871.json",
"Amount": 1,
"Type": "WeaponCharm"
}
],
"CurrencyRewards": []
},
"PaidRewards": {
"InventoryRewards": [],
"CurrencyRewards": []
}
},
{
"Rank": 4,
"FreeRewards": {
"InventoryRewards": [
{
"InventoryItemPath": "Inventory/spartan/backdropimages/103-000-ui-background-eb8d24cc.json",
"Amount": 1,
"Type": "SpartanBackdropImage"
}
],
"CurrencyRewards": []
},
"PaidRewards": {
"InventoryRewards": [],
"CurrencyRewards": []
}
},
{
"Rank": 5,
"FreeRewards": {
"InventoryRewards": [
{
"InventoryItemPath": "inventory/armor/helmets/005-001-olympus-a551ce3c.json",
"Amount": 1,
"Type": "ArmorHelmet"
}
],
"CurrencyRewards": []
},
"PaidRewards": {
"InventoryRewards": [],
"CurrencyRewards": []
}
},
{
"Rank": 6,
"FreeRewards": {
"InventoryRewards": [
{
"InventoryItemPath": "Inventory/Armor/ChestAttachments/001-001-olympus-79bee247.json",
"Amount": 1,
"Type": "ArmorChestAttachment"
}
],
"CurrencyRewards": []
},
"PaidRewards": {
"InventoryRewards": [],
"CurrencyRewards": []
}
},
{
"Rank": 7,
"FreeRewards": {
"InventoryRewards": [
{
"InventoryItemPath": "Inventory/Armor/HipAttachments/010-001-olympus-3f3bfd13.json",
"Amount": 1,
"Type": "ArmorHipAttachment"
}
],
"CurrencyRewards": []
},
"PaidRewards": {
"InventoryRewards": [],
"CurrencyRewards": []
}
},
{
"Rank": 8,
"FreeRewards": {
"InventoryRewards": [
{
"InventoryItemPath": "Inventory/Armor/Gloves/003-001-olympus-ea1058fb.json",
"Amount": 1,
"Type": "ArmorGlove"
}
],
"CurrencyRewards": []
},
"PaidRewards": {
"InventoryRewards": [],
"CurrencyRewards": []
}
},
{
"Rank": 9,
"FreeRewards": {
"InventoryRewards": [
{
"InventoryItemPath": "Inventory/Armor/Coatings/002-001-olympus-ba63ec9a.json",
"Amount": 1,
"Type": "ArmorCoating"
}
],
"CurrencyRewards": []
},
"PaidRewards": {
"InventoryRewards": [],
"CurrencyRewards": []
}
},
{
"Rank": 10,
"FreeRewards": {
"InventoryRewards": [
{
"InventoryItemPath": "Inventory/Armor/HelmetAttachments/004-001-olympus-8e89825e.json",
"Amount": 1,
"Type": "ArmorHelmetAttachment"
}
],
"CurrencyRewards": []
},
"PaidRewards": {
"InventoryRewards": [],
"CurrencyRewards": []
}
}
],
"Name": "Tactical Ops",
"Description": "Test your aim in a high stakes battle",
"OperationNumber": 1,
"DateRange": "March 8th, 2022 - March 21st, 2022",
"IsRitual": true,
"SummaryImagePath": "progression/RewardTracks/Olympus_Progression_Comps_Summary_CartArt_Event_TacOps.png",
"WeekNumber": null,
"BackgroundImagePath": ""
}
Date ranges here are included in the manifest so you won’t need to go far to find them.
Another challenge with events, however, is the lack of any data around current player progression that is baked into the default economy
endpoint that provides details on operations. But, worry not, because there is a little known workaround here, known as:
https://economy.svc.halowaypoint.com
/hi
/players
/xuid(YOUR_XUID)
/rewardtracks
/events
/ritualWinter
The very last part here (ritualWinter
) is the identifier (known as TrackId
) of the event. If you plug in any of the event IDs here, you will get a response like this:
{
"RewardTrackPath": "RewardTracks/Events/Rituals/ritualWinterGiveaway.json",
"TrackType": "Event",
"CurrentProgress": {
"Rank": 3,
"PartialProgress": 0,
"IsOwned": false,
"HasReachedMaxRank": false
},
"PreviousProgress": null,
"IsOwned": false,
"BaseXp": null,
"BoostXp": null
}
This will instantly tell you where in the progression ladder the user is. Not as convenient as getting them all in one go, but it works well nonetheless! The same works for operations, although with an ever-so-slightly different entrypoint:
https://economy.svc.halowaypoint.com
/hi
/players
/xuid(YOUR_XUID)
/rewardtracks
/operations
/S06OpPassM03
S06OpPassM03
is the operation or season TrackId
, and the response you will get is very similar to what you see for events:
{
"RewardTrackPath": "RewardTracks/Operations/S06OpPassM03.json",
"TrackType": "Operation",
"CurrentProgress": {
"Rank": 20,
"PartialProgress": 0,
"IsOwned": true,
"HasReachedMaxRank": true
},
"PreviousProgress": null,
"IsOwned": true,
"BaseXp": null,
"BoostXp": null
}
Conclusion #
Congratulations on sifting through walls of JSON to get here. As it turns out, getting information about battle pass (I use this term as a broad umbrella for all event types) progression is not as complicated as I initially thought. There are a few redundancy points that I had to navigate and even look through the old Fiddler snapshots of early Halo Infinite retail builds to get the details I needed about API behavior. I am just glad I didn’t need to deal with Bond-encoded data.
Armed with the knowledge above, you can now build your own Halo Infinite battle pass explorer experiences.