properties maps each member name to its own node, nested as deep as the body is:
Pet: type: object properties: owner: type: object properties: name: type: string
A property name or a schema name must not be empty, must not contain ., [, ], {, } or ;, and must not contain control characters. The six characters are reserved for the paths the broker compares and shows: ., [] and {} write $.owner.name, $[].photoUrl and $.pets{}.name. Any other name is valid, such as content-type, @type, $ref or first name:
contract validation failed - petstore_api.yaml: invalid property name "user.name" at schemas Pet properties, must not contain ".", "[", "]", "{", "}" or ";"
An empty name ends with must not be empty, and a name with a control character with must not contain control characters. A property spelled differently from the body is renamed to the key the body has. A key the body really has with one of these characters is left out of the contract when it is fixed, and a property no side declares is not checked (Which side decides). Keys that vary go in a map, whose keys are not property names (Maps).
An object without properties, or with properties: {}, is rejected:
contract validation failed - petstore_web.yaml: object schema without properties at schemas Pet properties metadata, declare the keys you read, or use "type: map" with "values" if the keys vary
The fix is to declare the keys that arrive in the body, except a fixed key with a reserved character in its name, which stays out. The side that reads the body marks each key its code does not read with optional: true, so the side that produces the body is free to drop it. The side that produces the body declares the keys its code sends. When the keys vary, both sides write a map with values instead (Maps).
Here petstore_web only counts a pet's vaccinations. Each vaccination arrives with a name and a date, and the code reads neither:
Pet: type: object properties: vaccinations: type: array items: type: object properties: name: type: string optional: true date: type: string optional: true