{
  "openapi": "3.1.0",
  "info": {
    "title": "Zoiva API",
    "summary": "Manage your houses and Zoiva radars, and read their data.",
    "description": "Authenticate with OAuth 2.0 client credentials: exchange your client id and secret for an access token at the token URL, then send `Authorization: Bearer <token>` on every request. Each route lists the scope it needs. Errors always look like `{\"error\": {\"code\": ..., \"message\": ..., \"request_id\": ...}}`.",
    "version": "2.1.0"
  },
  "paths": {
    "/v1/health": {
      "get": {
        "tags": [
          "Health"
        ],
        "summary": "Is the API up?",
        "operationId": "health_v1_health_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "type": "object",
                  "title": "Response Health V1 Health Get"
                }
              }
            }
          }
        }
      }
    },
    "/v1/houses": {
      "get": {
        "tags": [
          "Houses"
        ],
        "summary": "List houses",
        "operationId": "list_houses_v1_houses_get",
        "security": [
          {
            "OAuth2": [
              "zoiva/houses.read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "maximum": 100,
              "minimum": 1,
              "default": 50,
              "title": "Limit"
            }
          },
          {
            "name": "next_token",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string",
                  "maxLength": 2048
                },
                {
                  "type": "null"
                }
              ],
              "title": "Next Token"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Page_House_"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Houses"
        ],
        "summary": "Create a house",
        "description": "Zoiva generates the house_id: <org>-<10 hex>, or <org>-<id_prefix>-<10 hex> if you send an optional id_prefix (never personal details: it's permanent). The name is yours. Send an Idempotency-Key header so a retried request returns the same house instead of creating a second one.",
        "operationId": "create_house_v1_houses_post",
        "security": [
          {
            "OAuth2": [
              "zoiva/houses.write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string",
                  "pattern": "^[A-Za-z0-9_.:-]{8,128}$"
                },
                {
                  "type": "null"
                }
              ],
              "description": "A unique value per create (e.g. a UUID): retries with the same key are safe",
              "title": "Idempotency-Key"
            },
            "description": "A unique value per create (e.g. a UUID): retries with the same key are safe"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/HouseCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/House"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/v1/houses/{house_id}": {
      "get": {
        "tags": [
          "Houses"
        ],
        "summary": "Get a house",
        "operationId": "get_house_v1_houses__house_id__get",
        "security": [
          {
            "OAuth2": [
              "zoiva/houses.read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "house_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9][A-Za-z0-9._:@-]{0,127}$",
              "description": "House id (<org>-<10 hex>)",
              "title": "House Id"
            },
            "description": "House id (<org>-<10 hex>)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/House"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "Houses"
        ],
        "summary": "Change a house's name, metadata or summary frequency",
        "description": "Only the fields you send change. summary_frequency_minutes applies to every radar in the house (new ones too) without a frequency of its own. null clears it.",
        "operationId": "update_house_v1_houses__house_id__patch",
        "security": [
          {
            "OAuth2": [
              "zoiva/houses.write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "house_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9][A-Za-z0-9._:@-]{0,127}$",
              "description": "House id (<org>-<10 hex>)",
              "title": "House Id"
            },
            "description": "House id (<org>-<10 hex>)"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/HouseUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/House"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Houses"
        ],
        "summary": "Delete an empty house",
        "operationId": "delete_house_v1_houses__house_id__delete",
        "security": [
          {
            "OAuth2": [
              "zoiva/houses.write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "house_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9][A-Za-z0-9._:@-]{0,127}$",
              "description": "House id (<org>-<10 hex>)",
              "title": "House Id"
            },
            "description": "House id (<org>-<10 hex>)"
          }
        ],
        "responses": {
          "204": {
            "description": "Successful Response"
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/v1/houses/{house_id}/devices": {
      "patch": {
        "tags": [
          "Houses"
        ],
        "summary": "Change every radar in a house",
        "description": "The same room, location, metadata or summary_frequency_minutes for each radar in the house, as PATCH /v1/devices does for a list of radars. To set a frequency for the house itself (new radars too), use PATCH /v1/houses/{house_id}.",
        "operationId": "update_house_devices_v1_houses__house_id__devices_patch",
        "security": [
          {
            "OAuth2": [
              "zoiva/devices.write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "house_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9][A-Za-z0-9._:@-]{0,127}$",
              "description": "House id (<org>-<10 hex>)",
              "title": "House Id"
            },
            "description": "House id (<org>-<10 hex>)"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DeviceChanges"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BulkResult"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Houses"
        ],
        "summary": "List the radars in a house",
        "operationId": "list_house_devices_v1_houses__house_id__devices_get",
        "security": [
          {
            "OAuth2": [
              "zoiva/devices.read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "house_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9][A-Za-z0-9._:@-]{0,127}$",
              "description": "House id (<org>-<10 hex>)",
              "title": "House Id"
            },
            "description": "House id (<org>-<10 hex>)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Page_Device_"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Houses"
        ],
        "summary": "Add a radar to a house",
        "description": "Add a radar by the serial number on its label. Zoiva generates its device_id (<org>-<10 hex>, or <org>-<id_prefix>-<10 hex> with an optional id_prefix), which is used in all its data. The name is yours. Switch the radar on and it connects by itself, because its serial is in one of your houses. You then get a device.provisioned event. Adding the same serial to the same house again returns the existing radar (200), so retries are safe. A serial already in another house is refused (409).",
        "operationId": "add_device_v1_houses__house_id__devices_post",
        "security": [
          {
            "OAuth2": [
              "zoiva/devices.write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "house_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9][A-Za-z0-9._:@-]{0,127}$",
              "description": "House id (<org>-<10 hex>)",
              "title": "House Id"
            },
            "description": "House id (<org>-<10 hex>)"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DeviceCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Device"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/v1/devices": {
      "patch": {
        "tags": [
          "Radars"
        ],
        "summary": "Change many radars at once",
        "description": "The same room, location, metadata or summary_frequency_minutes for up to 100 radars. Each radar is updated on its own: one that fails (not found, changed at the same time) is listed in failed and the others still change. Moving radars between houses is one radar at a time.",
        "operationId": "bulk_update_devices_v1_devices_patch",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BulkDeviceUpdate"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BulkResult"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "zoiva/devices.write"
            ]
          }
        ]
      }
    },
    "/v1/devices/{device_id}": {
      "get": {
        "tags": [
          "Radars"
        ],
        "summary": "Get a radar",
        "operationId": "get_device_v1_devices__device_id__get",
        "security": [
          {
            "OAuth2": [
              "zoiva/devices.read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "device_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9][A-Za-z0-9._:@-]{0,127}$",
              "description": "Device id (<org>-<10 hex>)",
              "title": "Device Id"
            },
            "description": "Device id (<org>-<10 hex>)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Device"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "Radars"
        ],
        "summary": "Change a radar's details or summary frequency, or move it to another house",
        "description": "Only the fields you send change. metadata is replaced as a whole. The next data.summary describes the radar as it is then.",
        "operationId": "update_device_v1_devices__device_id__patch",
        "security": [
          {
            "OAuth2": [
              "zoiva/devices.write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "device_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9][A-Za-z0-9._:@-]{0,127}$",
              "description": "Device id (<org>-<10 hex>)",
              "title": "Device Id"
            },
            "description": "Device id (<org>-<10 hex>)"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DeviceUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Device"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Radars"
        ],
        "summary": "Remove a radar",
        "description": "The radar can no longer connect until it is added to a house again.",
        "operationId": "remove_device_v1_devices__device_id__delete",
        "security": [
          {
            "OAuth2": [
              "zoiva/devices.write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "device_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9][A-Za-z0-9._:@-]{0,127}$",
              "description": "Device id (<org>-<10 hex>)",
              "title": "Device Id"
            },
            "description": "Device id (<org>-<10 hex>)"
          }
        ],
        "responses": {
          "204": {
            "description": "Successful Response"
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/v1/devices/{device_id}/reprovision": {
      "post": {
        "tags": [
          "Radars"
        ],
        "summary": "Let a radar provision again",
        "description": "For a radar that needs to connect afresh, e.g. after it was reset or repaired. Once provisioned, a radar can't provision again, so nobody else can take over its identity. This opens a 24-hour window for it. Switch the radar on within the window. Its old certificate stops working as soon as it provisions.",
        "operationId": "allow_reprovision_v1_devices__device_id__reprovision_post",
        "security": [
          {
            "OAuth2": [
              "zoiva/devices.write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "device_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9][A-Za-z0-9._:@-]{0,127}$",
              "description": "Device id (<org>-<10 hex>)",
              "title": "Device Id"
            },
            "description": "Device id (<org>-<10 hex>)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Device"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/v1/devices/{device_id}/recent": {
      "get": {
        "tags": [
          "Data"
        ],
        "summary": "A radar's recent second-level samples (last 3 days)",
        "description": "Newest first, a few seconds per batch. Pass next_token back for older batches. Times are ISO 8601 in UTC ending in Z, e.g. 2026-10-06T11:18:25Z.",
        "operationId": "recent_v1_devices__device_id__recent_get",
        "security": [
          {
            "OAuth2": [
              "zoiva/data.read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "device_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9][A-Za-z0-9._:@-]{0,127}$",
              "description": "Device id (<org>-<10 hex>)",
              "title": "Device Id"
            },
            "description": "Device id (<org>-<10 hex>)"
          },
          {
            "name": "minutes",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "maximum": 4320,
              "minimum": 1,
              "description": "How far back to look (ignored if since is set)",
              "default": 10,
              "title": "Minutes"
            },
            "description": "How far back to look (ignored if since is set)"
          },
          {
            "name": "since",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string",
                  "format": "date-time"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Start (ISO 8601, UTC). Default: until, minus minutes",
              "title": "Since"
            },
            "description": "Start (ISO 8601, UTC). Default: until, minus minutes"
          },
          {
            "name": "until",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string",
                  "format": "date-time"
                },
                {
                  "type": "null"
                }
              ],
              "description": "End (ISO 8601, UTC). Default: now",
              "title": "Until"
            },
            "description": "End (ISO 8601, UTC). Default: now"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "maximum": 500,
              "minimum": 1,
              "description": "Sample batches per page (one batch is a few seconds)",
              "default": 100,
              "title": "Limit"
            },
            "description": "Sample batches per page (one batch is a few seconds)"
          },
          {
            "name": "next_token",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string",
                  "maxLength": 256
                },
                {
                  "type": "null"
                }
              ],
              "title": "Next Token"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Page_SampleBatch_"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/v1/houses/{house_id}/recent": {
      "get": {
        "tags": [
          "Data"
        ],
        "summary": "A house's recent second-level samples (last 3 days), every radar",
        "description": "Every radar now in the house, newest first. Each batch says which radar sent it (device_id). Pass next_token back for older batches.",
        "operationId": "house_recent_v1_houses__house_id__recent_get",
        "security": [
          {
            "OAuth2": [
              "zoiva/data.read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "house_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9][A-Za-z0-9._:@-]{0,127}$",
              "description": "House id (<org>-<10 hex>)",
              "title": "House Id"
            },
            "description": "House id (<org>-<10 hex>)"
          },
          {
            "name": "minutes",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "maximum": 4320,
              "minimum": 1,
              "description": "How far back to look (ignored if since is set)",
              "default": 10,
              "title": "Minutes"
            },
            "description": "How far back to look (ignored if since is set)"
          },
          {
            "name": "since",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string",
                  "format": "date-time"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Start (ISO 8601, UTC). Default: until, minus minutes",
              "title": "Since"
            },
            "description": "Start (ISO 8601, UTC). Default: until, minus minutes"
          },
          {
            "name": "until",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string",
                  "format": "date-time"
                },
                {
                  "type": "null"
                }
              ],
              "description": "End (ISO 8601, UTC). Default: now",
              "title": "Until"
            },
            "description": "End (ISO 8601, UTC). Default: now"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "maximum": 500,
              "minimum": 1,
              "description": "Sample batches per page, across all radars",
              "default": 100,
              "title": "Limit"
            },
            "description": "Sample batches per page, across all radars"
          },
          {
            "name": "next_token",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string",
                  "maxLength": 256
                },
                {
                  "type": "null"
                }
              ],
              "title": "Next Token"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Page_SampleBatch_"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/v1/summaries": {
      "get": {
        "tags": [
          "Data"
        ],
        "summary": "Five-minute summaries already sent (to backfill)",
        "description": "The data.summary events sent for windows starting between start and end, exactly as they were delivered, oldest first. Ask for every radar, one radar (device_id) or one house (house_id). Use it after your webhook endpoint was down, and skip the event_ids you already have. Summaries are kept for 30 days. Ask Zoiva for older ones.",
        "operationId": "list_summaries_v1_summaries_get",
        "security": [
          {
            "OAuth2": [
              "zoiva/data.read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "start",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "date-time",
              "description": "Windows starting from this time (ISO 8601)",
              "title": "Start"
            },
            "description": "Windows starting from this time (ISO 8601)"
          },
          {
            "name": "end",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "date-time",
              "description": "Up to this time, not included (ISO 8601). At most 31 days after start",
              "title": "End"
            },
            "description": "Up to this time, not included (ISO 8601). At most 31 days after start"
          },
          {
            "name": "device_id",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string",
                  "pattern": "^[A-Za-z0-9][A-Za-z0-9._:@-]{0,127}$"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Only this radar",
              "title": "Device Id"
            },
            "description": "Only this radar"
          },
          {
            "name": "house_id",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string",
                  "pattern": "^[A-Za-z0-9][A-Za-z0-9._:@-]{0,127}$"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Only radars while in this house",
              "title": "House Id"
            },
            "description": "Only radars while in this house"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "maximum": 500,
              "minimum": 1,
              "default": 100,
              "title": "Limit"
            }
          },
          {
            "name": "next_token",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string",
                  "maxLength": 2048
                },
                {
                  "type": "null"
                }
              ],
              "title": "Next Token"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Page_Summary_"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/v1/devices/{device_id}/samples": {
      "get": {
        "tags": [
          "Data"
        ],
        "summary": "A radar's second-level samples, full history",
        "description": "Every second-level sample received in the time range, one type at a time (type: presence, vitals, activity, gait, sleep, quality or features), as the radar sent it (nested fields as JSON objects), oldest first. For the last 3 days, /recent (every type together) is faster.",
        "operationId": "samples_v1_devices__device_id__samples_get",
        "security": [
          {
            "OAuth2": [
              "zoiva/data.read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "device_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9][A-Za-z0-9._:@-]{0,127}$",
              "description": "Device id (<org>-<10 hex>)",
              "title": "Device Id"
            },
            "description": "Device id (<org>-<10 hex>)"
          },
          {
            "name": "start",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "date-time",
              "description": "From this time (ISO 8601)",
              "title": "Start"
            },
            "description": "From this time (ISO 8601)"
          },
          {
            "name": "end",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "date-time",
              "description": "Up to this time, not included (ISO 8601). At most 24 hours after start",
              "title": "End"
            },
            "description": "Up to this time, not included (ISO 8601). At most 24 hours after start"
          },
          {
            "name": "type",
            "in": "query",
            "required": true,
            "schema": {
              "enum": [
                "presence",
                "vitals",
                "activity",
                "gait",
                "sleep",
                "quality",
                "features"
              ],
              "type": "string",
              "description": "Which part of the samples: presence, vitals, activity, gait, sleep, quality (with status, clock and transport) or features (any model's outputs)",
              "title": "Type"
            },
            "description": "Which part of the samples: presence, vitals, activity, gait, sleep, quality (with status, clock and transport) or features (any model's outputs)"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "maximum": 1000,
              "minimum": 1,
              "default": 500,
              "title": "Limit"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QueryResult"
                }
              }
            }
          },
          "202": {
            "description": "Still running: poll /v1/queries/{query_id}",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QueryResult"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/v1/houses/{house_id}/samples": {
      "get": {
        "tags": [
          "Data"
        ],
        "summary": "A house's second-level samples, every radar, full history",
        "description": "Every second-level sample received in the time range, one type at a time (type: presence, vitals, activity, gait, sleep, quality or features), as the radar sent it (nested fields as JSON objects), oldest first. For the last 3 days, /recent (every type together) is faster.",
        "operationId": "house_samples_v1_houses__house_id__samples_get",
        "security": [
          {
            "OAuth2": [
              "zoiva/data.read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "house_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9][A-Za-z0-9._:@-]{0,127}$",
              "description": "House id (<org>-<10 hex>)",
              "title": "House Id"
            },
            "description": "House id (<org>-<10 hex>)"
          },
          {
            "name": "start",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "date-time",
              "description": "From this time (ISO 8601)",
              "title": "Start"
            },
            "description": "From this time (ISO 8601)"
          },
          {
            "name": "end",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "date-time",
              "description": "Up to this time, not included (ISO 8601). At most 24 hours after start",
              "title": "End"
            },
            "description": "Up to this time, not included (ISO 8601). At most 24 hours after start"
          },
          {
            "name": "type",
            "in": "query",
            "required": true,
            "schema": {
              "enum": [
                "presence",
                "vitals",
                "activity",
                "gait",
                "sleep",
                "quality",
                "features"
              ],
              "type": "string",
              "description": "Which part of the samples: presence, vitals, activity, gait, sleep, quality (with status, clock and transport) or features (any model's outputs)",
              "title": "Type"
            },
            "description": "Which part of the samples: presence, vitals, activity, gait, sleep, quality (with status, clock and transport) or features (any model's outputs)"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "maximum": 1000,
              "minimum": 1,
              "default": 500,
              "title": "Limit"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QueryResult"
                }
              }
            }
          },
          "202": {
            "description": "Still running: poll /v1/queries/{query_id}",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QueryResult"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/v1/tables": {
      "get": {
        "tags": [
          "Data"
        ],
        "summary": "Your tables and their columns",
        "description": "What POST /v1/queries can read. samples_<type> holds one row per 5-second message, with its seconds in a samples list (CROSS JOIN UNNEST(samples) AS t(s) gives one row per second). events_<type> holds your radars' events. Every table has day (yyyy-MM-dd, UTC, the day Zoiva received the data). Filter on it to keep queries fast.",
        "operationId": "tables_v1_tables_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/DataTable"
                  },
                  "type": "array",
                  "title": "Response Tables V1 Tables Get"
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "zoiva/data.read"
            ]
          }
        ]
      }
    },
    "/v1/queries": {
      "post": {
        "tags": [
          "Data"
        ],
        "summary": "Run your own read-only SQL over your data",
        "description": "One SELECT (or WITH ... SELECT) statement over your tables (GET /v1/tables), in Trino SQL. Only your organisation's data is there. A query that would read more than 10 GiB is cancelled (query_too_large), and at most 5 of your queries run at once (429). The first rows come back within 20 seconds. A slower query answers 202 with a query_id: fetch it from GET /v1/queries/{query_id}, and page through rows with next_token.",
        "operationId": "run_query_v1_queries_post",
        "security": [
          {
            "OAuth2": [
              "zoiva/data.read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "maximum": 1000,
              "minimum": 1,
              "description": "Rows in the first page",
              "default": 500,
              "title": "Limit"
            },
            "description": "Rows in the first page"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/QueryCreate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QueryResult"
                }
              }
            }
          },
          "202": {
            "description": "Still running: poll /v1/queries/{query_id}",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QueryResult"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/v1/queries/{query_id}": {
      "get": {
        "tags": [
          "Data"
        ],
        "summary": "The result of a data request that was still running",
        "operationId": "query_result_v1_queries__query_id__get",
        "security": [
          {
            "OAuth2": [
              "zoiva/data.read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "query_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-f0-9-]{36}$",
              "title": "Query Id"
            }
          },
          {
            "name": "next_token",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string",
                  "maxLength": 2048
                },
                {
                  "type": "null"
                }
              ],
              "title": "Next Token"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "maximum": 1000,
              "minimum": 1,
              "default": 500,
              "title": "Limit"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QueryResult"
                }
              }
            }
          },
          "202": {
            "description": "Still running: poll /v1/queries/{query_id}",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QueryResult"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/v1/webhooks": {
      "get": {
        "tags": [
          "Webhooks"
        ],
        "summary": "List webhooks",
        "operationId": "list_webhooks_v1_webhooks_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Page_Webhook_"
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "zoiva/webhooks.write"
            ]
          }
        ]
      },
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Create a webhook",
        "description": "Returns the webhook with its signing secret, shown only this once. Filters are optional: without any, every event is delivered.",
        "operationId": "create_webhook_v1_webhooks_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookCreate"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookWithSecret"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "zoiva/webhooks.write"
            ]
          }
        ]
      }
    },
    "/v1/webhooks/{webhook_id}": {
      "get": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Get a webhook, with its delivery health",
        "operationId": "get_webhook_v1_webhooks__webhook_id__get",
        "security": [
          {
            "OAuth2": [
              "zoiva/webhooks.write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "webhook_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^wh-[0-9a-f]{10}$",
              "title": "Webhook Id"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Webhook"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Delete a webhook",
        "operationId": "delete_webhook_v1_webhooks__webhook_id__delete",
        "security": [
          {
            "OAuth2": [
              "zoiva/webhooks.write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "webhook_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^wh-[0-9a-f]{10}$",
              "title": "Webhook Id"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Successful Response"
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/v1/webhooks/{webhook_id}/rotate-secret": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Replace the signing secret",
        "description": "The new secret signs every delivery from now on. The old one stops working at once.",
        "operationId": "rotate_secret_v1_webhooks__webhook_id__rotate_secret_post",
        "security": [
          {
            "OAuth2": [
              "zoiva/webhooks.write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "webhook_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^wh-[0-9a-f]{10}$",
              "title": "Webhook Id"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookWithSecret"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/v1/webhooks/{webhook_id}/test": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Send a test event",
        "description": "Delivers a webhook.test event to this webhook within seconds, signed like any other.",
        "operationId": "test_webhook_v1_webhooks__webhook_id__test_post",
        "security": [
          {
            "OAuth2": [
              "zoiva/webhooks.write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "webhook_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^wh-[0-9a-f]{10}$",
              "title": "Webhook Id"
            }
          }
        ],
        "responses": {
          "202": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true,
                  "title": "Response Test Webhook V1 Webhooks  Webhook Id  Test Post"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/v1/webhooks/{webhook_id}/frequency": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Update the delivery frequency of a webhook",
        "description": "How often data.summary events are delivered to this webhook: every 5 to 60 minutes, in steps of 5. Device events are always delivered at once. Takes effect from the next delivery.",
        "operationId": "update_webhook_frequency_v1_webhooks__webhook_id__frequency_post",
        "security": [
          {
            "OAuth2": [
              "zoiva/webhooks.write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "webhook_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^wh-[0-9a-f]{10}$",
              "title": "Webhook Id"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookFrequency"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Webhook"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/v1/clients": {
      "get": {
        "tags": [
          "API clients"
        ],
        "summary": "List your API clients",
        "operationId": "list_clients_v1_clients_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Page_Client_"
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "zoiva/clients.write"
            ]
          }
        ]
      },
      "post": {
        "tags": [
          "API clients"
        ],
        "summary": "Create an API client",
        "description": "Returns its client_secret this once. A client can have at most the scopes of the token creating it.",
        "operationId": "create_client_v1_clients_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ClientCreate"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ClientWithSecret"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "zoiva/clients.write"
            ]
          }
        ]
      }
    },
    "/v1/clients/{client_id}": {
      "get": {
        "tags": [
          "API clients"
        ],
        "summary": "Get an API client",
        "operationId": "get_client_v1_clients__client_id__get",
        "security": [
          {
            "OAuth2": [
              "zoiva/clients.write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "client_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9]{10,128}$",
              "title": "Client Id"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Client"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "API clients"
        ],
        "summary": "Delete an API client",
        "description": "It can't get new tokens from now on. You can't delete your last client that has zoiva/clients.write.",
        "operationId": "delete_client_v1_clients__client_id__delete",
        "security": [
          {
            "OAuth2": [
              "zoiva/clients.write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "client_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9]{10,128}$",
              "title": "Client Id"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Successful Response"
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "BulkDeviceUpdate": {
        "properties": {
          "device_ids": {
            "items": {
              "type": "string",
              "pattern": "^[A-Za-z0-9][A-Za-z0-9._:@-]{0,127}$"
            },
            "type": "array",
            "maxItems": 100,
            "minItems": 1,
            "title": "Device Ids",
            "description": "Up to 100 radars, each once"
          },
          "changes": {
            "$ref": "#/components/schemas/DeviceChanges"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "device_ids",
          "changes"
        ],
        "title": "BulkDeviceUpdate",
        "examples": [
          {
            "changes": {
              "summary_frequency_minutes": 15
            },
            "device_ids": [
              "goku-3f9a1c7b2e",
              "goku-7c1e9a0b3d"
            ]
          }
        ]
      },
      "BulkFailure": {
        "properties": {
          "device_id": {
            "type": "string",
            "title": "Device Id"
          },
          "error": {
            "$ref": "#/components/schemas/ErrorDetail"
          }
        },
        "type": "object",
        "required": [
          "device_id",
          "error"
        ],
        "title": "BulkFailure"
      },
      "BulkResult": {
        "properties": {
          "updated": {
            "items": {
              "$ref": "#/components/schemas/Device"
            },
            "type": "array",
            "title": "Updated"
          },
          "failed": {
            "items": {
              "$ref": "#/components/schemas/BulkFailure"
            },
            "type": "array",
            "title": "Failed"
          }
        },
        "type": "object",
        "required": [
          "updated",
          "failed"
        ],
        "title": "BulkResult",
        "description": "Each radar is updated on its own: one that fails (not found, changed at the same time) doesn't stop\nthe others.",
        "examples": [
          {
            "failed": [
              {
                "device_id": "goku-7c1e9a0b3d",
                "error": {
                  "code": "not_found",
                  "message": "radar 'goku-7c1e9a0b3d' not found"
                }
              }
            ],
            "updated": [
              {
                "added_at": "2026-10-01T09:12:44.120Z",
                "device_id": "goku-3f9a1c7b2e",
                "house_id": "goku-0a1b2c3d4e",
                "location": "above the bed",
                "metadata": {},
                "name": "Bedroom radar",
                "provisioned_at": "2026-10-01T10:02:17.903Z",
                "room": "bedroom",
                "serial_number": "ZR-2026-0017",
                "status": "provisioned",
                "summary_frequency_minutes": 15
              }
            ]
          }
        ]
      },
      "Client": {
        "properties": {
          "client_id": {
            "type": "string",
            "title": "Client Id"
          },
          "name": {
            "type": "string",
            "title": "Name"
          },
          "scopes": {
            "items": {
              "type": "string"
            },
            "type": "array",
            "title": "Scopes"
          },
          "created_at": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Created At"
          }
        },
        "type": "object",
        "required": [
          "client_id",
          "name",
          "scopes"
        ],
        "title": "Client",
        "examples": [
          {
            "client_id": "4h1k2m3n5p6q7r8s9t0v1w2x3y",
            "created_at": "2026-10-02T08:00:00.000Z",
            "name": "nightly-sync",
            "scopes": [
              "zoiva/houses.read",
              "zoiva/data.read"
            ]
          }
        ]
      },
      "ClientCreate": {
        "properties": {
          "name": {
            "type": "string",
            "pattern": "^[A-Za-z0-9][A-Za-z0-9 ._-]{0,63}$",
            "title": "Name",
            "description": "What the client is for, e.g. 'nightly-sync' (letters, digits, space . _ -)"
          },
          "scopes": {
            "items": {
              "type": "string",
              "maxLength": 64
            },
            "type": "array",
            "maxItems": 20,
            "minItems": 1,
            "title": "Scopes",
            "description": "Only what it needs, e.g. ['zoiva/data.read']. At most your own scopes"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "name",
          "scopes"
        ],
        "title": "ClientCreate",
        "examples": [
          {
            "name": "nightly-sync",
            "scopes": [
              "zoiva/houses.read",
              "zoiva/data.read"
            ]
          }
        ]
      },
      "ClientWithSecret": {
        "properties": {
          "client_id": {
            "type": "string",
            "title": "Client Id"
          },
          "name": {
            "type": "string",
            "title": "Name"
          },
          "scopes": {
            "items": {
              "type": "string"
            },
            "type": "array",
            "title": "Scopes"
          },
          "created_at": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Created At"
          },
          "client_secret": {
            "type": "string",
            "title": "Client Secret",
            "description": "Shown only now: keep it in a secret store. Lost secrets can't be recovered."
          },
          "token_url": {
            "type": "string",
            "title": "Token Url",
            "description": "Exchange client_id and client_secret here for access tokens"
          }
        },
        "type": "object",
        "required": [
          "client_id",
          "name",
          "scopes",
          "client_secret",
          "token_url"
        ],
        "title": "ClientWithSecret",
        "examples": [
          {
            "client_id": "4h1k2m3n5p6q7r8s9t0v1w2x3y",
            "client_secret": "<shown only once>",
            "created_at": "2026-10-02T08:00:00.000Z",
            "name": "nightly-sync",
            "scopes": [
              "zoiva/houses.read",
              "zoiva/data.read"
            ],
            "token_url": "https://<your-auth-domain>/oauth2/token"
          }
        ]
      },
      "DataTable": {
        "properties": {
          "name": {
            "type": "string",
            "title": "Name"
          },
          "columns": {
            "items": {
              "$ref": "#/components/schemas/TableColumn"
            },
            "type": "array",
            "title": "Columns"
          }
        },
        "type": "object",
        "required": [
          "name",
          "columns"
        ],
        "title": "DataTable",
        "examples": [
          {
            "columns": [
              {
                "name": "device_id",
                "type": "string"
              },
              {
                "name": "house_id",
                "type": "string"
              },
              {
                "name": "received_at",
                "type": "bigint"
              },
              {
                "name": "day",
                "type": "string"
              },
              {
                "name": "samples",
                "type": "array<struct<seq:bigint,captured_at:string,presence:struct<...>>>"
              }
            ],
            "name": "samples_presence"
          }
        ]
      },
      "Device": {
        "properties": {
          "device_id": {
            "type": "string",
            "title": "Device Id",
            "description": "Generated by Zoiva: <org>-<10 hex characters>, or <org>-<id_prefix>-<10 hex characters>. Used in all data."
          },
          "serial_number": {
            "type": "string",
            "title": "Serial Number"
          },
          "name": {
            "type": "string",
            "title": "Name",
            "default": ""
          },
          "house_id": {
            "type": "string",
            "title": "House Id"
          },
          "room": {
            "type": "string",
            "title": "Room"
          },
          "location": {
            "type": "string",
            "title": "Location",
            "default": ""
          },
          "metadata": {
            "additionalProperties": {
              "type": "string"
            },
            "type": "object",
            "title": "Metadata",
            "default": {}
          },
          "summary_frequency_minutes": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Summary Frequency Minutes",
            "description": "Set on the radar. null means its house's frequency"
          },
          "status": {
            "type": "string",
            "enum": [
              "claimed",
              "provisioned"
            ],
            "title": "Status",
            "description": "claimed: added, waiting for the radar to come online. provisioned: it has connected"
          },
          "added_at": {
            "type": "string",
            "title": "Added At"
          },
          "provisioned_at": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Provisioned At"
          },
          "reprovision_until": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Reprovision Until",
            "description": "Set by POST .../reprovision: the radar may provision again until this time"
          }
        },
        "type": "object",
        "required": [
          "device_id",
          "serial_number",
          "house_id",
          "room",
          "status",
          "added_at"
        ],
        "title": "Device",
        "examples": [
          {
            "added_at": "2026-10-01T09:12:44.120Z",
            "device_id": "goku-3f9a1c7b2e",
            "house_id": "goku-0a1b2c3d4e",
            "location": "above the bed",
            "name": "Bedroom radar",
            "provisioned_at": "2026-10-01T10:02:17.903Z",
            "room": "bedroom",
            "serial_number": "ZR-2026-0017",
            "status": "provisioned"
          }
        ]
      },
      "DeviceChanges": {
        "properties": {
          "room": {
            "anyOf": [
              {
                "type": "string",
                "enum": [
                  "bathroom",
                  "basement",
                  "bedroom",
                  "dining_room",
                  "garage",
                  "hallway",
                  "kitchen",
                  "living_room",
                  "office",
                  "storage_room",
                  "utility_room",
                  "other"
                ]
              },
              {
                "type": "null"
              }
            ],
            "title": "Room"
          },
          "location": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 100
              },
              {
                "type": "null"
              }
            ],
            "title": "Location"
          },
          "metadata": {
            "anyOf": [
              {
                "additionalProperties": {
                  "type": "string",
                  "maxLength": 256
                },
                "propertyNames": {
                  "maxLength": 64
                },
                "type": "object",
                "maxProperties": 20,
                "description": "Up to 20 string key/value pairs of your own"
              },
              {
                "type": "null"
              }
            ],
            "title": "Metadata",
            "description": "Replaces each radar's metadata (all of it)"
          },
          "summary_frequency_minutes": {
            "anyOf": [
              {
                "type": "integer",
                "multipleOf": 5.0,
                "maximum": 60.0,
                "minimum": 5.0
              },
              {
                "type": "null"
              }
            ],
            "title": "Summary Frequency Minutes",
            "description": "As on PATCH /v1/devices/{device_id}. null goes back to the house's frequency"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "title": "DeviceChanges",
        "description": "What a bulk update may change on each radar. Moving radars between houses is one radar at a time\n(PATCH /v1/devices/{device_id}).",
        "examples": [
          {
            "location": "by the door",
            "summary_frequency_minutes": 15
          }
        ]
      },
      "DeviceCreate": {
        "properties": {
          "serial_number": {
            "type": "string",
            "pattern": "^[A-Za-z0-9][A-Za-z0-9._:@-]{0,127}$",
            "title": "Serial Number",
            "description": "The radar's serial number, printed on its label (unique in your org)"
          },
          "name": {
            "type": "string",
            "maxLength": 100,
            "title": "Name",
            "description": "Your name for the radar, e.g. 'Bedroom radar'",
            "default": ""
          },
          "room": {
            "type": "string",
            "enum": [
              "bathroom",
              "basement",
              "bedroom",
              "dining_room",
              "garage",
              "hallway",
              "kitchen",
              "living_room",
              "office",
              "storage_room",
              "utility_room",
              "other"
            ],
            "title": "Room"
          },
          "location": {
            "type": "string",
            "maxLength": 100,
            "title": "Location",
            "description": "Where in the room, e.g. 'above the bed'",
            "default": ""
          },
          "metadata": {
            "additionalProperties": {
              "type": "string",
              "maxLength": 256
            },
            "propertyNames": {
              "maxLength": 64
            },
            "type": "object",
            "maxProperties": 20,
            "title": "Metadata",
            "description": "Up to 20 string key/value pairs of your own about the radar's deployment, e.g. {'mounting': 'wall', 'height_m': '1.8', 'deployed_with': 'bed sensor'}. Sent in every data.summary.",
            "default": {}
          },
          "id_prefix": {
            "anyOf": [
              {
                "type": "string",
                "pattern": "^[a-z0-9]{1,16}$"
              },
              {
                "type": "null"
              }
            ],
            "title": "Id Prefix",
            "description": "Optional. A short label of yours at the start of the generated id, after your org's: <org>-<prefix>-<10 hex>, e.g. goku-east-0a1b2c3d4e. 1 to 16 lowercase letters and digits ('sim' is reserved). Leave it out for <org>-<10 hex>. It's permanent and appears wherever the id does (URLs, logs, webhooks and your data), so never put personal details in it: what it says is your responsibility."
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "serial_number",
          "room"
        ],
        "title": "DeviceCreate",
        "examples": [
          {
            "location": "above the bed",
            "name": "Bedroom radar",
            "room": "bedroom",
            "serial_number": "ZR-2026-0017"
          }
        ]
      },
      "DeviceUpdate": {
        "properties": {
          "house_id": {
            "anyOf": [
              {
                "type": "string",
                "pattern": "^[A-Za-z0-9][A-Za-z0-9._:@-]{0,127}$"
              },
              {
                "type": "null"
              }
            ],
            "title": "House Id",
            "description": "Move the radar to another house"
          },
          "name": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 100
              },
              {
                "type": "null"
              }
            ],
            "title": "Name"
          },
          "room": {
            "anyOf": [
              {
                "type": "string",
                "enum": [
                  "bathroom",
                  "basement",
                  "bedroom",
                  "dining_room",
                  "garage",
                  "hallway",
                  "kitchen",
                  "living_room",
                  "office",
                  "storage_room",
                  "utility_room",
                  "other"
                ]
              },
              {
                "type": "null"
              }
            ],
            "title": "Room"
          },
          "location": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 100
              },
              {
                "type": "null"
              }
            ],
            "title": "Location"
          },
          "metadata": {
            "anyOf": [
              {
                "additionalProperties": {
                  "type": "string",
                  "maxLength": 256
                },
                "propertyNames": {
                  "maxLength": 64
                },
                "type": "object",
                "maxProperties": 20,
                "description": "Up to 20 string key/value pairs of your own"
              },
              {
                "type": "null"
              }
            ],
            "title": "Metadata",
            "description": "Replaces the radar's metadata (all of it)"
          },
          "summary_frequency_minutes": {
            "anyOf": [
              {
                "type": "integer",
                "multipleOf": 5.0,
                "maximum": 60.0,
                "minimum": 5.0
              },
              {
                "type": "null"
              }
            ],
            "title": "Summary Frequency Minutes",
            "description": "How often this radar's data.summary reach your webhooks (5 to 60 minutes, in steps of 5), overriding its house's. null: back to its house's (or each webhook's)"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "title": "DeviceUpdate",
        "examples": [
          {
            "house_id": "goku-7b2e9f1c3a",
            "room": "living_room"
          }
        ]
      },
      "ErrorDetail": {
        "properties": {
          "code": {
            "type": "string",
            "title": "Code"
          },
          "message": {
            "type": "string",
            "title": "Message"
          }
        },
        "type": "object",
        "required": [
          "code",
          "message"
        ],
        "title": "ErrorDetail"
      },
      "FeatureStats": {
        "properties": {
          "count": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Count",
            "description": "How many readings the model gave in the window"
          },
          "mean": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Mean",
            "description": "Average value"
          },
          "min": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Min",
            "description": "Lowest value"
          },
          "max": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Max",
            "description": "Highest value"
          },
          "last_text": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Last Text",
            "description": "The latest text value, for models that give labels"
          },
          "unit": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Unit",
            "description": "The values' unit, when the model gives one"
          }
        },
        "additionalProperties": true,
        "type": "object",
        "title": "FeatureStats"
      },
      "HTTPValidationError": {
        "properties": {
          "detail": {
            "items": {
              "$ref": "#/components/schemas/ValidationError"
            },
            "type": "array",
            "title": "Detail"
          }
        },
        "type": "object",
        "title": "HTTPValidationError"
      },
      "House": {
        "properties": {
          "house_id": {
            "type": "string",
            "title": "House Id",
            "description": "Generated by Zoiva: <org>-<10 hex characters>, or <org>-<id_prefix>-<10 hex characters>"
          },
          "name": {
            "type": "string",
            "title": "Name"
          },
          "metadata": {
            "additionalProperties": {
              "type": "string"
            },
            "type": "object",
            "title": "Metadata"
          },
          "device_count": {
            "type": "integer",
            "title": "Device Count",
            "description": "Radars currently in the house",
            "default": 0
          },
          "summary_frequency_minutes": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Summary Frequency Minutes",
            "description": "Set on the house. null means each webhook's frequency"
          },
          "created_at": {
            "type": "string",
            "title": "Created At"
          },
          "updated_at": {
            "type": "string",
            "title": "Updated At"
          }
        },
        "type": "object",
        "required": [
          "house_id",
          "name",
          "metadata",
          "created_at",
          "updated_at"
        ],
        "title": "House",
        "examples": [
          {
            "created_at": "2026-10-01T09:12:44.120Z",
            "device_count": 2,
            "house_id": "goku-0a1b2c3d4e",
            "metadata": {
              "care_plan": "CP-SENZU"
            },
            "name": "Son family home",
            "updated_at": "2026-10-01T09:12:44.120Z"
          }
        ]
      },
      "HouseCreate": {
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 200,
            "title": "Name",
            "description": "Your name for the house, e.g. 'Son family home'",
            "default": ""
          },
          "metadata": {
            "additionalProperties": {
              "type": "string",
              "maxLength": 256
            },
            "propertyNames": {
              "maxLength": 64
            },
            "type": "object",
            "maxProperties": 20,
            "title": "Metadata",
            "description": "Up to 20 string key/value pairs of your own",
            "default": {}
          },
          "id_prefix": {
            "anyOf": [
              {
                "type": "string",
                "pattern": "^[a-z0-9]{1,16}$"
              },
              {
                "type": "null"
              }
            ],
            "title": "Id Prefix",
            "description": "Optional. A short label of yours at the start of the generated id, after your org's: <org>-<prefix>-<10 hex>, e.g. goku-east-0a1b2c3d4e. 1 to 16 lowercase letters and digits ('sim' is reserved). Leave it out for <org>-<10 hex>. It's permanent and appears wherever the id does (URLs, logs, webhooks and your data), so never put personal details in it: what it says is your responsibility."
          }
        },
        "additionalProperties": false,
        "type": "object",
        "title": "HouseCreate",
        "examples": [
          {
            "metadata": {
              "care_plan": "CP-SENZU"
            },
            "name": "Son family home"
          }
        ]
      },
      "HouseUpdate": {
        "properties": {
          "name": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 200
              },
              {
                "type": "null"
              }
            ],
            "title": "Name"
          },
          "metadata": {
            "anyOf": [
              {
                "additionalProperties": {
                  "type": "string",
                  "maxLength": 256
                },
                "propertyNames": {
                  "maxLength": 64
                },
                "type": "object",
                "maxProperties": 20,
                "description": "Up to 20 string key/value pairs of your own"
              },
              {
                "type": "null"
              }
            ],
            "title": "Metadata"
          },
          "summary_frequency_minutes": {
            "anyOf": [
              {
                "type": "integer",
                "multipleOf": 5.0,
                "maximum": 60.0,
                "minimum": 5.0
              },
              {
                "type": "null"
              }
            ],
            "title": "Summary Frequency Minutes",
            "description": "How often this house's radars' data.summary reach your webhooks (5 to 60 minutes, in steps of 5). It covers every radar in the house, new ones too. A radar's own setting overrides it. null goes back to each webhook's frequency"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "title": "HouseUpdate",
        "examples": [
          {
            "name": "Son family home, upstairs"
          }
        ]
      },
      "Page_Client_": {
        "properties": {
          "items": {
            "items": {
              "$ref": "#/components/schemas/Client"
            },
            "type": "array",
            "title": "Items"
          },
          "next_token": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Next Token",
            "description": "Pass back as ?next_token= for the next page"
          }
        },
        "type": "object",
        "required": [
          "items"
        ],
        "title": "Page[Client]"
      },
      "Page_Device_": {
        "properties": {
          "items": {
            "items": {
              "$ref": "#/components/schemas/Device"
            },
            "type": "array",
            "title": "Items"
          },
          "next_token": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Next Token",
            "description": "Pass back as ?next_token= for the next page"
          }
        },
        "type": "object",
        "required": [
          "items"
        ],
        "title": "Page[Device]"
      },
      "Page_House_": {
        "properties": {
          "items": {
            "items": {
              "$ref": "#/components/schemas/House"
            },
            "type": "array",
            "title": "Items"
          },
          "next_token": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Next Token",
            "description": "Pass back as ?next_token= for the next page"
          }
        },
        "type": "object",
        "required": [
          "items"
        ],
        "title": "Page[House]"
      },
      "Page_SampleBatch_": {
        "properties": {
          "items": {
            "items": {
              "$ref": "#/components/schemas/SampleBatch"
            },
            "type": "array",
            "title": "Items"
          },
          "next_token": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Next Token",
            "description": "Pass back as ?next_token= for the next page"
          }
        },
        "type": "object",
        "required": [
          "items"
        ],
        "title": "Page[SampleBatch]"
      },
      "Page_Summary_": {
        "properties": {
          "items": {
            "items": {
              "$ref": "#/components/schemas/Summary"
            },
            "type": "array",
            "title": "Items"
          },
          "next_token": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Next Token",
            "description": "Pass back as ?next_token= for the next page"
          }
        },
        "type": "object",
        "required": [
          "items"
        ],
        "title": "Page[Summary]"
      },
      "Page_Webhook_": {
        "properties": {
          "items": {
            "items": {
              "$ref": "#/components/schemas/Webhook"
            },
            "type": "array",
            "title": "Items"
          },
          "next_token": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Next Token",
            "description": "Pass back as ?next_token= for the next page"
          }
        },
        "type": "object",
        "required": [
          "items"
        ],
        "title": "Page[Webhook]"
      },
      "QueryCreate": {
        "properties": {
          "sql": {
            "type": "string",
            "maxLength": 20000,
            "minLength": 1,
            "title": "Sql",
            "description": "One read-only statement (SELECT or WITH) over your tables (GET /v1/tables), e.g. SELECT device_id, count(*) FROM samples_presence WHERE day = '2026-10-27' GROUP BY 1. Filter on day (yyyy-MM-dd, UTC, when the data arrived) to read little: a query reading more than your limit is cancelled."
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "sql"
        ],
        "title": "QueryCreate",
        "examples": [
          {
            "sql": "SELECT device_id, count(*) FROM samples_presence WHERE day = '2026-10-27' GROUP BY 1"
          }
        ]
      },
      "QueryResult": {
        "properties": {
          "query_id": {
            "type": "string",
            "title": "Query Id"
          },
          "status": {
            "type": "string",
            "enum": [
              "running",
              "succeeded"
            ],
            "title": "Status"
          },
          "rows": {
            "items": {
              "additionalProperties": true,
              "type": "object"
            },
            "type": "array",
            "title": "Rows",
            "default": []
          },
          "next_token": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Next Token"
          }
        },
        "type": "object",
        "required": [
          "query_id",
          "status"
        ],
        "title": "QueryResult",
        "description": "Data from your tables. If it isn't ready within the request, you get status \"running\"\n(HTTP 202) and fetch it later from GET /v1/queries/{query_id}.",
        "examples": [
          {
            "query_id": "3b9f2c1e-7a4d-4e8b-9c0f-5d6e7f8a9b0c",
            "rows": [
              {
                "device_id": "goku-3f9a1c7b2e",
                "house_id": "goku-0a1b2c3d4e",
                "minute": "2026-10-01T10:27:00Z",
                "presence_ratio": 1.0,
                "sample_count": 60,
                "target_count_max": 1
              }
            ],
            "status": "succeeded"
          },
          {
            "query_id": "3b9f2c1e-7a4d-4e8b-9c0f-5d6e7f8a9b0c",
            "rows": [],
            "status": "running"
          }
        ]
      },
      "RateStats": {
        "properties": {
          "mean": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Mean",
            "description": "Average over the minute. null when there were no readings"
          },
          "min": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Min",
            "description": "Lowest reading in the minute"
          },
          "max": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Max",
            "description": "Highest reading in the minute"
          },
          "readings": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Readings",
            "description": "How many one-second readings there were, leaving out those below min_confidence"
          }
        },
        "additionalProperties": true,
        "type": "object",
        "title": "RateStats"
      },
      "SampleBatch": {
        "properties": {
          "device_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Device Id",
            "description": "The radar (in a house's recent data)"
          },
          "received_at": {
            "type": "string",
            "title": "Received At",
            "description": "When Zoiva received the batch (ISO 8601, UTC)"
          },
          "context_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Context Id"
          },
          "message": {
            "anyOf": [
              {
                "additionalProperties": true,
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "title": "Message",
            "description": "Five seconds of samples' essentials, as a schema 2.2 feature_batch: for each sample, seq, captured_at, presence (state, target_count, primary_target.range), activity.state, vitals (heart_rate, respiratory_rate), quality.data_completeness.percentage, status.measurement_status. Every field the radar sent is in the archive (/samples)."
          }
        },
        "type": "object",
        "required": [
          "received_at",
          "message"
        ],
        "title": "SampleBatch",
        "examples": [
          {
            "context_id": "ctx-1a2b3c4d5e6f",
            "device_id": "goku-3f9a1c7b2e",
            "message": {
              "context_id": "ctx-1a2b3c4d5e6f",
              "message_type": "feature_batch",
              "samples": [
                {
                  "activity": {
                    "state": "stationary"
                  },
                  "captured_at": "2026-10-06T11:18:21.000Z",
                  "presence": {
                    "primary_target": {
                      "range": 1.84
                    },
                    "state": "present",
                    "target_count": 1
                  },
                  "quality": {
                    "data_completeness": {
                      "percentage": 100.0
                    }
                  },
                  "seq": 1204,
                  "status": {
                    "measurement_status": "valid"
                  },
                  "vitals": {
                    "available": false
                  }
                }
              ],
              "schema_version": "2.2"
            },
            "received_at": "2026-10-06T11:18:25.120Z"
          }
        ]
      },
      "Summary": {
        "properties": {
          "schema_version": {
            "type": "string",
            "title": "Schema Version",
            "description": "The summary format's version, e.g. 0.1"
          },
          "message_type": {
            "type": "string",
            "const": "data_summary",
            "title": "Message Type",
            "description": "Always data_summary",
            "default": "data_summary"
          },
          "event_type": {
            "type": "string",
            "const": "data.summary",
            "title": "Event Type",
            "description": "Always data.summary",
            "default": "data.summary"
          },
          "event_id": {
            "type": "string",
            "title": "Event Id",
            "description": "summary-<device_id>-<window start>. The same every time it's sent, so repeats can be ignored"
          },
          "org_id": {
            "type": "string",
            "title": "Org Id",
            "description": "Your organisation's id"
          },
          "device_id": {
            "type": "string",
            "title": "Device Id",
            "description": "The radar the summary is about"
          },
          "house_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "House Id",
            "description": "The house the radar was in, null if it was in none"
          },
          "window": {
            "$ref": "#/components/schemas/SummaryWindow",
            "description": "The five minutes summarised"
          },
          "generated_at": {
            "type": "string",
            "title": "Generated At",
            "description": "When Zoiva made the summary (ISO 8601, UTC)"
          },
          "device": {
            "$ref": "#/components/schemas/SummaryDevice",
            "description": "The radar, as registered when the window was summarised"
          },
          "coverage": {
            "$ref": "#/components/schemas/SummaryCoverage",
            "description": "How much data the window is based on"
          },
          "presence": {
            "$ref": "#/components/schemas/SummaryPresence",
            "description": "Whether someone was there, and when"
          },
          "range_m": {
            "$ref": "#/components/schemas/SummaryRange",
            "description": "How far from the radar the person was, while present"
          },
          "activity": {
            "$ref": "#/components/schemas/SummaryActivity",
            "description": "Moving and still"
          },
          "gait": {
            "$ref": "#/components/schemas/SummaryGait",
            "description": "Walking"
          },
          "sleep": {
            "$ref": "#/components/schemas/SummarySleep",
            "description": "Sleep states"
          },
          "vitals": {
            "$ref": "#/components/schemas/SummaryVitals",
            "description": "Heart and breathing rate, minute by minute"
          },
          "features": {
            "additionalProperties": {
              "$ref": "#/components/schemas/FeatureStats"
            },
            "type": "object",
            "title": "Features",
            "description": "Other model outputs over the window, by the model output's name",
            "default": {}
          },
          "settings": {
            "$ref": "#/components/schemas/SummarySettings",
            "description": "How the summary was made"
          }
        },
        "additionalProperties": true,
        "type": "object",
        "required": [
          "schema_version",
          "event_id",
          "org_id",
          "device_id",
          "house_id",
          "window",
          "generated_at",
          "device",
          "coverage",
          "presence",
          "range_m",
          "activity",
          "gait",
          "sleep",
          "vitals",
          "settings"
        ],
        "title": "Summary",
        "description": "One radar's five minutes: what the data.summary webhook carries, exactly.",
        "examples": [
          {
            "activity": {
              "moving_seconds": 41,
              "stationary_seconds": 221
            },
            "coverage": {
              "completeness": 1.0,
              "data_completeness_pct_mean": 99.4,
              "seconds_received": 300,
              "valid_seconds": 297
            },
            "device": {
              "devices_in_house": 2,
              "location": "above the bed",
              "metadata": {
                "deployed_with": "bed sensor",
                "height_m": "1.8",
                "mounting": "wall"
              },
              "name": "Bedroom radar",
              "other_device_ids": [
                "goku-7c1e9a0b3d"
              ],
              "room": "bedroom",
              "serial_number": "8e2f1c0a5b6d7e90"
            },
            "device_id": "goku-3f9a1c7b2e",
            "event_id": "summary-goku-3f9a1c7b2e-2026-11-03T14:30:00Z",
            "event_type": "data.summary",
            "features": {},
            "gait": {
              "cadence_spm_mean": 96.4,
              "walking_seconds": 18,
              "walking_speed_mps_max": 0.94,
              "walking_speed_mps_mean": 0.612
            },
            "generated_at": "2026-11-03T14:40:06.312Z",
            "house_id": "goku-0a1b2c3d4e",
            "message_type": "data_summary",
            "org_id": "goku",
            "presence": {
              "changes": 2,
              "first_present_at": "2026-11-03T14:30:04.000Z",
              "last_present_at": "2026-11-03T14:34:59.000Z",
              "presence_ratio": 0.873,
              "present_seconds": 262,
              "target_count_max": 1,
              "target_count_mean": 0.873
            },
            "range_m": {
              "max": 2.41,
              "mean": 1.634,
              "median": 1.52,
              "min": 1.08,
              "seconds": 262,
              "stddev": 0.292
            },
            "schema_version": "0.1",
            "settings": {
              "min_confidence": 0.6
            },
            "sleep": {
              "state_seconds": {}
            },
            "vitals": {
              "interval_seconds": 60,
              "minutes": [
                {
                  "breathing_rate_bpm": {
                    "max": 17.0,
                    "mean": 15.2,
                    "min": 14.0,
                    "readings": 57
                  },
                  "heart_rate_bpm": {
                    "max": 75.0,
                    "mean": 71.4,
                    "min": 68.0,
                    "readings": 58
                  },
                  "start": "2026-11-03T14:30:00Z"
                },
                {
                  "breathing_rate_bpm": {
                    "max": 16.0,
                    "mean": 15.0,
                    "min": 13.0,
                    "readings": 59
                  },
                  "heart_rate_bpm": {
                    "max": 76.0,
                    "mean": 72.0,
                    "min": 69.0,
                    "readings": 59
                  },
                  "start": "2026-11-03T14:31:00Z"
                },
                {
                  "breathing_rate_bpm": {
                    "readings": 0
                  },
                  "heart_rate_bpm": {
                    "readings": 0
                  },
                  "start": "2026-11-03T14:33:00Z"
                }
              ]
            },
            "window": {
              "end": "2026-11-03T14:35:00Z",
              "seconds": 300,
              "start": "2026-11-03T14:30:00Z"
            }
          }
        ]
      },
      "SummaryActivity": {
        "properties": {
          "moving_seconds": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Moving Seconds",
            "description": "Seconds in which the person was moving"
          },
          "stationary_seconds": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Stationary Seconds",
            "description": "Seconds in which the person was still"
          }
        },
        "additionalProperties": true,
        "type": "object",
        "title": "SummaryActivity"
      },
      "SummaryCoverage": {
        "properties": {
          "seconds_received": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Seconds Received",
            "description": "Seconds of data received, out of 300"
          },
          "valid_seconds": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Valid Seconds",
            "description": "Seconds the radar marked as a valid measurement"
          },
          "completeness": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Completeness",
            "description": "seconds_received divided by 300, from 0 to 1"
          },
          "data_completeness_pct_mean": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Data Completeness Pct Mean",
            "description": "On average, the percentage of the radar's frames that each second was made from"
          },
          "quality_mean": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Quality Mean",
            "description": "Average signal quality, from 0 to 1. null when the radar doesn't report it"
          }
        },
        "additionalProperties": true,
        "type": "object",
        "title": "SummaryCoverage"
      },
      "SummaryDevice": {
        "properties": {
          "name": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Name",
            "description": "Your name for the radar"
          },
          "serial_number": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Serial Number",
            "description": "The serial number on the radar's label"
          },
          "room": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Room",
            "description": "The room you gave it, e.g. bedroom"
          },
          "location": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Location",
            "description": "Where in the room, as you described it"
          },
          "metadata": {
            "anyOf": [
              {
                "additionalProperties": {
                  "type": "string"
                },
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "title": "Metadata",
            "description": "Your metadata for this radar, as it was then"
          },
          "devices_in_house": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Devices In House",
            "description": "How many radars were in its house, itself included"
          },
          "other_device_ids": {
            "anyOf": [
              {
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "title": "Other Device Ids",
            "description": "The other radars in its house"
          }
        },
        "additionalProperties": true,
        "type": "object",
        "title": "SummaryDevice"
      },
      "SummaryGait": {
        "properties": {
          "walking_seconds": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Walking Seconds",
            "description": "Seconds in which walking was detected"
          },
          "cadence_spm_mean": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Cadence Spm Mean",
            "description": "Average cadence while walking, in steps per minute"
          },
          "walking_speed_mps_mean": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Walking Speed Mps Mean",
            "description": "Average walking speed, in metres per second"
          },
          "walking_speed_mps_max": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Walking Speed Mps Max",
            "description": "Fastest walking speed, in metres per second"
          }
        },
        "additionalProperties": true,
        "type": "object",
        "title": "SummaryGait"
      },
      "SummaryPresence": {
        "properties": {
          "present_seconds": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Present Seconds",
            "description": "Seconds in which someone was detected"
          },
          "presence_ratio": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Presence Ratio",
            "description": "present_seconds divided by seconds_received, 0 to 1"
          },
          "changes": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Changes",
            "description": "How many times presence started or stopped"
          },
          "first_present_at": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "First Present At",
            "description": "The first moment someone was detected (ISO 8601)"
          },
          "last_present_at": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Last Present At",
            "description": "The last moment someone was detected (ISO 8601)"
          },
          "target_count_max": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Target Count Max",
            "description": "The most people detected at the same time"
          },
          "target_count_mean": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Target Count Mean",
            "description": "The average number of people detected"
          }
        },
        "additionalProperties": true,
        "type": "object",
        "title": "SummaryPresence"
      },
      "SummaryRange": {
        "properties": {
          "seconds": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Seconds",
            "description": "Seconds with a distance, that is, while someone was present"
          },
          "min": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Min",
            "description": "Nearest distance from the radar, in metres"
          },
          "max": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Max",
            "description": "Furthest distance from the radar, in metres"
          },
          "mean": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Mean",
            "description": "Average distance, in metres"
          },
          "median": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Median",
            "description": "Median distance, in metres"
          },
          "stddev": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Stddev",
            "description": "How much the distance varied (standard deviation), in metres"
          }
        },
        "additionalProperties": true,
        "type": "object",
        "title": "SummaryRange"
      },
      "SummarySettings": {
        "properties": {
          "min_confidence": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Min Confidence",
            "description": "Readings with a confidence below this (0 to 1) were left out of the summary"
          }
        },
        "additionalProperties": true,
        "type": "object",
        "title": "SummarySettings"
      },
      "SummarySleep": {
        "properties": {
          "state_seconds": {
            "anyOf": [
              {
                "additionalProperties": {
                  "type": "integer"
                },
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "title": "State Seconds",
            "description": "Seconds in each sleep state the radar reported, by state name"
          },
          "last_state": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Last State",
            "description": "The latest sleep state in the window"
          }
        },
        "additionalProperties": true,
        "type": "object",
        "title": "SummarySleep"
      },
      "SummaryVitals": {
        "properties": {
          "interval_seconds": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Interval Seconds",
            "description": "Length of each entry in minutes: 60 seconds"
          },
          "minutes": {
            "anyOf": [
              {
                "items": {
                  "$ref": "#/components/schemas/VitalsMinute"
                },
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "title": "Minutes",
            "description": "Five entries, one for each minute of the window"
          }
        },
        "additionalProperties": true,
        "type": "object",
        "title": "SummaryVitals"
      },
      "SummaryWindow": {
        "properties": {
          "start": {
            "type": "string",
            "title": "Start",
            "description": "When the window starts (ISO 8601, UTC, on a 5-minute boundary)"
          },
          "end": {
            "type": "string",
            "title": "End",
            "description": "When it ends, 5 minutes later"
          },
          "seconds": {
            "type": "integer",
            "title": "Seconds",
            "description": "Its length in seconds: 300",
            "default": 300
          }
        },
        "type": "object",
        "required": [
          "start",
          "end"
        ],
        "title": "SummaryWindow"
      },
      "TableColumn": {
        "properties": {
          "name": {
            "type": "string",
            "title": "Name"
          },
          "type": {
            "type": "string",
            "title": "Type"
          }
        },
        "type": "object",
        "required": [
          "name",
          "type"
        ],
        "title": "TableColumn"
      },
      "ValidationError": {
        "properties": {
          "loc": {
            "items": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "integer"
                }
              ]
            },
            "type": "array",
            "title": "Location"
          },
          "msg": {
            "type": "string",
            "title": "Message"
          },
          "type": {
            "type": "string",
            "title": "Error Type"
          },
          "input": {
            "title": "Input"
          },
          "ctx": {
            "type": "object",
            "title": "Context"
          }
        },
        "type": "object",
        "required": [
          "loc",
          "msg",
          "type"
        ],
        "title": "ValidationError"
      },
      "VitalsMinute": {
        "properties": {
          "start": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Start",
            "description": "The minute's start (ISO 8601, UTC)"
          },
          "heart_rate_bpm": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/RateStats"
              },
              {
                "type": "null"
              }
            ],
            "description": "Heart rate, in beats per minute"
          },
          "breathing_rate_bpm": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/RateStats"
              },
              {
                "type": "null"
              }
            ],
            "description": "Breathing rate, in breaths per minute"
          }
        },
        "additionalProperties": true,
        "type": "object",
        "title": "VitalsMinute"
      },
      "Webhook": {
        "properties": {
          "webhook_id": {
            "type": "string",
            "title": "Webhook Id"
          },
          "url": {
            "type": "string",
            "title": "Url"
          },
          "description": {
            "type": "string",
            "title": "Description",
            "default": ""
          },
          "event_types": {
            "anyOf": [
              {
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "title": "Event Types"
          },
          "device_ids": {
            "anyOf": [
              {
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "title": "Device Ids"
          },
          "house_ids": {
            "anyOf": [
              {
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "title": "House Ids"
          },
          "created_at": {
            "type": "string",
            "title": "Created At"
          },
          "consecutive_failures": {
            "type": "integer",
            "title": "Consecutive Failures",
            "description": "Deliveries that failed in a row (0 when healthy)",
            "default": 0
          },
          "last_failure_at": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Last Failure At"
          },
          "last_failure": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Last Failure",
            "description": "Why the last delivery failed, e.g. 'HTTP 500'"
          },
          "frequency_minutes": {
            "type": "integer",
            "title": "Frequency Minutes",
            "description": "How often data.summary events are delivered, in minutes (device events: at once)",
            "default": 5
          }
        },
        "type": "object",
        "required": [
          "webhook_id",
          "url",
          "created_at"
        ],
        "title": "Webhook",
        "examples": [
          {
            "consecutive_failures": 0,
            "created_at": "2026-10-02T08:05:00.000Z",
            "description": "Care dashboard",
            "event_types": [
              "data.summary",
              "device.offline"
            ],
            "frequency_minutes": 5,
            "url": "https://hooks.capsule-corp.example/zoiva",
            "webhook_id": "wh-4c1d9e0a7b"
          }
        ]
      },
      "WebhookCreate": {
        "properties": {
          "url": {
            "type": "string",
            "maxLength": 2048,
            "title": "Url",
            "description": "Your HTTPS endpoint, e.g. https://hooks.capsule-corp.example/zoiva"
          },
          "description": {
            "type": "string",
            "maxLength": 200,
            "title": "Description",
            "default": ""
          },
          "event_types": {
            "anyOf": [
              {
                "items": {
                  "type": "string",
                  "maxLength": 64
                },
                "type": "array",
                "maxItems": 50
              },
              {
                "type": "null"
              }
            ],
            "title": "Event Types",
            "description": "Only these event types, e.g. ['data.summary', 'device.offline']. Every event if unset"
          },
          "device_ids": {
            "anyOf": [
              {
                "items": {
                  "type": "string",
                  "pattern": "^[A-Za-z0-9][A-Za-z0-9._:@-]{0,127}$"
                },
                "type": "array",
                "maxItems": 100
              },
              {
                "type": "null"
              }
            ],
            "title": "Device Ids",
            "description": "Only events of these radars"
          },
          "house_ids": {
            "anyOf": [
              {
                "items": {
                  "type": "string",
                  "pattern": "^[A-Za-z0-9][A-Za-z0-9._:@-]{0,127}$"
                },
                "type": "array",
                "maxItems": 100
              },
              {
                "type": "null"
              }
            ],
            "title": "House Ids",
            "description": "Only events of these houses"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "url"
        ],
        "title": "WebhookCreate",
        "examples": [
          {
            "description": "Care dashboard",
            "event_types": [
              "data.summary",
              "device.offline"
            ],
            "url": "https://hooks.capsule-corp.example/zoiva"
          }
        ]
      },
      "WebhookFrequency": {
        "properties": {
          "frequency_minutes": {
            "type": "integer",
            "multipleOf": 5.0,
            "maximum": 60.0,
            "minimum": 5.0,
            "title": "Frequency Minutes",
            "description": "How often Zoiva delivers data.summary events to this webhook (in minutes). Device events (device.*, presence_changed) are always delivered immediately.",
            "default": 5
          }
        },
        "additionalProperties": false,
        "type": "object",
        "title": "WebhookFrequency",
        "examples": [
          {
            "frequency_minutes": 15
          }
        ]
      },
      "WebhookWithSecret": {
        "properties": {
          "webhook_id": {
            "type": "string",
            "title": "Webhook Id"
          },
          "url": {
            "type": "string",
            "title": "Url"
          },
          "description": {
            "type": "string",
            "title": "Description",
            "default": ""
          },
          "event_types": {
            "anyOf": [
              {
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "title": "Event Types"
          },
          "device_ids": {
            "anyOf": [
              {
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "title": "Device Ids"
          },
          "house_ids": {
            "anyOf": [
              {
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "title": "House Ids"
          },
          "created_at": {
            "type": "string",
            "title": "Created At"
          },
          "consecutive_failures": {
            "type": "integer",
            "title": "Consecutive Failures",
            "description": "Deliveries that failed in a row (0 when healthy)",
            "default": 0
          },
          "last_failure_at": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Last Failure At"
          },
          "last_failure": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Last Failure",
            "description": "Why the last delivery failed, e.g. 'HTTP 500'"
          },
          "frequency_minutes": {
            "type": "integer",
            "title": "Frequency Minutes",
            "description": "How often data.summary events are delivered, in minutes (device events: at once)",
            "default": 5
          },
          "secret": {
            "type": "string",
            "title": "Secret",
            "description": "Signs every delivery (Zoiva-Signature header). Shown only now: keep it in a secret store"
          }
        },
        "type": "object",
        "required": [
          "webhook_id",
          "url",
          "created_at",
          "secret"
        ],
        "title": "WebhookWithSecret",
        "examples": [
          {
            "consecutive_failures": 0,
            "created_at": "2026-10-02T08:05:00.000Z",
            "description": "Care dashboard",
            "event_types": [
              "data.summary",
              "device.offline"
            ],
            "frequency_minutes": 5,
            "secret": "whsec_<shown only once>",
            "url": "https://hooks.capsule-corp.example/zoiva",
            "webhook_id": "wh-4c1d9e0a7b"
          }
        ]
      }
    },
    "securitySchemes": {
      "OAuth2": {
        "type": "oauth2",
        "flows": {
          "clientCredentials": {
            "scopes": {
              "zoiva/houses.read": "List and read houses",
              "zoiva/houses.write": "Create, change and delete houses",
              "zoiva/devices.read": "List and read radars",
              "zoiva/devices.write": "Add, change, move and remove radars",
              "zoiva/data.read": "Read radar data (recent samples, metrics, archived samples)",
              "zoiva/webhooks.write": "Manage webhooks (event notifications)",
              "zoiva/clients.write": "Manage your organisation's API clients (credentials): admin systems only",
              "zoiva/simulate": "Fake radars (test stages only)"
            },
            "tokenUrl": "https://<your-auth-domain>/oauth2/token"
          }
        }
      }
    }
  },
  "tags": [
    {
      "name": "Houses",
      "description": "A house is a home or any other place with radars in it. Create a house, then add radars to it by serial number. A house's data is what its radars sent while they were in it."
    },
    {
      "name": "Radars",
      "description": "Your radars. A radar is added to a house by the serial number on its label (status `claimed`), and becomes `provisioned` once it has connected. Move it by changing its `house_id`. Removing it disconnects it at once. Change up to 100 radars at once with `PATCH /v1/devices`."
    },
    {
      "name": "Data",
      "description": "What your radars measure. Every 5 minutes, each radar's `data.summary` reaches your webhooks, and `/summaries` serves the ones already sent (30 days). `/recent` serves the last 3 days second by second, `/samples` every second ever recorded, and `/queries` runs your own SQL over your data. `/recent` and `/samples` work for one radar (`/devices/{device_id}/...`) or a whole house (`/houses/{house_id}/...`). Times are ISO 8601, UTC."
    },
    {
      "name": "Webhooks",
      "description": "Get events POSTed to your HTTPS endpoint: each radar's `data.summary` every 5 minutes (or less often: see `frequency`), and as they happen, radars connecting, dropping off and changing, and presence. Every delivery is signed with the webhook's secret."
    },
    {
      "name": "API clients",
      "description": "Your organisation's credentials: at most 3 clients, one per system, each with only the scopes it needs. Secrets are shown once, when a client is created."
    },
    {
      "name": "Health",
      "description": "Is the API up? No token needed."
    }
  ]
}
