Ah, I understand your question now I think. In short, the specification is dynamic.
The message contents themselves (both transmitted and received) will vary depending on the “endpoint” you’re dealing with (byte index 2 and 3 (3rd and 4th)) - though there is a CRC that should be sent with each message; that CRC is static between messages (it is not a CRC of the message itself, but rather a CRC of the schema). “Endpoint” not in the USB sense… think of it merely as the numeric address of a remote property, method or function.
You can get a list of endpoints by querying endpoint 0 and providing 1 for the CRC. That will return a JSON representation of all the other endpoints, their accepted parameters, their return type and flags for read and write. You can see a non-JSON version of the schema in this file: https://github.com/madcowswe/ODrive/blob/master/Arduino/ArduinoI2C/odrive_endpoints.h and an outdated JSON version here: https://pastebin.com/GF9M5Hu6 (this is what endpoint 0 returns, minus any whitespace)
For example: To set the Axis0 position setpoint, you’d use EndpointID 87 and provide a float value starting at the packet’s 7th byte followed by the CRC.
Note that all communication is Little Endian rather than network byte order / Big Endian. The CRC will change any time the firmware changes in a manner that alters the schema in any way. It’s basically just a safeguard against communicating with a different version of the protocol than your program is anticipating, which is fairly important since the order/number of endpoint is dictated by the execution order in the firmware and some users machines can kill or maim.
Hop on the discord and say hi in the development channel.