SGA‑OISOpen Interoperability Standard
1.0 Draft

Integrate with SGA in an afternoon

Four steps. That is the whole protocol.

The four steps: 1. Connect — open a connection to the SGA Engine, receive HELLO. 2. Describe — say who you are, what you can provide, and what data you want back. 3. Manifest — receive the manifest of each field and which devices will be providing it. 4. Exchange — send your data as you get it, and the engine pushes you the data you asked for.
Connect, describe what you provide and want, receive the manifest of who feeds whom, then exchange data as the round is played.

You implement once. You then work with every launch monitor, simulator, display and accessory that also implements it — including ones that do not exist yet.


The whole thing, in two frames#

Examples in
JSON
// The two frames your device sends. The engine sends session.hello first and
// answers with session.manifest; neither needs anything from you but a read.

{
  "type": "session.describe",

  "identity": { "device_id": "sga:device:ax3-00119423", "vendor": "acme",
                "model": "AX-3", "serial": "AX3-00119423",
                "firmware": "4.2.1", "attestation_tier": "A0",
                "sensors": ["radar_doppler"] },
  // A0 = no agreement, no key. Fine to start; see "How far your data counts".

  "roles": ["sensor.ball"],
  "protocol_version": "1.0",
  "built_against_bundle": "2026.09.1",

  "provides": [
    { "field": "ball.speed_mps",        "provenance": "measured" },
    { "field": "ball.launch_angle_deg", "provenance": "measured" },
    { "field": "ball.launch_direction_deg", "provenance": "measured" },
    { "field": "ball.spin_rpm",         "provenance": "measured" },
    { "field": "club.speed_mps",        "provenance": "inferred",
      "model": { "id": "acme.smash", "version": "2.1" } }
  ],

  "wants": [ { "field": "round.player_up" }, { "field": "origin.lie_surface" } ],

  "transports": ["tcp"]
}

{
  "type": "record.shot",
  "field": "ball.speed_mps",
  "ball": { "speed_mps": 64.8, "launch_angle_deg": 12.4,
            "launch_direction_deg": -1.7, "spin_rpm": 2810 }
}

That is a conformant launch monitor.

The JSON is the standard; the rest is plumbing. Those two frames are what your device has to produce, and they are the same bytes whatever you write them in — which is why they are the default above. The Python and C# options show the same two frames with a socket around them, and both run against the reference bay as they stand.

The plumbing really is that small: a 4-byte big-endian length, then that many bytes of UTF-8 JSON.

00 00 01 63   {"type":"session.describe","identity":{"device_id":"sga:dev...
└─ 355 ──┘    └─ exactly 355 bytes of UTF-8 JSON ───────────────────────────

Raw TCP is the binding every engine MUST accept (09 §2.0), so a microcontroller with a TCP stack and a JSON encoder is a fully conformant participant. Everything else in the standard is optional detail on top of it.


Three rules that are not obvious#

1. Units are fixed and live in the field name. ball.speed_mps is always meters per second. There is no units mode to set and no unit tag to send. Convert once, in your adapter, where you know what your sensor produces.

2. Say how you know each value. measured if a sensor observed it, calculated if you computed it from things you measured on that shot, inferred if you modeled it. Nobody is judged for inferred — plenty of excellent hardware infers plenty of fields. Claiming measured for something you modeled is the only failure, and it is what certification checks.

3. Absent is a fine answer. Omit any field you do not have. A shot record with four fields is valid and useful. There is no minimum beyond your device class. Whether a round counts toward a player rating depends on trust, not on how many fields it has (below).


How far your data counts#

A finished result is only as trusted as the lowest level in its chain: the launch monitor, the simulator, and the bay. You start at A0, which is fine for building and for casual play. Becoming a technology partner (free) takes you to A1, where rounds count toward an SGA Player Rating and leagues: you promise not to falsify data or build cheating features, to tell SGA about any way you find that would allow either, and you publish a fingerprint of each release. Signing what you send (A2, A3) unlocks sanctioned events and championships.

The full ladder: How far a result can be trusted.


Next#

you areread
a launch monitorLaunch monitor integration
simulator softwareSimulator software integration
an accessory, actuator, RFID reader, display or HUDAccessory and single-purpose device integration
hosting tournaments or leagues../spec/08-competition.md

Check yourself before you talk to us. The conformance kit is public, runs offline, and needs no account. From your copy of the repository:

shell
cd sga-ois/conformance
python -m sgaconform path/to/your-descriptor.json

The kit checks documents: your descriptor, and a simulator's scorecards. To watch live frames such as record.shot, use the reference bay (Testing your integration).

Full field reference: the field catalog, generated from the current bundle.

Need a field that does not exist? The right route is to work with the community to add it to the common namespace, so every product uses the same name for the same thing (extending the standard). We never want to be the reason you wait, though: your own x-yourvendor. namespace is always available, so you can ship today and move the field into the common namespace once it is agreed.


Testing your integration · Become a technology partner

Simulated Golf Association · SGA-OIS · generated from the specification sources