{
  "description": "Provider report of discovered routes, group routes and their relationships. Plugin and auth-link identity come from the authenticated publisher context.",
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "pluginIdentity": {
      "type": "object",
      "description": "Identifies a plugin client by its type and unique identifier.",
      "required": [
        "pluginType",
        "pluginId"
      ],
      "properties": {
        "pluginType": {
          "type": "string",
          "enum": [
            "WEBSOCKET",
            "HTTP",
            "WEBSERVICE",
            "APPLE_PUSH_NOTIFICATION",
            "HOSTED_COMPUTE"
          ]
        },
        "pluginId": {
          "type": "string",
          "description": "The ID of the plugin"
        }
      },
      "title": "ClientIdentity"
    },
    "authLinkIdentity": {
      "type": "string",
      "description": "The ID of the auth link the routes were discovered for."
    },
    "routes": {
      "type": "array",
      "maxItems": 1000,
      "items": {
        "type": "object",
        "description": "Provider input before host synchronization assigns persisted route identity and ownership. Canonical provider facts reuse Route property definitions. The Go projection reuses the Route wire adapter; validation uses this input schema, not persisted Route. Host identity fields may be omitted or empty and never establish authenticated publisher or installation authority. Nonempty id and associatedEndpointId may select an existing authorized route during scoped rediscovery; newly created routes and endpoints receive host-assigned IDs.\n",
        "required": [
          "associatedPluginRouteId",
          "interfaces",
          "ontologicalTypes"
        ],
        "properties": {
          "associatedPluginRouteId": {
            "allOf": [
              {
                "type": "string",
                "description": "The stable plugin-owned route identifier associated with this Port OS route. For synchronized plugin capability contracts and fixture parity checks, use this field to match a route from enumeration back to the discovery payload.\n"
              }
            ],
            "minLength": 1
          },
          "externalId": {
            "type": "string",
            "description": "The external ID of the route given by the reporting system"
          },
          "pluginProfileId": {
            "type": "string",
            "minLength": 1,
            "description": "Stable profile ID selected from the registered plugin declaration. Required for V2 plugin discovery; retained with the route for admission and presentation."
          },
          "interfaces": {
            "type": "array",
            "items": {
              "$ref": "#/properties/groupRoutes/items/properties/interfaces/items"
            },
            "description": "List of interfaces supported by the route. These denote what the route is capable of performing such as turning off/on, etc."
          },
          "ontologicalTypes": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "List of ontological types for the route. These are used mostly for customers to know what an endpoint is, like if its a lightbulb, or a speaker, etc."
          },
          "name": {
            "description": "The name of the route as a human readable string or a localizable asset. This is either a raw string or a localizable asset reference.",
            "allOf": [
              {
                "type": "object",
                "required": [
                  "type",
                  "value"
                ],
                "properties": {
                  "type": {
                    "type": "string",
                    "description": "The type identifier of the name value, defined as plain when not defined."
                  },
                  "value": {
                    "type": "string",
                    "description": "the raw value of a plain name."
                  }
                }
              }
            ]
          },
          "description": {
            "description": "The description of the route as a human readable string or a localizable asset.",
            "allOf": [
              {
                "$ref": "#/properties/routes/items/properties/name/allOf/0"
              }
            ]
          },
          "manufacturer": {
            "description": "The manufacturer of the route as a human readable string or a localizable asset.",
            "allOf": [
              {
                "$ref": "#/properties/routes/items/properties/name/allOf/0"
              }
            ]
          },
          "model": {
            "description": "The model of the route as a human readable string or a localizable asset.",
            "allOf": [
              {
                "$ref": "#/properties/routes/items/properties/name/allOf/0"
              }
            ]
          },
          "serialNumber": {
            "description": "The serial number of the route as a human readable string or a localizable asset.",
            "allOf": [
              {
                "$ref": "#/properties/routes/items/properties/name/allOf/0"
              }
            ]
          },
          "pluginCustomMetadata": {
            "type": "object",
            "additionalProperties": true,
            "description": "A set of custom metadata that the plugin uses and attaches to the route, this is meant specifically for the plugin to use."
          },
          "id": {
            "type": "string",
            "description": "Advisory value, including empty. Together with associatedEndpointId it may select an existing route within the authenticated scope. New persisted IDs are assigned by the host and validated by the separate persisted Route schema."
          },
          "associatedPluginId": {
            "type": "string",
            "description": "The ID of the associated plugin"
          },
          "associatedAuthLinkId": {
            "type": "string",
            "description": "The ID of the associated auth link"
          },
          "associatedEndpointId": {
            "type": "string",
            "description": "The ID of the associated endpoint"
          }
        },
        "additionalProperties": false,
        "title": "ProviderDiscoveredRoute"
      }
    },
    "groupRoutes": {
      "type": "array",
      "items": {
        "type": "object",
        "description": "A provider-reported group route (for example a vacuum map or room, a lighting room or a speaker group) as carried by discovery. It is shared by the route-discovery report message and `SynchronizationRequest.groupRoutes` so both decode to the same model.\n",
        "required": [
          "pluginRouteId",
          "name"
        ],
        "properties": {
          "pluginProfileId": {
            "type": "string",
            "minLength": 1,
            "description": "Stable group-route profile ID selected from the registered V2 plugin declaration."
          },
          "ontologicalTypes": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Actual classifications of the native group route, constrained by its selected plugin profile for V2 discovery."
          },
          "pluginRouteId": {
            "type": "string",
            "minLength": 1,
            "description": "Plugin-owned stable identifier of the group route. Must be unique across routes and group routes within one plugin and auth link.\n"
          },
          "name": {
            "type": "string",
            "description": "The name of the associated group route"
          },
          "interfaces": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "name"
              ],
              "properties": {
                "name": {
                  "type": "string",
                  "description": "The name of the interface"
                },
                "version": {
                  "type": "string",
                  "description": "the version of the interface"
                },
                "messages": {
                  "type": "array",
                  "nullable": true,
                  "description": "Supported inbound message subset for the declared interface version. An omitted or empty list declares no supported messages; null also declares none. A supplied array contains distinct canonical names for implemented messages.",
                  "items": {
                    "type": "object",
                    "required": [
                      "name"
                    ],
                    "properties": {
                      "name": {
                        "type": "string",
                        "description": "The name of the command"
                      }
                    },
                    "title": "ApplicationInterfaceMessage"
                  }
                },
                "attributes": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "required": [
                      "name"
                    ],
                    "properties": {
                      "name": {
                        "type": "string",
                        "description": "The name of the attribute"
                      },
                      "value": {
                        "description": "the value of the attribute, this is an opaque resource object, so it can be any type of struct.",
                        "type": "object",
                        "additionalProperties": true
                      }
                    },
                    "title": "ApplicationInterfaceAttribute"
                  },
                  "description": "Supported attribute subset. An omitted or empty list declares no supported attributes."
                },
                "configuration": {
                  "type": "object",
                  "additionalProperties": true,
                  "description": "The configuration of the interface"
                }
              },
              "title": "RouteInterface"
            }
          },
          "memberPluginRouteIds": {
            "type": "array",
            "uniqueItems": true,
            "items": {
              "type": "string"
            },
            "description": "List of plugin route IDs that are members of this group route"
          },
          "pluginCustomMetadata": {
            "type": "object",
            "additionalProperties": true,
            "description": "Private plugin metadata (native map, segment or cloud room addressing) returned to the plugin on dispatch; never exposed as application data.\n"
          }
        },
        "additionalProperties": false,
        "title": "GroupRoute"
      }
    },
    "relationships": {
      "type": "array",
      "items": {
        "type": "object",
        "description": "A provider-reported relationship between typed port resources. Identity is `externalId` within the reporting plugin and auth link; resubmitting the same `externalId` updates the relationship. Omission from a later synchronization is not a deletion instruction.\n",
        "additionalProperties": false,
        "required": [
          "externalId",
          "type",
          "source",
          "target"
        ],
        "properties": {
          "externalId": {
            "type": "string",
            "minLength": 1,
            "description": "Plugin-owned stable identifier of the relationship."
          },
          "type": {
            "type": "string",
            "description": "Typed relationship between port resources. `IS_PART_OF`: a resource is contained in a parent resource (acyclic). `CAN_CONTROL`: an endpoint or route can act on another resource with the listed interface messages; it never grants authorization. `LOCATED_IN`: an endpoint route is currently observed inside a group route; it is an observation, not membership. `MEMBER_OF`: an endpoint belongs to a group.\n",
            "enum": [
              "IS_PART_OF",
              "CAN_CONTROL",
              "LOCATED_IN",
              "MEMBER_OF"
            ]
          },
          "source": {
            "type": "object",
            "description": "One end of a discovered relationship, addressed by plugin-owned identity within the reporting plugin and auth link. Exactly one of `pluginRouteId` (a route), `groupPluginRouteId` (a group route), `endpointId` or `groupId` must be set.\n",
            "additionalProperties": false,
            "minProperties": 1,
            "maxProperties": 1,
            "properties": {
              "pluginRouteId": {
                "type": "string",
                "minLength": 1,
                "description": "The plugin route ID of a route reported in the same or an earlier synchronization."
              },
              "groupPluginRouteId": {
                "type": "string",
                "minLength": 1,
                "description": "The plugin route ID of a group route reported in the same or an earlier synchronization."
              },
              "endpointId": {
                "type": "string",
                "minLength": 1,
                "description": "The full ID of an endpoint owned by the reporting principal."
              },
              "groupId": {
                "type": "string",
                "minLength": 1,
                "description": "The full ID of a group owned by the reporting principal."
              }
            },
            "title": "PluginDiscoveryRelationshipVertex"
          },
          "target": {
            "$ref": "#/properties/relationships/items/properties/source"
          },
          "interfaces": {
            "type": "array",
            "minItems": 1,
            "description": "Optional interface applicability; used by `CAN_CONTROL` to declare which messages the source supports on the target.",
            "items": {
              "type": "object",
              "description": "Capability-interface applicability of a relationship, for example the robotic-vacuum-cleaner messages a `CAN_CONTROL` edge supports on a room.\n",
              "additionalProperties": false,
              "required": [
                "name",
                "version"
              ],
              "properties": {
                "name": {
                  "type": "string",
                  "minLength": 1,
                  "description": "The capability interface namespace.",
                  "example": "port1/systems/zero/capability-interfaces/robotic-vacuum-cleaner"
                },
                "version": {
                  "type": "string",
                  "minLength": 1,
                  "description": "The capability interface version.",
                  "example": "1.0"
                },
                "messages": {
                  "type": "array",
                  "uniqueItems": true,
                  "description": "Message names of the interface the relationship applies to. An explicit empty array applies to no messages. Omission retains every message supported by the source interface.",
                  "items": {
                    "type": "string",
                    "minLength": 1
                  },
                  "example": [
                    "clean-room"
                  ]
                }
              },
              "title": "RelationshipInterface"
            }
          }
        },
        "title": "PluginDiscoveryRelationship"
      }
    }
  }
}
