Prerequisites
To be able to use the Trackman Play-By-Play Data-feed you will need to implement one or more Webhooks that support a Validation Handshake in order for the delivery to work.
This is required because the Trackman logic is built using Microsoft Azure Event Grid Topics, see more at https://aka.ms/esvalidation.
Microsoft have provided a small C# sample (but many programming languages are supported) of how to do this:
if (eventGridEvent.Data is SubscriptionValidationEventData)
{
var eventData = (SubscriptionValidationEventData)eventGridEvent.Data;
log.Info($"Got SubscriptionValidation event data, validationCode: {eventData.ValidationCode}, validationUrl: {eventData.ValidationUrl}, topic: {eventGridEvent.Topic}");
// Do any additional validation (as required) such as validating that the Azure resource ID of the topic matches
// the expected topic and then return back the below response
var responseData = new SubscriptionValidationResponse()
{
ValidationResponse = eventData.ValidationCode
};
return req.CreateResponse(HttpStatusCode.OK, responseData);
}Validation of Webhook
This validation Handshake will happen when the webhook is first configured by Trackman in Azure or if it needs to be updated later.
- At the time of event subscription creation/update, Event Grid posts a subscription validation event to the target endpoint.
- The event contains a header value aeg-event-type: SubscriptionValidation.
- The event body has the same schema as other Event Grid events.
- The eventType property of the event is Microsoft.EventGrid.SubscriptionValidationEvent.
- The data property of the event includes a validationCode property with a randomly generated string. For example, validationCode: acb13….
- The event data also includes a validationUrl property with a URL for manually validating the subscription.
- The array contains only the validation event. Other events are sent in a separate request after you echo back the validation code.
- The EventGrid data plane SDKs have classes corresponding to the subscription validation event data and subscription validation response
An example validation message will look like this:
[
{
"id": "2d1781af-3a4c-4d7c-bd0c-e34b19da4e66",
"topic": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"subject": "",
"data": {
"validationCode": "512d38b6-c7b8-40c8-89fe-f46f9e9622b6",
"validationUrl": "https://rp-eastus2.eventgrid.azure.net:553/eventsubscriptions/myeventsub/validate?id=0000000000-0000-0000-0000-00000000000000&t=2022-10-28T04:23:35.1981776Z&apiVersion=2018-05-01-preview&token=1A1A1A1A"
},
"eventType": "Microsoft.EventGrid.SubscriptionValidationEvent",
"eventTime": "2022-10-28T04:23:35.1981776Z",
"metadataVersion": "1",
"dataVersion": "1"
}
]Calling this Webhook it must return the ValidationResponse in order for the handshake to pass. It needs to be a HTTP 200 OK and must happen within 30 seconds or else the operation will be canceled.
{
"validationResponse": "validationCode"
}Look here for more information: https://learn.microsoft.com/en-us/azure/event-grid/end-point-validation-cloud-events-schema.
Data Feeds
The following list is the types and supported feeds and examples of the returned data.
The data will be wrapped in a message like this:
{
"id": "1e532635-8086-4572-b3cb-041c331e87bb",
"subject": "PlayByPlayFeed",
"data": {
"Payload here"
},
"eventType": "PlayByPlaySession",
"dataVersion": "2.0",
"metadataVersion": "1",
"eventTime": "2023-04-26T06:55:08.8313986Z",
"topic": "/subscriptions/faeb7dfb-7680-4a94-a196-a86269ca0747/resourceGroups/tmbaseball-organization-topics-release/providers/Microsoft.EventGrid/topics/9939933f-99ee-4eb6-9962-8ab99e15ce44"
}Game Feeds
Session
{
"Version" : "2.0.0",
"Time": "2018-02-22T20:39:17.312076160Z", // UTC timestamp from when the session was last updated
"SessionId" : "abc93729-ac13-11e9-9ed5-989096a0d95d", // unique session identifier
"GameReference" : "20180222-1",
"GameScheduleReference" : // can be null - will have data if game was started by game schedule
{
"ExternalSessionId" : "ABC123"
},
"SessionType" : "Standard", // Other types could be BattingPractice or HomeRunDerby
"League": {
"Name" : "AAA",
"ShortName" : "AAA"
},
"Level" : {
"Name" : "BBB"
},
"Location" : {
"Venue" : {
"Name" : "Tulsa (OK)"
},
"Field" : {
"Name" : "ONEOK Field",
"ShortName" : "ONEOKField"
}
},
"HomeTeam" : {
"ExternalTeamId" : "TTT",
"Name" : "Tulsa Drillers",
"ShortName" : "Tulsa",
"Lineup" : [
{
"ForeignId" : "12345",
"NameRef" : "Zastryzny, Rob",
"Position" : "P" // Possible positions are 1B, 2B, 3B, LF, CF, RF, SS, C, P, DH
},
{
"ForeignId" : "34567",
"NameRef" : "Motter, Taylor",
"Position" : "1B"
}
]
},
"AwayTeam" : {
"ExternalTeamId" : "TTT",
"Name" : "Midland RockHounds",
"ShortName" : "MID_ROC",
"Lineup" : [
{
"ForeignId" : "12345",
"NameRef" : "Rob Zastryzny",
"Position" : "P" // Possible positions are 1B, 2B, 3B, LF, CF, RF, SS, C, P, DH
},
{
"ForeignId" : "34567",
"NameRef" : "Taylor Motter",
"Position" : "1B"
}
]
},
"SessionState" : {
"State" : "SessionEnded", // Possible values: SessionStarted, SessionEnded
"SessionStartedUtc" : "2019-07-22T06:49:55.5143686Z", // can be null
"SessionStartedLocal" : "2019-07-22T06:49:55.5143686", // can be null
"SessionEndedUtc" : "2019-07-22T09:49:55.5143686Z", // can be null
"SessionEndedLocal" : "2019-07-22T09:49:55.5143686" // can be null
}
}Ball
{
"Version": "1.2.0",
"Time": "2018-02-22T20:39:17.312076160Z", // UTC timestamp from when the message was last updated
"SessionId": "abc93729-ac13-11e9-9ed5-989096a0d95d", // unique session identifier
"PlayId": "53427fe0-52e3-4017-9aa2-236c87718f6c",
"TrackId": "b2cc3109-7410-4150-867a-7b1bfb0fbc0c",
"TrackStartTime": "2019-09-04T14:37:14.531193Z",
"Kind": "Pitch", // can be Pitch, Hit or CatcherThrow (V3 only)
"Pitch": { // only if Kind = Pitch
"Release": {
"Speed": 0.0,
"SpinRate": null,
"Extension": 0.0,
"VerticalAngle": 0.0,
"HorizontalAngle": 0.0,
"Height": 1.812,
"Side": 0.652,
},
"Location": {
"Speed" 0.0
"Time": 0.0,
"Height": 1.812,
"Side": 0.652
},
"LocationMiddle": {
"Height": 1.812,
"Side": 0.652
},
"LocationBack": {
"Height": 1.812,
"Side": 0.652
},
"Movement": {
"Horizontal": 0.0,
"Vertical": 0.0,
"InducedVertical": 0.0,
"SpinAxis": 247.914355,
"Tilt": "1.5"
},
"NineP": {
"X0": {
"X": 0.759779,
"Y": 15.24,
"Z": 1.680944
},
"V0": {
"X": -2.688154,
"Y": -41.431425,
"Z": -0.591442
},
"A0": {
"X": 4.581591,
"Y": 8.427442,
"Z": -5.936245
},
"Pfxx": 0.202506,
"Pfxz": 0.171071
}
},
"Hit": { // only if Kind = Hit
"Launch": {
"Speed": 0.0,
"SpinRate": null,
"VerticalAngle": 30.939,
"HorizontalAngle": 21.68,
"SpinAxis": 227.915447,
},
"LandingFlat": {
"Time": 4.30478,
"Distance": 100.771,
"Bearing": 30.179
}
},
"CatcherThrow": { // only if V3
"ExchangeTime": 1.123,
"PopTime": 1.123
"Location":{
"Time": 4.5
}
"Release":{
"Speed": 53.2
}
}
}PlayMetadata
{
"Version": "1.0.0", version of the content of the message - always present
"Time": "2018-02-22T20:39:17.312076160Z", // UTC timestamp from when the message was last updated - always present
"SessionId": "abc93729-ac13-11e9-9ed5-989096a0d95d", // unique session identifier - always present
"PlayId": "53427fe0-52e3-4017-9aa2-236c87718f6c", // unique play identifier - always present
"TaggerBehavior": {
"IsWarmup": false,
"SequenceNumber": 3
},
"GameState": {
"Inning": 1,
"TopBottom": "Top"
},
"Players": { // The players of the play
"Pitcher": null, // can be null
// The batter
"Batter": { // can be null
"ForeignId": "12345",
"NameRef": "Jolly Wolly",
"Handedness": "Left" // the Batting Handedness: Left, Right, Both, Undefined
},
"Catcher": null // can be null
}
}StrikeZone
{
"Version":"1.0.0",
"Time":"2025-02-12T14:45:36.0957935Z",
"PlayId":"4d3936a8-4211-4b2d-bdb5-1dbb2d03b6e6",
"SessionId": "eb3e6925-60f1-435e-95fc-4c95b49bdcf9",
"StrikeZones":[
{
"Method":"Front",
"Top":40.15501,
"Bottom":19.69626,
"Right":8.75,
"Left":-8.75
},
{
"Method":"Middle",
"Top":40.15501,
"Bottom":19.69626,
"Right":8.75,
"Left":-8.75
},
{
"Method":"Back",
"Top":40.15501,
"Bottom":19.69626,
"Right":8.75,
"Left":-8.75
}
],
"Type":"HeightBased",
"Decision":"In",
"Batter":{
"Height":71.26
}
},
}Practice Feeds
Session
{
"Version" : "3.0.0",
"Time": "2018-02-22T20:39:17.312076160Z",
"SessionId" : "abc93729-ac13-11e9-9ed5-989096a0d95d",
"SessionType" : "Pitching",
"Location" : {
"Venue" : {
"Name" : "Practice Field Trip 1"
},
"Field" : {
"Name" : "Lane 1"
}
},
"Batters" : [
{
"ForeignId" : "123456",
"NameRef" : "Zastryzny, Rob"
},
{
"ForeignId" : "345678",
"NameRef" : "Motter, Taylor"
}
],
"Pitchers" : [
{
"ForeignId" : "123456",
"NameRef" : "Henderson, Jason"
},
{
"ForeignId" : "345678",
"NameRef" : "Gonzarles, Juan"
}
],
"SessionState" : {
"State" : "SessionEnded",
"SessionStartedUtc" : "2019-07-22T06:49:55.5143686Z",
"SessionStartedLocal" : "2019-07-22T06:49:55.5143686",
"SessionEndedUtc" : "2019-07-22T09:49:55.5143686Z",
"SessionEndedLocal" : "2019-07-22T09:49:55.5143686"
}
}Ball
{
"Version": "1.2.0",
"Time": "2018-02-22T20:39:17.312076160Z",
"SessionId": "abc93729-ac13-11e9-9ed5-989096a0d95d",
"PlayId": "53427fe0-52e3-4017-9aa2-236c87718f6c",
"TrackId": "b2cc3109-7410-4150-867a-7b1bfb0fbc0c",
"TrackStartTime": "2019-09-04T14:37:14.531193Z",
"Kind": "Pitch",
"Pitch": {
"Release": {
"Speed": 0.0,
"SpinRate": null,
"Extension": 0.0,
"VerticalAngle": 0.0,
"HorizontalAngle": 0.0,
"Height": 1.812,
"SpinAxis3dTilt": "1:15",
"SpinAxis3dActiveSpinRate": 2126.816089,
"SpinAxis3dSpinEfficiency": 0.9985436257,
"SpinAxis3dTransverseAngle": 214.5010932,
"SpinAxis3dLongitudinalAngle": 3.092622133
},
"Location": {
"Time": 0.0,
"Height": 1.812,
"Side": 0.652
},
"Movement": {
"Horizontal": 0.0,
"Vertical": 0.0,
"InducedVertical": 0.0,
"SpinAxis": 247.914355
},
"NineP": {
"X0": [0.759779, 15.24, 1.680944],
"V0": [-2.688154, -41.431425, -0.591442],
"A0": [4.581591, 8.427442, -5.936245],
"Pfxx": 0.202506,
"Pfxz": 0.171071
}
},
"Hit": {
"Launch": {
"Speed": 0.0,
"SpinRate": null,
"VerticalAngle": 30.939,
"HorizontalAngle": 21.68
},
"LandingFlat": {
"Time": 4.30478,
"Distance": 100.771,
"Bearing": 30.179
}
}
}PlayMetadata
{
"Version": "1.0.0", // version of the content of the message - always present
"Time": "2018-02-22T20:39:17.312076160Z", // UTC timestamp from when the message was last updated - always present
"SessionId": "abc93729-ac13-11e9-9ed5-989096a0d95d", // unique session identifier - always present
"PlayId": "53427fe0-52e3-4017-9aa2-236c87718f6c", // unique play identifier - always present
"TaggerBehavior": {
"IsWarmup": false,
"SequenceNumber": 3
},
"Players": { // The players of the play
"Pitcher": null, // can be null
// The batter
"Batter": { // can be null
"NameRef": "Jolly Wolly",
"Handedness": "Left", // the Batting Handedness can be Left Right Both Undefined
"ForeignId": "32341" // Prioritized foreign identifier for the player without prefix and taking the Pro Id override into account
},
"Catcher": null // can be null
}
}